Decompiled source of FuelFromChests v0.2.1

FuelFromChests.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
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("FuelFromChests")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1")]
[assembly: AssemblyProduct("FuelFromChests")]
[assembly: AssemblyTitle("FuelFromChests")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FuelFromChests
{
	internal static class Chests
	{
		private static readonly HashSet<Container> All = new HashSet<Container>();

		private static readonly FieldRef<Container, ZNetView> ViewField = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private static readonly Func<Container, long, bool> Access = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Container, bool> Load = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true);

		internal static ZNetView View(Container chest)
		{
			return ViewField.Invoke(chest);
		}

		internal static void Track(Container chest)
		{
			if (!Object.op_Implicit((Object)(object)chest) || !Object.op_Implicit((Object)(object)View(chest)) || !View(chest).IsValid())
			{
				return;
			}
			All.RemoveWhere((Container c) => !Object.op_Implicit((Object)(object)c));
			All.Add(chest);
			Ownership.Register(View(chest), delegate(Player player)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				if (Eligible(chest, player, checkWard: false))
				{
					Vector3 val = ((Component)player).transform.position - ((Component)chest).transform.position;
					return (double)((Vector3)(ref val)).sqrMagnitude <= Math.Pow(Plugin.Range.Value + player.m_maxInteractDistance + 3f, 2.0);
				}
				return false;
			});
		}

		internal static void Forget(Container chest)
		{
			All.Remove(chest);
		}

		internal static bool Eligible(Container chest, Player player, bool checkWard = true)
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)chest) || !Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead() || ((Character)player).IsTeleporting())
			{
				return false;
			}
			ZNetView val = View(chest);
			Piece component = ((Component)chest).GetComponent<Piece>();
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !Object.op_Implicit((Object)(object)component) || !component.IsPlacedByPlayer() || Object.op_Implicit((Object)(object)((Component)chest).GetComponent<TombStone>()) || chest.GetInventory() == null || chest.IsInUse() || val.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0 || (Object.op_Implicit((Object)(object)chest.m_wagon) && chest.m_wagon.InUse()) || val.GetZDO().GetString("seneal.nocraft", "") == "1" || !Access(chest, player.GetPlayerID()))
			{
				return false;
			}
			if (checkWard && chest.m_checkGuardStone)
			{
				return PrivateArea.CheckAccess(((Component)chest).transform.position, 0f, false, false);
			}
			return true;
		}

		internal static List<Container> Nearby(Station station, Player player)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			All.RemoveWhere((Container c) => !Object.op_Implicit((Object)(object)c));
			List<Container> list = new List<Container>();
			float num = Plugin.Range.Value * Plugin.Range.Value;
			foreach (Container item in All)
			{
				if (Eligible(item, player))
				{
					Vector3 val = ((Component)item).transform.position - station.Position;
					if (((Vector3)(ref val)).sqrMagnitude <= num)
					{
						list.Add(item);
					}
				}
			}
			list.Sort(delegate(Container a, Container b)
			{
				//IL_0006: 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_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: 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_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val2 = ((Component)a).transform.position - station.Position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				val2 = ((Component)b).transform.position - station.Position;
				return sqrMagnitude.CompareTo(((Vector3)(ref val2)).sqrMagnitude);
			});
			return list;
		}

		internal static bool Ready(Container chest, Station station, Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (Eligible(chest, player) && View(chest).IsOwner())
			{
				Vector3 val = ((Component)chest).transform.position - station.Position;
				return ((Vector3)(ref val)).sqrMagnitude <= Plugin.Range.Value * Plugin.Range.Value;
			}
			return false;
		}

		internal static void Refresh(Container chest)
		{
			Load(chest);
		}
	}
	internal sealed class InventoryUnit : IResourceUnit
	{
		private static readonly Action<Inventory, bool, bool> Changed = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), (object)null, true);

		private readonly Inventory inventory;

		private readonly ItemData item;

		private readonly Func<bool> permitted;

		private readonly int stack;

		private readonly int index;

		private bool attempted;

		public bool IsDebited
		{
			get
			{
				if (attempted)
				{
					if (stack != 1)
					{
						return item.m_stack == stack - 1;
					}
					return !inventory.ContainsItem(item);
				}
				return false;
			}
		}

		internal InventoryUnit(Inventory source, ItemData resource, Func<bool> canDebit)
		{
			inventory = source;
			item = resource;
			permitted = canDebit;
			stack = item.m_stack;
			index = inventory.GetAllItems().IndexOf(item);
		}

		public bool Debit()
		{
			if (!permitted() || index < 0 || stack < 1 || item.m_stack != stack || !inventory.ContainsItem(item))
			{
				return false;
			}
			attempted = true;
			return inventory.RemoveItem(item, 1);
		}

		public void Refund()
		{
			if (IsDebited)
			{
				item.m_stack = stack;
				if (!inventory.ContainsItem(item))
				{
					inventory.GetAllItems().Insert(Math.Min(index, inventory.GetAllItems().Count), item);
				}
				attempted = false;
				Changed(inventory, arg2: false, arg3: false);
			}
		}
	}
	internal static class HoverHint
	{
		internal static string Append(Station station, string original)
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)Plugin.Instance) || !Plugin.Active.Value || !(station.IsOre ? Plugin.Ore.Value : Plugin.Fuel.Value) || !station.Refillable)
			{
				return original;
			}
			string text = Plugin.Selected(station);
			if (text == null)
			{
				string text2 = null;
				foreach (ItemDrop item in station.Inputs())
				{
					if (Transfer.Allowed(((Object)((Component)item).gameObject).name, Plugin.Excluded.Value))
					{
						if (text2 != null)
						{
							text2 = null;
							break;
						}
						text2 = ((Object)((Component)item).gameObject).name;
					}
				}
				text = text2;
			}
			string text3 = Localization.instance.Localize("$KEY_Use");
			return original + "\n<color=#d4b16d>" + Texts.Selected + ": " + station.ResourceName(text) + "</color>\n[<color=yellow><b>" + Texts.Key(Plugin.SelectKey.Value) + " + " + text3 + "</b></color>] " + Texts.ChooseResource + "\n[<color=yellow><b>" + Texts.Key(Plugin.FillKey.Value) + " + " + text3 + "</b></color>] " + Texts.Fill;
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnHoverAddOre")]
	internal static class SmelterOreHint
	{
		private static void Postfix(Smelter __instance, ref string __result)
		{
			__result = HoverHint.Append(new Station((Component)(object)__instance, isOre: true), __result);
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnHoverAddFuel")]
	internal static class SmelterFuelHint
	{
		private static void Postfix(Smelter __instance, ref string __result)
		{
			__result = HoverHint.Append(new Station((Component)(object)__instance), __result);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "OnHoverFuelSwitch")]
	internal static class CookingFuelHint
	{
		private static void Postfix(CookingStation __instance, ref string __result)
		{
			__result = HoverHint.Append(new Station((Component)(object)__instance), __result);
		}
	}
	[HarmonyPatch(typeof(Fireplace), "GetHoverText")]
	internal static class FireplaceHint
	{
		private static void Postfix(Fireplace __instance, ref string __result)
		{
			__result = HoverHint.Append(new Station((Component)(object)__instance), __result);
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class PlayerPickerInput
	{
		private static bool Prefix(ref bool __result)
		{
			if (!Plugin.BlockInput)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerController), "TakeInput")]
	internal static class ControllerPickerInput
	{
		private static bool Prefix(ref bool __result)
		{
			if (!Plugin.BlockInput)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
	internal static class PickerCursor
	{
		private static bool Prefix()
		{
			if (!Plugin.BlockInput)
			{
				return true;
			}
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
			return false;
		}
	}
	[HarmonyPatch(typeof(Menu), "Update")]
	internal static class PickerEscape
	{
		private static bool Prefix()
		{
			if (!Plugin.BlockInput || (Object.op_Implicit((Object)(object)Game.instance) && Game.instance.IsShuttingDown()))
			{
				return true;
			}
			if (!ZInput.GetKeyDown((KeyCode)27, true) && (!ZInput.GetButtonDown("JoyMenu") || (ZInput.GetButton("JoyLTrigger") && ZInput.GetButton("JoyLBumper")) || ZInput.GetButton("JoyAltKeys")))
			{
				return true;
			}
			ResourcePicker.Close();
			return false;
		}
	}
	[HarmonyPatch(typeof(Chat), "Update")]
	internal static class PickerChat
	{
		private static bool Prefix()
		{
			if (Plugin.BlockInput)
			{
				return !ZInput.GetButtonDown("Chat");
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	internal static class PickerScroll
	{
		private static float FilterScroll(float value)
		{
			if (!Plugin.BlockInput)
			{
				return value;
			}
			return 0f;
		}

		[HarmonyAfter(new string[] { "com.jubaroo.smarterhoe" })]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo native = AccessTools.Method(typeof(ZInput), "GetMouseScrollWheel", Type.EmptyTypes, (Type[])null);
			MethodInfo filter = AccessTools.Method(typeof(PickerScroll), "FilterScroll", (Type[])null, (Type[])null);
			foreach (CodeInstruction instruction in instructions)
			{
				yield return instruction;
				if (CodeInstructionExtensions.Calls(instruction, native) || IsSmarterHoeReader(instruction))
				{
					yield return new CodeInstruction(OpCodes.Call, (object)filter);
				}
			}
		}

		private static bool IsSmarterHoeReader(CodeInstruction instruction)
		{
			MethodInfo methodInfo = instruction.operand as MethodInfo;
			if (instruction.opcode == OpCodes.Call && methodInfo != null && methodInfo.IsStatic && methodInfo.DeclaringType?.FullName == "SmarterHoe.Plugin" && methodInfo.Name == "GetCameraScrollWheel" && methodInfo.ReturnType == typeof(float))
			{
				return methodInfo.GetParameters().Length == 0;
			}
			return false;
		}
	}
	internal static class Ownership
	{
		private sealed class State
		{
			internal readonly List<Func<Player, bool>> Permissions = new List<Func<Player, bool>>();

			internal Pending Request;

			internal bool Held;

			internal float ProtectedUntil;
		}

		private sealed class Pending
		{
			internal int Token;

			internal long Owner;

			internal bool Answered;

			internal bool Granted;
		}

		private sealed class Lease : IDisposable
		{
			private State state;

			internal Lease(State state)
			{
				this.state = state;
			}

			public void Dispose()
			{
				if (state != null)
				{
					state.Held = false;
					state.ProtectedUntil = 0f;
					state = null;
				}
			}
		}

		private const string RequestRpc = "FuelFromChests_RequestOwnership_v1";

		private const string ReplyRpc = "FuelFromChests_ReplyOwnership_v1";

		private const float TimeoutSeconds = 2f;

		private static readonly ConditionalWeakTable<ZNetView, State> States = new ConditionalWeakTable<ZNetView, State>();

		private static int nextToken;

		internal static void Register(ZNetView view, Func<Player, bool> canGrant)
		{
			if (!Valid(view) || canGrant == null)
			{
				return;
			}
			if (States.TryGetValue(view, out var value))
			{
				if (!value.Permissions.Contains(canGrant))
				{
					value.Permissions.Add(canGrant);
				}
				return;
			}
			State state = new State();
			state.Permissions.Add(canGrant);
			view.Register<long, int>("FuelFromChests_RequestOwnership_v1", (Action<long, long, int>)delegate(long sender, long playerId, int token)
			{
				OnRequest(view, state, sender, playerId, token);
			});
			view.Register<int, bool>("FuelFromChests_ReplyOwnership_v1", (Action<long, int, bool>)delegate(long sender, int token, bool granted)
			{
				OnReply(state, sender, token, granted);
			});
			States.Add(view, state);
		}

		internal static IEnumerator Acquire(ZNetView view, Player player, Action<bool> completed)
		{
			if (!Valid(view) || !States.TryGetValue(view, out var state) || state.Held || state.Request != null || !ValidPlayer(player, ZDOMan.GetSessionID()) || !Allowed(state, player))
			{
				completed?.Invoke(obj: false);
				yield break;
			}
			if (view.IsOwner())
			{
				state.ProtectedUntil = Time.realtimeSinceStartup + 2f;
				completed?.Invoke(obj: true);
				yield break;
			}
			long owner = view.GetZDO().GetOwner();
			if (owner == 0L)
			{
				completed?.Invoke(obj: false);
				yield break;
			}
			int num = Interlocked.Increment(ref nextToken);
			if (num == 0)
			{
				num = Interlocked.Increment(ref nextToken);
			}
			Pending request = new Pending
			{
				Token = num,
				Owner = owner
			};
			Pending request2 = (state.Request = request);
			bool acquired = false;
			float deadline = Time.realtimeSinceStartup + 2f;
			try
			{
				if (SendRequest(view, owner, player.GetPlayerID(), num))
				{
					while (Time.realtimeSinceStartup < deadline && Valid(view))
					{
						if (request2.Answered)
						{
							if (!request2.Granted)
							{
								break;
							}
							if (view.IsOwner())
							{
								acquired = ValidPlayer(player, ZDOMan.GetSessionID()) && Allowed(state, player);
								break;
							}
						}
						yield return null;
					}
				}
			}
			finally
			{
				if (state.Request == request2)
				{
					state.Request = null;
				}
				if (acquired)
				{
					state.ProtectedUntil = Time.realtimeSinceStartup + 2f;
				}
			}
			completed?.Invoke(acquired);
		}

		internal static IDisposable Hold(ZNetView view)
		{
			if (!Valid(view) || !view.IsOwner() || !States.TryGetValue(view, out var value) || value.Held || value.Request != null)
			{
				throw new InvalidOperationException("Cannot reserve an unowned or busy FuelFromChests view.");
			}
			value.Held = true;
			return new Lease(value);
		}

		private static bool Valid(ZNetView view)
		{
			if (Object.op_Implicit((Object)(object)view))
			{
				return view.IsValid();
			}
			return false;
		}

		private static bool ValidPlayer(Player player, long sender)
		{
			if (!Object.op_Implicit((Object)(object)player) || sender == 0L || player.GetPlayerID() == 0L)
			{
				return false;
			}
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if (Valid(component))
			{
				return component.GetZDO().GetOwner() == sender;
			}
			return false;
		}

		private static bool Allowed(State state, Player player)
		{
			try
			{
				foreach (Func<Player, bool> permission in state.Permissions)
				{
					if (!permission(player))
					{
						return false;
					}
				}
				return state.Permissions.Count != 0;
			}
			catch
			{
				return false;
			}
		}

		private static bool SendRequest(ZNetView view, long owner, long playerId, int token)
		{
			try
			{
				view.InvokeRPC(owner, "FuelFromChests_RequestOwnership_v1", new object[2] { playerId, token });
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void OnRequest(ZNetView view, State state, long sender, long playerId, int token)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if (!Valid(view) || !view.IsOwner() || sender == 0L || token == 0)
			{
				return;
			}
			bool flag = false;
			try
			{
				Player player = Player.GetPlayer(playerId);
				if (!state.Held && state.Request == null && Time.realtimeSinceStartup >= state.ProtectedUntil && Object.op_Implicit((Object)(object)player) && player.GetPlayerID() == playerId && ValidPlayer(player, sender) && Allowed(state, player))
				{
					ZDO zDO = view.GetZDO();
					ZDOMan.instance.ForceSendZDO(sender, zDO.m_uid);
					zDO.SetOwner(sender);
					flag = true;
				}
			}
			catch
			{
				flag = false;
			}
			try
			{
				if (Valid(view))
				{
					view.InvokeRPC(sender, "FuelFromChests_ReplyOwnership_v1", new object[2] { token, flag });
				}
			}
			catch
			{
			}
		}

		private static void OnReply(State state, long sender, int token, bool granted)
		{
			Pending request = state.Request;
			if (request != null && !request.Answered && request.Token == token && request.Owner == sender)
			{
				request.Granted = granted;
				request.Answered = true;
			}
		}
	}
	[BepInPlugin("com.valheim.fuelfromchests", "Fuel From Chests", "0.2.1")]
	[BepInIncompatibility("aedenthorn.CraftFromContainers")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class ConfigDisplay
		{
			public bool Browsable;
		}

		public const string Guid = "com.valheim.fuelfromchests";

		public const string Name = "Fuel From Chests";

		public const string Version = "0.2.1";

		internal static Plugin Instance;

		internal static ConfigEntry<bool> Active;

		internal static ConfigEntry<bool> Fuel;

		internal static ConfigEntry<bool> Ore;

		internal static ConfigEntry<float> Range;

		internal static ConfigEntry<string> Excluded;

		internal static ConfigEntry<KeyCode> FillKey;

		internal static ConfigEntry<KeyCode> SelectKey;

		private ConfigEntry<string> savedSelections;

		private SelectionMemory selections;

		private Station pickerStation;

		private float blockInputUntil;

		private bool pickerWasOpen;

		private Harmony harmony;

		private bool busy;

		private float lastStart;

		internal static bool BlockInput
		{
			get
			{
				if (!ResourcePicker.BlocksInput)
				{
					if (Object.op_Implicit((Object)(object)Instance))
					{
						return Time.unscaledTime < Instance.blockInputUntil;
					}
					return false;
				}
				return true;
			}
		}

		internal static bool IsBusy
		{
			get
			{
				if (Object.op_Implicit((Object)(object)Instance))
				{
					return Instance.busy;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Expected O, but got Unknown
			Instance = this;
			Active = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable fuel and ore pulling. Crafting and building are not changed.");
			Range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 32f, new ConfigDescription("Chest search radius in metres, measured from the station.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			Fuel = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PullFuel", true, "Pull fuel into fireplaces, smelters and cooking station fuel inputs.");
			Ore = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PullOre", true, "Pull smelter inputs, including ordinary wood for charcoal kilns.");
			Excluded = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ExcludedPrefabs", "FineWood,RoundLog", "Comma-separated prefab names never selected by this mod. FineWood and RoundLog protect fine/core wood from kilns.");
			FillKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "FillAllKey", (KeyCode)304, "Hold while interacting to fill available capacity.");
			SelectKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "SelectKey", (KeyCode)308, "Hold while interacting to choose the resource. Mouse or arrow keys + Enter select; Escape cancels.");
			savedSelections = ((BaseUnityPlugin)this).Config.Bind<string>("Selections", "RememberedResources", "", new ConfigDescription("Saved resource choices by station type and input. Managed by the in-game picker.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigDisplay
				{
					Browsable = false
				}
			}));
			selections = new SelectionMemory(savedSelections.Value);
			try
			{
				harmony = new Harmony("com.valheim.fuelfromchests");
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Fuel From Chests active. Standalone station feeding; no crafting/building hooks or mod dependencies.");
			}
			catch (Exception ex)
			{
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				((Behaviour)this).enabled = false;
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
		}

		internal static void Report(Exception error)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogError((object)error);
			}
		}

		internal static bool KeyHeld(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if (ZInput.GetKey(key, false))
			{
				return true;
			}
			if ((int)key != 308 || !ZInput.GetKey((KeyCode)307, false))
			{
				if ((int)key == 304)
				{
					return ZInput.GetKey((KeyCode)303, false);
				}
				return false;
			}
			return true;
		}

		private void Update()
		{
			if (ResourcePicker.IsOpen && (!Active.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || pickerStation == null || !pickerStation.Near(Player.m_localPlayer) || InventoryGui.IsVisible() || Menu.IsVisible() || Console.IsVisible() || TextInput.IsVisible() || StoreGui.IsVisible() || (Object.op_Implicit((Object)(object)Chat.instance) && Chat.instance.HasFocus())))
			{
				ResourcePicker.Close();
			}
			ResourcePicker.Tick();
			if (pickerWasOpen && !ResourcePicker.IsOpen)
			{
				blockInputUntil = Time.unscaledTime + 0.2f;
			}
			pickerWasOpen = ResourcePicker.IsOpen;
		}

		private void SaveChoice(Station station, string resource)
		{
			selections.Set(station.SelectionKey, resource);
			savedSelections.Value = selections.Serialize();
		}

		internal static string Selected(Station station)
		{
			return Instance?.selections?.Get(station.SelectionKey);
		}

		private string ResolveChoice(Station station, Player player)
		{
			string text = Selected(station);
			if (text != null)
			{
				return text;
			}
			List<PickerOption> options = station.Options(player);
			List<string> list = new List<string>();
			foreach (PickerOption item in options)
			{
				list.Add(item.Id);
			}
			text = selections.Resolve(station.SelectionKey, list, (string id) => options.Exists((PickerOption option) => option.Id == id && option.Count > 0));
			if (text != null)
			{
				SaveChoice(station, text);
			}
			return text;
		}

		private void OpenPicker(Station station, Player player)
		{
			List<PickerOption> list = station.Options(player);
			if (list.Count == 0)
			{
				((Character)player).Message((MessageType)2, Texts.EmptyOptions, 0, (Sprite)null, false);
				return;
			}
			pickerStation = station;
			ResourcePicker.Show(station.Name, list, Selected(station), delegate(string choice)
			{
				blockInputUntil = Time.unscaledTime + 0.2f;
				if (station.Valid && station.Near(player) && Transfer.Allowed(choice, Excluded.Value))
				{
					SaveChoice(station, choice);
				}
			});
			pickerWasOpen = ResourcePicker.IsOpen;
		}

		internal static void RegisterStation(Component component)
		{
			Station station = new Station(component);
			if (station.Valid)
			{
				Ownership.Register(station.View, (Player player) => station.Near(player, checkWard: false));
			}
		}

		internal static bool Begin(Station station, Humanoid user, ItemData explicitItem = null)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Instance;
			Player val = (Player)(object)((user is Player) ? user : null);
			if (!Object.op_Implicit((Object)(object)instance) || !((Behaviour)instance).enabled || (Object)(object)val != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			if (instance.busy || BlockInput)
			{
				return true;
			}
			if (!Active.Value || explicitItem != null || !(station.IsOre ? Ore.Value : Fuel.Value) || !station.Refillable || !station.Near(val))
			{
				return false;
			}
			if (KeyHeld(SelectKey.Value))
			{
				instance.OpenPicker(station, val);
				return true;
			}
			if (Time.unscaledTime - instance.lastStart < 0.15f)
			{
				return true;
			}
			if (station.Space == 0)
			{
				return false;
			}
			bool bulk = KeyHeld(FillKey.Value);
			string text = instance.ResolveChoice(station, val);
			if (text == null)
			{
				((Character)val).Message((MessageType)2, Texts.EmptyOptions, 0, (Sprite)null, false);
				return true;
			}
			instance.busy = true;
			instance.lastStart = Time.unscaledTime;
			((MonoBehaviour)instance).StartCoroutine(instance.Guard(instance.Fill(station, val, bulk, text)));
			return true;
		}

		private IEnumerator Guard(IEnumerator work)
		{
			try
			{
				while (true)
				{
					bool flag = false;
					object obj = null;
					try
					{
						flag = work.MoveNext();
						if (flag)
						{
							obj = work.Current;
						}
					}
					catch (Exception error)
					{
						Report(error);
					}
					if (flag)
					{
						yield return obj;
						continue;
					}
					break;
				}
			}
			finally
			{
				Plugin plugin = this;
				(work as IDisposable)?.Dispose();
				plugin.busy = false;
			}
		}

		private IEnumerator Fill(Station station, Player player, bool bulk, string selected)
		{
			bool owned = false;
			IEnumerator acquire = Ownership.Acquire(station.View, player, delegate(bool value)
			{
				owned = value;
			});
			try
			{
				while (acquire.MoveNext())
				{
					yield return acquire.Current;
				}
			}
			finally
			{
				IDisposable disposable = acquire as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}
			}
			if (!owned || !station.Near(player))
			{
				if (Object.op_Implicit((Object)(object)player))
				{
					((Character)player).Message((MessageType)2, "Station unavailable. Fuel From Chests must be installed on all peers.", 0, (Sprite)null, false);
				}
				yield break;
			}
			int added = 0;
			int wanted = (bulk ? station.Space : Math.Min(1, station.Space));
			using (Ownership.Hold(station.View))
			{
				added += Feed(station, player, ((Humanoid)player).GetInventory(), wanted, selected, () => station.Near(player));
				float deadline = Time.realtimeSinceStartup + 8f;
				foreach (Container chest in Chests.Nearby(station, player))
				{
					if (added >= wanted || !station.Valid || !station.View.IsOwner() || !station.Near(player) || station.Space == 0 || Time.realtimeSinceStartup > deadline)
					{
						break;
					}
					Chests.Refresh(chest);
					if (station.Find(chest.GetInventory(), selected) == null)
					{
						continue;
					}
					bool chestOwned = false;
					{
						acquire = Ownership.Acquire(Chests.View(chest), player, delegate(bool value)
						{
							chestOwned = value;
						});
						try
						{
							while (acquire.MoveNext())
							{
								yield return acquire.Current;
							}
						}
						finally
						{
							IDisposable disposable2 = acquire as IDisposable;
							if (disposable2 != null)
							{
								disposable2.Dispose();
							}
						}
					}
					if (!chestOwned || !station.Near(player) || !station.View.IsOwner() || !Chests.Ready(chest, station, player))
					{
						continue;
					}
					using (Ownership.Hold(Chests.View(chest)))
					{
						Chests.Refresh(chest);
						added += Feed(station, player, chest.GetInventory(), wanted - added, selected, () => Chests.Ready(chest, station, player));
					}
				}
			}
			if (Object.op_Implicit((Object)(object)player))
			{
				((Character)player).Message((MessageType)2, (added > 0) ? ("$msg_added " + station.ResourceName(selected) + " × " + added) : Texts.NoneAvailable, 0, (Sprite)null, false);
			}
		}

		private static int Feed(Station station, Player player, Inventory inventory, int budget, string selected, Func<bool> sourceReady)
		{
			int i;
			for (i = 0; i < budget; i++)
			{
				if (!Active.Value)
				{
					break;
				}
				if (!station.Valid)
				{
					break;
				}
				if (!station.View.IsOwner())
				{
					break;
				}
				if (!station.Near(player))
				{
					break;
				}
				if (station.Space <= 0)
				{
					break;
				}
				if (!sourceReady())
				{
					break;
				}
				ItemData item = station.Find(inventory, selected);
				if (item == null || Transfer.One(new InventoryUnit(inventory, item, () => station.View.IsOwner() && station.Near(player) && sourceReady()), () => station.Add(item, player, selected)) != TransferResult.Added)
				{
					break;
				}
			}
			return i;
		}

		private void OnDestroy()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			ResourcePicker.Destroy();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class ChestAwake
	{
		private static void Postfix(Container __instance)
		{
			Chests.Track(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class ChestDestroyed
	{
		private static void Postfix(Container __instance)
		{
			Chests.Forget(__instance);
		}
	}
	[HarmonyPatch(typeof(Smelter), "Awake")]
	internal static class SmelterAwake
	{
		private static void Postfix(Smelter __instance)
		{
			Plugin.RegisterStation((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Awake")]
	internal static class FireplaceAwake
	{
		private static void Postfix(Fireplace __instance)
		{
			Plugin.RegisterStation((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "Awake")]
	internal static class CookingAwake
	{
		private static void Postfix(CookingStation __instance)
		{
			Plugin.RegisterStation((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
	internal static class SmelterFuel
	{
		private static bool Prefix(Smelter __instance, Humanoid user, ItemData item, ref bool __result)
		{
			if (!Plugin.Begin(new Station((Component)(object)__instance), user, item))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	internal static class SmelterOre
	{
		private static bool Prefix(Smelter __instance, Humanoid user, ItemData item, ref bool __result)
		{
			if (!Plugin.Begin(new Station((Component)(object)__instance, isOre: true), user, item))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")]
	internal static class CookingFuel
	{
		private static bool Prefix(CookingStation __instance, Humanoid user, ItemData item, ref bool __result)
		{
			if (!Plugin.Begin(new Station((Component)(object)__instance), user, item))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Interact")]
	internal static class FireplaceFuel
	{
		private static bool Prefix(Fireplace __instance, Humanoid user, bool hold, bool alt, ref bool __result)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Station station = new Station((Component)(object)__instance);
			if (hold && Plugin.IsBusy)
			{
				__result = false;
				return false;
			}
			bool flag = Plugin.SelectKey != null && Plugin.KeyHeld(Plugin.SelectKey.Value);
			if (hold || !station.Valid || (__instance.m_canTurnOff && !alt && !flag && station.Current > 0f) || !Plugin.Begin(station, user))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	internal sealed class PickerOption
	{
		internal readonly string Id;

		internal readonly string Label;

		internal readonly Sprite Icon;

		internal readonly int Count;

		internal readonly bool Enabled;

		internal PickerOption(string id, string label, Sprite icon, int count, bool enabled = true)
		{
			Id = id ?? string.Empty;
			Label = label ?? string.Empty;
			Icon = icon;
			Count = Math.Max(0, count);
			Enabled = enabled;
		}
	}
	internal static class ResourcePicker
	{
		private sealed class Row
		{
			internal readonly Image Background;

			internal readonly Outline Outline;

			internal readonly Image Icon;

			internal readonly TextMeshProUGUI Label;

			internal readonly TextMeshProUGUI Count;

			internal Row(Image background, Outline outline, Image icon, TextMeshProUGUI label, TextMeshProUGUI count)
			{
				Background = background;
				Outline = outline;
				Icon = icon;
				Label = label;
				Count = count;
			}
		}

		private sealed class RowPointer : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerClickHandler
		{
			internal int Index;

			public void OnPointerEnter(PointerEventData eventData)
			{
				Hover(Index, value: true);
			}

			public void OnPointerExit(PointerEventData eventData)
			{
				Hover(Index, value: false);
			}

			public void OnPointerClick(PointerEventData eventData)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				Click(Index, eventData.button);
			}
		}

		private sealed class BackdropPointer : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
		{
			public void OnPointerClick(PointerEventData eventData)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				if ((int)eventData.button == 0)
				{
					Close();
				}
			}
		}

		private const float PanelWidth = 440f;

		private const float PanelPadding = 14f;

		private const float HeaderHeight = 44f;

		private const float RowHeight = 44f;

		private const float RowPitch = 48f;

		private const float FooterHeight = 26f;

		private const int MaxVisibleRows = 9;

		private static readonly Color PanelColor = Rgba(29, 24, 21, 0.92f);

		private static readonly Color PanelBorder = Rgba(214, 178, 110, 0.32f);

		private static readonly Color RowColor = Rgba(14, 11, 9, 0.55f);

		private static readonly Color HoverColor = Rgba(217, 183, 109, 0.07f);

		private static readonly Color SelectedColor = Rgba(217, 183, 109, 0.14f);

		private static readonly Color DisabledColor = Rgba(14, 11, 9, 0.28f);

		private static readonly Color Brass = Rgba(217, 183, 109, 1f);

		private static readonly Color BrassHalf = Rgba(217, 183, 109, 0.5f);

		private static readonly Color Parchment = Rgba(239, 229, 211, 1f);

		private static readonly Color Ash = Rgba(169, 160, 134, 1f);

		private static readonly List<PickerOption> Options = new List<PickerOption>();

		private static readonly List<Row> Rows = new List<Row>();

		private static GameObject canvasObject;

		private static RectTransform panel;

		private static RectTransform viewport;

		private static RectTransform content;

		private static RectTransform canvasBounds;

		private static ScrollRect scroll;

		private static TMP_FontAsset bodyFont;

		private static TMP_FontAsset titleFont;

		private static Action<string> onSelect;

		private static int selectedIndex = -1;

		private static int hoveredIndex = -1;

		private static float blockUntil;

		private static bool waitForUseRelease;

		internal static bool IsOpen
		{
			get
			{
				if (Object.op_Implicit((Object)(object)canvasObject))
				{
					return canvasObject.activeSelf;
				}
				return false;
			}
		}

		internal static bool BlocksInput
		{
			get
			{
				if (!IsOpen && !waitForUseRelease)
				{
					return Time.unscaledTime < blockUntil;
				}
				return true;
			}
		}

		internal static void Show(string title, IReadOnlyList<PickerOption> choices, string selected, Action<string> selectedCallback)
		{
			Close();
			Options.Clear();
			if (choices != null)
			{
				for (int i = 0; i < choices.Count; i++)
				{
					PickerOption pickerOption = choices[i];
					if (pickerOption != null)
					{
						Options.Add(pickerOption);
					}
				}
			}
			selectedIndex = FindEnabled(selected);
			if (selectedIndex < 0)
			{
				selectedIndex = FindNextEnabled(-1, 1);
			}
			onSelect = selectedCallback;
			if (Application.isBatchMode || !Object.op_Implicit((Object)(object)Hud.instance) || !Object.op_Implicit((Object)(object)Hud.instance.m_rootObject))
			{
				onSelect = null;
				return;
			}
			try
			{
				Build(title ?? string.Empty);
				ZCursor.LockState = (CursorLockMode)0;
				ZCursor.Show();
				RefreshRows();
				EnsureSelectedVisible();
			}
			catch (Exception error)
			{
				Plugin.Report(error);
				Destroy();
			}
		}

		internal static void Close()
		{
			if (IsOpen || Object.op_Implicit((Object)(object)canvasObject) || onSelect != null)
			{
				if (ZInput.instance != null)
				{
					waitForUseRelease |= ZInput.GetButton("Use") || ZInput.GetButton("JoyUse");
					ZInput.ResetButtonStatus("Use");
					ZInput.ResetButtonStatus("JoyUse");
				}
				blockUntil = Mathf.Max(blockUntil, Time.unscaledTime + 0.2f);
			}
			onSelect = null;
			Options.Clear();
			selectedIndex = -1;
			hoveredIndex = -1;
			DestroyUi();
		}

		internal static void Tick()
		{
			if (waitForUseRelease && !ZInput.GetButton("Use") && !ZInput.GetButton("JoyUse"))
			{
				waitForUseRelease = false;
			}
			if (!IsOpen)
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)panel) || !Object.op_Implicit((Object)(object)viewport) || !Object.op_Implicit((Object)(object)content))
			{
				Close();
				return;
			}
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
			ClampPanel();
			if (ZInput.GetKeyDown((KeyCode)27, false))
			{
				Close();
			}
			else if (ZInput.GetKeyDown((KeyCode)273, false))
			{
				Move(-1);
			}
			else if (ZInput.GetKeyDown((KeyCode)274, false))
			{
				Move(1);
			}
			else if (ZInput.GetKeyDown((KeyCode)13, false) || ZInput.GetKeyDown((KeyCode)271, false))
			{
				Confirm();
			}
		}

		internal static void Destroy()
		{
			Close();
			bodyFont = null;
			titleFont = null;
		}

		private static void Build(string title)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_0466: Unknown result type (might be due to invalid IL or missing references)
			//IL_0561: Unknown result type (might be due to invalid IL or missing references)
			Rows.Clear();
			ResolveFonts();
			canvasObject = new GameObject("FuelFromChests.ResourcePicker", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			((Object)canvasObject).hideFlags = (HideFlags)61;
			canvasObject.transform.SetParent(Hud.instance.m_rootObject.transform, false);
			Stretch(canvasObject.GetComponent<RectTransform>());
			Canvas component = canvasObject.GetComponent<Canvas>();
			Canvas componentInParent = Hud.instance.m_rootObject.GetComponentInParent<Canvas>();
			canvasBounds = (RectTransform)((!Object.op_Implicit((Object)(object)componentInParent)) ? ((object)canvasObject.GetComponent<RectTransform>()) : ((object)(RectTransform)((Component)componentInParent.rootCanvas).transform));
			component.renderMode = (RenderMode)0;
			component.overrideSorting = true;
			component.sortingOrder = ((!Object.op_Implicit((Object)(object)componentInParent)) ? 1 : (componentInParent.rootCanvas.sortingOrder + 1));
			CanvasScaler component2 = canvasObject.GetComponent<CanvasScaler>();
			component2.uiScaleMode = (ScaleMode)1;
			component2.referenceResolution = new Vector2(1920f, 1080f);
			component2.screenMatchMode = (ScreenMatchMode)0;
			component2.matchWidthOrHeight = 0.5f;
			RectTransform obj = Rect(canvasObject.transform, "Blocker");
			Stretch(obj);
			Image obj2 = ((Component)obj).gameObject.AddComponent<Image>();
			((Graphic)obj2).color = Color.clear;
			((Graphic)obj2).raycastTarget = true;
			((Component)obj).gameObject.AddComponent<BackdropPointer>();
			int num = Math.Min(9, Math.Max(1, Options.Count));
			float num2 = (float)num * 48f - 4f;
			float num3 = 58f + num2 + 8f + 26f + 14f;
			panel = Rect(canvasObject.transform, "Panel");
			RectTransform obj3 = panel;
			RectTransform obj4 = panel;
			RectTransform obj5 = panel;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			obj5.pivot = val;
			Vector2 anchorMin = (obj4.anchorMax = val);
			obj3.anchorMin = anchorMin;
			panel.anchoredPosition = Vector2.zero;
			panel.sizeDelta = new Vector2(440f, num3);
			Image obj6 = ((Component)panel).gameObject.AddComponent<Image>();
			((Graphic)obj6).color = PanelColor;
			((Graphic)obj6).raycastTarget = true;
			AddOutline(((Component)panel).gameObject, PanelBorder, 1f);
			RectTransform obj7 = Rect((Transform)(object)panel, "InnerBorder");
			obj7.anchorMin = Vector2.zero;
			obj7.anchorMax = Vector2.one;
			obj7.offsetMin = new Vector2(5f, 5f);
			obj7.offsetMax = new Vector2(-5f, -5f);
			Image obj8 = ((Component)obj7).gameObject.AddComponent<Image>();
			((Graphic)obj8).color = Color.clear;
			((Graphic)obj8).raycastTarget = false;
			AddOutline(((Component)obj7).gameObject, Rgba(214, 178, 110, 0.12f), 1f);
			RectTransform obj9 = TopRect((Transform)(object)panel, "Header", 14f, 14f, 412f, 44f);
			TextMeshProUGUI obj10 = Text((Transform)(object)obj9, "Title", 22f, Parchment, (TextAlignmentOptions)514, (FontStyles)32, titleFont);
			((TMP_Text)obj10).text = title;
			((TMP_Text)obj10).margin = new Vector4(32f, 0f, 32f, 0f);
			AddRule(obj9, "RuleLeft", left: true);
			AddRule(obj9, "RuleRight", left: false);
			float num4 = 58f;
			viewport = TopRect((Transform)(object)panel, "Viewport", 14f, num4, 412f, num2);
			((Component)viewport).gameObject.AddComponent<RectMask2D>();
			Image obj11 = ((Component)viewport).gameObject.AddComponent<Image>();
			((Graphic)obj11).color = Color.clear;
			((Graphic)obj11).raycastTarget = true;
			content = Rect((Transform)(object)viewport, "Content");
			content.anchorMin = new Vector2(0f, 1f);
			content.anchorMax = new Vector2(1f, 1f);
			content.pivot = new Vector2(0.5f, 1f);
			content.anchoredPosition = Vector2.zero;
			content.sizeDelta = new Vector2(0f, Math.Max(num2, (float)Options.Count * 48f - 4f));
			scroll = ((Component)viewport).gameObject.AddComponent<ScrollRect>();
			scroll.content = content;
			scroll.viewport = viewport;
			scroll.horizontal = false;
			scroll.vertical = Options.Count > num;
			scroll.movementType = (MovementType)2;
			scroll.inertia = true;
			scroll.decelerationRate = 0.12f;
			scroll.scrollSensitivity = 28f;
			if (Options.Count == 0)
			{
				BuildEmptyRow();
			}
			else
			{
				for (int i = 0; i < Options.Count; i++)
				{
					BuildRow(i);
				}
			}
			float y = num4 + num2 + 8f;
			TextMeshProUGUI obj12 = Text((Transform)(object)TopRect((Transform)(object)panel, "Footer", 14f, y, 412f, 26f), "Hint", 12f, Ash, (TextAlignmentOptions)514, (FontStyles)0);
			((TMP_Text)obj12).text = Texts.PickerHelp;
			((TMP_Text)obj12).characterSpacing = 0.5f;
			Canvas.ForceUpdateCanvases();
			ClampPanel();
		}

		private static void BuildRow(int index)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			PickerOption pickerOption = Options[index];
			RectTransform obj = TopRect((Transform)(object)content, "Option." + index, 0f, (float)index * 48f, 0f, 44f);
			obj.anchorMin = new Vector2(0f, 1f);
			obj.anchorMax = new Vector2(1f, 1f);
			obj.sizeDelta = new Vector2(0f, 44f);
			Image val = ((Component)obj).gameObject.AddComponent<Image>();
			((Graphic)val).color = RowColor;
			((Graphic)val).raycastTarget = true;
			Outline outline = AddOutline(((Component)obj).gameObject, Rgba(214, 178, 110, 0.14f), 1f);
			((Component)obj).gameObject.AddComponent<RowPointer>().Index = index;
			RectTransform val2 = LeftRect((Transform)(object)obj, "IconSlot", 7f, 5f, 34f, 34f);
			Image obj2 = ((Component)val2).gameObject.AddComponent<Image>();
			((Graphic)obj2).color = Rgba(64, 51, 40, pickerOption.Enabled ? 0.62f : 0.3f);
			((Graphic)obj2).raycastTarget = false;
			AddOutline(((Component)val2).gameObject, Rgba(214, 178, 110, pickerOption.Enabled ? 0.3f : 0.12f), 1f);
			Image val3 = null;
			if (Object.op_Implicit((Object)(object)pickerOption.Icon))
			{
				RectTransform obj3 = Rect((Transform)(object)val2, "Icon");
				obj3.anchorMin = Vector2.zero;
				obj3.anchorMax = Vector2.one;
				obj3.offsetMin = new Vector2(4f, 4f);
				obj3.offsetMax = new Vector2(-4f, -4f);
				val3 = ((Component)obj3).gameObject.AddComponent<Image>();
				val3.sprite = pickerOption.Icon;
				val3.preserveAspect = true;
				((Graphic)val3).raycastTarget = false;
				((Graphic)val3).color = (Color)(pickerOption.Enabled ? Color.white : new Color(1f, 1f, 1f, 0.38f));
			}
			TextMeshProUGUI val4 = Text((Transform)(object)LeftRect((Transform)(object)obj, "Label", 52f, 0f, 270f, 44f), "Text", 17f, pickerOption.Enabled ? Parchment : Ash, (TextAlignmentOptions)4097, (FontStyles)0);
			((TMP_Text)val4).text = pickerOption.Label;
			((TMP_Text)val4).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val4).overflowMode = (TextOverflowModes)1;
			TextMeshProUGUI val5 = Text((Transform)(object)RightRect((Transform)(object)obj, "Count", 9f, 0f, 66f, 44f), "Text", 15f, pickerOption.Enabled ? Brass : Ash, (TextAlignmentOptions)4100, (FontStyles)0);
			((TMP_Text)val5).text = "x" + pickerOption.Count;
			Rows.Add(new Row(val, outline, val3, val4, val5));
		}

		private static void BuildEmptyRow()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = TopRect((Transform)(object)content, "Empty", 0f, 0f, 0f, 44f);
			obj.anchorMin = new Vector2(0f, 1f);
			obj.anchorMax = new Vector2(1f, 1f);
			obj.sizeDelta = new Vector2(0f, 44f);
			((TMP_Text)Text((Transform)(object)obj, "Text", 16f, Ash, (TextAlignmentOptions)514, (FontStyles)2)).text = Texts.EmptyOptions;
		}

		private static void Move(int direction)
		{
			int num = FindNextEnabled(selectedIndex, direction);
			if (num >= 0 && num != selectedIndex)
			{
				selectedIndex = num;
				RefreshRows();
				EnsureSelectedVisible();
			}
		}

		private static int FindEnabled(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return -1;
			}
			for (int i = 0; i < Options.Count; i++)
			{
				if (Options[i].Enabled && string.Equals(Options[i].Id, id, StringComparison.Ordinal))
				{
					return i;
				}
			}
			return -1;
		}

		private static int FindNextEnabled(int from, int direction)
		{
			if (Options.Count == 0)
			{
				return -1;
			}
			int num = from;
			for (int i = 0; i < Options.Count; i++)
			{
				num = (num + direction + Options.Count) % Options.Count;
				if (Options[num].Enabled)
				{
					return num;
				}
			}
			return -1;
		}

		private static void Confirm()
		{
			if (selectedIndex < 0 || selectedIndex >= Options.Count || !Options[selectedIndex].Enabled)
			{
				return;
			}
			string id = Options[selectedIndex].Id;
			Action<string> action = onSelect;
			Close();
			try
			{
				action?.Invoke(id);
			}
			catch (Exception error)
			{
				Plugin.Report(error);
			}
		}

		private static void Hover(int index, bool value)
		{
			if (index >= 0 && index < Options.Count && Options[index].Enabled)
			{
				hoveredIndex = (value ? index : ((hoveredIndex == index) ? (-1) : hoveredIndex));
				RefreshRows();
			}
		}

		private static void Click(int index, InputButton button)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if ((int)button == 0 && index >= 0 && index < Options.Count && Options[index].Enabled)
			{
				selectedIndex = index;
				Confirm();
			}
		}

		private static void RefreshRows()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < Rows.Count && i < Options.Count; i++)
			{
				PickerOption pickerOption = Options[i];
				Row row = Rows[i];
				bool flag = i == selectedIndex;
				bool flag2 = i == hoveredIndex;
				((Graphic)row.Background).color = ((!pickerOption.Enabled) ? DisabledColor : (flag ? SelectedColor : (flag2 ? HoverColor : RowColor)));
				((Shadow)row.Outline).effectColor = ((!pickerOption.Enabled) ? Rgba(214, 178, 110, 0.08f) : (flag ? BrassHalf : (flag2 ? Rgba(217, 183, 109, 0.2f) : Rgba(214, 178, 110, 0.14f))));
				((Graphic)row.Label).color = (pickerOption.Enabled ? Parchment : Ash);
				((Graphic)row.Count).color = (Color)(pickerOption.Enabled ? Brass : new Color(Ash.r, Ash.g, Ash.b, 0.55f));
				if (Object.op_Implicit((Object)(object)row.Icon))
				{
					((Graphic)row.Icon).color = (Color)(pickerOption.Enabled ? Color.white : new Color(1f, 1f, 1f, 0.38f));
				}
			}
		}

		private static void EnsureSelectedVisible()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)scroll) && scroll.vertical && selectedIndex >= 0)
			{
				Rect rect = viewport.rect;
				float height = ((Rect)(ref rect)).height;
				rect = content.rect;
				float height2 = ((Rect)(ref rect)).height;
				float num = (float)selectedIndex * 48f;
				float num2 = num + 44f;
				float num3 = content.anchoredPosition.y;
				if (num < num3)
				{
					num3 = num;
				}
				else if (num2 > num3 + height)
				{
					num3 = num2 - height;
				}
				content.anchoredPosition = new Vector2(0f, Mathf.Clamp(num3, 0f, Mathf.Max(0f, height2 - height)));
			}
		}

		private static void DestroyUi()
		{
			Rows.Clear();
			panel = null;
			viewport = null;
			content = null;
			canvasBounds = null;
			scroll = null;
			if (Object.op_Implicit((Object)(object)canvasObject))
			{
				canvasObject.SetActive(false);
				Object.Destroy((Object)(object)canvasObject);
			}
			canvasObject = null;
		}

		private static RectTransform Rect(Transform parent, string name)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				hideFlags = (HideFlags)61
			}.GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			return component;
		}

		private static RectTransform TopRect(Transform parent, string name, float x, float y, float width, float height)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, name);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			obj.anchorMax = val;
			obj.anchorMin = val;
			obj.pivot = new Vector2(0f, 1f);
			obj.anchoredPosition = new Vector2(x, 0f - y);
			obj.sizeDelta = new Vector2(width, height);
			return obj;
		}

		private static RectTransform LeftRect(Transform parent, string name, float x, float y, float width, float height)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, name);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 0.5f);
			obj.anchorMax = val;
			obj.anchorMin = val;
			obj.pivot = new Vector2(0f, 0.5f);
			obj.anchoredPosition = new Vector2(x, 0f - y);
			obj.sizeDelta = new Vector2(width, height);
			return obj;
		}

		private static RectTransform RightRect(Transform parent, string name, float x, float y, float width, float height)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, name);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(1f, 0.5f);
			obj.anchorMax = val;
			obj.anchorMin = val;
			obj.pivot = new Vector2(1f, 0.5f);
			obj.anchoredPosition = new Vector2(0f - x, 0f - y);
			obj.sizeDelta = new Vector2(width, height);
			return obj;
		}

		private static void Stretch(RectTransform rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = Vector2.zero;
			rect.offsetMax = Vector2.zero;
		}

		private static TextMeshProUGUI Text(Transform parent, string name, float size, Color color, TextAlignmentOptions alignment, FontStyles style, TMP_FontAsset preferredFont = null)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, name);
			Stretch(obj);
			TextMeshProUGUI obj2 = ((Component)obj).gameObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj2).font = (Object.op_Implicit((Object)(object)preferredFont) ? preferredFont : bodyFont);
			((TMP_Text)obj2).fontSize = size;
			((Graphic)obj2).color = color;
			((TMP_Text)obj2).alignment = alignment;
			((TMP_Text)obj2).fontStyle = style;
			((Graphic)obj2).raycastTarget = false;
			((TMP_Text)obj2).richText = false;
			return obj2;
		}

		private static void ResolveFonts()
		{
			TMP_FontAsset obj = (Object.op_Implicit((Object)(object)Hud.instance.m_healthText) ? Hud.instance.m_healthText.font : null);
			if (!Object.op_Implicit((Object)(object)obj))
			{
				throw new InvalidOperationException("Valheim HUD font is unavailable.");
			}
			bodyFont = obj;
			titleFont = obj;
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			foreach (TMP_FontAsset val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					if (string.Equals(((Object)val).name, "SeneaLUI Forum-Regular.ttf", StringComparison.Ordinal))
					{
						titleFont = val;
					}
					else if (string.Equals(((Object)val).name, "SeneaLUI Alegreya-Regular.ttf", StringComparison.Ordinal))
					{
						bodyFont = val;
					}
				}
			}
		}

		private static void ClampPanel()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)panel) && Object.op_Implicit((Object)(object)canvasBounds) && Object.op_Implicit((Object)(object)canvasObject))
			{
				Rect rect = canvasBounds.rect;
				float num = Mathf.Max(1f, ((Rect)(ref rect)).width - 32f);
				float num2 = Mathf.Max(1f, ((Rect)(ref rect)).height - 32f);
				Vector3 lossyScale = ((Transform)canvasBounds).lossyScale;
				Vector3 lossyScale2 = canvasObject.transform.lossyScale;
				float num3 = ((Mathf.Abs(lossyScale.x) > 0.0001f) ? Mathf.Abs(lossyScale2.x / lossyScale.x) : 1f);
				float num4 = ((Mathf.Abs(lossyScale.y) > 0.0001f) ? Mathf.Abs(lossyScale2.y / lossyScale.y) : 1f);
				float num5 = Mathf.Min(new float[3]
				{
					1f,
					num / (440f * num3),
					num2 / (panel.sizeDelta.y * num4)
				});
				((Transform)panel).localScale = Vector3.one * Mathf.Max(0.01f, num5);
			}
		}

		private static Outline AddOutline(GameObject target, Color color, float distance)
		{
			//IL_0007: 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)
			Outline obj = target.AddComponent<Outline>();
			((Shadow)obj).effectColor = color;
			((Shadow)obj).effectDistance = new Vector2(distance, 0f - distance);
			((Shadow)obj).useGraphicAlpha = false;
			return obj;
		}

		private static void AddRule(RectTransform header, string name, bool left)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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)
			RectTransform obj = Rect((Transform)(object)header, name);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(left ? 0f : 1f, 0.5f);
			obj.anchorMax = val;
			obj.anchorMin = val;
			obj.pivot = new Vector2(left ? 0f : 1f, 0.5f);
			obj.anchoredPosition = new Vector2(left ? 0f : 0f, 0f);
			obj.sizeDelta = new Vector2(46f, 1f);
			Image obj2 = ((Component)obj).gameObject.AddComponent<Image>();
			((Graphic)obj2).color = BrassHalf;
			((Graphic)obj2).raycastTarget = false;
		}

		private static Color Rgba(int r, int g, int b, float a)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f, a);
		}
	}
	internal sealed class SelectionMemory
	{
		private readonly Dictionary<string, string> choices = new Dictionary<string, string>(StringComparer.Ordinal);

		internal SelectionMemory(string serialized = "")
		{
			string[] array = (serialized ?? "").Split(';');
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num > 0 && num != text.Length - 1 && ValidEscapes(text))
				{
					try
					{
						Set(Uri.UnescapeDataString(text.Substring(0, num)), Uri.UnescapeDataString(text.Substring(num + 1)));
					}
					catch (UriFormatException)
					{
					}
				}
			}
		}

		private static bool ValidEscapes(string value)
		{
			for (int i = 0; i < value.Length; i++)
			{
				if (value[i] == '%')
				{
					if (i + 2 >= value.Length || !Uri.IsHexDigit(value[i + 1]) || !Uri.IsHexDigit(value[i + 2]))
					{
						return false;
					}
					i += 2;
				}
			}
			return true;
		}

		internal string Get(string stationKey)
		{
			if (stationKey == null || !choices.TryGetValue(stationKey, out var value))
			{
				return null;
			}
			return value;
		}

		internal void Set(string stationKey, string prefab)
		{
			if (!string.IsNullOrWhiteSpace(stationKey) && !string.IsNullOrWhiteSpace(prefab) && stationKey.Length <= 256 && prefab.Length <= 128 && (choices.Count < 256 || choices.ContainsKey(stationKey)))
			{
				choices[stationKey] = prefab;
			}
		}

		internal string Resolve(string stationKey, IReadOnlyList<string> allowed, Func<string, bool> inStock)
		{
			string text = Get(stationKey);
			if (text != null)
			{
				return text;
			}
			if (allowed == null || allowed.Count == 0)
			{
				return null;
			}
			string text2 = allowed[0];
			foreach (string item in allowed)
			{
				if (inStock(item))
				{
					text2 = item;
					break;
				}
			}
			Set(stationKey, text2);
			return text2;
		}

		internal string Serialize()
		{
			return string.Join(";", from pair in choices.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> pair) => pair.Key, StringComparer.Ordinal)
				select Uri.EscapeDataString(pair.Key) + "=" + Uri.EscapeDataString(pair.Value));
		}
	}
	internal sealed class Station
	{
		private static readonly Func<Smelter, int> QueueSize = AccessTools.MethodDelegate<Func<Smelter, int>>(AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null), (object)null, true);

		private readonly Component component;

		private readonly bool ore;

		internal readonly ZNetView View;

		internal Vector3 Position => component.transform.position;

		internal string SelectionKey => ((Object)component.gameObject).name.Replace("(Clone)", "").Trim() + (ore ? ":input" : ":fuel");

		internal string Name
		{
			get
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				Localization instance = Localization.instance;
				Component obj = component;
				Smelter val = (Smelter)(object)((obj is Smelter) ? obj : null);
				object obj3;
				if (val == null)
				{
					Component obj2 = component;
					Fireplace val2 = (Fireplace)(object)((obj2 is Fireplace) ? obj2 : null);
					obj3 = ((val2 != null) ? val2.m_name : ((CookingStation)component).m_name);
				}
				else
				{
					obj3 = val.m_name;
				}
				return instance.Localize((string)obj3);
			}
		}

		internal bool Valid
		{
			get
			{
				if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)View))
				{
					return View.IsValid();
				}
				return false;
			}
		}

		internal bool IsOre => ore;

		internal float Current
		{
			get
			{
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Expected O, but got Unknown
				if (!ore)
				{
					return View.GetZDO().GetFloat(ZDOVars.s_fuel, 0f);
				}
				return QueueSize((Smelter)component);
			}
		}

		private float Maximum
		{
			get
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				Component obj = component;
				Smelter val = (Smelter)(object)((obj is Smelter) ? obj : null);
				if (val == null)
				{
					Component obj2 = component;
					return ((Fireplace)(((obj2 is Fireplace) ? obj2 : null)?)).m_maxFuel ?? ((float)((CookingStation)component).m_maxFuel);
				}
				return ore ? val.m_maxOre : val.m_maxFuel;
			}
		}

		internal int Space
		{
			get
			{
				if (!Valid)
				{
					return 0;
				}
				return Transfer.Space(Current, Maximum);
			}
		}

		internal bool Refillable
		{
			get
			{
				if (Valid)
				{
					if (!(component is Smelter))
					{
						Component obj = component;
						Fireplace val = (Fireplace)(object)((obj is Fireplace) ? obj : null);
						if (val == null || !val.m_canRefill || val.m_infiniteFuel)
						{
							Component obj2 = component;
							return ((CookingStation)(((obj2 is CookingStation) ? obj2 : null)?)).m_useFuel ?? false;
						}
					}
					return true;
				}
				return false;
			}
		}

		internal Station(Component target, bool isOre = false)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			component = target;
			ore = isOre;
			View = (ZNetView)AccessTools.Field(((object)target).GetType(), "m_nview").GetValue(target);
		}

		internal bool Near(Player player, bool checkWard = true)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (Valid && Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead() && !((Character)player).IsTeleporting())
			{
				Vector3 val = ((Component)player).transform.position - Position;
				if ((double)((Vector3)(ref val)).sqrMagnitude <= Math.Pow(player.m_maxInteractDistance + 3f, 2.0))
				{
					if (checkWard)
					{
						return PrivateArea.CheckAccess(Position, 0f, false, false);
					}
					return true;
				}
			}
			return false;
		}

		internal IEnumerable<ItemDrop> Inputs()
		{
			if (ore)
			{
				foreach (ItemConversion item in ((Smelter)component).m_conversion)
				{
					if (Object.op_Implicit((Object)(object)item.m_from))
					{
						yield return item.m_from;
					}
				}
				yield break;
			}
			Component obj = component;
			Smelter val = (Smelter)(object)((obj is Smelter) ? obj : null);
			object obj3;
			if (val == null)
			{
				Component obj2 = component;
				Fireplace val2 = (Fireplace)(object)((obj2 is Fireplace) ? obj2 : null);
				obj3 = ((val2 != null) ? val2.m_fuelItem : ((CookingStation)component).m_fuelItem);
			}
			else
			{
				obj3 = val.m_fuelItem;
			}
			ItemDrop val3 = (ItemDrop)obj3;
			if (Object.op_Implicit((Object)(object)val3))
			{
				yield return val3;
			}
		}

		internal ItemData Find(Inventory inventory, string selectedPrefab)
		{
			if (inventory == null)
			{
				return null;
			}
			foreach (ItemDrop item in Inputs())
			{
				string name = ((Object)((Component)item).gameObject).name;
				if (name != selectedPrefab || !Transfer.Allowed(name, Plugin.Excluded.Value))
				{
					continue;
				}
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (Object.op_Implicit((Object)(object)allItem.m_dropPrefab) && ((Object)allItem.m_dropPrefab).name == name && allItem.m_stack > 0 && allItem.m_worldLevel >= Game.m_worldLevel && !allItem.m_equipped && !allItem.m_shared.m_questItem)
					{
						return allItem;
					}
				}
			}
			return null;
		}

		internal string ResourceName(string prefab)
		{
			foreach (ItemDrop item in Inputs())
			{
				if (((Object)((Component)item).gameObject).name == prefab)
				{
					return Localization.instance.Localize(item.m_itemData.m_shared.m_name);
				}
			}
			return prefab ?? Texts.ChooseResource;
		}

		internal List<PickerOption> Options(Player player)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			HashSet<Inventory> hashSet = new HashSet<Inventory>();
			if (Object.op_Implicit((Object)(object)player) && ((Humanoid)player).GetInventory() != null)
			{
				hashSet.Add(((Humanoid)player).GetInventory());
			}
			foreach (Container item in Chests.Nearby(this, player))
			{
				Chests.Refresh(item);
				hashSet.Add(item.GetInventory());
			}
			foreach (Inventory item2 in hashSet)
			{
				foreach (ItemData allItem in item2.GetAllItems())
				{
					if (Object.op_Implicit((Object)(object)allItem.m_dropPrefab) && allItem.m_stack > 0 && allItem.m_worldLevel >= Game.m_worldLevel && !allItem.m_equipped && !allItem.m_shared.m_questItem)
					{
						dictionary.TryGetValue(((Object)allItem.m_dropPrefab).name, out var value);
						dictionary[((Object)allItem.m_dropPrefab).name] = (int)Math.Min(2147483647L, (long)value + (long)allItem.m_stack);
					}
				}
			}
			List<PickerOption> list = new List<PickerOption>();
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			foreach (ItemDrop item3 in Inputs())
			{
				string name = ((Object)((Component)item3).gameObject).name;
				if (hashSet2.Add(name) && Transfer.Allowed(name, Plugin.Excluded.Value))
				{
					dictionary.TryGetValue(name, out var value2);
					list.Add(new PickerOption(name, Localization.instance.Localize(item3.m_itemData.m_shared.m_name), item3.m_itemData.GetIcon(), value2));
				}
			}
			return list;
		}

		internal bool Add(ItemData item, Player player, string selectedPrefab)
		{
			if (!Refillable || !View.IsOwner() || !Near(player) || Space < 1 || !Object.op_Implicit((Object)(object)item.m_dropPrefab) || ((Object)item.m_dropPrefab).name != selectedPrefab || !Transfer.Allowed(selectedPrefab, Plugin.Excluded.Value))
			{
				return false;
			}
			float current = Current;
			try
			{
				if (ore)
				{
					View.InvokeRPC("RPC_AddOre", new object[2]
					{
						((Object)item.m_dropPrefab).name,
						item.m_cheated
					});
				}
				else
				{
					View.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
				}
			}
			catch (Exception error)
			{
				Plugin.Report(error);
			}
			if (Valid)
			{
				return Current > current;
			}
			return false;
		}
	}
	internal static class Texts
	{
		private static bool Russian
		{
			get
			{
				if (Localization.instance != null)
				{
					return Localization.instance.GetSelectedLanguage() == "Russian";
				}
				return false;
			}
		}

		internal static string ChooseResource
		{
			get
			{
				if (!Russian)
				{
					return "Choose resource";
				}
				return "Выбрать ресурс";
			}
		}

		internal static string Fill
		{
			get
			{
				if (!Russian)
				{
					return "Fill";
				}
				return "Заполнить";
			}
		}

		internal static string Selected
		{
			get
			{
				if (!Russian)
				{
					return "Selected";
				}
				return "Выбрано";
			}
		}

		internal static string NoneAvailable
		{
			get
			{
				if (!Russian)
				{
					return "Selected resource unavailable in inventory and nearby chests";
				}
				return "Этот ресурс недоступен в инвентаре и ближайших сундуках";
			}
		}

		internal static string EmptyOptions
		{
			get
			{
				if (!Russian)
				{
					return "No enabled resources for this station";
				}
				return "Нет разрешённых ресурсов для этой печи";
			}
		}

		internal static string PickerHelp
		{
			get
			{
				if (!Russian)
				{
					return "Up / Down: Select   Enter: Choose   Esc: Close";
				}
				return "Стрелки: выбор   Enter: принять   Esc: закрыть";
			}
		}

		internal unsafe static string Key(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			switch (key - 303)
			{
			case 4:
			case 5:
				return "Alt";
			case 0:
			case 1:
				return "Shift";
			case 2:
			case 3:
				return "Ctrl";
			default:
				return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			}
		}
	}
	internal interface IResourceUnit
	{
		bool IsDebited { get; }

		bool Debit();

		void Refund();
	}
	internal enum TransferResult
	{
		NoResource,
		Rejected,
		Added
	}
	internal static class Transfer
	{
		internal static TransferResult One(IResourceUnit unit, Func<bool> accept)
		{
			bool flag = false;
			try
			{
				if (!unit.Debit())
				{
					return TransferResult.NoResource;
				}
				flag = accept();
				return (!flag) ? TransferResult.Rejected : TransferResult.Added;
			}
			finally
			{
				if (!flag && unit.IsDebited)
				{
					unit.Refund();
				}
			}
		}

		internal static int Space(double current, double maximum)
		{
			if (double.IsNaN(current) || double.IsInfinity(current) || double.IsNaN(maximum) || double.IsInfinity(maximum) || current < 0.0 || maximum <= current)
			{
				return 0;
			}
			return (int)Math.Min(1000.0, Math.Floor(maximum - current));
		}

		internal static bool Allowed(string prefab, string excluded)
		{
			if (string.IsNullOrEmpty(prefab))
			{
				return false;
			}
			string[] array = (excluded ?? "").Split(',');
			foreach (string text in array)
			{
				if (string.Equals(prefab, text.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
			}
			return true;
		}
	}
}