Decompiled source of Quads Craft From Chests v1.5.2

plugins/CraftFromChests.dll

Decompiled 13 hours ago
using System;
using System.Collections;
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 System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DHack.Shared;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI (Claude Code).")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.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 CraftFromChests
{
	internal class ChestOverlay : MonoBehaviour
	{
		private class ModButton
		{
			public GameObject Root;

			public TMP_Text[] Tmp;

			public Text[] Legacy;

			public Func<string> Label;

			public void Refresh()
			{
				string text = Label();
				TMP_Text[] tmp = Tmp;
				foreach (TMP_Text val in tmp)
				{
					if ((Object)(object)val != (Object)null && val.text != text)
					{
						val.text = text;
					}
				}
				Text[] legacy = Legacy;
				foreach (Text val2 in legacy)
				{
					if ((Object)(object)val2 != (Object)null && val2.text != text)
					{
						val2.text = text;
					}
				}
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__ToggleLines;

			public static UnityAction <1>__CycleRange;
		}

		private static readonly float[] RangeSteps = new float[5] { 5f, 10f, 15f, 20f, 30f };

		private readonly List<LineRenderer> _lines = new List<LineRenderer>();

		private Material _lineMaterial;

		private ModButton _linesButton;

		private ModButton _rangeButton;

		private bool _buttonsFailed;

		private float[] _layoutKey;

		private void Update()
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			if (InventoryGui.IsVisible())
			{
				EnsureButtons();
			}
			ChestScanner.RefreshWhenSettled();
			Player localPlayer = Player.m_localPlayer;
			CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null);
			bool flag = (Object)(object)localPlayer != (Object)null && InventoryGui.IsVisible() && Plugin.Enabled.Value && ((Object)(object)val != (Object)null || ChestScanner.HandCrafting()) && OtherChestMods.Which == null;
			if (Alive(_linesButton))
			{
				_linesButton.Root.SetActive(flag);
			}
			if (Alive(_rangeButton))
			{
				_rangeButton.Root.SetActive(flag);
			}
			Vector3? origin = ((!flag || !Plugin.ShowLines.Value) ? ((Vector3?)null) : new Vector3?(((Object)(object)val != (Object)null) ? ((Component)val).transform.position : ((Component)localPlayer).transform.position));
			DrawLines(origin);
		}

		private void EnsureButtons()
		{
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			InventoryGui instance = InventoryGui.instance;
			if (_buttonsFailed || (Object)(object)instance == (Object)null || (Object)(object)instance.m_tabUpgrade == (Object)null)
			{
				return;
			}
			if (!Alive(_linesButton) || !Alive(_rangeButton))
			{
				DestroyButtons();
				_layoutKey = null;
				try
				{
					object obj = <>O.<0>__ToggleLines;
					if (obj == null)
					{
						UnityAction val = ToggleLines;
						<>O.<0>__ToggleLines = val;
						obj = (object)val;
					}
					_linesButton = CreateButton(instance, "CFC_LineToggle", (UnityAction)obj, () => Translations.T("craftfromchests_lines", Localization.instance.Localize(Plugin.ShowLines.Value ? "$hud_on" : "$hud_off").ToUpperInvariant()));
					object obj2 = <>O.<1>__CycleRange;
					if (obj2 == null)
					{
						UnityAction val2 = CycleRange;
						<>O.<1>__CycleRange = val2;
						obj2 = (object)val2;
					}
					_rangeButton = CreateButton(instance, "CFC_RangeToggle", (UnityAction)obj2, () => Translations.T("craftfromchests_range", Plugin.Radius.Value.ToString("0.#")));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Could not create the mod buttons: " + ex));
					DestroyButtons();
					_buttonsFailed = true;
					return;
				}
			}
			RectTransform val3 = (RectTransform)((Component)instance.m_tabUpgrade).transform;
			float[] array = new float[10]
			{
				val3.anchoredPosition.x,
				val3.anchoredPosition.y,
				val3.sizeDelta.x,
				val3.sizeDelta.y,
				((Transform)val3).localScale.x,
				Plugin.ToggleWidth.Value,
				Plugin.RangeButtonWidth.Value,
				Plugin.ToggleOffsetX.Value,
				Plugin.ToggleOffsetY.Value,
				Plugin.ToggleScale.Value
			};
			if (!SameLayout(array, _layoutKey))
			{
				float leftEdge = val3.anchoredPosition.x - val3.pivot.x * val3.sizeDelta.x + val3.sizeDelta.x;
				leftEdge = Place(_linesButton, val3, leftEdge, Plugin.ToggleWidth.Value);
				Place(_rangeButton, val3, leftEdge, Plugin.RangeButtonWidth.Value);
				_layoutKey = array;
			}
			_linesButton.Refresh();
			_rangeButton.Refresh();
		}

		private static bool SameLayout(float[] a, float[] b)
		{
			if (b == null || a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}

		private ModButton CreateButton(InventoryGui gui, string name, UnityAction onClick, Func<string> label)
		{
			Transform parent = ((Component)gui.m_tabUpgrade).transform.parent;
			Transform val = parent.Find(name);
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)gui.m_tabUpgrade).gameObject, parent);
			((Object)val2).name = name;
			Image component = val2.GetComponent<Image>();
			MonoBehaviour[] components = val2.GetComponents<MonoBehaviour>();
			foreach (MonoBehaviour val3 in components)
			{
				if (val3 is Button || ((object)val3).GetType().Name == "ButtonTextColor")
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			Button val4 = val2.AddComponent<Button>();
			((Selectable)val4).targetGraphic = (Graphic)(object)component;
			((UnityEvent)val4.onClick).AddListener(onClick);
			TMP_Text[] componentsInChildren = val2.GetComponentsInChildren<TMP_Text>(true);
			TMP_Text[] array = componentsInChildren;
			foreach (TMP_Text val5 in array)
			{
				val5.fontSizeMax = val5.fontSize;
				val5.fontSizeMin = val5.fontSize * 0.6f;
				val5.enableAutoSizing = true;
				val5.textWrappingMode = (TextWrappingModes)0;
			}
			return new ModButton
			{
				Root = val2,
				Tmp = componentsInChildren,
				Legacy = val2.GetComponentsInChildren<Text>(true),
				Label = label
			};
		}

		private static float Place(ModButton b, RectTransform tab, float leftEdge, float widthMultiple)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_0037: 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)
			//IL_0052: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)b.Root.transform;
			val.anchorMin = tab.anchorMin;
			val.anchorMax = tab.anchorMax;
			val.pivot = tab.pivot;
			((Transform)val).localScale = ((Transform)tab).localScale * Plugin.ToggleScale.Value;
			val.sizeDelta = new Vector2(tab.sizeDelta.x * widthMultiple, tab.sizeDelta.y);
			float num = leftEdge + 8f;
			val.anchoredPosition = new Vector2(num + val.pivot.x * val.sizeDelta.x + Plugin.ToggleOffsetX.Value, tab.anchoredPosition.y + Plugin.ToggleOffsetY.Value);
			return num + val.sizeDelta.x;
		}

		private static void ToggleLines()
		{
			Plugin.ShowLines.Value = !Plugin.ShowLines.Value;
		}

		private static void CycleRange()
		{
			if (Plugin.Synced != null && Plugin.Synced.FromServer)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, Translations.T("craftfromchests_server_range"), 0, (Sprite)null, false);
				}
				return;
			}
			float value = Plugin.Radius.Value;
			float[] rangeSteps = RangeSteps;
			foreach (float num in rangeSteps)
			{
				if (num > value + 0.01f)
				{
					Plugin.Radius.Value = num;
					return;
				}
			}
			Plugin.Radius.Value = RangeSteps[0];
		}

		private static bool Alive(ModButton b)
		{
			if (b != null)
			{
				return (Object)(object)b.Root != (Object)null;
			}
			return false;
		}

		private void DestroyButtons()
		{
			if (Alive(_linesButton))
			{
				Object.Destroy((Object)(object)_linesButton.Root);
			}
			if (Alive(_rangeButton))
			{
				Object.Destroy((Object)(object)_rangeButton.Root);
			}
			_linesButton = (_rangeButton = null);
		}

		private void OnDestroy()
		{
			DestroyButtons();
			if ((Object)(object)_lineMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_lineMaterial);
			}
		}

		private void DrawLines(Vector3? origin)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = (origin.HasValue ? ChestScanner.GetNearby() : null);
			int num = list?.Count ?? 0;
			while (_lines.Count < num)
			{
				_lines.Add(CreateLine());
			}
			float num2 = 0.65f + 0.35f * Mathf.Sin(Time.time * 3f);
			Color val = default(Color);
			((Color)(ref val))..ctor(0.3f, 0.9f, 1f, num2);
			for (int i = 0; i < _lines.Count; i++)
			{
				LineRenderer val2 = _lines[i];
				((Component)val2).gameObject.SetActive(i < num);
				if (i < num)
				{
					Color startColor = (val2.endColor = val);
					val2.startColor = startColor;
					val2.SetPosition(0, origin.Value + Vector3.up * 1f);
					val2.SetPosition(1, ((Component)list[i]).transform.position + Vector3.up * 0.6f);
				}
			}
		}

		private LineRenderer CreateLine()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			if ((Object)(object)_lineMaterial == (Object)null)
			{
				Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default") ?? Shader.Find("Hidden/Internal-Colored");
				_lineMaterial = new Material(val);
			}
			GameObject val2 = new GameObject("CFC_Line");
			val2.transform.SetParent(((Component)this).transform, false);
			LineRenderer val3 = val2.AddComponent<LineRenderer>();
			((Renderer)val3).material = _lineMaterial;
			val3.positionCount = 2;
			val3.useWorldSpace = true;
			float startWidth = (val3.endWidth = 0.05f);
			val3.startWidth = startWidth;
			val3.numCapVertices = 4;
			return val3;
		}
	}
	internal static class ChestScanner
	{
		private class Claim
		{
			public float At;

			public float RetryAt = -1f;
		}

		internal struct Need
		{
			public string Name;

			public int Amount;

			public int Quality;
		}

		private static readonly List<Container> AllContainers = new List<Container>();

		private static readonly List<Container> Nearby = new List<Container>();

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

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

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

		internal static bool Suspend;

		internal static bool Consuming;

		private static int _contextFrame = -1;

		private static bool _contextActive;

		private static float _nearbyAt = -10f;

		private static Vector3 _nearbyOrigin;

		private const float Settle = 1f;

		private const float Retry = 5f;

		private const float Forget = 10f;

		private static readonly Dictionary<(long, uint), Claim> Claims = new Dictionary<(long, uint), Claim>();

		private static float _prunedAt;

		private static float _refreshAt = -1f;

		private static readonly MethodInfo UpdateCraftingPanel = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null);

		private static int _countFrame = -1;

		private static readonly Dictionary<string, Dictionary<int, int>> Counts = new Dictionary<string, Dictionary<int, int>>();

		internal static void Register(Container c)
		{
			if ((Object)(object)c != (Object)null && !((Object)c).name.StartsWith("piece_recycler") && !AllContainers.Contains(c))
			{
				AllContainers.Add(c);
			}
		}

		private static bool CraftingNow()
		{
			if (_contextFrame == Time.frameCount)
			{
				return _contextActive;
			}
			_contextFrame = Time.frameCount;
			Player localPlayer = Player.m_localPlayer;
			_contextActive = (Object)(object)localPlayer != (Object)null && ((Object)(object)localPlayer.GetCurrentCraftingStation() != (Object)null || HandCrafting());
			return _contextActive;
		}

		internal static bool Applies(Inventory inv)
		{
			if (Suspend || !Plugin.Enabled.Value)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || inv != ((Humanoid)localPlayer).GetInventory())
			{
				return false;
			}
			if (CraftingNow())
			{
				return OtherChestMods.Which == null;
			}
			return false;
		}

		internal static bool HandCrafting()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null && InventoryGui.IsVisible())
			{
				return instance.InCraftTab();
			}
			return false;
		}

		internal static List<Container> GetNearby()
		{
			//IL_0049: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !CraftingNow())
			{
				Nearby.Clear();
				return Nearby;
			}
			CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
			Vector3 val = (((Object)(object)currentCraftingStation != (Object)null) ? ((Component)currentCraftingStation).transform.position : ((Component)localPlayer).transform.position);
			Vector3 val2;
			if (Time.unscaledTime - _nearbyAt < 0.25f)
			{
				val2 = val - _nearbyOrigin;
				if (((Vector3)(ref val2)).sqrMagnitude < 0.04f)
				{
					Nearby.RemoveAll((Container c) => (Object)(object)c == (Object)null);
					return Nearby;
				}
			}
			_nearbyAt = Time.unscaledTime;
			_nearbyOrigin = val;
			InvalidateCounts();
			Nearby.Clear();
			float num = Plugin.Radius.Value * Plugin.Radius.Value;
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			AllContainers.RemoveAll((Container c) => (Object)(object)c == (Object)null);
			foreach (Container allContainer in AllContainers)
			{
				val2 = ((Component)allContainer).transform.position - val;
				if (!(((Vector3)(ref val2)).sqrMagnitude > num) && allContainer.GetInventory() != null && IsStorage(allContainer) && !InUse(allContainer) && (!allContainer.m_checkGuardStone || PrivateArea.CheckAccess(((Component)allContainer).transform.position, 0f, false, false)) && CheckAccess(allContainer, playerID))
				{
					Nearby.Add(allContainer);
				}
			}
			return Nearby;
		}

		private static bool IsStorage(Container c)
		{
			if ((Object)(object)((Component)c).GetComponentInParent<Piece>() == (Object)null || (Object)(object)((Component)c).GetComponent<TombStone>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)c).GetComponentInParent<Character>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Vagon>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Ship>() != (Object)null)
			{
				return false;
			}
			if (SpecialContainers.NotStorage(c))
			{
				return false;
			}
			object? value = NView.GetValue(c);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val != (Object)null && val.IsValid())
			{
				return val.GetZDO().GetLong("dhc_home", 0L) == 0;
			}
			return false;
		}

		private static bool InUse(Container c)
		{
			if (c.IsInUse())
			{
				return true;
			}
			object? value = NView.GetValue(c);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val != (Object)null && val.IsValid() && !val.IsOwner())
			{
				return val.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1;
			}
			return false;
		}

		private static (long, uint) Id(ZDOID uid)
		{
			return (((ZDOID)(ref uid)).UserID, ((ZDOID)(ref uid)).ID);
		}

		private static Claim ClaimOn((long, uint) id, float now)
		{
			if (!Claims.TryGetValue(id, out var value))
			{
				return null;
			}
			if ((value.RetryAt >= 0f) ? (now < value.RetryAt) : (now - value.At < 10f))
			{
				return value;
			}
			Claims.Remove(id);
			return null;
		}

		private static bool Settled(Container c)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			object? value = NView.GetValue(c);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			float unscaledTime = Time.unscaledTime;
			Claim claim = ClaimOn(Id(val.GetZDO().m_uid), unscaledTime);
			if (!val.IsOwner())
			{
				if (claim != null && claim.RetryAt < 0f)
				{
					claim.RetryAt = unscaledTime + 5f;
				}
				return false;
			}
			if (claim != null)
			{
				return unscaledTime - claim.At >= 1f;
			}
			return true;
		}

		private static bool Ready(Container c)
		{
			if (!Settled(c))
			{
				return false;
			}
			Load?.Invoke(c, null);
			return true;
		}

		private static void Request(Container c)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			object? value = NView.GetValue(c);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || !val.IsValid() || val.IsOwner() || InUse(c))
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _prunedAt > 30f)
			{
				_prunedAt = unscaledTime;
				foreach (var item in new List<(long, uint)>(Claims.Keys))
				{
					ClaimOn(item, unscaledTime);
				}
			}
			(long, uint) tuple = Id(val.GetZDO().m_uid);
			Claim claim = ClaimOn(tuple, unscaledTime);
			if (claim != null)
			{
				if (claim.RetryAt < 0f)
				{
					claim.RetryAt = unscaledTime + 5f;
				}
			}
			else
			{
				val.ClaimOwnership();
				Claims[tuple] = new Claim
				{
					At = unscaledTime
				};
				_refreshAt = unscaledTime + 1f + 0.1f;
			}
		}

		internal static void RefreshWhenSettled()
		{
			if (_refreshAt < 0f || Time.unscaledTime < _refreshAt)
			{
				return;
			}
			_refreshAt = -1f;
			if (UpdateCraftingPanel == null || (Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible() || !CraftingNow())
			{
				return;
			}
			try
			{
				UpdateCraftingPanel.Invoke(InventoryGui.instance, new object[1] { false });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not refresh the recipe list: " + ex.Message));
			}
		}

		private static void InvalidateCounts()
		{
			_countFrame = -1;
		}

		internal static int CountInChests(string name, int quality, bool matchWorldLevel)
		{
			if (_countFrame != Time.frameCount)
			{
				Counts.Clear();
				_countFrame = Time.frameCount;
			}
			if (!Counts.TryGetValue(name, out var value))
			{
				value = (Counts[name] = new Dictionary<int, int>());
			}
			int key = quality * 2 + (matchWorldLevel ? 1 : 0);
			if (value.TryGetValue(key, out var value2))
			{
				return value2;
			}
			int num = 0;
			foreach (Container item in GetNearby())
			{
				int num2 = item.GetInventory().CountItems(name, quality, matchWorldLevel);
				if (num2 > 0 && !InUse(item))
				{
					if (Settled(item))
					{
						num += num2;
					}
					else
					{
						Request(item);
					}
				}
			}
			value[key] = num;
			return num;
		}

		internal static int OwnCount(Inventory inv, string name, int quality, bool matchWorldLevel)
		{
			int num = 0;
			foreach (ItemData allItem in inv.GetAllItems())
			{
				if (allItem.m_shared.m_name == name && (quality < 0 || allItem.m_quality == quality) && (!matchWorldLevel || allItem.m_worldLevel >= Game.m_worldLevel))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		internal static bool Charged(Requirement req, CraftingStation station)
		{
			if ((Object)(object)req.m_resItem == (Object)null)
			{
				return false;
			}
			if (!((Object)(object)station != (Object)null))
			{
				return !req.m_upgraderResource;
			}
			return station.m_upgrader == req.m_upgraderResource;
		}

		internal static string FindShortfall(Inventory inv, List<Need> needs)
		{
			foreach (Need need in needs)
			{
				Suspend = true;
				int num;
				try
				{
					num = inv.CountItems(need.Name, need.Quality, true);
				}
				finally
				{
					Suspend = false;
				}
				int num2 = need.Amount - num;
				if (num2 <= 0)
				{
					continue;
				}
				int num3 = 0;
				foreach (Container item in GetNearby())
				{
					if (num3 >= num2)
					{
						break;
					}
					if ((Object)(object)item == (Object)null || InUse(item))
					{
						continue;
					}
					Inventory inventory = item.GetInventory();
					if (inventory.CountItems(need.Name, need.Quality, true) > 0)
					{
						if (!Ready(item))
						{
							Request(item);
						}
						else
						{
							num3 += inventory.CountItems(need.Name, need.Quality, true);
						}
					}
				}
				InvalidateCounts();
				if (num3 < num2)
				{
					return need.Name;
				}
			}
			return null;
		}

		internal static ItemData FindInChests(string name, int quality)
		{
			foreach (Container item in GetNearby())
			{
				ItemData val = (((Object)(object)item != (Object)null && Settled(item)) ? item.GetInventory().GetItem(name, quality, false) : null);
				if (val != null)
				{
					return val;
				}
			}
			return null;
		}

		internal static int RemoveFromChests(string name, int amount, int quality, bool matchWorldLevel)
		{
			foreach (Container item in GetNearby())
			{
				if (amount <= 0)
				{
					break;
				}
				if ((Object)(object)item == (Object)null || InUse(item))
				{
					continue;
				}
				Inventory inventory = item.GetInventory();
				if (inventory.CountItems(name, quality, matchWorldLevel) <= 0)
				{
					continue;
				}
				if (!Ready(item))
				{
					Request(item);
					continue;
				}
				int num = Math.Min(inventory.CountItems(name, quality, matchWorldLevel), amount);
				if (num > 0)
				{
					inventory.RemoveItem(name, num, quality, matchWorldLevel);
					amount -= num;
				}
			}
			InvalidateCounts();
			return amount;
		}
	}
	internal static class HaveLabel
	{
		private const string LabelName = "CFC_Have";

		internal static void Show(Transform elementRoot, Requirement req, int quality, int craftMultiplier)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			Transform val = elementRoot.Find("res_amount");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			TMP_Text orCreate = GetOrCreate(elementRoot, val);
			if (!((Object)(object)orCreate == (Object)null))
			{
				string name = req.m_resItem.m_itemData.m_shared.m_name;
				Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
				ChestScanner.Suspend = true;
				int num;
				try
				{
					num = inventory.CountItems(name, -1, true);
				}
				finally
				{
					ChestScanner.Suspend = false;
				}
				int num2 = ChestScanner.CountInChests(name, -1, matchWorldLevel: true);
				int num3 = num + num2;
				int num4 = req.GetAmount(quality) * craftMultiplier;
				((Component)orCreate).gameObject.SetActive(true);
				orCreate.text = num3.ToString();
				((Graphic)orCreate).color = ((num3 >= num4) ? new Color(0.55f, 1f, 0.6f) : new Color(1f, 0.62f, 0.45f));
				UITooltip component = ((Component)elementRoot).GetComponent<UITooltip>();
				if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.m_text))
				{
					component.m_text = component.m_text + "\n" + Translations.T("craftfromchests_have", num3, num, num2);
				}
			}
		}

		internal static void Hide(Transform elementRoot)
		{
			Transform val = (((Object)(object)elementRoot != (Object)null) ? elementRoot.Find("CFC_Have") : null);
			if ((Object)(object)val != (Object)null)
			{
				((Component)val).gameObject.SetActive(false);
			}
		}

		private static TMP_Text GetOrCreate(Transform elementRoot, Transform amount)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_004e: 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_0064: 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_0081: 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_009c: 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)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			Transform val = elementRoot.Find("CFC_Have");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).GetComponent<TMP_Text>();
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)amount).gameObject, elementRoot);
			((Object)val2).name = "CFC_Have";
			RectTransform val3 = (RectTransform)amount;
			RectTransform val4 = (RectTransform)val2.transform;
			val4.anchorMin = new Vector2(1f - val3.anchorMax.x, val3.anchorMin.y);
			val4.anchorMax = new Vector2(1f - val3.anchorMin.x, val3.anchorMax.y);
			val4.pivot = new Vector2(1f - val3.pivot.x, val3.pivot.y);
			val4.anchoredPosition = new Vector2(0f - val3.anchoredPosition.x, val3.anchoredPosition.y);
			val4.sizeDelta = val3.sizeDelta;
			TMP_Text component = val2.GetComponent<TMP_Text>();
			component.horizontalAlignment = (HorizontalAlignmentOptions)1;
			component.enableAutoSizing = false;
			component.fontSize = ((Component)amount).GetComponent<TMP_Text>().fontSize * 0.9f;
			return component;
		}

		internal static void DestroyAll()
		{
			if ((Object)(object)InventoryGui.instance == (Object)null)
			{
				return;
			}
			TMP_Text[] componentsInChildren = ((Component)InventoryGui.instance).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "CFC_Have")
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	internal static class InventoryGui_SetupRequirement
	{
		private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
		{
			try
			{
				bool flag = (Object)(object)InventoryGui.instance != (Object)null && elementRoot.IsChildOf(((Component)InventoryGui.instance).transform);
				if (flag && __result && Plugin.Enabled.Value && Plugin.ShowHaveCounts.Value && (Object)(object)req.m_resItem != (Object)null && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && ChestScanner.Applies(((Humanoid)player).GetInventory()))
				{
					HaveLabel.Show(elementRoot, req, quality, craftMultiplier);
				}
				else
				{
					HaveLabel.Hide(elementRoot);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not show the 'you have' count: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "HideRequirement")]
	internal static class InventoryGui_HideRequirement
	{
		private static void Postfix(Transform elementRoot)
		{
			HaveLabel.Hide(elementRoot);
		}
	}
	internal static class OtherChestMods
	{
		private static readonly string[][] Always = new string[7][]
		{
			new string[2] { "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes" },
			new string[2] { "aedenthorn.CraftFromContainers", "CraftFromContainers" },
			new string[2] { "toxo.craftfromchests", "Craft From Chests (Toxo)" },
			new string[2] { "poneis.valheim.CraftFromChestsPlus", "CraftFromChestsPlus" },
			new string[2] { "com.teflonted.valheim.craftfromchests", "Teflon Ted's Craft From Chests" },
			new string[2] { "com.morda.storeandcraft", "StoreAndCraft" },
			new string[2] { "com.zellds.smartcraftstorage", "SmartCraft-Storage" }
		};

		private const string ValheimPlus = "org.bepinex.plugins.valheim_plus";

		private static bool _looked;

		private static string _always;

		private static PropertyInfo _vpCurrent;

		private static PropertyInfo _vpSection;

		private static PropertyInfo _vpEnabled;

		private static float _vpReadAt = -10f;

		private static bool _vpOn;

		private static string _said;

		internal static string Which
		{
			get
			{
				if (!_looked)
				{
					Look();
				}
				string text = _always ?? (ValheimPlusOn() ? "ValheimPlus (its CraftFromChest section is on)" : null);
				if (text != _said)
				{
					_said = text;
					if (text != null)
					{
						Plugin.Log.LogWarning((object)(text + " also lets you craft from chests; CraftFromChests stands down so chests aren't counted twice"));
					}
					else
					{
						Plugin.Log.LogInfo((object)"CraftFromChests crafts from chests again (no other chest-crafting mod is doing it)");
					}
				}
				return text;
			}
		}

		private static void Look()
		{
			_looked = true;
			string[][] always = Always;
			foreach (string[] array in always)
			{
				if (_always == null && Chainloader.PluginInfos.ContainsKey(array[0]))
				{
					_always = array[1];
				}
			}
			if (!Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.valheim_plus", out var value) || !((Object)(object)value.Instance != (Object)null))
			{
				return;
			}
			try
			{
				Type type = ((object)value.Instance).GetType().Assembly.GetType("ValheimPlus.Configurations.Configuration");
				_vpCurrent = type?.GetProperty("Current", BindingFlags.Static | BindingFlags.Public);
				_vpSection = type?.GetProperty("CraftFromChest", BindingFlags.Instance | BindingFlags.Public);
				_vpEnabled = _vpSection?.PropertyType.GetProperty("IsEnabled", BindingFlags.Instance | BindingFlags.Public);
				if (_vpEnabled == null)
				{
					Plugin.Log.LogWarning((object)"ValheimPlus is installed but its CraftFromChest setting can't be read; if you turn it on, turn this mod off");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not read ValheimPlus' settings: " + ex.Message));
			}
		}

		private static bool ValheimPlusOn()
		{
			if (_vpEnabled == null)
			{
				return false;
			}
			if (Time.unscaledTime - _vpReadAt < 3f)
			{
				return _vpOn;
			}
			_vpReadAt = Time.unscaledTime;
			try
			{
				object value = _vpCurrent.GetValue(null, null);
				object obj = ((value != null) ? _vpSection.GetValue(value, null) : null);
				_vpOn = obj != null && (bool)_vpEnabled.GetValue(obj, null);
			}
			catch (Exception)
			{
				_vpOn = false;
			}
			return _vpOn;
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class Player_OnSpawned
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Plugin.Announce(reloaded: false);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake
	{
		private static void Postfix(Container __instance)
		{
			ChestScanner.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Inventory), "CountItems", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(bool)
	})]
	internal static class Inventory_CountItems
	{
		private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
		{
			if (name != null && ChestScanner.Applies(__instance))
			{
				__result += ChestScanner.CountInChests(name, quality, matchWorldLevel);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
	{
		typeof(string),
		typeof(bool)
	})]
	internal static class Inventory_HaveItem
	{
		private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
		{
			if (!__result && ChestScanner.Applies(__instance))
			{
				__result = ChestScanner.CountInChests(name, -1, matchWorldLevel) > 0;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources")]
	internal static class Player_ConsumeResources
	{
		private static void Prefix(out bool __state)
		{
			__state = ChestScanner.Consuming;
			ChestScanner.Consuming = true;
		}

		private static void Finalizer(bool __state)
		{
			ChestScanner.Consuming = __state;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class InventoryGui_DoCrafting
	{
		[HarmonyPriority(0)]
		private static bool Prefix(Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem, bool ___m_multiCrafting, int ___m_multiCraftAmount, bool __runOriginal, out bool __state)
		{
			__state = ChestScanner.Consuming;
			if (!__runOriginal)
			{
				return false;
			}
			if ((Object)(object)player == (Object)null || (Object)(object)___m_craftRecipe == (Object)null || !ChestScanner.Applies(((Humanoid)player).GetInventory()))
			{
				return true;
			}
			if (player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25))
			{
				return true;
			}
			int num = ((___m_craftUpgradeItem == null) ? 1 : (___m_craftUpgradeItem.m_quality + 1));
			int num2 = ((!___m_multiCrafting) ? 1 : ___m_multiCraftAmount);
			List<ChestScanner.Need> list = new List<ChestScanner.Need>();
			if (___m_craftRecipe.m_requireOnlyOneIngredient)
			{
				int amount = default(int);
				ItemData val = default(ItemData);
				try
				{
					___m_craftRecipe.GetAmount(num, ref amount, ref val, num2);
				}
				catch (Exception)
				{
					return true;
				}
				if (val == null)
				{
					return true;
				}
				list.Add(new ChestScanner.Need
				{
					Name = val.m_shared.m_name,
					Amount = amount,
					Quality = val.m_quality
				});
			}
			else
			{
				CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
				Requirement[] resources = ___m_craftRecipe.m_resources;
				foreach (Requirement val2 in resources)
				{
					if (ChestScanner.Charged(val2, currentCraftingStation))
					{
						int num3 = val2.GetAmount(num) * num2;
						if (num3 > 0)
						{
							list.Add(new ChestScanner.Need
							{
								Name = val2.m_resItem.m_itemData.m_shared.m_name,
								Amount = num3,
								Quality = -1
							});
						}
					}
				}
			}
			string text = ChestScanner.FindShortfall(((Humanoid)player).GetInventory(), list);
			if (text != null)
			{
				((Character)player).Message((MessageType)2, Translations.T("craftfromchests_short", Localization.instance.Localize(text)), 0, (Sprite)null, false);
				return false;
			}
			ChestScanner.Consuming = true;
			return true;
		}

		private static void Finalizer(bool __state)
		{
			ChestScanner.Consuming = __state;
		}
	}
	[HarmonyPatch(typeof(Player), "GetFirstRequiredItem")]
	internal static class Player_GetFirstRequiredItem
	{
		[HarmonyPriority(0)]
		private static void Postfix(Player __instance, Inventory inventory, Recipe recipe, int qualityLevel, int craftMultiplier, ref int amount, ref int extraAmount, ref ItemData __result)
		{
			if (__result != null || (Object)(object)recipe == (Object)null || !ChestScanner.Applies(inventory))
			{
				return;
			}
			CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation();
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (!ChestScanner.Charged(val, currentCraftingStation))
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * craftMultiplier;
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				for (int j = 1; j <= val.m_resItem.m_itemData.m_shared.m_maxQuality; j++)
				{
					if (inventory.CountItems(name, j, true) >= num)
					{
						ItemData val2 = ChestScanner.FindInChests(name, j);
						if (val2 != null)
						{
							amount = num;
							extraAmount = val.m_extraAmountOnlyOneIngredient;
							__result = val2;
							return;
						}
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class Inventory_RemoveItem
	{
		[HarmonyPriority(0)]
		private static bool Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased, bool __runOriginal)
		{
			if (!__runOriginal)
			{
				return false;
			}
			if (!ChestScanner.Consuming || !ChestScanner.Applies(__instance))
			{
				return true;
			}
			int num = ChestScanner.OwnCount(__instance, name, itemQuality, worldLevelBased);
			if (num >= amount)
			{
				return true;
			}
			int num2 = ChestScanner.RemoveFromChests(name, amount - num, itemQuality, worldLevelBased);
			if (num2 > 0)
			{
				Plugin.Log.LogWarning((object)$"Crafting: the chests were {num2} {name} short after all");
			}
			amount = num;
			return num > 0;
		}
	}
	[BepInPlugin("com.quad.craftfromchests", "CraftFromChests", "1.5.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.quad.craftfromchests";

		public const string OldGuid = "com.dhack.craftfromchests";

		public const string Name = "CraftFromChests";

		public const string Version = "1.5.2";

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> Radius;

		internal static ConfigEntry<bool> ShowHaveCounts;

		internal static ConfigEntry<bool> ShowLines;

		internal static ConfigEntry<float> ToggleOffsetX;

		internal static ConfigEntry<float> ToggleOffsetY;

		internal static ConfigEntry<float> ToggleScale;

		internal static ConfigEntry<float> ToggleWidth;

		internal static ConfigEntry<float> RangeButtonWidth;

		internal static ManualLogSource Log;

		internal static ServerSettings Synced;

		private Harmony _harmony;

		private static string BuildTime
		{
			get
			{
				try
				{
					string path = "CraftFromChests.dll";
					string text = Path.Combine(Paths.BepInExRootPath, "scripts", path);
					string text2 = Path.Combine(Paths.PluginPath, "CraftFromChests", path);
					string path2 = (File.Exists(text) ? text : text2);
					return File.GetLastWriteTime(path2).ToString("HH:mm:ss");
				}
				catch
				{
					return "?";
				}
			}
		}

		private void Awake()
		{
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Expected O, but got Unknown
			Migration.FromOldGuid((BaseUnityPlugin)(object)this, "com.dhack.craftfromchests");
			Translations.Load((BaseUnityPlugin)(object)this);
			Log = ((BaseUnityPlugin)this).Logger;
			Synced = new ServerSettings("com.quad.craftfromchests", ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			ShowHaveCounts = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowHaveCounts", true, "In the crafting window, show how many of each material you HAVE (inventory + chests) next to how many you need.");
			ShowLines = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowLines", false, "Draw lines from the crafting station to every chest it can use.");
			ToggleOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleOffsetX", 0f, "Move the 'Chest lines' button horizontally from its default spot beside the Upgrade tab (UI pixels).");
			ToggleOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleOffsetY", 0f, "Move the 'Chest lines' button vertically (UI pixels). Negative = down.");
			ToggleScale = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleScale", 1f, "Size multiplier for the 'Chest lines' button.");
			ToggleWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleWidth", 2f, "'Chest lines' button width as a multiple of the Upgrade tab's width.");
			RangeButtonWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "RangeButtonWidth", 1.4f, "'Range' button width as a multiple of the Upgrade tab's width.");
			((Component)this).gameObject.AddComponent<ChestOverlay>();
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the mod on or off.");
			Radius = Synced.Add<float>(((BaseUnityPlugin)this).Config.Bind<float>("General", "Radius", 20f, new ConfigDescription("How far (in meters) from the crafting station a chest can be and still be used. In multiplayer the server's value applies.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 50f), Array.Empty<object>())));
			_harmony = new Harmony("com.quad.craftfromchests");
			_harmony.PatchAll();
			Container[] array = Object.FindObjectsOfType<Container>();
			foreach (Container c in array)
			{
				ChestScanner.Register(c);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"CraftFromChests 1.5.2 loaded");
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Announce(reloaded: true);
			}
		}

		internal static void Announce(bool reloaded)
		{
			string text = Localization.instance.Localize(Enabled.Value ? "$hud_on" : "$hud_off").ToLowerInvariant();
			string text2 = Translations.T(reloaded ? "craftfromchests_chat_reloaded" : "craftfromchests_chat_loaded", "CraftFromChests", "1.5.2", text, Radius.Value.ToString("0.#"), BuildTime);
			if ((Object)(object)Chat.instance != (Object)null)
			{
				((Terminal)Chat.instance).AddString("[Mod]", text2, (Type)1, false);
				AccessTools.Field(typeof(Chat), "m_hideTimer").SetValue(Chat.instance, 0f);
				((Component)((Terminal)Chat.instance).m_chatWindow).gameObject.SetActive(true);
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)1, Translations.T(reloaded ? "craftfromchests_reloaded" : "craftfromchests_loaded", "CraftFromChests"), 0, (Sprite)null, false);
			}
		}

		private void Update()
		{
			Synced?.Update();
		}

		private void OnDestroy()
		{
			Translations.Unload();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Synced?.Dispose();
			ChestScanner.Suspend = false;
			ChestScanner.Consuming = false;
			HaveLabel.DestroyAll();
		}
	}
}
namespace DHack.Shared
{
	internal static class Migration
	{
		private sealed class Notice : MonoBehaviour
		{
			public string Text;

			private float _next;

			private void Update()
			{
				if (!(Time.unscaledTime < _next) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)MessageHud.instance == (Object)null))
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, Text, 0, (Sprite)null, false);
					_next = Time.unscaledTime + 60f;
				}
			}
		}

		public static void FromOldGuid(BaseUnityPlugin plugin, string oldGuid)
		{
			BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin);
			string text = ((metadata != null) ? metadata.GUID : null);
			if (string.IsNullOrEmpty(oldGuid) || string.IsNullOrEmpty(text) || oldGuid == text)
			{
				return;
			}
			ManualLogSource val = Logger.CreateLogSource(text);
			try
			{
				bool flag = false;
				if (Directory.Exists(Paths.ConfigPath))
				{
					string[] files = Directory.GetFiles(Paths.ConfigPath, oldGuid + ".*");
					foreach (string text2 in files)
					{
						string fileName = Path.GetFileName(text2);
						if (!fileName.EndsWith(".cfg") && !fileName.EndsWith(".json") && !fileName.EndsWith(".txt"))
						{
							continue;
						}
						string text3 = Path.Combine(Paths.ConfigPath, text + fileName.Substring(oldGuid.Length));
						if (!File.Exists(text3))
						{
							File.Copy(text2, text3);
							val.LogInfo((object)("Settings moved over from " + fileName + " (the mod's id is now " + text + "; the old file is kept as a backup)"));
							if (fileName == oldGuid + ".cfg")
							{
								flag = true;
							}
						}
					}
				}
				if (flag)
				{
					plugin.Config.Reload();
				}
			}
			catch (Exception ex)
			{
				val.LogWarning((object)("Couldn't move the settings over from " + oldGuid + ".cfg: " + ex.Message));
			}
			finally
			{
				Logger.Sources.Remove((ILogSource)(object)val);
			}
			((MonoBehaviour)plugin).StartCoroutine(StandDownIfTwin(plugin, oldGuid));
		}

		private static IEnumerator StandDownIfTwin(BaseUnityPlugin plugin, string oldGuid)
		{
			yield return null;
			yield return null;
			if (!((Object)(object)plugin == (Object)null) && Chainloader.PluginInfos.TryGetValue(oldGuid, out var value) && !((Object)(object)value.Instance == (Object)null) && !((Object)(object)value.Instance == (Object)(object)plugin))
			{
				BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin);
				string text = ((metadata != null) ? metadata.Name : null) ?? oldGuid;
				string text2 = (string.IsNullOrEmpty(value.Location) ? "an older copy" : value.Location);
				string text3 = text + ": two copies are installed, an old one (" + text2 + ") and this one. Both would run and do everything twice, so this one stands down. Delete the old copy, then restart the game.";
				Debug.LogError((object)("[" + text + "] " + text3));
				Object.Destroy((Object)(object)plugin);
				GameObject val = new GameObject("Migration_TwoCopies_" + text);
				Object.DontDestroyOnLoad((Object)(object)val);
				val.AddComponent<Notice>().Text = text3;
			}
		}
	}
	internal static class Translations
	{
		public static readonly string[] Shipped = new string[9] { "English", "Russian", "German", "Chinese", "Portuguese_Brazilian", "Spanish", "French", "Polish", "Turkish" };

		private static readonly Dictionary<string, Dictionary<string, string>> Languages = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		private static Dictionary<string, string> _english = new Dictionary<string, string>();

		private static string _overrideDir;

		private static bool _hooked;

		private static readonly MethodInfo AddWord = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null);

		private static readonly FieldInfo Cache = AccessTools.Field(typeof(Localization), "m_cache");

		public static void Load(BaseUnityPlugin plugin)
		{
			Languages.Clear();
			Assembly assembly = ((object)plugin).GetType().Assembly;
			foreach (string item in from n in assembly.GetManifestResourceNames()
				where n.StartsWith("translations/") && n.EndsWith(".json")
				select n)
			{
				using StreamReader streamReader = new StreamReader(assembly.GetManifestResourceStream(item));
				Languages[Path.GetFileNameWithoutExtension(item.Substring("translations/".Length))] = Parse(streamReader.ReadToEnd());
			}
			_english = (Languages.TryGetValue("English", out var value) ? value : new Dictionary<string, string>());
			_overrideDir = Path.Combine(Paths.ConfigPath, "translations", plugin.Info.Metadata.GUID);
			if (!_hooked)
			{
				Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(Apply));
				_hooked = true;
			}
			Apply();
		}

		public static void Unload()
		{
			if (_hooked)
			{
				Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(Apply));
				_hooked = false;
			}
		}

		public static string T(string key, params object[] args)
		{
			string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + key) : null);
			if (string.IsNullOrEmpty(text) || text == "[" + key + "]")
			{
				text = (_english.TryGetValue(key, out var value) ? value : key);
			}
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch (FormatException)
			{
				string value2;
				return _english.TryGetValue(key, out value2) ? SafeFormat(value2, args) : text;
			}
		}

		private static string SafeFormat(string text, object[] args)
		{
			try
			{
				return string.Format(text, args);
			}
			catch (FormatException)
			{
				return text;
			}
		}

		private static void Apply()
		{
			Localization instance = Localization.instance;
			if (instance == null || AddWord == null)
			{
				return;
			}
			string selectedLanguage = instance.GetSelectedLanguage();
			Dictionary<string, string> dictionary = new Dictionary<string, string>(_english);
			if (Languages.TryGetValue(selectedLanguage, out var value))
			{
				foreach (KeyValuePair<string, string> item in value)
				{
					dictionary[item.Key] = item.Value;
				}
			}
			try
			{
				string path = Path.Combine(_overrideDir, selectedLanguage + ".json");
				if (File.Exists(path))
				{
					foreach (KeyValuePair<string, string> item2 in Parse(File.ReadAllText(path)))
					{
						dictionary[item2.Key] = item2.Value;
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("Translations: couldn't read the player's " + selectedLanguage + " file: " + ex.Message));
			}
			foreach (KeyValuePair<string, string> item3 in dictionary)
			{
				AddWord.Invoke(instance, new object[2] { item3.Key, item3.Value });
			}
			object obj = Cache?.GetValue(instance);
			obj?.GetType().GetMethod("EvictAll")?.Invoke(obj, null);
		}

		private static Dictionary<string, string> Parse(string json)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (Match item in Regex.Matches(json, "\"((?:[^\"\\\\]|\\\\.)*)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\""))
			{
				dictionary[Unescape(item.Groups[1].Value)] = Unescape(item.Groups[2].Value);
			}
			return dictionary;
		}

		private static string Unescape(string s)
		{
			return Regex.Replace(s, "\\\\(u[0-9a-fA-F]{4}|.)", delegate(Match m)
			{
				string value = m.Groups[1].Value;
				return value[0] switch
				{
					'n' => "\n", 
					't' => "\t", 
					'u' => ((char)Convert.ToInt32(value.Substring(1), 16)).ToString(), 
					_ => value, 
				};
			});
		}
	}
	internal sealed class ServerSettings : IDisposable
	{
		private readonly string _send;

		private readonly string _ask;

		private readonly ConfigFile _config;

		private readonly ManualLogSource _log;

		private readonly List<ConfigEntryBase> _entries = new List<ConfigEntryBase>();

		private readonly Dictionary<ConfigEntryBase, object> _own = new Dictionary<ConfigEntryBase, object>();

		private readonly Dictionary<ConfigEntryBase, object> _server = new Dictionary<ConfigEntryBase, object>();

		private ZRoutedRpc _registeredOn;

		private bool _applying;

		private bool _changePending;

		private float _nextChangeSend;

		private const float ChangeSendGap = 0.5f;

		public bool FromServer => _server.Count > 0;

		private static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		public ServerSettings(string guid, ConfigFile config, ManualLogSource log)
		{
			_send = guid + ".settings";
			_ask = guid + ".settings.ask";
			_config = config;
			_log = log;
		}

		public ConfigEntry<T> Add<T>(ConfigEntry<T> entry)
		{
			if (entry == null || _entries.Contains((ConfigEntryBase)(object)entry))
			{
				return entry;
			}
			_entries.Add((ConfigEntryBase)(object)entry);
			entry.SettingChanged += OnChanged;
			return entry;
		}

		public void Update()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != _registeredOn)
			{
				Unregister();
				Restore();
				_changePending = false;
				if (instance != null)
				{
					Register(instance);
				}
			}
			if (_changePending && Time.unscaledTime >= _nextChangeSend)
			{
				SendChange();
			}
		}

		public void Dispose()
		{
			Unregister();
			Restore();
			foreach (ConfigEntryBase entry in _entries)
			{
				Unsubscribe(entry);
			}
			_entries.Clear();
		}

		private void Register(ZRoutedRpc rpc)
		{
			Forget(rpc, _send);
			Forget(rpc, _ask);
			rpc.Register<ZPackage>(_send, (Action<long, ZPackage>)OnValues);
			rpc.Register(_ask, (Action<long>)OnAsk);
			rpc.m_onNewPeer = (Action<long>)Delegate.Combine(rpc.m_onNewPeer, new Action<long>(OnNewPeer));
			_registeredOn = rpc;
		}

		private void Unregister()
		{
			if (_registeredOn != null)
			{
				ZRoutedRpc registeredOn = _registeredOn;
				registeredOn.m_onNewPeer = (Action<long>)Delegate.Remove(registeredOn.m_onNewPeer, new Action<long>(OnNewPeer));
				Forget(_registeredOn, _send);
				Forget(_registeredOn, _ask);
				_registeredOn = null;
			}
		}

		private static void Forget(ZRoutedRpc rpc, string name)
		{
			if (AccessTools.Field(typeof(ZRoutedRpc), "m_functions")?.GetValue(rpc) is IDictionary dictionary)
			{
				dictionary.Remove(StringExtensionMethods.GetStableHashCode(name));
			}
		}

		private void OnNewPeer(long peer)
		{
			if (IsServer)
			{
				Send(peer);
			}
			else if ((Object)(object)ZNet.instance != (Object)null)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null && serverPeer.m_uid == peer)
				{
					Ask();
				}
			}
		}

		private void OnAsk(long sender)
		{
			if (IsServer)
			{
				Send(sender);
			}
		}

		private void Ask()
		{
			try
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(ZNet.instance.GetServerPeer().m_uid, _ask, Array.Empty<object>());
				}
			}
			catch (Exception)
			{
			}
		}

		private void Send(long target)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			if (ZRoutedRpc.instance == null || _entries.Count == 0)
			{
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(_entries.Count);
			foreach (ConfigEntryBase entry in _entries)
			{
				val.Write(entry.Definition.Section);
				val.Write(entry.Definition.Key);
				val.Write(TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType));
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(target, _send, new object[1] { val });
		}

		private void OnValues(long sender, ZPackage pkg)
		{
			if (IsServer)
			{
				return;
			}
			ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null);
			if (val == null || val.m_uid != sender)
			{
				return;
			}
			try
			{
				int num = pkg.ReadInt();
				List<string> list = new List<string>();
				for (int i = 0; i < num; i++)
				{
					string section = pkg.ReadString();
					string key = pkg.ReadString();
					string text = pkg.ReadString();
					ConfigEntryBase val2 = ((IEnumerable<ConfigEntryBase>)_entries).FirstOrDefault((Func<ConfigEntryBase, bool>)((ConfigEntryBase x) => x.Definition.Section == section && x.Definition.Key == key));
					if (val2 != null)
					{
						object obj;
						try
						{
							obj = TomlTypeConverter.ConvertToValue(text, val2.SettingType);
						}
						catch (Exception)
						{
							continue;
						}
						if (!_own.ContainsKey(val2))
						{
							_own[val2] = val2.BoxedValue;
						}
						_server[val2] = obj;
						if (!object.Equals(val2.BoxedValue, obj))
						{
							Apply(val2, obj);
							list.Add(section + "/" + key + " = " + text);
						}
					}
				}
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)($"Settings from the server apply while you play here ({_server.Count})" + ((list.Count > 0) ? (": " + string.Join(", ", list.ToArray())) : "")));
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not read the server's settings: " + ex2.Message));
				}
			}
		}

		private void Apply(ConfigEntryBase e, object value)
		{
			bool saveOnConfigSet = _config.SaveOnConfigSet;
			_applying = true;
			try
			{
				_config.SaveOnConfigSet = false;
				e.BoxedValue = value;
			}
			finally
			{
				_config.SaveOnConfigSet = saveOnConfigSet;
				_applying = false;
			}
		}

		private void Restore()
		{
			if (_server.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<ConfigEntryBase, object> item in _own)
			{
				Apply(item.Key, item.Value);
			}
			_own.Clear();
			_server.Clear();
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)"Your own settings apply again");
			}
		}

		private void OnChanged(object sender, EventArgs args)
		{
			if (_applying)
			{
				return;
			}
			ConfigEntryBase val = (ConfigEntryBase)((sender is ConfigEntryBase) ? sender : null);
			if (val == null)
			{
				return;
			}
			if (_server.TryGetValue(val, out var value))
			{
				_own[val] = val.BoxedValue;
				Apply(val, value);
			}
			else if (IsServer)
			{
				_changePending = true;
				if (Time.unscaledTime >= _nextChangeSend)
				{
					SendChange();
				}
			}
		}

		private void SendChange()
		{
			_changePending = false;
			_nextChangeSend = Time.unscaledTime + 0.5f;
			if (IsServer && ZNet.instance.GetPeers().Count > 0)
			{
				Send(0L);
			}
		}

		private void Unsubscribe(ConfigEntryBase e)
		{
			((object)e).GetType().GetEvent("SettingChanged")?.RemoveEventHandler(e, new EventHandler(OnChanged));
		}
	}
	internal static class SpecialContainers
	{
		private static readonly string[] Prefabs = new string[2] { "piece_ledgerchest", "piece_recycler" };

		public static bool NotStorage(Container c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return false;
			}
			Piece componentInParent = ((Component)c).GetComponentInParent<Piece>();
			string text = (((Object)(object)componentInParent != (Object)null) ? ((Object)((Component)componentInParent).gameObject).name : ((Object)((Component)c).gameObject).name);
			string[] prefabs = Prefabs;
			foreach (string value in prefabs)
			{
				if (text.StartsWith(value, StringComparison.Ordinal) || ((Object)((Component)c).gameObject).name.StartsWith(value, StringComparison.Ordinal))
				{
					return true;
				}
			}
			if ((Object)(object)(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponentInChildren<Incinerator>(true) : ((Component)c).GetComponentInParent<Incinerator>()) != (Object)null)
			{
				return true;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
			if (val != null)
			{
				if (val.GetInt("dh_arena_chest", 0) == 0)
				{
					return val.GetBool("dh_notstorage", false);
				}
				return true;
			}
			return false;
		}
	}
}