Decompiled source of LocalCraftFromStoragePrime v1.0.0

plugins/LocalCraftFromStoragePrime.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LuminairPrime")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Craft from nearby chests and put materials away with one click.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+02cd96c4dedfeca267efeeb46e6b6909a78ad570")]
[assembly: AssemblyProduct("LocalCraftFromStoragePrime")]
[assembly: AssemblyTitle("LocalCraftFromStoragePrime")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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 LocalCraftFromStoragePrime
{
	internal sealed class PluginConfiguration
	{
		private sealed class RadiusRange : AcceptableValueRange<float>
		{
			private readonly string _setting;

			internal RadiusRange(string setting)
				: base(1f, 128f)
			{
				_setting = setting;
			}

			public override object Clamp(object value)
			{
				float num = (float)value;
				if (float.IsNaN(num) || float.IsInfinity(num))
				{
					Plugin.Log.LogWarning((object)(_setting + " must be a finite number from 1 to 128. Using 50 metres."));
				}
				return RuntimeSettings.NormalizeRadius(num);
			}
		}

		private readonly ConfigFile _config;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<bool> _craftingFromStorage;

		private ConfigEntry<bool> _buildingFromStorage;

		private ConfigEntry<float> _craftingDistance;

		private ConfigEntry<float> _buildingDistance;

		private ConfigEntry<bool> _allowShips;

		private ConfigEntry<bool> _allowCarts;

		private ConfigEntry<string> _reserves;

		private IReadOnlyDictionary<string, int> _parsedReserves = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		private ConfigEntry<bool> _depositEnabled;

		private ConfigEntry<float> _depositRange;

		private ConfigEntry<string> _depositExcludedItems;

		private ConfigEntry<bool> _depositFeedback;

		private ConfigEntry<string> _depositIncludedItems;

		private ConfigEntry<string> _depositButtonLabel;

		internal RuntimeSettings Current { get; private set; }

		internal ConfigEntry<bool> DebugLogging { get; private set; }

		internal ConfigEntry<bool> ExportDepositCatalogue { get; private set; }

		internal ConfigEntry<bool> StoragePerfDiagnostics { get; private set; }

		internal PluginConfiguration(ConfigFile config)
		{
			_config = config;
			Current = RuntimeSettings.Default;
		}

		internal bool IsReservesEntry(SettingChangedEventArgs args)
		{
			return (object)args.ChangedSetting == _reserves;
		}

		internal void Bind()
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			_enabled = _config.Bind<bool>("01 - General", "Enabled", true, "Control all features at once. Changes take effect immediately without a restart.");
			_allowShips = _config.Bind<bool>("01 - General", "IncludeShipContainers", false, "Use containers on ships for all features.");
			_allowCarts = _config.Bind<bool>("01 - General", "IncludeCartContainers", false, "Use containers on carts for all features.");
			_reserves = _config.Bind<string>("01 - General", "ReserveItems", "", "Add *:1 if you want crafting from storage to always leave 1 item behind so you can easily refill the storage again. *:1,Wood:5 leaves one of each item except five wood; Wood:0 exempts wood. See docs/deposit-items.md for keys.");
			_craftingFromStorage = _config.Bind<bool>("02 - Craft from Storage", "Enabled", true, "Use nearby containers for crafting and upgrades.");
			_craftingDistance = _config.Bind<float>("02 - Craft from Storage", "Distance", 50f, new ConfigDescription("Search distance in m from the current crafting station, or the player when no station is active. Stay below 64 m; above that containers may be unavailable or buggy.", (AcceptableValueBase)(object)new RadiusRange("Craft from Storage Distance"), Array.Empty<object>()));
			_buildingFromStorage = _config.Bind<bool>("03 - Build from Storage", "Enabled", true, "Use nearby containers for building with the Hammer and other build tools.");
			_buildingDistance = _config.Bind<float>("03 - Build from Storage", "Distance", 50f, new ConfigDescription("Search distance in m from the player using the build tool. Stay below 64 m; above that containers may be unavailable or buggy.", (AcceptableValueBase)(object)new RadiusRange("Build from Storage Distance"), Array.Empty<object>()));
			_depositEnabled = _config.Bind<bool>("04 - Store Nearby", "Enabled", true, "Show the Store Nearby button in the player inventory.");
			_depositButtonLabel = _config.Bind<string>("04 - Store Nearby", "ButtonLabel", "Store Nearby", "'Store Nearby' button text can be replaced up to 32 text elements, or left blank for a square button without text.");
			_depositRange = _config.Bind<float>("04 - Store Nearby", "Distance", 50f, new ConfigDescription("Search distance in m from the player for Store Nearby. Stay below 64 m; above that containers may be unavailable or buggy.", (AcceptableValueBase)(object)new RadiusRange("Store Nearby Distance"), Array.Empty<object>()));
			_depositExcludedItems = _config.Bind<string>("04 - Store Nearby", "ExcludedItems", "", "Item keys to keep out of Store Nearby. Exclusions override inclusions. See docs/deposit-items.md for keys.");
			_depositIncludedItems = _config.Bind<string>("04 - Store Nearby", "IncludedItems", "raspberries, blueberries, cloudberries, vineberry, mushroomcommon, mushroomyellow, mushroomblue, jotunpuffs, magecap, smokepuff, pukeberries, trophies", "Additional item keys or trophy type aliases eligible for Store Nearby. See docs/deposit-items.md for keys and aliases.");
			_depositFeedback = _config.Bind<bool>("04 - Store Nearby", "FailureMessages", true, "Explain an unsuccessful Store Nearby click.");
			DebugLogging = _config.Bind<bool>("05 - Diagnostics", "DebugLogging", false, "Log container discovery, exclusions, counts, withdrawals, and deposits.");
			ExportDepositCatalogue = _config.Bind<bool>("05 - Diagnostics", "ExportDepositItemCatalogue", false, "Export registered item details for diagnostics after entering a world. Writes BepInEx/config/LuminairPrime.valheim.localcraftfromstorageprime.deposit-items.md.");
			StoragePerfDiagnostics = _config.Bind<bool>("05 - Diagnostics", "StoragePerformanceDiagnostics", false, "Record container search and frame-interval timings for performance diagnosis.");
		}

		internal void ReadSettings(bool reparseReserves = true)
		{
			if (reparseReserves)
			{
				List<string> list = new List<string>();
				_parsedReserves = StorageRules.ParseReserves(_reserves.Value, list.Add);
				foreach (string item in list)
				{
					Plugin.Log.LogWarning((object)("Ignored invalid reserve entry '" + item + "'. Use Item, Item:Count, or *:Count. Item counts may be zero; wildcard counts must be above zero."));
				}
			}
			Current = new RuntimeSettings(_enabled.Value, _craftingFromStorage.Value, _buildingFromStorage.Value, _craftingDistance.Value, _buildingDistance.Value, _allowShips.Value, _allowCarts.Value, _parsedReserves, _depositEnabled.Value, _depositRange.Value, DepositRules.ParseExcludedNames(_depositExcludedItems.Value), _depositFeedback.Value, DepositRules.ParseExcludedNames(_depositIncludedItems.Value), _depositButtonLabel.Value);
		}
	}
	internal static class RuntimeSettingsLimits
	{
		internal const float MinDistance = 1f;

		internal const float MaxDistance = 128f;

		internal const float DefaultDistance = 50f;

		internal const float SafeTestedMax = 64f;
	}
	internal sealed class RuntimeSettings
	{
		internal static RuntimeSettings Default { get; } = new RuntimeSettings(enabled: true, craftingFromStorage: true, buildingFromStorage: true, 50f, 50f, allowShips: false, allowCarts: false, StorageRules.DefaultReserves, depositEnabled: true, 50f, DepositRules.ParseExcludedNames(""), depositFeedback: true, DepositRules.ParseExcludedNames("raspberries, blueberries, cloudberries, vineberry, mushroomcommon, mushroomyellow, mushroomblue, jotunpuffs, magecap, smokepuff, pukeberries, trophies"));

		internal bool Enabled { get; }

		internal bool CraftingFromStorage { get; }

		internal bool BuildingFromStorage { get; }

		internal float CraftingDistance { get; }

		internal float BuildingDistance { get; }

		internal bool AllowShips { get; }

		internal bool AllowCarts { get; }

		internal IReadOnlyDictionary<string, int> Reserves { get; }

		internal bool DepositEnabled { get; }

		internal float DepositRange { get; }

		internal IReadOnlyCollection<string> DepositExcludedNames { get; }

		internal bool DepositFeedback { get; }

		internal IReadOnlyCollection<string> DepositIncludedNames { get; }

		internal string DepositButtonLabel { get; }

		internal RuntimeSettings(bool enabled, bool craftingFromStorage, bool buildingFromStorage, float craftingDistance, float buildingDistance, bool allowShips, bool allowCarts, IReadOnlyDictionary<string, int> reserves, bool depositEnabled, float depositRange, IReadOnlyCollection<string> depositExcludedNames, bool depositFeedback = true, IReadOnlyCollection<string>? depositIncludedNames = null, string? depositButtonLabel = "Store Nearby")
		{
			Enabled = enabled;
			CraftingFromStorage = craftingFromStorage;
			BuildingFromStorage = buildingFromStorage;
			CraftingDistance = NormalizeRadius(craftingDistance);
			BuildingDistance = NormalizeRadius(buildingDistance);
			AllowShips = allowShips;
			AllowCarts = allowCarts;
			Reserves = reserves;
			DepositEnabled = depositEnabled;
			DepositRange = NormalizeRadius(depositRange);
			DepositExcludedNames = depositExcludedNames;
			DepositFeedback = depositFeedback;
			DepositIncludedNames = (IReadOnlyCollection<string>)(((object)depositIncludedNames) ?? ((object)Array.Empty<string>()));
			DepositButtonLabel = DepositLabel.Normalize(depositButtonLabel);
		}

		internal int ReserveFor(string itemName)
		{
			if (Reserves.TryGetValue(StorageRules.NormalizeItemName(itemName), out var value))
			{
				return value;
			}
			if (!Reserves.TryGetValue("*", out var value2))
			{
				return 0;
			}
			return value2;
		}

		internal static float NormalizeRadius(float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Math.Max(1f, Math.Min(128f, value));
			}
			return 50f;
		}
	}
	internal enum EnumerateCaller
	{
		Presence,
		Scan,
		Capture
	}
	internal sealed class PerformanceCounters
	{
		internal const double WindowSeconds = 5.0;

		internal const double SpikeThresholdMs = 50.0;

		private readonly long[] _enumCounts = new long[3];

		private readonly double[] _enumMsTotal = new double[3];

		private readonly double[] _enumMsMax = new double[3];

		private readonly long[] _enumReturnedTotal = new long[3];

		private readonly int[] _enumReturnedMax = new int[3];

		private long _presencePolls;

		private double _presenceMsTotal;

		private double _presenceMsMax;

		private long _presenceTrue;

		private long _presenceFalse;

		private long _presenceFailed;

		private long _discoveryHits;

		private double _discoveryHitMsTotal;

		private double _discoveryHitMsMax;

		private long _discoveryScans;

		private double _discoveryScanMsTotal;

		private double _discoveryScanMsMax;

		private long _discoveryEligibleTotal;

		private int _discoveryEligibleMax;

		private long _craftLoads;

		private double _craftLoadMsTotal;

		private double _craftLoadMsMax;

		private long _craftInventoriesTotal;

		private int _craftInventoriesMax;

		private long _hammerHits;

		private long _hammerCaptures;

		private double _hammerCaptureMsTotal;

		private double _hammerCaptureMsMax;

		private long _frames;

		private double _frameMsTotal;

		private double _frameMsMax;

		private bool _maxFrameHadEnum;

		private long _spikes;

		private long _spikesWithEnum;

		private long _enumFrames;

		private readonly long[] _buildUiCalls = new long[2];

		private readonly double[] _buildUiTotal = new double[2];

		private readonly double[] _buildUiMax = new double[2];

		internal long PresencePolls => _presencePolls;

		internal long DiscoveryHits => _discoveryHits;

		internal long DiscoveryScans => _discoveryScans;

		internal long CraftLoads => _craftLoads;

		internal long HammerHits => _hammerHits;

		internal long HammerCaptures => _hammerCaptures;

		internal long Frames => _frames;

		internal long Spikes => _spikes;

		internal bool HasActivity
		{
			get
			{
				if (_enumCounts[0] + _enumCounts[1] + _enumCounts[2] <= 0 && _presencePolls <= 0 && _discoveryHits <= 0 && _discoveryScans <= 0 && _craftLoads <= 0 && _hammerHits <= 0)
				{
					return _hammerCaptures > 0;
				}
				return true;
			}
		}

		internal void OnBuildUi(bool rows, double elapsedMs)
		{
			int num = ((!rows) ? 1 : 0);
			_buildUiCalls[num]++;
			_buildUiTotal[num] += Math.Max(0.0, elapsedMs);
			_buildUiMax[num] = Math.Max(_buildUiMax[num], elapsedMs);
		}

		internal long EnumCount(EnumerateCaller caller)
		{
			return _enumCounts[(int)caller];
		}

		internal double EnumMsTotal(EnumerateCaller caller)
		{
			return _enumMsTotal[(int)caller];
		}

		internal double EnumMsMax(EnumerateCaller caller)
		{
			return _enumMsMax[(int)caller];
		}

		internal long EnumReturnedTotal(EnumerateCaller caller)
		{
			return _enumReturnedTotal[(int)caller];
		}

		internal int EnumReturnedMax(EnumerateCaller caller)
		{
			return _enumReturnedMax[(int)caller];
		}

		internal void OnEnumerate(EnumerateCaller caller, double elapsedMs, int returned)
		{
			if (elapsedMs < 0.0)
			{
				elapsedMs = 0.0;
			}
			if (returned < 0)
			{
				returned = 0;
			}
			_enumCounts[(int)caller]++;
			_enumMsTotal[(int)caller] += elapsedMs;
			if (elapsedMs > _enumMsMax[(int)caller])
			{
				_enumMsMax[(int)caller] = elapsedMs;
			}
			_enumReturnedTotal[(int)caller] += returned;
			if (returned > _enumReturnedMax[(int)caller])
			{
				_enumReturnedMax[(int)caller] = returned;
			}
		}

		internal void OnPresence(double elapsedMsInclusive, bool? result)
		{
			if (elapsedMsInclusive < 0.0)
			{
				elapsedMsInclusive = 0.0;
			}
			_presencePolls++;
			_presenceMsTotal += elapsedMsInclusive;
			if (elapsedMsInclusive > _presenceMsMax)
			{
				_presenceMsMax = elapsedMsInclusive;
			}
			if (result == true)
			{
				_presenceTrue++;
			}
			else if (result == false)
			{
				_presenceFalse++;
			}
			else
			{
				_presenceFailed++;
			}
		}

		internal void OnDiscoveryHit(double elapsedMsInclusive)
		{
			if (elapsedMsInclusive < 0.0)
			{
				elapsedMsInclusive = 0.0;
			}
			_discoveryHits++;
			_discoveryHitMsTotal += elapsedMsInclusive;
			if (elapsedMsInclusive > _discoveryHitMsMax)
			{
				_discoveryHitMsMax = elapsedMsInclusive;
			}
		}

		internal void OnDiscoveryScan(double elapsedMsInclusive, int eligible)
		{
			if (elapsedMsInclusive < 0.0)
			{
				elapsedMsInclusive = 0.0;
			}
			if (eligible < 0)
			{
				eligible = 0;
			}
			_discoveryScans++;
			_discoveryScanMsTotal += elapsedMsInclusive;
			if (elapsedMsInclusive > _discoveryScanMsMax)
			{
				_discoveryScanMsMax = elapsedMsInclusive;
			}
			_discoveryEligibleTotal += eligible;
			if (eligible > _discoveryEligibleMax)
			{
				_discoveryEligibleMax = eligible;
			}
		}

		internal void OnCraftLoad(double elapsedMsInclusive, int inventories)
		{
			if (elapsedMsInclusive < 0.0)
			{
				elapsedMsInclusive = 0.0;
			}
			if (inventories < 0)
			{
				inventories = 0;
			}
			_craftLoads++;
			_craftLoadMsTotal += elapsedMsInclusive;
			if (elapsedMsInclusive > _craftLoadMsMax)
			{
				_craftLoadMsMax = elapsedMsInclusive;
			}
			_craftInventoriesTotal += inventories;
			if (inventories > _craftInventoriesMax)
			{
				_craftInventoriesMax = inventories;
			}
		}

		internal void OnHammerHit()
		{
			_hammerHits++;
		}

		internal void OnHammerCapture(double elapsedMsInclusive)
		{
			if (elapsedMsInclusive < 0.0)
			{
				elapsedMsInclusive = 0.0;
			}
			_hammerCaptures++;
			_hammerCaptureMsTotal += elapsedMsInclusive;
			if (elapsedMsInclusive > _hammerCaptureMsMax)
			{
				_hammerCaptureMsMax = elapsedMsInclusive;
			}
		}

		internal void TickFrame(double frameMs, bool hadEnum)
		{
			if (frameMs < 0.0)
			{
				frameMs = 0.0;
			}
			_frames++;
			_frameMsTotal += frameMs;
			if (frameMs > _frameMsMax)
			{
				_frameMsMax = frameMs;
				_maxFrameHadEnum = hadEnum;
			}
			if (hadEnum)
			{
				_enumFrames++;
			}
			if (frameMs > 50.0)
			{
				_spikes++;
				if (hadEnum)
				{
					_spikesWithEnum++;
				}
			}
		}

		internal void Reset()
		{
			Array.Clear(_buildUiCalls, 0, 2);
			Array.Clear(_buildUiTotal, 0, 2);
			Array.Clear(_buildUiMax, 0, 2);
			Array.Clear(_enumCounts, 0, _enumCounts.Length);
			Array.Clear(_enumMsTotal, 0, _enumMsTotal.Length);
			Array.Clear(_enumMsMax, 0, _enumMsMax.Length);
			Array.Clear(_enumReturnedTotal, 0, _enumReturnedTotal.Length);
			Array.Clear(_enumReturnedMax, 0, _enumReturnedMax.Length);
			_presencePolls = 0L;
			_presenceMsTotal = 0.0;
			_presenceMsMax = 0.0;
			_presenceTrue = 0L;
			_presenceFalse = 0L;
			_presenceFailed = 0L;
			_discoveryHits = 0L;
			_discoveryHitMsTotal = 0.0;
			_discoveryHitMsMax = 0.0;
			_discoveryScans = 0L;
			_discoveryScanMsTotal = 0.0;
			_discoveryScanMsMax = 0.0;
			_discoveryEligibleTotal = 0L;
			_discoveryEligibleMax = 0;
			_craftLoads = 0L;
			_craftLoadMsTotal = 0.0;
			_craftLoadMsMax = 0.0;
			_craftInventoriesTotal = 0L;
			_craftInventoriesMax = 0;
			_hammerHits = 0L;
			_hammerCaptures = 0L;
			_hammerCaptureMsTotal = 0.0;
			_hammerCaptureMsMax = 0.0;
			_frames = 0L;
			_frameMsTotal = 0.0;
			_frameMsMax = 0.0;
			_maxFrameHadEnum = false;
			_spikes = 0L;
			_spikesWithEnum = 0L;
			_enumFrames = 0L;
		}

		internal bool TryCloseWindow(double windowSeconds, int gc0Delta, int gc1Delta, int gc2Delta, out string summary)
		{
			summary = string.Empty;
			if (windowSeconds < 5.0 || (_frames == 0L && !HasActivity))
			{
				if (windowSeconds >= 5.0)
				{
					Reset();
				}
				return false;
			}
			double num = ((_frames > 0) ? (_frameMsTotal / (double)_frames) : 0.0);
			summary = string.Format(CultureInfo.InvariantCulture, "[PerfDiag] backend=registry {0:0.0}s window: frames={1} avgFrame={2:0.0}ms maxFrame={3:0.0}ms(maxEnum={4}) spikes>50ms={5}(enum={6}) enumFrames={7} | enum presence={8} total={9:0.00}ms max={10:0.00}ms returned={11}(max{12}) | enum scan={13} total={14:0.00}ms max={15:0.00}ms returned={16}(max{17}) | enum capture={18} total={19:0.00}ms max={20:0.00}ms returned={21}(max{22}) | presence(incl.)={23} total={24:0.00}ms max={25:0.00}ms true={26} false={27} failed={28} | discovery hits={29} total={30:0.00}ms max={31:0.00}ms scans(incl.)={32} total={33:0.00}ms max={34:0.00}ms eligible={35}(max{36}) | craftLoad(incl.)={37} total={38:0.00}ms max={39:0.00}ms inv={40}(max{41}) | hammer hits={42} captures(incl.)={43} total={44:0.00}ms max={45:0.00}ms | gc0=+{46} gc1=+{47} gc2=+{48} (CPU timings are execution time; frame intervals include render/GC gaps outside timers. Do not sum inclusive timings.)", windowSeconds, _frames, num, _frameMsMax, _maxFrameHadEnum ? "yes" : "no", _spikes, _spikesWithEnum, _enumFrames, _enumCounts[0], _enumMsTotal[0], _enumMsMax[0], _enumReturnedTotal[0], _enumReturnedMax[0], _enumCounts[1], _enumMsTotal[1], _enumMsMax[1], _enumReturnedTotal[1], _enumReturnedMax[1], _enumCounts[2], _enumMsTotal[2], _enumMsMax[2], _enumReturnedTotal[2], _enumReturnedMax[2], _presencePolls, _presenceMsTotal, _presenceMsMax, _presenceTrue, _presenceFalse, _presenceFailed, _discoveryHits, _discoveryHitMsTotal, _discoveryHitMsMax, _discoveryScans, _discoveryScanMsTotal, _discoveryScanMsMax, _discoveryEligibleTotal, _discoveryEligibleMax, _craftLoads, _craftLoadMsTotal, _craftLoadMsMax, _craftInventoriesTotal, _craftInventoriesMax, _hammerHits, _hammerCaptures, _hammerCaptureMsTotal, _hammerCaptureMsMax, gc0Delta, gc1Delta, gc2Delta);
			summary += string.Format(CultureInfo.InvariantCulture, " | buildRows(incl.)={0} total={1:0.00}ms max={2:0.00}ms buildIcons(excl.capture)={3} total={4:0.00}ms max={5:0.00}ms (mod UI only; deferred canvas work excluded)", _buildUiCalls[0], _buildUiTotal[0], _buildUiMax[0], _buildUiCalls[1], _buildUiTotal[1], _buildUiMax[1]);
			Reset();
			return true;
		}
	}
	internal static class PerformanceDiagnostics
	{
		internal static readonly PerformanceCounters Counters = new PerformanceCounters();

		private static bool _hasBaseline;

		private static float _lastFrameTime;

		private static float _windowStart;

		private static bool _hadEnumSinceTick;

		private static int _gc0Start;

		private static int _gc1Start;

		private static int _gc2Start;

		internal static bool Enabled => Plugin.StoragePerfDiagnostics?.Value ?? false;

		internal static long HammerCaptures => Counters.HammerCaptures;

		internal static long Timestamp()
		{
			try
			{
				if (!Enabled)
				{
					return 0L;
				}
				return Stopwatch.GetTimestamp();
			}
			catch
			{
				return 0L;
			}
		}

		internal static double ElapsedMs(long start)
		{
			try
			{
				return (double)(Stopwatch.GetTimestamp() - start) * 1000.0 / (double)Stopwatch.Frequency;
			}
			catch
			{
				return 0.0;
			}
		}

		internal static void OnBuildUi(bool rows, long start)
		{
			if (Enabled && start != 0L)
			{
				Counters.OnBuildUi(rows, ElapsedMs(start));
			}
		}

		internal static void OnEnumerate(EnumerateCaller caller, long start, int returned)
		{
			if (Enabled)
			{
				Counters.OnEnumerate(caller, ElapsedMs(start), returned);
				_hadEnumSinceTick = true;
			}
		}

		internal static void OnPresence(long start, bool? result)
		{
			if (Enabled)
			{
				Counters.OnPresence(ElapsedMs(start), result);
			}
		}

		internal static void OnDiscoveryHit(long start)
		{
			if (Enabled)
			{
				Counters.OnDiscoveryHit(ElapsedMs(start));
			}
		}

		internal static void OnDiscoveryScan(long start, int eligible)
		{
			if (Enabled)
			{
				Counters.OnDiscoveryScan(ElapsedMs(start), eligible);
			}
		}

		internal static void OnCraftLoad(long start, int inventories)
		{
			if (Enabled)
			{
				Counters.OnCraftLoad(ElapsedMs(start), inventories);
			}
		}

		internal static void OnHammerCapture(long start)
		{
			if (Enabled)
			{
				Counters.OnHammerCapture(ElapsedMs(start));
			}
		}

		internal static void OnHammerHit()
		{
			if (Enabled)
			{
				Counters.OnHammerHit();
			}
		}

		internal static void TickFrame()
		{
			float realtimeSinceStartup;
			try
			{
				realtimeSinceStartup = Time.realtimeSinceStartup;
			}
			catch
			{
				return;
			}
			if (!Enabled)
			{
				_hasBaseline = false;
				_lastFrameTime = realtimeSinceStartup;
				_windowStart = realtimeSinceStartup;
				_hadEnumSinceTick = false;
				return;
			}
			if (!_hasBaseline)
			{
				_hasBaseline = true;
				_lastFrameTime = realtimeSinceStartup;
				_windowStart = realtimeSinceStartup;
				_hadEnumSinceTick = false;
				_gc0Start = GC.CollectionCount(0);
				_gc1Start = GC.CollectionCount(1);
				_gc2Start = GC.CollectionCount(2);
				Counters.Reset();
				return;
			}
			double num = (double)(realtimeSinceStartup - _lastFrameTime) * 1000.0;
			_lastFrameTime = realtimeSinceStartup;
			bool hadEnumSinceTick = _hadEnumSinceTick;
			_hadEnumSinceTick = false;
			if (num < 0.0)
			{
				num = 0.0;
			}
			Counters.TickFrame(num, hadEnumSinceTick);
			if (num > 50.0)
			{
				Plugin.Log.LogInfo((object)string.Format("[PerfDiag] slow frame {0:0.0}ms enum={1}.", num, hadEnumSinceTick ? "yes" : "no"));
			}
			double num2 = realtimeSinceStartup - _windowStart;
			if (num2 >= 5.0)
			{
				int num3 = GC.CollectionCount(0);
				int num4 = GC.CollectionCount(1);
				int num5 = GC.CollectionCount(2);
				if (Counters.TryCloseWindow(num2, num3 - _gc0Start, num4 - _gc1Start, num5 - _gc2Start, out string summary))
				{
					Plugin.Log.LogInfo((object)summary);
				}
				_windowStart = realtimeSinceStartup;
				_gc0Start = num3;
				_gc1Start = num4;
				_gc2Start = num5;
			}
		}
	}
	internal sealed class BuildMaterialTotals
	{
		private readonly Dictionary<string, int> _items = new Dictionary<string, int>(StringComparer.Ordinal);

		internal int Count(string name)
		{
			if (!_items.TryGetValue(name, out var value))
			{
				return 0;
			}
			return value;
		}

		internal void Add(string name, int amount)
		{
			_items[name] = StorageRules.SaturatingAdd(Count(name), amount);
		}

		internal void AddStorage(BuildMaterialTotals chest, Func<string, int> reserveFor)
		{
			foreach (KeyValuePair<string, int> item in chest._items)
			{
				Add(item.Key, StorageRules.AvailableAfterReserve(item.Value, reserveFor(item.Key)));
			}
		}
	}
	internal static class BuildMenuIcons
	{
		private static readonly Color Unavailable = new Color(1f, 1f, 0f, 0.75f);

		private static HammerMaterialSnapshot? _lastSnapshot;

		private static bool _wasEnabled;

		internal static bool Enabled
		{
			get
			{
				if (Plugin.Settings.Enabled)
				{
					return Plugin.Settings.BuildingFromStorage;
				}
				return false;
			}
		}

		internal static void Refresh(BuildUi ui, bool force = false)
		{
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			if (!force && !((Component)ui).gameObject.activeInHierarchy)
			{
				return;
			}
			bool enabled = Enabled;
			if (!force && !enabled && !_wasEnabled)
			{
				return;
			}
			bool flag = _wasEnabled && !enabled;
			_wasEnabled = enabled;
			if (!enabled)
			{
				_lastSnapshot = null;
			}
			if (!enabled)
			{
				if (!flag || !(PatchContracts.BuildPieceButtonsField?.GetValue(ui) is List<BuildUiPieceButton> list))
				{
					return;
				}
				object? obj = PatchContracts.BuildSpecialPieceButtonField?.GetValue(ui);
				BuildUiPieceButton val = (BuildUiPieceButton)((obj is BuildUiPieceButton) ? obj : null);
				foreach (BuildUiPieceButton item2 in list)
				{
					item2.UpdateRequirements();
				}
				if ((Object)(object)val != (Object)null)
				{
					val.UpdateRequirements();
				}
				return;
			}
			List<BuildUiPieceButton> list2 = null;
			List<BuildUiPieceButton> list3 = null;
			BuildUiPieceButton val2 = null;
			long start = 0L;
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					_lastSnapshot = null;
					return;
				}
				HammerMaterialSnapshot orRefresh = HammerMaterialReader.GetOrRefresh(localPlayer, Plugin.Settings);
				if ((!force && orRefresh == _lastSnapshot) || !(PatchContracts.BuildPieceButtonsField?.GetValue(ui) is List<BuildUiPieceButton> list4))
				{
					return;
				}
				list3 = list4;
				object? obj2 = PatchContracts.BuildSpecialPieceButtonField?.GetValue(ui);
				val2 = (BuildUiPieceButton)((obj2 is BuildUiPieceButton) ? obj2 : null);
				start = PerformanceDiagnostics.Timestamp();
				_lastSnapshot = orRefresh;
				list2 = new List<BuildUiPieceButton>(list4);
				if ((Object)(object)val2 != (Object)null)
				{
					list2.Add(val2);
				}
				bool flag2 = Plugin.DebugLogging?.Value ?? false;
				long start2 = (flag2 ? Stopwatch.GetTimestamp() : 0);
				Vector3 position = ((Component)localPlayer).transform.position;
				Dictionary<string, CraftingStation> dictionary = new Dictionary<string, CraftingStation>();
				List<(Image, Piece, CraftingStation, bool)> list5 = new List<(Image, Piece, CraftingStation, bool)>();
				foreach (BuildUiPieceButton item3 in list2)
				{
					Piece piece = item3.Piece;
					if ((Object)(object)piece == (Object)null)
					{
						continue;
					}
					object? obj3 = PatchContracts.BuildPieceIconField?.GetValue(item3);
					Image val3 = (Image)((obj3 is Image) ? obj3 : null);
					if (val3 == null)
					{
						continue;
					}
					CraftingStation value = null;
					if ((Object)(object)piece.m_craftingStation != (Object)null)
					{
						string name = piece.m_craftingStation.m_name;
						if (!dictionary.TryGetValue(name, out value))
						{
							value = CraftingStation.HaveBuildStationInRange(name, position);
							dictionary.Add(name, value);
						}
					}
					bool item = (Object)(object)piece.m_craftingStation == (Object)null || (Object)(object)value != (Object)null;
					list5.Add((val3, piece, value, item));
				}
				BuildMaterialTotals carried = ReadInventory(((Humanoid)localPlayer).GetInventory());
				Color[] array = (Color[])(object)new Color[list5.Count];
				for (int i = 0; i < list5.Count; i++)
				{
					(Image, Piece, CraftingStation, bool) tuple = list5[i];
					array[i] = (CanBuild(tuple.Item2, tuple.Item3, carried, orRefresh, tuple.Item4) ? Color.white : Unavailable);
				}
				for (int j = 0; j < list5.Count; j++)
				{
					((Graphic)list5[j].Item1).color = array[j];
				}
				if (flag2)
				{
					Plugin.Debug($"Hammer batch: {list5.Count} icons, snapshot available={orRefresh.Available}, {PerformanceDiagnostics.ElapsedMs(start2):0.00} ms.");
				}
			}
			catch (Exception ex)
			{
				if (list2 != null || list3 != null)
				{
					foreach (BuildUiPieceButton item4 in list2 ?? list3)
					{
						object? obj4 = PatchContracts.BuildPieceIconField?.GetValue(item4);
						Image val4 = (Image)((obj4 is Image) ? obj4 : null);
						if (val4 != null)
						{
							((Graphic)val4).color = Color.white;
						}
					}
				}
				if (list2 == null && (Object)(object)val2 != (Object)null)
				{
					object? obj5 = PatchContracts.BuildPieceIconField?.GetValue(val2);
					Image val5 = (Image)((obj5 is Image) ? obj5 : null);
					if (val5 != null)
					{
						((Graphic)val5).color = Color.white;
					}
				}
				Plugin.Debug("Could not refresh Hammer batch: " + ex.GetType().Name + ": " + ex.Message);
			}
			finally
			{
				PerformanceDiagnostics.OnBuildUi(rows: false, start);
			}
		}

		private static BuildMaterialTotals ReadInventory(Inventory inventory)
		{
			BuildMaterialTotals buildMaterialTotals = new BuildMaterialTotals();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_worldLevel >= Game.m_worldLevel)
				{
					buildMaterialTotals.Add(allItem.m_shared.m_name, allItem.m_stack);
				}
			}
			return buildMaterialTotals;
		}

		private static bool CanBuild(Piece piece, CraftingStation? station, BuildMaterialTotals carried, HammerMaterialSnapshot storage, bool hasOrigin)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece.m_craftingStation != (Object)null && (Object)(object)station == (Object)null && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)27))
			{
				return false;
			}
			if (piece.m_dlc.Length > 0 && !DLCMan.instance.IsDLCInstalled(piece.m_dlc))
			{
				return false;
			}
			if (ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
			{
				return true;
			}
			if (!storage.Available)
			{
				return true;
			}
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val.m_resItem == (Object)null) && val.m_amount > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					int right = (hasOrigin ? storage.Count(name) : 0);
					if (StorageRules.SaturatingAdd(carried.Count(name), right) < val.m_amount)
					{
						return false;
					}
				}
			}
			return true;
		}
	}
	internal static class BuildRequirementColors
	{
		internal static void Refresh(Hud hud, MaterialLinkContext? context)
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			if (context == null || context.Mode != LinkMode.BuildDisplay || context.Piece?.m_resources == null || context.PlayerInventory == null || hud?.m_requirementItems == null)
			{
				return;
			}
			int num = Math.Min(context.Piece.m_resources.Length, hud.m_requirementItems.Length);
			HammerMaterialSnapshot displaySnapshot = context.DisplaySnapshot;
			if (!displaySnapshot.Available)
			{
				return;
			}
			for (int i = 0; i < num; i++)
			{
				Requirement val = context.Piece.m_resources[i];
				if (!MaterialRequirements.TryGetRequirementName(val, out string itemName))
				{
					continue;
				}
				int amount = val.GetAmount(0);
				int left;
				using (MaterialLinkScope.Enter(null))
				{
					left = context.PlayerInventory.CountItems(itemName, -1, true);
				}
				if (StorageRules.SaturatingAdd(left, displaySnapshot.Count(itemName)) >= amount)
				{
					GameObject obj = hud.m_requirementItems[i];
					object obj2;
					if (obj == null)
					{
						obj2 = null;
					}
					else
					{
						Transform obj3 = obj.transform.Find("res_amount");
						obj2 = ((obj3 != null) ? ((Component)obj3).GetComponent<TMP_Text>() : null);
					}
					TMP_Text val2 = (TMP_Text)obj2;
					object obj4;
					if (obj == null)
					{
						obj4 = null;
					}
					else
					{
						Transform obj5 = obj.transform.Find("res_icon");
						obj4 = ((obj5 != null) ? ((Component)obj5).GetComponent<Image>() : null);
					}
					Image val3 = (Image)obj4;
					if ((Object)(object)val2 != (Object)null)
					{
						((Graphic)val2).color = Color.white;
					}
					if ((Object)(object)val3 != (Object)null)
					{
						((Behaviour)val3).enabled = true;
						((Graphic)val3).color = Color.white;
					}
				}
			}
		}
	}
	internal static class HammerMaterialReader
	{
		private static readonly HammerSnapshotCache _cache = new HammerSnapshotCache();

		private static Player? _player;

		internal static HammerMaterialSnapshot GetOrRefresh(Player player, RuntimeSettings settings)
		{
			float now = Time.unscaledTime;
			if ((Object)(object)player == (Object)null)
			{
				return HammerMaterialSnapshot.Unavailable(now);
			}
			if (settings == null || !settings.Enabled || !settings.BuildingFromStorage)
			{
				return HammerMaterialSnapshot.Unavailable(now);
			}
			if (player != _player)
			{
				_cache.Clear();
				_player = player;
			}
			long sessionEpoch = ContainerAccessService.SessionEpoch;
			int playerId = (int)player.GetPlayerID();
			int worldLevel = Game.m_worldLevel;
			bool diagOn = PerformanceDiagnostics.Enabled;
			long num = (diagOn ? PerformanceDiagnostics.HammerCaptures : 0);
			HammerMaterialSnapshot? result = _cache.GetOrRefresh(now, sessionEpoch, playerId, settings, worldLevel, delegate
			{
				long start = (diagOn ? PerformanceDiagnostics.Timestamp() : 0);
				try
				{
					HammerMaterialSnapshot result2 = Capture(player, settings, now);
					if (diagOn)
					{
						PerformanceDiagnostics.OnHammerCapture(start);
					}
					return result2;
				}
				catch (Exception ex)
				{
					if (diagOn)
					{
						PerformanceDiagnostics.OnHammerCapture(start);
					}
					Plugin.Debug("Hammer snapshot capture failed: " + ex.GetType().Name + ": " + ex.Message);
					return HammerMaterialSnapshot.Unavailable(now);
				}
			}) ?? HammerMaterialSnapshot.Unavailable(now);
			if (diagOn && PerformanceDiagnostics.HammerCaptures == num)
			{
				PerformanceDiagnostics.OnHammerHit();
			}
			return result;
		}

		internal static void Invalidate()
		{
			_cache.Invalidate();
		}

		internal static void Clear()
		{
			_cache.Clear();
			_player = null;
		}

		private static HammerMaterialSnapshot Capture(Player player, RuntimeSettings settings, float sampleTime)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			Vector3 position = ((Component)player).transform.position;
			float buildingDistance = settings.BuildingDistance;
			float num = buildingDistance * buildingDistance;
			BuildMaterialTotals buildMaterialTotals = new BuildMaterialTotals();
			foreach (Container item in PerformanceDiagnostics.Enabled ? ContainerDiscovery.EnumerateTimed(EnumerateCaller.Capture) : ContainerDiscovery.EnumerateLoaded())
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				Vector3 val = ((Component)item).transform.position - position;
				if (((Vector3)(ref val)).sqrMagnitude > num || !ContainerEligibility.Check(player, item, settings, out ZNetView _, null, advisory: true))
				{
					continue;
				}
				Inventory val2 = ContainerApi.LoadInventory(item);
				if (val2 == null)
				{
					continue;
				}
				BuildMaterialTotals buildMaterialTotals2 = new BuildMaterialTotals();
				foreach (ItemData allItem in val2.GetAllItems())
				{
					if (allItem?.m_shared != null && allItem.m_worldLevel >= Game.m_worldLevel)
					{
						buildMaterialTotals2.Add(allItem.m_shared.m_name, allItem.m_stack);
					}
				}
				buildMaterialTotals.AddStorage(buildMaterialTotals2, settings.ReserveFor);
			}
			return HammerMaterialSnapshot.FromTotals(buildMaterialTotals, sampleTime);
		}
	}
	internal sealed class HammerMaterialSnapshot
	{
		private readonly BuildMaterialTotals _totals;

		internal bool Available { get; }

		internal float SampleTime { get; }

		private HammerMaterialSnapshot(BuildMaterialTotals totals, bool available, float sampleTime)
		{
			_totals = totals;
			Available = available;
			SampleTime = sampleTime;
		}

		internal static HammerMaterialSnapshot FromTotals(BuildMaterialTotals totals, float sampleTime)
		{
			return new HammerMaterialSnapshot(totals, available: true, sampleTime);
		}

		internal static HammerMaterialSnapshot Unavailable(float sampleTime)
		{
			return new HammerMaterialSnapshot(new BuildMaterialTotals(), available: false, sampleTime);
		}

		internal int Count(string name)
		{
			if (!Available)
			{
				return 0;
			}
			return _totals.Count(name);
		}
	}
	internal sealed class HammerSnapshotCache
	{
		internal const float TimeToLive = 0.25f;

		private HammerMaterialSnapshot? _current;

		private float _expiry;

		private long _epoch;

		private int _player;

		private object? _settings;

		private int _world;

		private bool _invalidated = true;

		internal HammerMaterialSnapshot? GetOrRefresh(float now, long sessionEpoch, int playerId, object? settingsToken, int worldLevel, Func<HammerMaterialSnapshot?> capture)
		{
			if (!_invalidated && _current != null && _epoch == sessionEpoch && _player == playerId && _settings == settingsToken && _world == worldLevel && now < _expiry)
			{
				return _current;
			}
			HammerMaterialSnapshot hammerMaterialSnapshot = capture();
			if (hammerMaterialSnapshot == null)
			{
				if (_epoch != sessionEpoch || _player != playerId || _settings != settingsToken || _world != worldLevel)
				{
					return null;
				}
				return _current;
			}
			_current = hammerMaterialSnapshot;
			_expiry = now + 0.25f;
			_epoch = sessionEpoch;
			_player = playerId;
			_settings = settingsToken;
			_world = worldLevel;
			_invalidated = false;
			return hammerMaterialSnapshot;
		}

		internal void Invalidate()
		{
			_invalidated = true;
			_current = null;
		}

		internal void Clear()
		{
			Invalidate();
		}
	}
	internal static class BuildActionRules
	{
		internal const float PlacementDistanceTolerance = 0.1f;

		internal const float PlacementAngleTolerance = 1f;

		internal const float StationDisplacementTolerance = 0.1f;

		internal static bool SamePieceName(string? ghostPieceName, string? expectedPieceName)
		{
			if (!string.IsNullOrEmpty(expectedPieceName))
			{
				return ghostPieceName == expectedPieceName;
			}
			return false;
		}

		internal static bool SamePlacement(bool sameGhost, bool samePrefab, bool samePiece, float distanceSquared, float angle)
		{
			if ((sameGhost || (samePrefab && samePiece)) && distanceSquared >= 0f && distanceSquared <= 0.010000001f && angle >= 0f)
			{
				return angle <= 1f;
			}
			return false;
		}

		internal static bool RejectUnmatchedStorageCheck(bool actionMatchesPlayer, bool linkedCheck, bool outerMatchesPiece, bool carriedCovers, bool freeBuild)
		{
			if (actionMatchesPlayer && linkedCheck && !outerMatchesPiece && !carriedCovers)
			{
				return !freeBuild;
			}
			return false;
		}

		internal static bool IsWithinRange(float distanceSquared, float radius)
		{
			return distanceSquared <= radius * radius;
		}

		internal static bool SourceEligible(float originDistanceSquared, float? livePlayerDistanceSquared, float radius, bool usesLivePlayerRange)
		{
			if (IsWithinRange(originDistanceSquared, radius))
			{
				if (usesLivePlayerRange)
				{
					if (livePlayerDistanceSquared.HasValue)
					{
						return IsWithinRange(livePlayerDistanceSquared.Value, radius);
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal static bool IsPlayerDisplaced(float distanceSquared, float radius)
		{
			return distanceSquared > radius * radius;
		}

		internal static bool IsStationDisplaced(float distanceSquared)
		{
			return distanceSquared > 0.010000001f;
		}
	}
	internal sealed class BuildPaymentOperation : PaymentOperation
	{
		private readonly Piece _piece;

		private readonly ItemData _tool;

		private readonly Vector3 _position;

		private readonly Quaternion _rotation;

		private readonly GameObject? _ghost;

		private readonly string? _ghostName;

		private readonly string? _pieceName;

		internal override StorageOperationKind Kind => StorageOperationKind.Build;

		internal override bool UsesLivePlayerRange => true;

		internal BuildPaymentOperation(MaterialLinkContext context)
			: base(context, 0, 1)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			_piece = context.Piece;
			_tool = (ItemData)PaymentGameAccess.Invoke(Player, "GetRightItem");
			_ghost = PaymentGameAccess.ReadField<GameObject>(Player, "m_placementGhost");
			_ghostName = (((Object)(object)_ghost != (Object)null) ? ((Object)_ghost).name : null);
			_pieceName = _piece.m_name;
			_position = (((Object)(object)_ghost != (Object)null) ? _ghost.transform.position : Vector3.zero);
			_rotation = (((Object)(object)_ghost != (Object)null) ? _ghost.transform.rotation : Quaternion.identity);
		}

		internal bool SamePlacement(Player player, string phase = "pending", bool logAccepted = false)
		{
			//IL_0022: 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)
			//IL_0032: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = PaymentGameAccess.ReadField<GameObject>(player, "m_placementGhost");
			float num;
			Vector3 val2;
			if (!((Object)(object)val != (Object)null))
			{
				num = float.PositiveInfinity;
			}
			else
			{
				val2 = val.transform.position - _position;
				num = ((Vector3)(ref val2)).sqrMagnitude;
			}
			float num2 = num;
			float num3 = (((Object)(object)val != (Object)null) ? Quaternion.Angle(val.transform.rotation, _rotation) : float.PositiveInfinity);
			bool flag = (Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)_ghost;
			bool flag2 = (Object)(object)val != (Object)null && _ghostName != null && ((Object)val).name == _ghostName;
			bool flag3 = SamePieceComponent(val);
			bool flag4 = (Object)(object)player == (Object)(object)Player && BuildActionRules.SamePlacement(flag, flag2, flag3, num2, num3);
			if (!flag4 || logAccepted)
			{
				ConfigEntry<bool>? debugLogging = Plugin.DebugLogging;
				if (debugLogging != null && debugLogging.Value)
				{
					string[] obj = new string[18]
					{
						$"Storage {Id}: placement {phase} piece={((Object)_piece).name} accepted={flag4} sameGhost={flag} samePrefab={flag2} samePiece={flag3} ",
						"ghost=",
						((Object)(object)val != (Object)null) ? ((Object)val).name : "missing",
						" expected=",
						_ghostName ?? "missing",
						" ghostPiece=",
						((Object)(object)val != (Object)null) ? GhostPieceName(val) : "missing",
						" expectedPiece=",
						_pieceName ?? "missing",
						" ",
						$"distance={Mathf.Sqrt(num2):F4}m angle={num3:F3}deg ",
						$"limit={0.1f:F2}m/{1f:F1}deg ",
						"from=",
						null,
						null,
						null,
						null,
						null
					};
					val2 = _position;
					obj[13] = ((Vector3)(ref val2)).ToString("F3");
					obj[14] = " to=";
					object obj2;
					if (!((Object)(object)val != (Object)null))
					{
						obj2 = "missing";
					}
					else
					{
						val2 = val.transform.position;
						obj2 = ((Vector3)(ref val2)).ToString("F3");
					}
					obj[15] = (string)obj2;
					obj[16] = " ";
					obj[17] = string.Format("status={0}.", PaymentGameAccess.ReadField<object>(player, "m_placementStatus"));
					Plugin.Debug(string.Concat(obj));
				}
			}
			return flag4;
		}

		private bool SamePieceComponent(GameObject? ghost)
		{
			if ((Object)(object)ghost == (Object)null || (Object)(object)_piece == (Object)null)
			{
				return false;
			}
			try
			{
				Piece component = ghost.GetComponent<Piece>();
				if (component == null || (Object)(object)component == (Object)null)
				{
					return false;
				}
				if (component == _piece || (Object)(object)component == (Object)(object)_piece)
				{
					return true;
				}
				return BuildActionRules.SamePieceName(component.m_name, _pieceName);
			}
			catch
			{
				return false;
			}
		}

		private static string GhostPieceName(GameObject ghost)
		{
			try
			{
				Piece component = ghost.GetComponent<Piece>();
				return (component != null && (Object)(object)component != (Object)null) ? component.m_name : "missing";
			}
			catch
			{
				return "unavailable";
			}
		}

		internal bool CheckPlacement(Player player, Piece piece)
		{
			if (SamePlacement(player, "recalculated", logAccepted: true) && (Object)(object)piece == (Object)(object)_piece)
			{
				return true;
			}
			Rejected = "cancelled";
			return false;
		}

		internal override string? InvalidReason()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Invalid comparison between Unknown and O
			object obj = base.InvalidReason();
			if (obj == null)
			{
				if (((Character)Player).InPlaceMode() && !((Object)(object)MaterialLinkContextFactory.SelectedPiece(Player) != (Object)(object)_piece) && (object)(ItemData)PaymentGameAccess.Invoke(Player, "GetRightItem") == _tool && SamePlacement(Player) && !Hud.IsPieceSelectionVisible() && !InventoryGui.IsVisible() && !Menu.IsVisible() && ((Character)Player).HaveStamina(_tool.m_shared.m_attack.m_attackStamina) && (!_tool.m_shared.m_useDurability || !(_tool.m_durability <= 0f)))
				{
					return null;
				}
				obj = "cancelled";
			}
			return (string?)obj;
		}

		protected override void Execute()
		{
			using (MaterialLinkScope.Enter(Context))
			{
				using PlacementActionScope placementActionScope = PlacementActionScope.Enter(Player);
				placementActionScope.CheckedPiece = _piece;
				if (!Player.HaveRequirements(_piece, (RequirementMode)0) || !Debit())
				{
					if (Rejected == null)
					{
						Rejected = "action changed";
					}
					return;
				}
				try
				{
					if (!Player.TryPlacePiece(_piece))
					{
						if (Rejected == null)
						{
							Rejected = "placement rejected";
						}
						Plugin.Debug(string.Format("Storage {0}: build refused piece={1} reason={2} status={3}.", Id, ((Object)_piece).name, Rejected, PaymentGameAccess.ReadField<object>(Player, "m_placementStatus")));
					}
					else
					{
						Consumed = true;
						CompleteBuild();
					}
				}
				catch
				{
					if (Consumed)
					{
						Plugin.Log.LogError((object)$"Storage {Id}: placement output started before an exception; payment retained, world effects were not rolled back.");
					}
					throw;
				}
			}
		}

		private void CompleteBuild()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Invalid comparison between Unknown and I4
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Invalid comparison between Unknown and I4
			Hud.instance.m_buildUi.AddRecentPiece(Player.GetSelectedPiece());
			PaymentGameAccess.WriteField(Player, "m_lastToolUseTime", Time.time);
			((Character)Player).UseStamina((float)PaymentGameAccess.Invoke(Player, "GetBuildStamina"));
			PieceTable buildTool = Player.GetBuildTool();
			if ((int)buildTool.m_skill != 0)
			{
				if ((int)buildTool.m_skill == 107)
				{
					Game.instance.IncrementPlayerStat((PlayerStatType)168, 1f, false);
				}
				int num = PaymentGameAccess.ReadField<int>(Player, "m_buildRemoveDebt");
				if (num > 0)
				{
					PaymentGameAccess.WriteField(Player, "m_buildRemoveDebt", num - 1);
				}
				else
				{
					((Character)Player).RaiseSkill(buildTool.m_skill, 1f);
					if ((int)buildTool.m_skill == 107)
					{
						Game.instance.IncrementPlayerStat((PlayerStatType)169, 1f, false);
					}
				}
			}
			if (_tool.m_shared.m_useDurability)
			{
				ItemData tool = _tool;
				tool.m_durability -= (float)PaymentGameAccess.Invoke(Player, "GetPlaceDurability", _tool) * Game.m_durabilityRate;
			}
			_tool.m_shared.m_buildEffect.Create(((Component)Player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)Player).GetZDOID());
		}
	}
	internal sealed class PlacementActionScope : IDisposable
	{
		[ThreadStatic]
		private static PlacementActionScope? _current;

		private readonly PlacementActionScope? _previous;

		private bool _disposed;

		internal Player? Player { get; }

		internal Piece? CheckedPiece { get; set; }

		internal static PlacementActionScope? Current => _current;

		private PlacementActionScope(Player? player)
		{
			_previous = _current;
			Player = player;
			_current = this;
		}

		internal static PlacementActionScope Enter(Player? player)
		{
			return new PlacementActionScope(player);
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				_current = _previous;
			}
		}
	}
	internal sealed class PlacementState : IDisposable
	{
		internal PlacementActionScope? Action { get; }

		internal MaterialLinkScope? Link { get; }

		internal PlacementState(PlacementActionScope? action, MaterialLinkScope? link)
		{
			Action = action;
			Link = link;
		}

		public void Dispose()
		{
			Link?.Dispose();
			Action?.Dispose();
		}
	}
	internal sealed class CraftPaymentOperation : PaymentOperation
	{
		private readonly InventoryGui _gui;

		private readonly Recipe _recipe;

		private readonly ItemData? _upgrade;

		private readonly int _quality;

		private readonly int _quantity;

		private readonly int _variant;

		private readonly CraftingStation? _station;

		internal override StorageOperationKind Kind => StorageOperationKind.Craft;

		internal CraftPaymentOperation(MaterialLinkContext context, InventoryGui gui)
			: base(context, (PaymentGameAccess.ReadField<ItemData>(gui, "m_craftUpgradeItem")?.m_quality ?? 0) + 1, (!PaymentGameAccess.ReadField<bool>(gui, "m_multiCrafting")) ? 1 : PaymentGameAccess.ReadField<int>(gui, "m_multiCraftAmount"))
		{
			_gui = gui;
			_recipe = context.Recipe;
			_station = context.Station;
			_upgrade = PaymentGameAccess.ReadField<ItemData>(gui, "m_craftUpgradeItem");
			_quality = _upgrade?.m_quality ?? 0;
			_quantity = ((!PaymentGameAccess.ReadField<bool>(gui, "m_multiCrafting")) ? 1 : PaymentGameAccess.ReadField<int>(gui, "m_multiCraftAmount"));
			_variant = PaymentGameAccess.ReadField<int>(gui, "m_craftVariant");
		}

		internal override string? InvalidReason()
		{
			object obj = base.InvalidReason();
			if (obj == null)
			{
				if (!((Object)(object)_gui == (Object)null) && InventoryGui.IsVisible() && !((Object)(object)Player.GetCurrentCraftingStation() != (Object)(object)_station) && !((Object)(object)PaymentGameAccess.ReadField<Recipe>(_gui, "m_craftRecipe") != (Object)(object)_recipe) && !((Object)(object)MaterialLinkContextFactory.ForSelectedRecipe(Player, LinkMode.Consumption)?.Recipe != (Object)(object)_recipe) && PaymentGameAccess.ReadField<ItemData>(_gui, "m_craftUpgradeItem") == _upgrade && (_upgrade == null || (((Humanoid)Player).GetInventory().ContainsItem(_upgrade) && _upgrade.m_quality == _quality)) && ((!PaymentGameAccess.ReadField<bool>(_gui, "m_multiCrafting")) ? 1 : PaymentGameAccess.ReadField<int>(_gui, "m_multiCraftAmount")) == _quantity && PaymentGameAccess.ReadField<int>(_gui, "m_craftVariant") == _variant)
				{
					return null;
				}
				obj = "cancelled";
			}
			return (string?)obj;
		}

		protected override void Execute()
		{
			PlayerInventorySnapshot playerInventorySnapshot = new PlayerInventorySnapshot(Player);
			try
			{
				using (MaterialLinkScope.Enter(Context))
				{
					PaymentGameAccess.Invoke(_gui, "DoCrafting", Player);
				}
				if (!Consumed && Debited)
				{
					PlayerRestored();
					playerInventorySnapshot.Restore();
				}
				if (!Consumed && Rejected == null)
				{
					Rejected = "craft rejected";
				}
			}
			catch
			{
				Consumed = false;
				PlayerRestored();
				playerInventorySnapshot.Restore();
				throw;
			}
		}
	}
	internal sealed class PlayerInventorySnapshot
	{
		private readonly Player _player;

		private readonly List<(ItemData Item, int Stack, bool Equipped)> _items;

		internal PlayerInventorySnapshot(Player player)
		{
			_player = player;
			_items = (from i in ((Humanoid)player).GetInventory().GetAllItems()
				select (i: i, m_stack: i.m_stack, ((Humanoid)player).IsItemEquiped(i))).ToList();
		}

		internal void Restore()
		{
			List<ItemData> allItems = ((Humanoid)_player).GetInventory().GetAllItems();
			List<ItemData> list = allItems.Where((ItemData i) => !_items.Any<(ItemData, int, bool)>(((ItemData Item, int Stack, bool Equipped) x) => x.Item == i)).ToList();
			allItems.Clear();
			foreach (var item3 in _items)
			{
				item3.Item.m_stack = item3.Stack;
				allItems.Add(item3.Item);
			}
			foreach (ItemData item in list)
			{
				Attempt(delegate
				{
					((Humanoid)_player).UnequipItem(item, true);
				}, "unequip");
			}
			Attempt(delegate
			{
				PaymentGameAccess.Invoke(((Humanoid)_player).GetInventory(), "Changed", false, false);
			}, "change notification");
			foreach (var item2 in _items)
			{
				if (item2.Equipped && !((Humanoid)_player).IsItemEquiped(item2.Item))
				{
					Attempt(delegate
					{
						((Humanoid)_player).EquipItem(item2.Item, false);
					}, "re-equip");
				}
			}
		}

		private static void Attempt(Action action, string step)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Inventory restore " + step + " failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	internal static class DepositAction
	{
		internal static bool IsRunning
		{
			get
			{
				StorageOperation? current = ContainerAccessService.Current;
				if (current == null)
				{
					return false;
				}
				return current.Kind == StorageOperationKind.Deposit;
			}
		}

		internal static void Run()
		{
			Player localPlayer = Player.m_localPlayer;
			InventoryGui instance = InventoryGui.instance;
			if (!ContainerAccessService.Busy && Plugin.Settings.Enabled && Plugin.Settings.DepositEnabled && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)instance == (Object)null) && InventoryGui.IsVisible() && !instance.IsContainerOpen() && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting())
			{
				ContainerAccessService.Start(new DepositOperation(localPlayer, instance));
				DepositButton.RefreshOperationState();
			}
		}
	}
	internal static class DepositButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Run;
		}

		private static readonly Color DisabledFrameColor = new Color(0.566f, 0.566f, 0.566f, 0.502f);

		private static Button? _button;

		private static TMP_Text? _label;

		private static bool _manualLabelTint;

		private static Color _labelColor = Color.white;

		private static RectTransform? _rect;

		private static RectTransform? _panel;

		private static RectTransform? _stackRect;

		private static readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4];

		private static Vector2 _baseSize;

		private static float _padding;

		private static float _desiredWidth;

		private static float _lastPanelWidth;

		private static bool _baselineMeasured;

		private static bool _labelDirty;

		private static bool _layoutPending;

		private static float _nextRangeCheck;

		private static bool _nearby;

		private const float FallbackRightOffset = -1.3f;

		private const float MaxOverhang = 5f;

		private const float MaxInset = -50f;

		private const float VerticalOffset = -10f;

		internal static void RefreshOperationState()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_button == (Object)null))
			{
				bool flag = DepositRules.CanActivate(_nearby, ContainerAccessService.Busy);
				((Selectable)_button).interactable = flag;
				if (_manualLabelTint && (Object)(object)_label != (Object)null)
				{
					((Graphic)_label).color = (flag ? _labelColor : Color.grey);
				}
			}
		}

		private static void TryAlignRightOffset(InventoryGui? gui)
		{
			//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)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_rect == (Object)null || (Object)(object)_panel == (Object)null || (Object)(object)gui == (Object)null)
			{
				return;
			}
			Rect rect = _panel.rect;
			if (!DepositLabel.IsPositiveFinite(((Rect)(ref rect)).width))
			{
				return;
			}
			if ((Object)(object)_stackRect == (Object)null && (Object)(object)gui.m_stackAllButton != (Object)null)
			{
				_stackRect = ((Component)gui.m_stackAllButton).GetComponent<RectTransform>();
			}
			if ((Object)(object)_stackRect == (Object)null)
			{
				return;
			}
			rect = _stackRect.rect;
			if (!DepositLabel.IsPositiveFinite(((Rect)(ref rect)).width))
			{
				return;
			}
			rect = _stackRect.rect;
			if (DepositLabel.IsPositiveFinite(((Rect)(ref rect)).height))
			{
				_stackRect.GetWorldCorners(_corners);
				float x = ((Transform)_panel).InverseTransformPoint(_corners[2]).x;
				rect = _panel.rect;
				float num = x - ((Rect)(ref rect)).xMax;
				if (num <= 5f && num >= -50f && (!(Mathf.Abs(_rect.anchoredPosition.x - num) < 0.01f) || _rect.anchoredPosition.y != -10f))
				{
					_rect.anchoredPosition = new Vector2(num, -10f);
					Plugin.Debug($"Store Nearby: aligned right offset to Place Stacks ({num:0.##} UI units).");
				}
			}
		}

		internal static void Create(InventoryGui gui)
		{
			//IL_00ad: 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_00b5: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Expected O, but got Unknown
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Expected O, but got Unknown
			Destroy();
			Button val = (((Object)(object)gui.m_takeAllButton != (Object)null) ? gui.m_takeAllButton : gui.m_stackAllButton);
			if ((Object)(object)val == (Object)null || (Object)(object)gui.m_player == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Could not create the Deposit button because the inventory controls were unavailable.");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, (Transform)(object)gui.m_player, false);
			((Object)val2).name = "LCFSP_StoreNearbyButton";
			_button = val2.GetComponent<Button>();
			if ((Object)(object)_button == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				Plugin.Log.LogWarning((object)"Could not create the Deposit button.");
				return;
			}
			RectTransform? obj = (_rect = val2.GetComponent<RectTransform>());
			_panel = gui.m_player;
			Rect rect = obj.rect;
			_baseSize = ((Rect)(ref rect)).size;
			obj.anchorMin = new Vector2(1f, 0f);
			obj.anchorMax = new Vector2(1f, 0f);
			obj.pivot = new Vector2(1f, 1f);
			obj.anchoredPosition = new Vector2(-1.3f, -10f);
			((Transform)obj).localScale = Vector3.one;
			TryAlignRightOffset(gui);
			Localize[] componentsInChildren = val2.GetComponentsInChildren<Localize>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Behaviour)componentsInChildren[i]).enabled = false;
			}
			_label = val2.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)_label != (Object)null)
			{
				float fontSize = DepositLabel.FixedFontSize(_label.enableAutoSizing, _label.fontSize, _label.fontSizeMax);
				_label.richText = false;
				_label.parseCtrlCharacters = false;
				_label.textWrappingMode = (TextWrappingModes)3;
				_label.enableAutoSizing = false;
				_label.fontSize = fontSize;
				_label.overflowMode = (TextOverflowModes)1;
			}
			else
			{
				Plugin.Log.LogWarning((object)"Store Nearby button has no TMP label; custom text and sizing are unavailable.");
			}
			ApplyDisabledStyle(val2, _button, _label);
			RefreshLabel();
			_button.onClick = new ButtonClickedEvent();
			ButtonClickedEvent onClick = _button.onClick;
			object obj2 = <>O.<0>__Run;
			if (obj2 == null)
			{
				UnityAction val3 = DepositAction.Run;
				<>O.<0>__Run = val3;
				obj2 = (object)val3;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj2);
			RefreshVisibility(gui);
		}

		private static void ApplyDisabledStyle(GameObject clone, Button button, TMP_Text? label)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			ButtonTextColor component = clone.GetComponent<ButtonTextColor>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_disabledColor = Color.grey;
			}
			else if ((Object)(object)label != (Object)null)
			{
				_manualLabelTint = true;
				_labelColor = ((Graphic)label).color;
			}
			if ((Object)(object)((Selectable)button).targetGraphic == (Object)null)
			{
				((Selectable)button).targetGraphic = (Graphic)(object)(clone.GetComponent<Image>() ?? clone.GetComponentInChildren<Image>(true));
			}
			if ((Object)(object)((Selectable)button).targetGraphic != (Object)null)
			{
				if ((int)((Selectable)button).transition == 0)
				{
					((Selectable)button).transition = (Transition)1;
				}
				ColorBlock colors = ((Selectable)button).colors;
				((ColorBlock)(ref colors)).disabledColor = DisabledFrameColor;
				((Selectable)button).colors = colors;
			}
		}

		internal static void RefreshLabel()
		{
			if (!((Object)(object)_label == (Object)null))
			{
				string depositButtonLabel = Plugin.Settings.DepositButtonLabel;
				if (!_baselineMeasured || !(_label.text == depositButtonLabel))
				{
					_label.text = depositButtonLabel;
					_labelDirty = true;
					_layoutPending = true;
					RefreshLayout();
				}
			}
		}

		private static void RefreshLayout(InventoryGui? gui = null)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_rect == (Object)null || (Object)(object)_panel == (Object)null || (Object)(object)_label == (Object)null)
			{
				return;
			}
			TryAlignRightOffset(gui ?? InventoryGui.instance);
			Rect rect = _panel.rect;
			float width = ((Rect)(ref rect)).width;
			if (!_layoutPending && width == _lastPanelWidth)
			{
				return;
			}
			if (!DepositLabel.IsPositiveFinite(_baseSize.x) || !DepositLabel.IsPositiveFinite(_baseSize.y))
			{
				rect = _rect.rect;
				_baseSize = ((Rect)(ref rect)).size;
			}
			if (!DepositLabel.IsPositiveFinite(_baseSize.x) || !DepositLabel.IsPositiveFinite(_baseSize.y))
			{
				return;
			}
			if (!DepositLabel.IsPositiveFinite(width))
			{
				SetSize(_baseSize.x);
				_layoutPending = true;
				return;
			}
			if (_labelDirty || !_baselineMeasured)
			{
				if ((Object)(object)_label.font == (Object)null)
				{
					return;
				}
				string text = _label.text;
				float num = (_baselineMeasured ? 0f : _label.GetPreferredValues("Store Nearby").x);
				bool flag = !string.IsNullOrEmpty(text);
				float x = _label.GetPreferredValues(text).x;
				if (!_baselineMeasured)
				{
					if (!DepositLabel.IsPositiveFinite(num))
					{
						return;
					}
					_padding = Mathf.Max(0f, _baseSize.x - num);
					_baselineMeasured = true;
				}
				_desiredWidth = (flag ? (x + _padding) : 0f);
				_labelDirty = false;
			}
			float width2;
			bool num2 = DepositLabel.TryFitWidth(DepositLabel.GetMinimumWidth(_baseSize.y), width, _desiredWidth, out width2);
			if (num2)
			{
				SetSize(width2);
			}
			_layoutPending = !num2;
			_lastPanelWidth = width;
		}

		private static void SetSize(float width)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_rect == (Object)null))
			{
				Rect rect = _rect.rect;
				if (((Rect)(ref rect)).width != width)
				{
					_rect.SetSizeWithCurrentAnchors((Axis)0, width);
				}
				rect = _rect.rect;
				if (((Rect)(ref rect)).height != _baseSize.y)
				{
					_rect.SetSizeWithCurrentAnchors((Axis)1, _baseSize.y);
				}
			}
		}

		internal static void RefreshVisibility(InventoryGui? gui = null)
		{
			_nextRangeCheck = 0f;
			if ((Object)(object)_button != (Object)null)
			{
				if (gui == null)
				{
					gui = InventoryGui.instance;
				}
				((Component)_button).gameObject.SetActive((Object)(object)gui != (Object)null && DepositRules.ShowButton(Plugin.Settings.Enabled && Plugin.Settings.DepositEnabled, gui.IsContainerOpen()));
				RefreshAvailability(gui);
			}
		}

		internal static void RefreshAvailability(InventoryGui? gui = null)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_button == (Object)null || !((Component)_button).gameObject.activeInHierarchy || !InventoryGui.IsVisible())
			{
				_nextRangeCheck = 0f;
				return;
			}
			RefreshLayout(gui);
			if (Time.unscaledTime < _nextRangeCheck)
			{
				return;
			}
			_nextRangeCheck = Time.unscaledTime + 1f;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_nearby = false;
				((Selectable)_button).interactable = false;
				return;
			}
			bool flag = ContainerDiscovery.AnyWithinRange(((Component)localPlayer).transform.position, Plugin.Settings.DepositRange) ?? _nearby;
			if (flag != _nearby)
			{
				Plugin.Debug(string.Format("Store Nearby: container {0} within {1:0.#}m.", flag ? "found" : "not found", Plugin.Settings.DepositRange));
			}
			_nearby = flag;
			RefreshOperationState();
		}

		internal static void Destroy()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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)
			if ((Object)(object)_button != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_button).gameObject);
				_button = null;
			}
			_label = null;
			_manualLabelTint = false;
			_labelColor = Color.white;
			_rect = null;
			_panel = null;
			_stackRect = null;
			_baseSize = Vector2.zero;
			_padding = 0f;
			_desiredWidth = 0f;
			_lastPanelWidth = 0f;
			_baselineMeasured = false;
			_labelDirty = false;
			_layoutPending = false;
			_nearby = false;
			_nextRangeCheck = 0f;
		}
	}
	internal static class DepositItemCatalogue
	{
		private static ObjectDB? _database;

		private static RuntimeSettings? _settings;

		private static float _nextCheck;

		internal static void Clear()
		{
			_database = null;
			_settings = null;
			_nextCheck = 0f;
		}

		internal static void Tick(RuntimeSettings settings, bool export)
		{
			if (Time.unscaledTime < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.unscaledTime + 1f;
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				_database = null;
				return;
			}
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_items.Count == 0 || ((Object)(object)_database == (Object)(object)instance && _settings == settings))
			{
				return;
			}
			_database = instance;
			_settings = settings;
			try
			{
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
				List<(string, string, string, string, bool)> list = new List<(string, string, string, string, bool)>();
				foreach (GameObject item in instance.m_items)
				{
					ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null);
					if (!((Object)(object)val == (Object)null) && val.m_itemData?.m_shared != null)
					{
						SharedData shared = val.m_itemData.m_shared;
						string text = StorageRules.NormalizeItemName(shared.m_name);
						hashSet.Add(text);
						dictionary.TryGetValue(text, out var value);
						dictionary[text] = value + 1;
						list.Add((text, Localization.instance.Localize(shared.m_name), ((Object)item).name, ((object)Unsafe.As<ItemType, ItemType>(ref shared.m_itemType)/*cast due to .constrained prefix*/).ToString(), shared.m_questItem));
					}
				}
				WarnUnknown("04 - Store Nearby / IncludedItems", settings.DepositIncludedNames, hashSet, allowTypeAliases: true);
				WarnUnknown("04 - Store Nearby / ExcludedItems", settings.DepositExcludedNames, hashSet, allowTypeAliases: false);
				WarnUnknownReserves("01 - General / ReserveItems", settings.Reserves, hashSet);
				if (!export)
				{
					return;
				}
				list.Sort(((string Key, string Name, string Prefab, string Type, bool Quest) a, (string Key, string Name, string Prefab, string Type, bool Quest) b) => StringComparer.Ordinal.Compare(a.Prefab, b.Prefab));
				IReadOnlyCollection<string> excludedNames = DepositRules.ParseExcludedNames("");
				IReadOnlyCollection<string> includedNames = DepositRules.ParseExcludedNames("raspberries, blueberries, cloudberries, vineberry, mushroomcommon, mushroomyellow, mushroomblue, jotunpuffs, magecap, smokepuff, pukeberries, trophies");
				StringBuilder stringBuilder = new StringBuilder("# Registered deposit item catalogue\n\n");
				stringBuilder.AppendLine(string.Format("Game: {0}; mod: {1}; generated UTC: {2:O}.", Version.GetVersionString(false), "1.0.0", DateTime.UtcNow));
				stringBuilder.AppendLine($"Source: live ObjectDB.m_items ({instance.m_items.Count} entries; {list.Count} item rows; {hashSet.Count} distinct keys).");
				stringBuilder.AppendLine("Display language: " + Cell(Localization.instance.GetSelectedLanguage()) + ". This snapshot includes installed mod items.");
				stringBuilder.AppendLine("Baseline means a new configuration, outside the hotbar and unequipped. Current exclusions and protections still apply.");
				stringBuilder.AppendLine("Internal/test/unobtainable prefabs are retained; runtime registration does not prove obtainability.");
				stringBuilder.AppendLine("Duplicate keys intentionally affect every matching prefab. Blank keys cannot be configured.\n");
				stringBuilder.AppendLine("Installed plugins:");
				foreach (PluginInfo value2 in Chainloader.PluginInfos.Values)
				{
					stringBuilder.AppendLine($"- {Cell(value2.Metadata.GUID)} {value2.Metadata.Version}");
				}
				stringBuilder.AppendLine("\n| Config key | Display name | Prefab | Type | Default deposit | Notes |");
				stringBuilder.AppendLine("|---|---|---|---|---|---|");
				foreach (var item2 in list)
				{
					bool flag = DepositRules.CanDeposit(item2.Item4, equipped: false, item2.Item5, inHotbar: false, item2.Item1, excludedNames, includedNames);
					string text2 = ((item2.Item1.Length == 0) ? "Blank key" : ((dictionary[item2.Item1] > 1) ? "Shared key" : ""));
					if (item2.Item5)
					{
						text2 += " Quest protected";
					}
					stringBuilder.AppendLine("| " + Cell(item2.Item1) + " | " + Cell(item2.Item2) + " | " + Cell(item2.Item3) + " | " + Cell(item2.Item4) + " | " + (flag ? "Yes" : "No") + " | " + text2.Trim() + " |");
				}
				string text3 = Path.Combine(Paths.ConfigPath, "LuminairPrime.valheim.localcraftfromstorageprime.deposit-items.md");
				File.WriteAllText(text3, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				Plugin.Log.LogInfo((object)$"Exported {list.Count} deposit item rows to {text3}.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Item catalogue validation/export failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void WarnUnknown(string setting, IReadOnlyCollection<string> names, HashSet<string> known, bool allowTypeAliases)
		{
			List<string> list = new List<string>();
			foreach (string name in names)
			{
				if (!known.Contains(name) && (!allowTypeAliases || !DepositRules.TypeAliases.ContainsKey(name)))
				{
					list.Add(name);
				}
			}
			if (list.Count > 0)
			{
				Plugin.Log.LogWarning((object)(setting + ": no registered item key matches " + string.Join(", ", list) + ". Entries are retained; export the item catalogue to check names."));
			}
		}

		private static void WarnUnknownReserves(string setting, IReadOnlyDictionary<string, int> reserves, HashSet<string> known)
		{
			List<string> list = new List<string>();
			foreach (string key in reserves.Keys)
			{
				if (key != "*" && !known.Contains(key))
				{
					list.Add(key);
				}
			}
			if (list.Count > 0)
			{
				Plugin.Log.LogWarning((object)(setting + ": no registered item key matches " + string.Join(", ", list) + ". Entries are retained; export the item catalogue to check names."));
			}
		}

		private static string Cell(string value)
		{
			return value.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
				.Replace("|", "&#124;")
				.Replace("\r", " ")
				.Replace("\n", " ");
		}
	}
	internal static class DepositLabel
	{
		internal const string Default = "Store Nearby";

		internal const int MaxTextElements = 32;

		internal static float FixedFontSize(bool autoSizing, float fontSize, float fontSizeMax)
		{
			if (!autoSizing || !IsPositiveFinite(fontSizeMax))
			{
				return fontSize;
			}
			return fontSizeMax;
		}

		internal static string Normalize(string? value)
		{
			if (value == null)
			{
				return "Store Nearby";
			}
			string text = value.Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ')
				.Trim();
			if (text.Length == 0)
			{
				return string.Empty;
			}
			TextElementEnumerator textElementEnumerator = StringInfo.GetTextElementEnumerator(text);
			int num = 0;
			while (textElementEnumerator.MoveNext())
			{
				if (num++ == 32)
				{
					text = text.Substring(0, textElementEnumerator.ElementIndex).TrimEnd();
					break;
				}
			}
			return text;
		}

		internal static bool IsPositiveFinite(float value)
		{
			if (value > 0f && !float.IsInfinity(value))
			{
				return !float.IsNaN(value);
			}
			return false;
		}

		internal static float GetMinimumWidth(float baseHeight)
		{
			if (!IsPositiveFinite(baseHeight))
			{
				return 0f;
			}
			return baseHeight;
		}

		internal static bool TryFitWidth(float minWidth, float panelWidth, float desiredWidth, out float width)
		{
			width = 0f;
			if (!IsPositiveFinite(minWidth))
			{
				return false;
			}
			width = minWidth;
			if (!IsPositiveFinite(panelWidth))
			{
				return false;
			}
			float val = panelWidth * 0.6f;
			float val2 = Math.Min(minWidth, val);
			if (float.IsNaN(desiredWidth) || float.IsInfinity(desiredWidth))
			{
				desiredWidth = minWidth;
			}
			width = Math.Max(val2, Math.Min(desiredWidth, val));
			return true;
		}
	}
	internal sealed class DepositOperation : StorageOperation
	{
		private readonly InventoryGui _gui;

		private readonly Inventory _source;

		private readonly List<ItemData> _items = new List<ItemData>();

		private readonly DepositOutcome _outcome = new DepositOutcome();

		private IReadOnlyList<NearbyContainer>? _containers;

		private int _index;

		internal override StorageOperationKind Kind => StorageOperationKind.Deposit;

		internal DepositOperation(Player player, InventoryGui gui)
			: base(player)
		{
			_gui = gui;
			_source = ((Humanoid)player).GetInventory();
		}

		internal override string? InvalidReason()
		{
			object obj = base.InvalidReason();
			if (obj == null)
			{
				if (!((Object)(object)_gui == (Object)null) && InventoryGui.IsVisible() && !_gui.IsContainerOpen())
				{
					return null;
				}
				obj = "cancelled";
			}
			return (string?)obj;
		}

		internal override bool CanUse(Container chest)
		{
			//IL_000f: 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_0024: 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)
			if ((Object)(object)chest != (Object)null)
			{
				Vector3 val = ((Component)chest).transform.position - ((Component)Player).transform.position;
				ZNetView view;
				if (((Vector3)(ref val)).sqrMagnitude <= Settings.DepositRange * Settings.DepositRange)
				{
					return ContainerEligibility.Check(Player, chest, Settings, out view, null, advisory: true);
				}
			}
			return false;
		}

		internal override void Step()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if (PatchContracts.DragItemField.GetValue(_gui) != null)
			{
				_outcome.HeldItem = true;
				ContainerAccessService.Finish("held item");
				return;
			}
			if (_containers == null)
			{
				foreach (ItemData allItem in _source.GetAllItems())
				{
					if (Eligible(allItem))
					{
						_items.Add(allItem);
					}
				}
				if (_items.Count == 0)
				{
					_outcome.NoItems = true;
					ContainerAccessService.Finish("no items");
					return;
				}
				_containers = ContainerDiscovery.FindNearby(Player, ((Component)Player).transform.position, Settings.DepositRange, Settings, null, delegate(string reason)
				{
					_outcome.Unavailable++;
					Plugin.Debug($"Storage {Id}: skipped {reason}.");
				});
			}
			while (_index < _containers.Count && DepositTransfer.HasRemaining(_source, _items))
			{
				Container container = _containers[_index].Container;
				if (!CanUse(container))
				{
					_outcome.Unavailable++;
					_index++;
					continue;
				}
				Inventory val = ContainerApi.LoadInventory(container);
				bool flag = false;
				foreach (ItemData item in _items)
				{
					if (_source.ContainsItem(item) && Eligible(item) && val != null && val.ContainsItemByName(item.m_shared.m_name))
					{
						flag = true;
					}
				}
				if (!flag)
				{
					_outcome.Checked++;
					_index++;
					continue;
				}
				ContainerHold containerHold = ContainerAccessService.Acquire(this, container);
				if (containerHold == null)
				{
					return;
				}
				_outcome.Checked++;
				int moved = 0;
				bool changed = false;
				try
				{
					foreach (ItemData item2 in _items)
					{
						if (_source.ContainsItem(item2) && Eligible(item2) && containerHold.Inventory.ContainsItemByName(item2.m_shared.m_name))
						{
							_outcome.Matched = true;
							if (!CanUse(container) || !containerHold.Valid())
							{
								throw new StorageAbort("held chest changed");
							}
							DepositTransfer.MoveIntoAvailableSpace(_source, containerHold.Inventory, item2, ref moved, ref changed);
							containerHold.Saved();
						}
					}
				}
				finally
				{
					_outcome.Moved = StorageRules.SaturatingAdd(_outcome.Moved, moved);
					foreach (ItemData item3 in _items)
					{
						DepositTransfer.RemoveEmptySourceReference(_source, item3);
					}
					if (changed && (Object)(object)containerHold.View != (Object)null && containerHold.View.IsValid() && containerHold.View.IsOwner())
					{
						ContainerApi.Save(container);
					}
					containerHold.Release();
					Holds.Remove(containerHold);
				}
				_index++;
			}
			ContainerAccessService.Finish("completed");
		}

		private bool Eligible(ItemData item)
		{
			if (item?.m_shared != null)
			{
				return DepositRules.CanDeposit(((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString(), item.m_equipped || ((Humanoid)Player).IsItemEquiped(item), item.m_shared.m_questItem, item.m_gridPos.y == 0, item.m_shared.m_name, Settings.DepositExcludedNames, Settings.DepositIncludedNames);
			}
			return false;
		}

		internal override void Finish(string reason)
		{
			base.Finish(reason);
			_outcome.Remaining = DepositTransfer.HasRemaining(_source, _items);
			DepositOutcome outcome = _outcome;
			bool cancelled;
			switch (reason)
			{
			case "disconnect":
			case "world exit":
			case "plugin destroyed":
			case "settings changed":
			case "cancelled":
			case "manual open":
			case "manual open pending":
			case "player unavailable":
			case "session changed":
				cancelled = true;
				break;
			default:
				cancelled = false;
				break;
			}
			outcome.Cancelled = cancelled;
			_outcome.TimedOut = reason.Contains("timeout");
			if (reason != "completed" && !_outcome.Cancelled && !_outcome.NoItems && !_outcome.HeldItem)
			{
				_outcome.Unavailable++;
				_outcome.RelevantAccessFailure = true;
				_outcome.Failed = reason == "exception";
			}
			Plugin.Debug($"Deposit {Id}: movedUnits={_outcome.Moved}, checkedChests={_outcome.Checked}, unavailableChests={_outcome.Unavailable}, matched={_outcome.Matched}, remaining={_outcome.Remaining}, reason={reason}.");
			string text = _outcome.Message();
			if (Settings.DepositFeedback && text != null && (Object)(object)Player == (Object)(object)Player.m_localPlayer && InventoryGui.IsVisible())
			{
				((Character)Player).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	internal sealed class DepositOutcome
	{
		internal bool Cancelled;

		internal bool TimedOut;

		internal int Moved;

		internal int Checked;

		internal int Unavailable;

		internal bool Matched;

		internal bool Remaining;

		internal bool RelevantAccessFailure;

		internal bool NoItems;

		internal bool HeldItem;

		internal bool Failed;

		internal string? Message()
		{
			if (Cancelled)
			{
				return null;
			}
			if (TimedOut)
			{
				if (Moved <= 0)
				{
					return "Storage did not respond. Try again.";
				}
				return "Some items were stored, but storage stopped responding.";
			}
			if (Failed)
			{
				return "Couldn't finish storing items. Check the log.";
			}
			if (HeldItem)
			{
				return "Finish moving the held item first.";
			}
			if (NoItems)
			{
				return "Nothing eligible to store.";
			}
			if (Moved > 0)
			{
				if (!Remaining || !RelevantAccessFailure)
				{
					return null;
				}
				return "Some storage was unavailable.";
			}
			if (Unavailable > 0 || Checked == 0)
			{
				return "No eligible storage.";
			}
			if (!Matched)
			{
				return "No available container holds these items.";
			}
			return "No room in matching containers.";
		}
	}
	internal static class DepositRules
	{
		internal const string DefaultExclusions = "";

		internal const string DefaultInclusions = "raspberries, blueberries, cloudberries, vineberry, mushroomcommon, mushroomyellow, mushroomblue, jotunpuffs, magecap, smokepuff, pukeberries, trophies";

		internal static readonly IReadOnlyDictionary<string, string> TypeAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "trophies", "Trophy" },
			{ "trophy", "Trophy" }
		};

		internal static bool ShowButton(bool enabled, bool containerOpen)
		{
			if (enabled)
			{
				return !containerOpen;
			}
			return false;
		}

		internal static bool CanActivate(bool present, bool running)
		{
			if (present)
			{
				return !running;
			}
			return false;
		}

		internal static IReadOnlyCollection<string> ParseExcludedNames(string? text)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(text))
			{
				return hashSet;
			}
			string[] array = text.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = StorageRules.NormalizeItemName(array[i]);
				if (text2.Length > 0)
				{
					hashSet.Add(text2);
				}
			}
			return hashSet;
		}

		internal static bool IsIncludedByType(string entry, string itemType)
		{
			if (TypeAliases.TryGetValue(entry, out string value))
			{
				return string.Equals(value, itemType, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		internal static bool CanDeposit(string itemType, bool equipped, bool questItem, bool inHotbar, string itemName, IReadOnlyCollection<string> excludedNames, IReadOnlyCollection<string>? includedNames = null)
		{
			if (equipped || questItem || inHotbar)
			{
				return false;
			}
			string text = StorageRules.NormalizeItemName(itemName);
			foreach (string excludedName in excludedNames)
			{
				if (string.Equals(text, excludedName, StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
			}
			if (text.Length == 0)
			{
				return false;
			}
			if (includedNames != null)
			{
				foreach (string includedName in includedNames)
				{
					if (string.Equals(text, includedName, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (IsIncludedByType(includedName, itemType))
					{
						return true;
					}
				}
			}
			return string.Equals(itemType, "Material", StringComparison.Ordinal);
		}

		internal static int CountMoved(int beforeStack, bool sourceContainsItem, int afterStack)
		{
			if (!sourceContainsItem)
			{
				return Math.Max(0, beforeStack);
			}
			return Math.Max(0, beforeStack - afterStack);
		}

		internal static int MoveAmount(int sourceStack, int destinationSpace)
		{
			return Math.Min(Math.Max(0, sourceStack), Math.Max(0, destinationSpace));
		}
	}
	internal static class DepositTransfer
	{
		internal static void MoveIntoAvailableSpace(Inventory source, Inventory target, ItemData item, ref int moved, ref bool changed)
		{
			while (source.ContainsItem(item) && item.m_stack > 0)
			{
				ItemData val = FindCompatibleStack(target, item);
				int x;
				int y;
				int destinationSpace;
				if (val != null)
				{
					x = val.m_gridPos.x;
					y = val.m_gridPos.y;
					destinationSpace = val.m_shared.m_maxStackSize - val.m_stack;
				}
				else
				{
					if (!TryFindEmptySlot(target, out x, out y))
					{
						break;
					}
					destinationSpace = item.m_shared.m_maxStackSize;
				}
				int num = DepositRules.MoveAmount(item.m_stack, destinationSpace);
				if (num != 0)
				{
					int stack = item.m_stack;
					bool flag;
					try
					{
						flag = target.MoveItemToThis(source, item, num, x, y);
					}
					catch
					{
						RecordMove(source, item, stack, ref moved, ref changed);
						RemoveEmptySourceReference(source, item);
						throw;
					}
					int num2 = RecordMove(source, item, stack, ref moved, ref changed);
					if (!flag || num2 == 0)
					{
						break;
					}
					continue;
				}
				break;
			}
		}

		private static ItemData? FindCompatibleStack(Inventory target, ItemData sourceItem)
		{
			foreach (ItemData allItem in target.GetAllItems())
			{
				if (allItem != null && allItem.m_shared != null && allItem.IsSameType(sourceItem) && allItem.m_stack < allItem.m_shared.m_maxStackSize)
				{
					return allItem;
				}
			}
			return null;
		}

		private static bool TryFindEmptySlot(Inventory inventory, out int x, out int y)
		{
			for (int i = 0; i < inventory.GetHeight(); i++)
			{
				for (int j = 0; j < inventory.GetWidth(); j++)
				{
					if (inventory.GetItemAt(j, i) == null)
					{
						x = j;
						y = i;
						return true;
					}
				}
			}
			x = -1;
			y = -1;
			return false;
		}

		private static int RecordMove(Inventory source, ItemData item, int before, ref int totalMoved, ref bool changed)
		{
			int num = DepositRules.CountMoved(before, source.ContainsItem(item), item.m_stack);
			if (num > 0)
			{
				totalMoved = StorageRules.SaturatingAdd(totalMoved, num);
				changed = true;
			}
			return num;
		}

		internal static void RemoveEmptySourceReference(Inventory source, ItemData item)
		{
			if (source.ContainsItem(item) && item.m_stack <= 0)
			{
				try
				{
					source.RemoveItem(item);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Could not remove an empty inventory stack: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		internal static bool HasRemaining(Inventory source, IReadOnlyList<ItemData> items)
		{
			foreach (ItemData item in items)
			{
				if (source.ContainsItem(item) && item.m_stack > 0)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class LinkedMaterialCounts
	{
		internal static bool ContextAllowsItem(MaterialLinkContext context, string itemName)
		{
			if (context.Recipe?.m_resources != null)
			{
				Requirement[] resources = context.Recipe.m_resources;
				foreach (Requirement val in resources)
				{
					if (MaterialRequirements.TryGetRequirementName(val, out string itemName2) && !(itemName2 != itemName) && val.m_upgraderResource == (context.Station?.m_upgrader ?? false))
					{
						return true;
					}
				}
				return false;
			}
			if (context.Piece?.m_resources != null)
			{
				Requirement[] resources = context.Piece.m_resources;
				for (int i = 0; i < resources.Length; i++)
				{
					if (MaterialRequirements.TryGetRequirementName(resources[i], out string itemName3) && itemName3 == itemName)
					{
						return true;
					}
				}
				return false;
			}
			return false;
		}

		internal static int CountAvailable(MaterialLinkContext context, string itemName, int quality, bool matchWorldLevel, int stopAt = int.MaxValue)
		{
			if (!context.IsActive || (Object)(object)context.Player == (Object)null || string.IsNullOrEmpty(itemName))
			{
				return 0;
			}
			if (context.Mode == LinkMode.BuildDisplay && quality == -1 && matchWorldLevel)
			{
				HammerMaterialSnapshot displaySnapshot = context.DisplaySnapshot;
				if (displaySnapshot.Available)
				{
					return Math.Min(displaySnapshot.Count(itemName), stopAt);
				}
			}
			return CountAvailableIn(context.CountingInventories, itemName, quality, matchWorldLevel, stopAt);
		}

		internal static IReadOnlyList<Inventory> LoadCountingInventories(MaterialLinkContext context)
		{
			long start = PerformanceDiagnostics.Timestamp();
			List<Inventory> list = new List<Inventory>();
			foreach (NearbyContainer item in FindNearbyContainers(context))
			{
				Inventory val = ContainerApi.LoadInventory(item.Container);
				if (val != null)
				{
					list.Add(val);
				}
			}
			PerformanceDiagnostics.OnCraftLoad(start, list.Count);
			return list;
		}

		internal static int CountAvailableIn(IReadOnlyList<Inventory> inventories, string itemName, int quality, bool matchWorldLevel, int stopAt)
		{
			int num = 0;
			int reserve = Plugin.Settings.ReserveFor(itemName);
			foreach (Inventory inventory in inventories)
			{
				int itemCount = inventory.CountItems(itemName, quality, matchWorldLevel);
				num = StorageRules.SaturatingAdd(num, StorageRules.AvailableAfterReserve(itemCount, reserve));
				if (num >= stopAt)
				{
					return stopAt;
				}
			}
			return num;
		}

		private static IReadOnlyList<NearbyContainer> FindNearbyContainers(MaterialLinkContext context)
		{
			//IL_004b: 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)
			RuntimeSettings settings = Plugin.Settings;
			float radius = context.FeatureDistance(settings);
			if (context.Mode != LinkMode.Placement && (context.Mode != LinkMode.BuildRequirement || !context.RequiresFreshBuildSources))
			{
				return ContainerDiscovery.FindNearby(context.Player, context.Origin, radius, settings, context.Station);
			}
			return ContainerDiscovery.FindForAction(context.Player, context.Origin, radius, settings, context.Station);
		}
	}
	internal enum LinkMode
	{
		Disabled,
		RequirementCheck,
		RequirementDisplay,
		BuildRequirement,
		BuildDisplay,
		Consumption,
		Placement
	}
	internal sealed class MaterialLinkContext
	{
		private IReadOnlyList<Inventory>? _countingInventories;

		private HammerMaterialSnapshot? _displaySnapshot;

		internal LinkMode Mode { get; }

		internal bool RequiresFreshBuildSources { get; }

		internal Player? Player { get; }

		internal Inventory? PlayerInventory { get; }

		internal CraftingStation? Station { get; }

		internal Recipe? Recipe { get; }

		internal Piece? Piece { get; }

		internal Vector3 Origin { get; }

		internal Vector3? StationOrigin { get; }

		internal bool IsActive { get; }

		internal bool AllowsCounting
		{
			get
			{
				bool flag = IsActive;
				if (flag)
				{
					LinkMode mode = Mode;
					bool flag2 = (uint)(mode - 1) <= 3u;
					flag = flag2;
				}
				return flag;
			}
		}

		internal bool AllowsPayment
		{
			get
			{
				bool flag = IsActive;
				if (flag)
				{
					LinkMode mode = Mode;
					bool flag2 = (uint)(mode - 5) <= 1u;
					flag = flag2;
				}
				return flag;
			}
		}

		internal IReadOnlyList<Inventory> CountingInventories => _countingInventories ?? (_countingInventories = LinkedMaterialCounts.LoadCountingInventories(this));

		internal HammerMaterialSnapshot DisplaySnapshot => _displaySnapshot ?? (_displaySnapshot = (((Object)(object)Player != (Object)null) ? HammerMaterialReader.GetOrRefresh(Player, Plugin.Settings) : HammerMaterialSnapshot.Unavailable(Time.unscaledTime)));

		internal MaterialLinkContext(LinkMode mode, Player? player, CraftingStation? station, Recipe? recipe, Piece? piece, Vector3? originOverride = null, bool requiresFreshBuildSources = false)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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)
			//IL_0095: 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)
			Mode = mode;
			RequiresFreshBuildSources = requiresFreshBuildSources;
			Player = player;
			PlayerInventory = ((player != null) ? ((Humanoid)player).GetInventory() : null);
			Station = station;
			Recipe = recipe;
			Piece = piece;
			StationOrigin = (((Object