Decompiled source of ManualStationPull v0.2.5

BepInEx/plugins/ManualStationPull/ManualStationPull.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using Zephyrfit.ManualStationPull.Core;
using Zephyrfit.ManualStationPull.Models;
using Zephyrfit.ManualStationPull.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ManualStationPull")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.5.0")]
[assembly: AssemblyInformationalVersion("0.2.5")]
[assembly: AssemblyProduct("ManualStationPull")]
[assembly: AssemblyTitle("ManualStationPull")]
[assembly: AssemblyVersion("0.2.5.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 Zephyrfit.ManualStationPull
{
	[BepInPlugin("Zephyrfit.ManualStationPull", "Manual Station Pull", "0.2.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class ManualStationPullPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Zephyrfit.ManualStationPull";

		public const string PluginName = "Manual Station Pull";

		public const string PluginVersion = "0.2.5";

		private Harmony _harmony;

		internal static ManualStationPullPlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal ModConfig Settings { get; private set; }

		internal ContainerScanner Scanner { get; private set; }

		internal SelectionStore Selections { get; private set; }

		internal StationInputService Stations { get; private set; }

		internal SelectorHotkeyService SelectorHotkey { get; private set; }

		internal OreSelectorUI SelectorUI { get; private set; }

		private void Awake()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new ModConfig(((BaseUnityPlugin)this).Config);
			Scanner = new ContainerScanner(Settings);
			Selections = new SelectionStore(Settings);
			Stations = new StationInputService(Settings, Scanner, Selections);
			SelectorHotkey = new SelectorHotkeyService(Settings, Stations);
			SelectorUI = ((Component)this).gameObject.AddComponent<OreSelectorUI>();
			SelectorUI.Initialize(Settings, Stations, Selections);
			_harmony = new Harmony("Zephyrfit.ManualStationPull");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Manual Station Pull 0.2.5 loaded.");
		}

		private void Update()
		{
			SelectorUI?.Tick();
			SelectorHotkey?.Tick();
		}

		private void OnDestroy()
		{
			SelectorUI?.Close();
			Selections?.Flush();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			Instance = null;
		}

		internal static bool IsFrenchLanguage()
		{
			try
			{
				Localization instance = Localization.instance;
				string text = ((instance != null) ? instance.GetSelectedLanguage() : null);
				if (string.IsNullOrWhiteSpace(text))
				{
					return false;
				}
				text = text.Trim();
				return text.Equals("French", StringComparison.OrdinalIgnoreCase) || text.Equals("Français", StringComparison.OrdinalIgnoreCase) || text.Equals("Francais", StringComparison.OrdinalIgnoreCase) || text.Equals("fr", StringComparison.OrdinalIgnoreCase) || text.StartsWith("fr-", StringComparison.OrdinalIgnoreCase) || text.StartsWith("fr_", StringComparison.OrdinalIgnoreCase);
			}
			catch
			{
				return false;
			}
		}

		internal static string L(string fr, string en)
		{
			if (!IsFrenchLanguage())
			{
				return en;
			}
			return fr;
		}
	}
}
namespace Zephyrfit.ManualStationPull.UI
{
	internal sealed class OreSelectorUI : MonoBehaviour
	{
		private ModConfig _settings;

		private StationInputService _stations;

		private SelectionStore _selections;

		private Canvas _canvas;

		private GameObject _panel;

		private RectTransform _content;

		private Text _title;

		private Text _subtitle;

		private Font _font;

		private Button _firstSelectable;

		private Text _closeButtonText;

		private Smelter _smelter;

		private Switch _inputSwitch;

		private Player _player;

		private List<InputCandidate> _candidates;

		private bool _previousCursorVisible;

		private CursorLockMode _previousCursorLock;

		internal bool IsOpen
		{
			get
			{
				if ((Object)(object)_panel != (Object)null)
				{
					return _panel.activeSelf;
				}
				return false;
			}
		}

		internal void Initialize(ModConfig settings, StationInputService stations, SelectionStore selections)
		{
			_settings = settings;
			_stations = stations;
			_selections = selections;
			BuildUI();
		}

		internal void Tick()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen)
			{
				if (((int)_settings.CloseSelectorKey.Value != 0 && Input.GetKeyDown(_settings.CloseSelectorKey.Value)) || ZInput.GetButtonDown("JoyMenu"))
				{
					Close();
				}
				else if ((Object)(object)_smelter == (Object)null || (Object)(object)_player == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)_player != (Object)(object)Player.m_localPlayer)
				{
					Close();
				}
				else if (Vector3.Distance(((Component)_player).transform.position, ((Component)_smelter).transform.position) > 6f)
				{
					Close();
				}
			}
		}

		internal void Open(Smelter smelter, Switch inputSwitch, Player player, List<InputCandidate> candidates)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panel == (Object)null)
			{
				BuildUI();
			}
			_smelter = smelter;
			_inputSwitch = inputSwitch;
			_player = player;
			_candidates = candidates ?? new List<InputCandidate>();
			RefreshLocalizedText();
			RebuildRows();
			_previousCursorVisible = Cursor.visible;
			_previousCursorLock = Cursor.lockState;
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
			_panel.SetActive(true);
			if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)_firstSelectable != (Object)null && ((Selectable)_firstSelectable).interactable)
			{
				EventSystem.current.SetSelectedGameObject(((Component)_firstSelectable).gameObject);
			}
		}

		internal void Close()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_panel == (Object)null) && _panel.activeSelf)
			{
				_panel.SetActive(false);
				_smelter = null;
				_inputSwitch = null;
				_player = null;
				_candidates = null;
				Cursor.visible = _previousCursorVisible;
				Cursor.lockState = _previousCursorLock;
			}
		}

		private void BuildUI()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			_font = FindValheimFont();
			GameObject val = new GameObject("ManualStationPull_Canvas", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			val.transform.SetParent(((Component)this).transform, false);
			_canvas = val.GetComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 5000;
			CanvasScaler component = val.GetComponent<CanvasScaler>();
			component.uiScaleMode = (ScaleMode)1;
			component.referenceResolution = new Vector2(1920f, 1080f);
			component.screenMatchMode = (ScreenMatchMode)0;
			component.matchWidthOrHeight = 0.5f;
			_panel = CreatePanel(val.transform);
			_panel.SetActive(false);
		}

		private GameObject CreatePanel(Transform parent)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Expected O, but got Unknown
			GameObject val = CreateUIObject("SelectorPanel", parent, typeof(Image), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.sizeDelta = new Vector2(560f, 480f);
			((Graphic)val.GetComponent<Image>()).color = new Color(0.055f, 0.045f, 0.035f, 0.96f);
			VerticalLayoutGroup component2 = val.GetComponent<VerticalLayoutGroup>();
			((LayoutGroup)component2).padding = new RectOffset(24, 24, 20, 20);
			((HorizontalOrVerticalLayoutGroup)component2).spacing = 10f;
			((LayoutGroup)component2).childAlignment = (TextAnchor)1;
			((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true;
			ContentSizeFitter component3 = val.GetComponent<ContentSizeFitter>();
			component3.horizontalFit = (FitMode)0;
			component3.verticalFit = (FitMode)0;
			_title = CreateText("Title", val.transform, 26, (FontStyle)1, (TextAnchor)4);
			AddLayout(((Component)_title).gameObject, 42f);
			_subtitle = CreateText("Subtitle", val.transform, 15, (FontStyle)0, (TextAnchor)4);
			((Graphic)_subtitle).color = new Color(0.84f, 0.84f, 0.84f, 1f);
			AddLayout(((Component)_subtitle).gameObject, 52f);
			GameObject val2 = CreateUIObject("Scroll", val.transform, typeof(Image), typeof(ScrollRect), typeof(LayoutElement));
			((Graphic)val2.GetComponent<Image>()).color = new Color(0.02f, 0.02f, 0.02f, 0.32f);
			LayoutElement component4 = val2.GetComponent<LayoutElement>();
			component4.preferredHeight = 300f;
			component4.flexibleHeight = 1f;
			GameObject val3 = CreateUIObject("Viewport", val2.transform, typeof(Image), typeof(Mask));
			Stretch(val3.GetComponent<RectTransform>(), 8f, 8f, 8f, 8f);
			((Graphic)val3.GetComponent<Image>()).color = new Color(1f, 1f, 1f, 0.01f);
			val3.GetComponent<Mask>().showMaskGraphic = false;
			GameObject val4 = CreateUIObject("Content", val3.transform, typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
			_content = val4.GetComponent<RectTransform>();
			_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 = Vector2.zero;
			VerticalLayoutGroup component5 = val4.GetComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component5).spacing = 6f;
			((HorizontalOrVerticalLayoutGroup)component5).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)component5).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)component5).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)component5).childForceExpandWidth = true;
			val4.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			ScrollRect component6 = val2.GetComponent<ScrollRect>();
			component6.viewport = val3.GetComponent<RectTransform>();
			component6.content = _content;
			component6.horizontal = false;
			component6.vertical = true;
			component6.movementType = (MovementType)2;
			component6.scrollSensitivity = 28f;
			Button val5 = CreateButton(val.transform, ManualStationPullPlugin.L("Fermer", "Close"), selected: false);
			_closeButtonText = ((Component)val5).GetComponentInChildren<Text>(true);
			((UnityEvent)val5.onClick).AddListener(new UnityAction(Close));
			AddLayout(((Component)val5).gameObject, 38f);
			return val;
		}

		private void RefreshLocalizedText()
		{
			if ((Object)(object)_title != (Object)null)
			{
				_title.text = ManualStationPullPlugin.L("Choisir le minerai", "Select furnace input");
			}
			if ((Object)(object)_subtitle != (Object)null)
			{
				_subtitle.text = ManualStationPullPlugin.L("Le choix est mémorisé pour cette fonderie. Les quantités incluent l'inventaire et les coffres accessibles à proximité.", "The choice is remembered for this furnace. Quantities include your inventory and accessible nearby containers.");
			}
			if ((Object)(object)_closeButtonText != (Object)null)
			{
				_closeButtonText.text = ManualStationPullPlugin.L("Fermer", "Close");
			}
		}

		private void RebuildRows()
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_content == (Object)null)
			{
				return;
			}
			for (int num = ((Transform)_content).childCount - 1; num >= 0; num--)
			{
				Object.Destroy((Object)(object)((Component)((Transform)_content).GetChild(num)).gameObject);
			}
			_firstSelectable = null;
			string prefabName = null;
			_selections.TryGet(_smelter, _player, out prefabName);
			foreach (InputCandidate candidate in _candidates)
			{
				InputCandidate captured = candidate;
				bool selected = string.Equals(prefabName, candidate.PrefabName, StringComparison.Ordinal);
				Button val = CreateCandidateButton((Transform)(object)_content, candidate, selected);
				((Selectable)val).interactable = candidate.Available;
				((UnityEvent)val.onClick).AddListener((UnityAction)delegate
				{
					Select(captured);
				});
				if ((Object)(object)_firstSelectable == (Object)null && ((Selectable)val).interactable)
				{
					_firstSelectable = val;
				}
			}
			if (_candidates.Count == 0)
			{
				Text obj = CreateText("Empty", (Transform)(object)_content, 17, (FontStyle)2, (TextAnchor)4);
				obj.text = ManualStationPullPlugin.L("Aucune ressource compatible disponible.", "No compatible resource is available.");
				((Graphic)obj).color = new Color(0.75f, 0.75f, 0.75f, 1f);
				AddLayout(((Component)obj).gameObject, 50f);
			}
		}

		private Button CreateCandidateButton(Transform parent, InputCandidate candidate, bool selected)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateUIObject("Input_" + candidate.PrefabName, parent, typeof(Image), typeof(Button), typeof(LayoutElement));
			((Graphic)val.GetComponent<Image>()).color = (selected ? new Color(0.29f, 0.38f, 0.23f, 0.96f) : new Color(0.18f, 0.15f, 0.11f, 0.96f));
			AddLayout(val, 58f);
			Button component = val.GetComponent<Button>();
			ColorBlock colors = ((Selectable)component).colors;
			((ColorBlock)(ref colors)).highlightedColor = new Color(0.34f, 0.29f, 0.2f, 1f);
			((ColorBlock)(ref colors)).pressedColor = new Color(0.24f, 0.21f, 0.15f, 1f);
			((ColorBlock)(ref colors)).disabledColor = new Color(0.1f, 0.1f, 0.1f, 0.6f);
			((Selectable)component).colors = colors;
			HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
			((LayoutGroup)obj).padding = new RectOffset(10, 12, 6, 6);
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 10f;
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			GameObject obj2 = CreateUIObject("Icon", val.transform, typeof(Image), typeof(LayoutElement));
			LayoutElement component2 = obj2.GetComponent<LayoutElement>();
			component2.preferredWidth = 44f;
			component2.preferredHeight = 44f;
			Image component3 = obj2.GetComponent<Image>();
			component3.sprite = candidate.Icon;
			component3.preserveAspect = true;
			((Graphic)component3).color = (Color)(candidate.Available ? Color.white : new Color(1f, 1f, 1f, 0.35f));
			Text obj3 = CreateText("Name", val.transform, 19, (FontStyle)1, (TextAnchor)3);
			obj3.text = (selected ? "✓  " : string.Empty) + candidate.DisplayName;
			((Component)obj3).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
			((Graphic)obj3).color = (Color)(candidate.Available ? Color.white : new Color(0.55f, 0.55f, 0.55f, 1f));
			Text val2 = CreateText("Counts", val.transform, 15, (FontStyle)0, (TextAnchor)5);
			val2.text = "x" + candidate.Total + "\n" + ManualStationPullPlugin.L("Coffres", "Chests") + ": " + candidate.Nearby;
			((Graphic)val2).color = (candidate.Available ? new Color(0.9f, 0.82f, 0.6f, 1f) : new Color(0.5f, 0.5f, 0.5f, 1f));
			((Component)val2).gameObject.AddComponent<LayoutElement>().preferredWidth = 105f;
			return component;
		}

		private void Select(InputCandidate candidate)
		{
			if (candidate != null && candidate.Available && !((Object)(object)_smelter == (Object)null) && !((Object)(object)_player == (Object)null))
			{
				Smelter smelter = _smelter;
				Switch inputSwitch = _inputSwitch;
				Player player = _player;
				_selections.Set(smelter, player, candidate.PrefabName);
				Close();
				if (!_stations.TryAddByPrefab(smelter, inputSwitch, player, candidate.PrefabName))
				{
					((Character)player).Message((MessageType)2, ManualStationPullPlugin.L("Impossible d'ajouter cette ressource.", "Could not add this resource."), 0, (Sprite)null, false);
				}
			}
		}

		private Text CreateText(string name, Transform parent, int size, FontStyle style, TextAnchor alignment)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			Text component = CreateUIObject(name, parent, typeof(Text)).GetComponent<Text>();
			component.font = _font;
			component.fontSize = size;
			component.fontStyle = style;
			component.alignment = alignment;
			((Graphic)component).color = Color.white;
			component.horizontalOverflow = (HorizontalWrapMode)0;
			component.verticalOverflow = (VerticalWrapMode)0;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private Button CreateButton(Transform parent, string label, bool selected)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateUIObject("Button", parent, typeof(Image), typeof(Button));
			((Graphic)val.GetComponent<Image>()).color = (selected ? new Color(0.28f, 0.36f, 0.22f, 1f) : new Color(0.22f, 0.18f, 0.13f, 1f));
			Button component = val.GetComponent<Button>();
			Text obj = CreateText("Label", val.transform, 17, (FontStyle)1, (TextAnchor)4);
			obj.text = label;
			Stretch(((Graphic)obj).rectTransform, 4f, 4f, 4f, 4f);
			return component;
		}

		private static GameObject CreateUIObject(string name, Transform parent, params Type[] components)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			foreach (Type type in components)
			{
				if (type != typeof(RectTransform))
				{
					val.AddComponent(type);
				}
			}
			return val;
		}

		private static void AddLayout(GameObject obj, float preferredHeight)
		{
			(obj.GetComponent<LayoutElement>() ?? obj.AddComponent<LayoutElement>()).preferredHeight = preferredHeight;
		}

		private static void Stretch(RectTransform rect, float left, float right, float top, float bottom)
		{
			//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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = new Vector2(left, bottom);
			rect.offsetMax = new Vector2(0f - right, 0f - top);
		}

		private static Font FindValheimFont()
		{
			try
			{
				Font val = ((IEnumerable<Font>)Resources.FindObjectsOfTypeAll<Font>()).FirstOrDefault((Func<Font, bool>)((Font f) => (Object)(object)f != (Object)null && (((Object)f).name.IndexOf("Averia", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)f).name.IndexOf("Norse", StringComparison.OrdinalIgnoreCase) >= 0)));
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			catch
			{
			}
			return Resources.GetBuiltinResource<Font>("Arial.ttf");
		}
	}
}
namespace Zephyrfit.ManualStationPull.Patches
{
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class ContainerAwakePatch
	{
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class PlayerTakeInputPatch
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			ManualStationPullPlugin instance = ManualStationPullPlugin.Instance;
			if (instance != null && instance.SelectorUI?.IsOpen == true && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "IsVisible")]
	internal static class InventoryGuiVisiblePatch
	{
		private static void Postfix(ref bool __result)
		{
			ManualStationPullPlugin instance = ManualStationPullPlugin.Instance;
			if (instance != null && instance.SelectorUI?.IsOpen == true)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddOre", new Type[]
	{
		typeof(Switch),
		typeof(Humanoid),
		typeof(ItemData)
	})]
	[HarmonyPriority(600)]
	[HarmonyBefore(new string[] { "chazman.RunicCrafting" })]
	internal static class SmelterAddOrePatch
	{
		private static bool Prefix(Smelter __instance, Switch sw, Humanoid user, ItemData item, ref bool __result)
		{
			ManualStationPullPlugin instance = ManualStationPullPlugin.Instance;
			if ((Object)(object)instance == (Object)null || StationInputService.IsBypassing || !instance.Settings.Enabled.Value)
			{
				return true;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			bool flag = StationInputService.IsCharcoalKiln(__instance);
			if (flag && !instance.Settings.PullKilnWood.Value)
			{
				return true;
			}
			if (item != null)
			{
				return true;
			}
			if (instance.SelectorUI.IsOpen)
			{
				__result = true;
				return false;
			}
			List<InputCandidate> list = instance.Stations.GetCandidates(__instance, val).FindAll((InputCandidate c) => c.Available);
			if (flag)
			{
				InputCandidate inputCandidate = list.Find((InputCandidate c) => string.Equals(c.PrefabName, "Wood", StringComparison.Ordinal));
				if (inputCandidate == null)
				{
					return true;
				}
				instance.Selections.Set(__instance, val, inputCandidate.PrefabName);
				__result = instance.Stations.TryAddByPrefab(__instance, sw, val, inputCandidate.PrefabName);
				return false;
			}
			string selectedPrefab;
			bool flag2 = instance.Selections.TryGet(__instance, val, out selectedPrefab);
			InputCandidate inputCandidate2 = (flag2 ? list.Find((InputCandidate c) => string.Equals(c.PrefabName, selectedPrefab, StringComparison.Ordinal)) : null);
			if (list.Count == 0)
			{
				string shortcutLabel = instance.SelectorHotkey.GetShortcutLabel();
				((Character)val).Message((MessageType)2, flag2 ? ManualStationPullPlugin.L("Le minerai sélectionné n'est plus disponible — " + shortcutLabel + " pour changer.", "The selected input is no longer available — press " + shortcutLabel + " to change it.") : ManualStationPullPlugin.L("Aucun minerai compatible disponible à proximité.", "No compatible input is available nearby."), 0, (Sprite)null, false);
				__result = false;
				return false;
			}
			if (!flag2 && list.Count == 1)
			{
				inputCandidate2 = list[0];
				instance.Selections.Set(__instance, val, inputCandidate2.PrefabName);
				__result = instance.Stations.TryAddByPrefab(__instance, sw, val, inputCandidate2.PrefabName);
				return false;
			}
			if (!flag2)
			{
				((Character)val).Message((MessageType)2, ManualStationPullPlugin.L("Aucun minerai sélectionné — " + instance.SelectorHotkey.GetShortcutLabel() + " pour choisir.", "No input selected — press " + instance.SelectorHotkey.GetShortcutLabel() + " to choose."), 0, (Sprite)null, false);
				__result = false;
				return false;
			}
			if (inputCandidate2 == null)
			{
				((Character)val).Message((MessageType)2, ManualStationPullPlugin.L("Le minerai sélectionné n'est plus disponible — " + instance.SelectorHotkey.GetShortcutLabel() + " pour changer.", "The selected input is no longer available — press " + instance.SelectorHotkey.GetShortcutLabel() + " to change it."), 0, (Sprite)null, false);
				__result = false;
				return false;
			}
			__result = instance.Stations.TryAddByPrefab(__instance, sw, val, inputCandidate2.PrefabName);
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnHoverAddOre")]
	internal static class SmelterHoverOrePatch
	{
		private static void Postfix(Smelter __instance, ref string __result)
		{
			ManualStationPullPlugin instance = ManualStationPullPlugin.Instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || !instance.Settings.Enabled.Value)
			{
				return;
			}
			instance.SelectorHotkey?.MarkHovered(__instance);
			if (!instance.Settings.ShowHoverHint.Value)
			{
				return;
			}
			if (instance.Selections.TryGet(__instance, localPlayer, out var prefabName))
			{
				ItemDrop val = instance.Stations.FindConversionDrop(__instance, prefabName);
				if ((Object)(object)val != (Object)null)
				{
					int availableCountCached = instance.Stations.GetAvailableCountCached(__instance, localPlayer, prefabName);
					__result = __result + "\n<color=#c6d8ff>" + ManualStationPullPlugin.L("Sélection", "Selected") + ": " + StationInputService.LocalizeItem(val) + " (" + availableCountCached + ")</color>";
				}
			}
			else if (!StationInputService.IsCharcoalKiln(__instance))
			{
				__result = __result + "\n<color=#c6d8ff>" + ManualStationPullPlugin.L("Sélection", "Selected") + ": —</color>";
			}
			if (!StationInputService.IsCharcoalKiln(__instance))
			{
				__result = __result + "\n[<color=yellow><b>" + instance.SelectorHotkey.GetShortcutLabel() + "</b></color>] " + ManualStationPullPlugin.L("Choisir le minerai", "Select input");
			}
		}
	}
}
namespace Zephyrfit.ManualStationPull.Models
{
	internal sealed class InputCandidate
	{
		internal string PrefabName;

		internal string SharedName;

		internal string DisplayName;

		internal Sprite Icon;

		internal int Carried;

		internal int Nearby;

		internal int Total;

		internal ItemDrop SourceDrop;

		internal bool Available => Total > 0;
	}
}
namespace Zephyrfit.ManualStationPull.Core
{
	internal static class ContainerRegistry
	{
		private static readonly HashSet<Container> Containers = new HashSet<Container>();

		internal static void Register(Container container)
		{
			if ((Object)(object)container != (Object)null)
			{
				Containers.Add(container);
			}
		}

		internal static List<Container> Snapshot()
		{
			Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
			return Containers.Where((Container c) => (Object)(object)c != (Object)null).ToList();
		}

		internal static void Clear()
		{
			Containers.Clear();
		}
	}
	internal sealed class ContainerScanner
	{
		private sealed class SpatialCacheEntry
		{
			internal Vector3 Origin;

			internal float CreatedAt;

			internal List<Container> Containers;
		}

		private readonly ModConfig _settings;

		private readonly Dictionary<string, SpatialCacheEntry> _spatialCache = new Dictionary<string, SpatialCacheEntry>(StringComparer.Ordinal);

		private static readonly MethodInfo ContainerCheckAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);

		private static readonly MethodInfo ContainerLoad = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null);

		private static readonly FieldInfo ContainerNView = AccessTools.Field(typeof(Container), "m_nview");

		internal ContainerScanner(ModConfig settings)
		{
			_settings = settings;
		}

		internal List<Container> GetEligibleContainers(Vector3 origin, Player player, string cacheKey)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			List<Container> spatialCandidates = GetSpatialCandidates(origin, cacheKey);
			List<Container> list = new List<Container>(spatialCandidates.Count);
			foreach (Container item in spatialCandidates)
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)((Component)item).transform == (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<TombStone>() != (Object)null) && (!_settings.SkipInUseContainers.Value || !item.IsInUse()) && (!_settings.RespectContainerAccess.Value || CheckContainerAccess(item, player.GetPlayerID())) && (!_settings.RespectWards.Value || !item.m_checkGuardStone || PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false)))
				{
					ZNetView containerView = GetContainerView(item);
					if (!((Object)(object)containerView == (Object)null) && containerView.IsValid() && containerView.GetZDO() != null && (!_settings.RespectMultiUserChestIgnore.Value || !containerView.GetZDO().GetBool("MUC_Ignore", false)) && GetLoadedInventory(item) != null)
					{
						list.Add(item);
					}
				}
			}
			return list;
		}

		private List<Container> GetSpatialCandidates(Vector3 origin, string cacheKey)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: 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)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			string key = (string.IsNullOrEmpty(cacheKey) ? "global" : cacheKey);
			float unscaledTime = Time.unscaledTime;
			if (_spatialCache.TryGetValue(key, out var value) && unscaledTime - value.CreatedAt <= _settings.SpatialCacheSeconds.Value)
			{
				Vector3 val = value.Origin - origin;
				if (((Vector3)(ref val)).sqrMagnitude <= 1f)
				{
					return value.Containers.Where((Container c) => (Object)(object)c != (Object)null).ToList();
				}
			}
			float radiusSqr = _settings.RangeMeters.Value * _settings.RangeMeters.Value;
			List<Container> list = ContainerRegistry.Snapshot().Where(delegate(Container c)
			{
				//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 ((Object)(object)c != (Object)null && (Object)(object)((Component)c).transform != (Object)null)
				{
					Vector3 val2 = ((Component)c).transform.position - origin;
					return ((Vector3)(ref val2)).sqrMagnitude <= radiusSqr;
				}
				return false;
			}).OrderBy(delegate(Container c)
			{
				//IL_0006: 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_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)
				Vector3 val2 = ((Component)c).transform.position - origin;
				return ((Vector3)(ref val2)).sqrMagnitude;
			})
				.ToList();
			_spatialCache[key] = new SpatialCacheEntry
			{
				Origin = origin,
				CreatedAt = unscaledTime,
				Containers = list
			};
			return list;
		}

		internal static bool CheckContainerAccess(Container container, long playerId)
		{
			if ((Object)(object)container == (Object)null || ContainerCheckAccess == null)
			{
				return false;
			}
			try
			{
				object obj = ContainerCheckAccess.Invoke(container, new object[1] { playerId });
				return obj is bool && (bool)obj;
			}
			catch
			{
				return false;
			}
		}

		internal static Inventory GetLoadedInventory(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			try
			{
				Inventory inventory = container.GetInventory();
				if (inventory != null)
				{
					return inventory;
				}
				ContainerLoad?.Invoke(container, null);
				return container.GetInventory();
			}
			catch
			{
				return null;
			}
		}

		internal static ZNetView GetContainerView(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			try
			{
				if (ContainerNView != null)
				{
					object? value = ContainerNView.GetValue(container);
					ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			return ((Component)container).GetComponent<ZNetView>() ?? ((Component)container).GetComponentInParent<ZNetView>();
		}
	}
	internal enum SelectionPersistenceMode
	{
		Session,
		World
	}
	internal sealed class ModConfig
	{
		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<float> RangeMeters;

		internal readonly ConfigEntry<float> SpatialCacheSeconds;

		internal readonly ConfigEntry<KeyCode> SelectorModifier;

		internal readonly ConfigEntry<KeyCode> SelectorKey;

		internal readonly ConfigEntry<KeyCode> CloseSelectorKey;

		internal readonly ConfigEntry<bool> ShowHoverHint;

		internal readonly ConfigEntry<bool> PullKilnWood;

		internal readonly ConfigEntry<bool> ShowUnavailableInputs;

		internal readonly ConfigEntry<SelectionPersistenceMode> SelectionPersistence;

		internal readonly ConfigEntry<bool> RespectContainerAccess;

		internal readonly ConfigEntry<bool> RespectWards;

		internal readonly ConfigEntry<bool> SkipInUseContainers;

		internal readonly ConfigEntry<bool> RespectMultiUserChestIgnore;

		internal readonly ConfigEntry<bool> DebugLogging;

		internal ModConfig(ConfigFile config)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable Manual Station Pull.");
			RangeMeters = config.Bind<float>("General", "RangeMeters", 20f, new ConfigDescription("Nearby-container search range in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			SpatialCacheSeconds = config.Bind<float>("General", "SpatialCacheSeconds", 0.35f, new ConfigDescription("How long nearby-container spatial results are reused. Access checks still run on every interaction.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			SelectorModifier = config.Bind<KeyCode>("Controls", "SelectorModifier", (KeyCode)308, "Modifier used with SelectorKey to open the ore selector while aiming at a furnace input. Set None for a single-key shortcut.");
			SelectorKey = config.Bind<KeyCode>("Controls", "SelectorKey", (KeyCode)282, "Key used with SelectorModifier to open the ore selector while aiming at a furnace input.");
			CloseSelectorKey = config.Bind<KeyCode>("Controls", "CloseSelectorKey", (KeyCode)27, "Key used to close the selector.");
			ShowHoverHint = config.Bind<bool>("Controls", "ShowHoverHint", true, "Show current selection and selector shortcut in hover text.");
			PullKilnWood = config.Bind<bool>("Stations", "PullKilnWood", true, "Allow charcoal kilns to manually pull exact vanilla Wood through their normal input path. FineWood, RoundLog, YggdrasilWood and other woods are never offered or pulled by this feature.");
			ShowUnavailableInputs = config.Bind<bool>("Stations", "ShowUnavailableInputs", false, "Show station-compatible inputs with zero availability as disabled rows in the selector.");
			SelectionPersistence = config.Bind<SelectionPersistenceMode>("Selection", "Persistence", SelectionPersistenceMode.World, "Session = forget choices on restart. World = remember each local player's choice per world and station ZDO.");
			RespectContainerAccess = config.Bind<bool>("Safety", "RespectContainerAccess", true, "Respect Container.CheckAccess(long). Recommended with SimpleAccessControl/private containers.");
			RespectWards = config.Bind<bool>("Safety", "RespectWards", true, "Ignore containers blocked by ward/private-area access.");
			SkipInUseContainers = config.Bind<bool>("Safety", "SkipInUseContainers", true, "Ignore containers currently being used by another interaction.");
			RespectMultiUserChestIgnore = config.Bind<bool>("Safety", "RespectMultiUserChestIgnore", true, "Ignore containers marked MUC_Ignore.");
			DebugLogging = config.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable detailed diagnostic logging.");
		}
	}
	internal sealed class SelectionStore
	{
		private readonly ModConfig _settings;

		private readonly Dictionary<string, string> _session = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> _world = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly string _path;

		private bool _loaded;

		private bool _dirty;

		internal SelectionStore(ModConfig settings)
		{
			_settings = settings;
			_path = Path.Combine(Paths.ConfigPath, "Zephyrfit.ManualStationPull.selections.tsv");
		}

		internal bool TryGet(Smelter smelter, Player player, out string prefabName)
		{
			prefabName = null;
			string text = BuildKey(smelter, player);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			if (_settings.SelectionPersistence.Value == SelectionPersistenceMode.Session)
			{
				return _session.TryGetValue(text, out prefabName);
			}
			EnsureLoaded();
			return _world.TryGetValue(text, out prefabName);
		}

		internal void Set(Smelter smelter, Player player, string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return;
			}
			string text = BuildKey(smelter, player);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			if (!_session.TryGetValue(text, out var value) || !string.Equals(value, prefabName, StringComparison.Ordinal))
			{
				_session[text] = prefabName;
			}
			if (_settings.SelectionPersistence.Value == SelectionPersistenceMode.World)
			{
				EnsureLoaded();
				if (!_world.TryGetValue(text, out var value2) || !string.Equals(value2, prefabName, StringComparison.Ordinal))
				{
					_world[text] = prefabName;
					_dirty = true;
					Flush();
				}
			}
		}

		internal void Flush()
		{
			if (!_dirty || !_loaded)
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(_path));
				using (StreamWriter streamWriter = new StreamWriter(_path, append: false))
				{
					streamWriter.WriteLine("# ManualStationPull 0.2.x - local per-player station selections");
					foreach (KeyValuePair<string, string> item in _world)
					{
						streamWriter.WriteLine(item.Key + "\t" + item.Value);
					}
				}
				_dirty = false;
			}
			catch (Exception ex)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not save selection store: " + ex.Message));
				}
			}
		}

		internal unsafe static string StationCacheKey(Smelter smelter)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)smelter == (Object)null)
			{
				return "station:null";
			}
			try
			{
				ZNetView obj = ((Component)smelter).GetComponent<ZNetView>() ?? ((Component)smelter).GetComponentInParent<ZNetView>();
				ZDO val = ((obj != null) ? obj.GetZDO() : null);
				if (val != null)
				{
					ZDOID uid = val.m_uid;
					return "zdo:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
				}
			}
			catch
			{
			}
			return "unity:" + ((Object)smelter).GetInstanceID();
		}

		private string BuildKey(Smelter smelter, Player player)
		{
			long num = 0L;
			try
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					num = ZNet.instance.GetWorldUID();
				}
			}
			catch
			{
			}
			long num2 = 0L;
			try
			{
				if ((Object)(object)player != (Object)null)
				{
					num2 = player.GetPlayerID();
				}
			}
			catch
			{
			}
			return num + "|" + num2 + "|" + StationCacheKey(smelter);
		}

		private void EnsureLoaded()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			if (!File.Exists(_path))
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(_path);
				foreach (string text in array)
				{
					if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#"))
					{
						int num = text.LastIndexOf('\t');
						if (num > 0 && num < text.Length - 1)
						{
							string key = text.Substring(0, num);
							string value = text.Substring(num + 1);
							_world[key] = value;
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not load selection store: " + ex.Message));
				}
			}
		}
	}
	internal sealed class SelectorHotkeyService
	{
		private readonly ModConfig _settings;

		private readonly StationInputService _stations;

		private static readonly FieldInfo AddOreSwitchField = AccessTools.Field(typeof(Smelter), "m_addOreSwitch");

		private Smelter _hoveredSmelter;

		private float _lastHoverAt = -999f;

		internal SelectorHotkeyService(ModConfig settings, StationInputService stations)
		{
			_settings = settings;
			_stations = stations;
		}

		internal void MarkHovered(Smelter smelter)
		{
			if (!((Object)(object)smelter == (Object)null))
			{
				_hoveredSmelter = smelter;
				_lastHoverAt = Time.unscaledTime;
			}
		}

		internal void Tick()
		{
			ManualStationPullPlugin instance = ManualStationPullPlugin.Instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || !_settings.Enabled.Value || (Object)(object)instance.SelectorUI == (Object)null || instance.SelectorUI.IsOpen || !ShortcutPressed())
			{
				return;
			}
			if ((Object)(object)_hoveredSmelter == (Object)null || Time.unscaledTime - _lastHoverAt > 0.2f)
			{
				((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Vise l'entrée de la fonderie pour choisir le minerai.", "Aim at the furnace input to select a material."), 0, (Sprite)null, false);
				return;
			}
			Smelter hoveredSmelter = _hoveredSmelter;
			if (StationInputService.IsCharcoalKiln(hoveredSmelter))
			{
				((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Le four à charbon utilise uniquement du bois classique.", "The charcoal kiln only uses standard Wood."), 0, (Sprite)null, false);
				return;
			}
			Switch val = ResolveOreSwitch(hoveredSmelter);
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not resolve m_addOreSwitch for " + ((Object)hoveredSmelter).name + "."));
				}
				((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Impossible d'ouvrir le sélecteur sur cette station.", "Could not open the selector for this station."), 0, (Sprite)null, false);
			}
			else
			{
				List<InputCandidate> candidates = _stations.GetCandidates(hoveredSmelter, localPlayer);
				if (candidates.Count == 0)
				{
					((Character)localPlayer).Message((MessageType)2, ManualStationPullPlugin.L("Aucune ressource compatible disponible à proximité.", "No compatible input is available nearby."), 0, (Sprite)null, false);
				}
				else
				{
					instance.SelectorUI.Open(hoveredSmelter, val, localPlayer, candidates);
				}
			}
		}

		internal string GetShortcutLabel()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			string text = FriendlyKeyName(_settings.SelectorKey.Value);
			if ((int)_settings.SelectorModifier.Value == 0)
			{
				return text;
			}
			return FriendlyKeyName(_settings.SelectorModifier.Value) + " + " + text;
		}

		private bool ShortcutPressed()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			KeyCode value = _settings.SelectorKey.Value;
			if ((int)value == 0 || !Input.GetKeyDown(value))
			{
				return false;
			}
			KeyCode value2 = _settings.SelectorModifier.Value;
			if ((int)value2 == 0)
			{
				return true;
			}
			return IsHeld(value2);
		}

		private static bool IsHeld(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
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			switch (key - 303)
			{
			case 4:
			case 5:
				if (!Input.GetKey((KeyCode)308))
				{
					return Input.GetKey((KeyCode)307);
				}
				return true;
			case 2:
			case 3:
				if (!Input.GetKey((KeyCode)306))
				{
					return Input.GetKey((KeyCode)305);
				}
				return true;
			case 0:
			case 1:
				if (!Input.GetKey((KeyCode)304))
				{
					return Input.GetKey((KeyCode)303);
				}
				return true;
			default:
				return Input.GetKey(key);
			}
		}

		internal unsafe static string FriendlyKeyName(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected I4, but got Unknown
			if ((int)key != 0)
			{
				switch (key - 303)
				{
				case 4:
				case 5:
					return "Alt";
				case 2:
				case 3:
					return "Ctrl";
				case 0:
				case 1:
					return "Shift";
				default:
					return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
				}
			}
			return "";
		}

		private static Switch ResolveOreSwitch(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null || AddOreSwitchField == null)
			{
				return null;
			}
			try
			{
				object? value = AddOreSwitchField.GetValue(smelter);
				return (Switch)((value is Switch) ? value : null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to resolve ore switch: " + ex.Message));
				}
				return null;
			}
		}
	}
	internal sealed class StationInputService
	{
		private sealed class ItemStackSnapshot
		{
			internal readonly ItemData Item;

			internal readonly int Stack;

			internal ItemStackSnapshot(ItemData item, int stack)
			{
				Item = item;
				Stack = stack;
			}
		}

		private sealed class TransferTransaction
		{
			private readonly Container _sourceContainer;

			private readonly Inventory _sourceInventory;

			private readonly Inventory _playerInventory;

			private readonly GameObject _prefab;

			private bool _finished;

			internal ItemData StagedItem { get; }

			internal TransferTransaction(Container sourceContainer, Inventory sourceInventory, Inventory playerInventory, GameObject prefab, ItemData stagedItem)
			{
				_sourceContainer = sourceContainer;
				_sourceInventory = sourceInventory;
				_playerInventory = playerInventory;
				_prefab = prefab;
				StagedItem = stagedItem;
			}

			internal void Commit()
			{
				if (!_finished)
				{
					SaveContainer(_sourceContainer);
					_finished = true;
				}
			}

			internal void Rollback()
			{
				if (_finished)
				{
					return;
				}
				try
				{
					if (_playerInventory != null && StagedItem != null)
					{
						_playerInventory.RemoveItem(StagedItem, 1);
					}
					if (_sourceInventory != null && (Object)(object)_prefab != (Object)null)
					{
						_sourceInventory.AddItem(_prefab, 1);
					}
					SaveContainer(_sourceContainer);
				}
				catch (Exception ex)
				{
					ManualLogSource log = ManualStationPullPlugin.Log;
					if (log != null)
					{
						log.LogError((object)("Rollback failed: " + ex.Message));
					}
				}
				finally
				{
					_finished = true;
				}
			}
		}

		private sealed class CountCacheEntry
		{
			internal float At;

			internal int Count;
		}

		private readonly ModConfig _settings;

		private readonly ContainerScanner _scanner;

		private readonly SelectionStore _selections;

		private readonly Dictionary<string, CountCacheEntry> _countCache = new Dictionary<string, CountCacheEntry>(StringComparer.Ordinal);

		private static readonly MethodInfo ContainerSave = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null);

		private static readonly MethodInfo SmelterGetQueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null);

		private static readonly MethodInfo SmelterOnAddOre = AccessTools.Method(typeof(Smelter), "OnAddOre", new Type[3]
		{
			typeof(Switch),
			typeof(Humanoid),
			typeof(ItemData)
		}, (Type[])null);

		[ThreadStatic]
		private static bool _bypass;

		internal static bool IsBypassing => _bypass;

		internal StationInputService(ModConfig settings, ContainerScanner scanner, SelectionStore selections)
		{
			_settings = settings;
			_scanner = scanner;
			_selections = selections;
		}

		internal List<InputCandidate> GetCandidates(Smelter smelter, Player player)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			List<InputCandidate> list = new List<InputCandidate>();
			if ((Object)(object)smelter == (Object)null || smelter.m_conversion == null || (Object)(object)player == (Object)null)
			{
				return list;
			}
			string cacheKey = SelectionStore.StationCacheKey(smelter);
			List<Container> eligibleContainers = _scanner.GetEligibleContainers(((Component)smelter).transform.position, player, cacheKey);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (ItemConversion item in smelter.m_conversion)
			{
				ItemDrop val = item?.m_from;
				if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null)
				{
					continue;
				}
				string prefabName = Utils.GetPrefabName(((Component)val).gameObject);
				if (string.IsNullOrEmpty(prefabName) || !hashSet.Add(prefabName) || (IsCharcoalKiln(smelter) && !string.Equals(prefabName, "Wood", StringComparison.Ordinal)))
				{
					continue;
				}
				int num = CountPrefab(((Humanoid)player).GetInventory(), prefabName);
				int num2 = 0;
				foreach (Container item2 in eligibleContainers)
				{
					num2 += CountPrefab(ContainerScanner.GetLoadedInventory(item2), prefabName);
				}
				int num3 = num + num2;
				if (num3 > 0 || _settings.ShowUnavailableInputs.Value)
				{
					list.Add(new InputCandidate
					{
						PrefabName = prefabName,
						SharedName = val.m_itemData.m_shared.m_name,
						DisplayName = LocalizeItem(val),
						Icon = SafeGetIcon(val),
						Carried = num,
						Nearby = num2,
						Total = num3,
						SourceDrop = val
					});
				}
			}
			return list.OrderByDescending((InputCandidate c) => c.Available).ThenBy<InputCandidate, string>((InputCandidate c) => c.DisplayName, StringComparer.CurrentCultureIgnoreCase).ToList();
		}

		internal bool TryAddByPrefab(Smelter smelter, Switch inputSwitch, Player player, string prefabName)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)smelter == (Object)null || (Object)(object)inputSwitch == (Object)null || (Object)(object)player == (Object)null || string.IsNullOrEmpty(prefabName))
			{
				return false;
			}
			if (!HasQueueSpace(smelter))
			{
				return false;
			}
			if (!IsAcceptedInput(smelter, prefabName))
			{
				return false;
			}
			ItemData val = FindPrefab(((Humanoid)player).GetInventory(), prefabName);
			if (val != null)
			{
				return InvokeVanillaOre(smelter, inputSwitch, player, val);
			}
			if (!TryBeginTransaction(((Component)smelter).transform.position, player, prefabName, SelectionStore.StationCacheKey(smelter), out var transaction))
			{
				return false;
			}
			try
			{
				if (InvokeVanillaOre(smelter, inputSwitch, player, transaction.StagedItem))
				{
					transaction.Commit();
					InvalidateCountCache(smelter, prefabName);
					return true;
				}
				transaction.Rollback();
				return false;
			}
			catch
			{
				transaction.Rollback();
				throw;
			}
		}

		internal int GetAvailableCountCached(Smelter smelter, Player player, string prefabName)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)smelter == (Object)null || (Object)(object)player == (Object)null || string.IsNullOrEmpty(prefabName))
			{
				return 0;
			}
			string key = SelectionStore.StationCacheKey(smelter) + "|" + player.GetPlayerID() + "|" + prefabName;
			float unscaledTime = Time.unscaledTime;
			if (_countCache.TryGetValue(key, out var value) && unscaledTime - value.At <= 0.35f)
			{
				return value.Count;
			}
			int num = CountPrefab(((Humanoid)player).GetInventory(), prefabName);
			foreach (Container eligibleContainer in _scanner.GetEligibleContainers(((Component)smelter).transform.position, player, SelectionStore.StationCacheKey(smelter)))
			{
				num += CountPrefab(ContainerScanner.GetLoadedInventory(eligibleContainer), prefabName);
			}
			_countCache[key] = new CountCacheEntry
			{
				At = unscaledTime,
				Count = num
			};
			return num;
		}

		internal bool IsAcceptedInput(Smelter smelter, string prefabName)
		{
			if (smelter?.m_conversion == null || string.IsNullOrEmpty(prefabName))
			{
				return false;
			}
			foreach (ItemConversion item in smelter.m_conversion)
			{
				ItemDrop val = item?.m_from;
				if (!((Object)(object)val == (Object)null))
				{
					string prefabName2 = Utils.GetPrefabName(((Component)val).gameObject);
					if ((!IsCharcoalKiln(smelter) || string.Equals(prefabName2, "Wood", StringComparison.Ordinal)) && string.Equals(prefabName2, prefabName, StringComparison.Ordinal))
					{
						return true;
					}
				}
			}
			return false;
		}

		internal ItemDrop FindConversionDrop(Smelter smelter, string prefabName)
		{
			if (smelter?.m_conversion == null || string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			foreach (ItemConversion item in smelter.m_conversion)
			{
				ItemDrop val = item?.m_from;
				if ((Object)(object)val != (Object)null && string.Equals(Utils.GetPrefabName(((Component)val).gameObject), prefabName, StringComparison.Ordinal))
				{
					return val;
				}
			}
			return null;
		}

		internal static string LocalizeItem(ItemDrop drop)
		{
			if (drop?.m_itemData?.m_shared == null)
			{
				return "?";
			}
			string name = drop.m_itemData.m_shared.m_name;
			if (Localization.instance == null)
			{
				return name;
			}
			return Localization.instance.Localize(name);
		}

		private bool TryBeginTransaction(Vector3 origin, Player player, string prefabName, string stationKey, out TransferTransaction transaction)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			transaction = null;
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			foreach (Container eligibleContainer in _scanner.GetEligibleContainers(origin, player, stationKey))
			{
				try
				{
					ZNetView containerView = ContainerScanner.GetContainerView(eligibleContainer);
					if ((Object)(object)containerView == (Object)null || !containerView.IsValid())
					{
						continue;
					}
					if (!containerView.IsOwner())
					{
						containerView.ClaimOwnership();
					}
					Inventory loadedInventory = ContainerScanner.GetLoadedInventory(eligibleContainer);
					ItemData val = FindPrefab(loadedInventory, prefabName);
					if (loadedInventory != null && val != null && !((Object)(object)val.m_dropPrefab == (Object)null))
					{
						ItemData val2 = val.Clone();
						val2.m_stack = 1;
						if (!TryInsertUniqueStagedItem(inventory, val2, prefabName, out var stagedItem))
						{
							((Character)player).Message((MessageType)2, ManualStationPullPlugin.L("Garde une case libre dans ton inventaire pour le transfert.", "Keep one free inventory slot for the transfer."), 0, (Sprite)null, false);
							return false;
						}
						int num = CountPrefab(loadedInventory, prefabName);
						bool num2 = loadedInventory.RemoveItem(val, 1);
						int num3 = CountPrefab(loadedInventory, prefabName);
						if (num2 && num3 < num)
						{
							transaction = new TransferTransaction(eligibleContainer, loadedInventory, inventory, val.m_dropPrefab, stagedItem);
							Debug("Staged 1x " + prefabName + " from " + ((Object)eligibleContainer).name + ".");
							return true;
						}
						if (stagedItem != null)
						{
							inventory.RemoveItem(stagedItem, 1);
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log = ManualStationPullPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Skipped container '" + ((eligibleContainer != null) ? ((Object)eligibleContainer).name : null) + "': " + ex.GetType().Name + ": " + ex.Message));
					}
				}
			}
			return false;
		}

		private static bool TryInsertUniqueStagedItem(Inventory inventory, ItemData stagedTemplate, string expectedPrefabName, out ItemData stagedItem)
		{
			stagedItem = null;
			if (inventory == null || stagedTemplate == null || (Object)(object)stagedTemplate.m_dropPrefab == (Object)null)
			{
				return false;
			}
			try
			{
				List<ItemStackSnapshot> list = new List<ItemStackSnapshot>();
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (MatchesPrefab(allItem, expectedPrefabName))
					{
						list.Add(new ItemStackSnapshot(allItem, allItem.m_stack));
					}
				}
				stagedTemplate.m_stack = 1;
				if (!inventory.AddItem(stagedTemplate))
				{
					return false;
				}
				foreach (ItemData item in inventory.GetAllItems())
				{
					if (!MatchesPrefab(item, expectedPrefabName))
					{
						continue;
					}
					ItemStackSnapshot itemStackSnapshot = list.FirstOrDefault((ItemStackSnapshot snapshot) => snapshot.Item == item);
					if (itemStackSnapshot == null)
					{
						if (item.m_stack >= 1)
						{
							stagedItem = item;
							return true;
						}
					}
					else if (item.m_stack > itemStackSnapshot.Stack)
					{
						stagedItem = item;
						return true;
					}
				}
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"Inventory.AddItem succeeded but the staged item could not be identified.");
				}
				return false;
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = ManualStationPullPlugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not stage an inventory item: " + ex.GetType().Name + ": " + ex.Message));
				}
				return false;
			}
		}

		private static bool MatchesPrefab(ItemData item, string expectedPrefabName)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null || string.IsNullOrEmpty(expectedPrefabName))
			{
				return false;
			}
			return string.Equals(Utils.GetPrefabName(item.m_dropPrefab), expectedPrefabName, StringComparison.Ordinal);
		}

		private static bool HasQueueSpace(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null)
			{
				return false;
			}
			try
			{
				if (SmelterGetQueueSize == null)
				{
					ManualLogSource log = ManualStationPullPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)"Smelter.GetQueueSize reflection handle was not found.");
					}
					return true;
				}
				object obj = SmelterGetQueueSize.Invoke(smelter, null);
				return ((obj is int num) ? num : Convert.ToInt32(obj)) < smelter.m_maxOre;
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = ManualStationPullPlugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not read smelter queue size: " + ex.Message));
				}
				return true;
			}
		}

		private static bool InvokeVanillaOre(Smelter smelter, Switch inputSwitch, Player player, ItemData item)
		{
			if (SmelterOnAddOre == null)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogError((object)"Smelter.OnAddOre reflection handle was not found.");
				}
				return false;
			}
			try
			{
				_bypass = true;
				object obj = SmelterOnAddOre.Invoke(smelter, new object[3] { inputSwitch, player, item });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			finally
			{
				_bypass = false;
			}
		}

		internal static bool IsCharcoalKiln(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null)
			{
				return false;
			}
			string text = Utils.GetPrefabName(((Component)smelter).gameObject) ?? string.Empty;
			if (text.IndexOf("charcoal", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("kiln", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			if (smelter.m_conversion != null)
			{
				foreach (ItemConversion item in smelter.m_conversion)
				{
					ItemDrop val = item?.m_from;
					if ((Object)(object)val != (Object)null && string.Equals(Utils.GetPrefabName(((Component)val).gameObject), "Wood", StringComparison.Ordinal) && (Object)(object)smelter.m_fuelItem == (Object)null)
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static ItemData FindPrefab(Inventory inventory, string prefabName)
		{
			if (inventory == null || string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && string.Equals(Utils.GetPrefabName(allItem.m_dropPrefab), prefabName, StringComparison.Ordinal))
				{
					return allItem;
				}
			}
			return null;
		}

		internal static int CountPrefab(Inventory inventory, string prefabName)
		{
			if (inventory == null || string.IsNullOrEmpty(prefabName))
			{
				return 0;
			}
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && string.Equals(Utils.GetPrefabName(allItem.m_dropPrefab), prefabName, StringComparison.Ordinal))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		private static Sprite SafeGetIcon(ItemDrop drop)
		{
			try
			{
				object result;
				if (drop == null)
				{
					result = null;
				}
				else
				{
					ItemData itemData = drop.m_itemData;
					result = ((itemData != null) ? itemData.GetIcon() : null);
				}
				return (Sprite)result;
			}
			catch
			{
				return null;
			}
		}

		private void InvalidateCountCache(Smelter smelter, string prefabName)
		{
			string prefix = SelectionStore.StationCacheKey(smelter) + "|";
			foreach (string item in _countCache.Keys.Where((string k) => k.StartsWith(prefix, StringComparison.Ordinal) && k.EndsWith("|" + prefabName, StringComparison.Ordinal)).ToList())
			{
				_countCache.Remove(item);
			}
		}

		private static void SaveContainer(Container container)
		{
			if ((Object)(object)container == (Object)null || ContainerSave == null)
			{
				return;
			}
			try
			{
				ContainerSave.Invoke(container, null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not save container '" + ((Object)container).name + "': " + ex.Message));
				}
			}
		}

		private static void Debug(string message)
		{
			ManualStationPullPlugin instance = ManualStationPullPlugin.Instance;
			if (instance != null && instance.Settings?.DebugLogging.Value == true)
			{
				ManualLogSource log = ManualStationPullPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}
	}
}