Decompiled source of QuickStash v1.4.0

QuickStash.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using QuickStash.Config;
using QuickStash.Core;
using QuickStash.Features;
using QuickStash.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("valheimcrew")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Guardado rapido en cofres cercanos, favoritos y crafteo desde cofres para Valheim.")]
[assembly: AssemblyFileVersion("1.4.0.0")]
[assembly: AssemblyInformationalVersion("1.4.0+3d63666ad94932f7c278bffb56aad8ffbb2f7bfd")]
[assembly: AssemblyProduct("QuickStash")]
[assembly: AssemblyTitle("QuickStash")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.4.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 QuickStash
{
	[BepInPlugin("com.valheimcrew.quickstash", "QuickStash", "1.4.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.valheimcrew.quickstash";

		public const string PluginName = "QuickStash";

		public const string PluginVersion = "1.4.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			PluginConfig.Enabled.SettingChanged += delegate
			{
				StashButton.ApplyConfig();
			};
			PluginConfig.ShowButton.SettingChanged += delegate
			{
				StashButton.ApplyConfig();
			};
			PluginConfig.ButtonLabel.SettingChanged += delegate
			{
				StashButton.ApplyConfig();
			};
			PluginConfig.ButtonOffsetX.SettingChanged += delegate
			{
				StashButton.ApplyConfig();
			};
			PluginConfig.ButtonOffsetY.SettingChanged += delegate
			{
				StashButton.ApplyConfig();
			};
			PluginConfig.PullButtonLabel.SettingChanged += delegate
			{
				PullButton.ApplyConfig();
			};
			PluginConfig.PullButtonOffsetX.SettingChanged += delegate
			{
				PullButton.ApplyConfig();
			};
			PluginConfig.PullButtonOffsetY.SettingChanged += delegate
			{
				PullButton.ApplyConfig();
			};
			_harmony = new Harmony("com.valheimcrew.quickstash");
			ApplyPatches();
			WarnAboutConflicts();
			Log.LogInfo((object)"QuickStash 1.4.0 cargado.");
		}

		private void ApplyPatches()
		{
			PatchGroup("registro de cofres", typeof(Container_Awake_Patch), typeof(Container_OnDestroyed_Patch));
			PatchGroup("ciclo de vida", typeof(Player_OnSpawned_Patch), typeof(Game_Logout_Patch));
			PatchGroup("guardado rapido", typeof(Container_RPC_StackResponse_Patch), typeof(InventoryGui_Awake_Patch));
			PatchGroup("favoritos", typeof(InventoryGrid_OnLeftDown_Patch), typeof(InventoryGrid_UpdateGui_Patch));
			PatchGroup("crafteo desde cofres", typeof(Player_HaveRequirementItems_Patch), typeof(InventoryGui_SetupRequirement_Patch), typeof(InventoryGui_DoCrafting_Patch));
			PatchGroup("boton de traer material", typeof(InventoryGui_Awake_PullButton_Patch), typeof(InventoryGui_UpdateRecipe_Patch));
			PatchGroup("construccion desde cofres", typeof(Player_HaveRequirements_Piece_Patch), typeof(Player_TryPlacePiece_Patch));
			PatchGroup("hornos automaticos", typeof(Smelter_UpdateSmelter_Patch));
		}

		private void PatchGroup(string feature, params Type[] patchTypes)
		{
			foreach (Type type in patchTypes)
			{
				try
				{
					_harmony.PatchAll(type);
				}
				catch (Exception ex)
				{
					Log.LogError((object)("No se pudo enganchar '" + feature + "' (" + type.Name + "). Esa parte del mod queda desactivada; probablemente el juego cambio en un parche. Detalle: " + ex.Message));
				}
			}
		}

		private void Update()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (PluginConfig.Enabled.Value && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				FavoriteStore.Tick();
				StashService.Tick();
				SafeTick(AnimalFeeder.Tick, "alimentacion de animales");
				SafeTick(AnimalDiagnostics.Tick, "diagnostico de animales");
				KeyboardShortcut value = PluginConfig.Hotkey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown() && !IsTyping())
				{
					StashService.Run();
				}
			}
		}

		private static void SafeTick(Action tick, string what)
		{
			try
			{
				tick();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Fallo en {what}: {arg}");
			}
		}

		private static bool IsTyping()
		{
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return true;
			}
			if (!Console.IsVisible() && !TextInput.IsVisible())
			{
				return StoreGui.IsVisible();
			}
			return true;
		}

		private void OnDestroy()
		{
			FavoriteStore.FlushIfDirty();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private static void WarnAboutConflicts()
		{
			string[] array = Chainloader.PluginInfos.Keys.Where((string guid) => guid.IndexOf("craftfromcontainers", StringComparison.OrdinalIgnoreCase) >= 0 || guid.IndexOf("quick_stack", StringComparison.OrdinalIgnoreCase) >= 0 || guid.IndexOf("quickstack", StringComparison.OrdinalIgnoreCase) >= 0).ToArray();
			if (array.Length != 0)
			{
				Log.LogWarning((object)("Hay otros mods cargados que cubren lo mismo que QuickStash: " + string.Join(", ", array) + ". Conviene dejar solo uno para evitar comportamientos duplicados."));
			}
		}
	}
}
namespace QuickStash.UI
{
	[HarmonyPatch(typeof(InventoryGrid), "OnLeftDown")]
	internal static class InventoryGrid_OnLeftDown_Patch
	{
		private static bool Prefix(InventoryGrid __instance, UIInputHandler clickHandler)
		{
			//IL_001c: 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_0057: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.Enabled.Value || (Object)(object)clickHandler == (Object)null)
			{
				return true;
			}
			if (!ZInput.GetKey(PluginConfig.FavoriteModifier.Value, true))
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || __instance.m_inventory != ((Humanoid)localPlayer).GetInventory())
			{
				return true;
			}
			try
			{
				Vector2i buttonPos = __instance.GetButtonPos(((Component)clickHandler).gameObject);
				if (buttonPos.x < 0 || buttonPos.y < 0)
				{
					return true;
				}
				ItemData itemAt = __instance.m_inventory.GetItemAt(buttonPos.x, buttonPos.y);
				if (itemAt == null || ZInput.GetKey(PluginConfig.SlotModifier.Value, true))
				{
					bool flag = FavoriteStore.ToggleSlot(buttonPos.x, buttonPos.y);
					((Character)localPlayer).Message((MessageType)1, flag ? "Casilla protegida" : "Casilla desprotegida", 0, (Sprite)null, false);
				}
				else
				{
					bool flag2 = FavoriteStore.ToggleItem(itemAt);
					string text = Localization.instance.Localize(itemAt.m_shared.m_name);
					((Character)localPlayer).Message((MessageType)1, (flag2 ? "Favorito: " : "Ya no es favorito: ") + text, 0, (Sprite)null, false);
				}
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al marcar favorito: {arg}");
				return true;
			}
		}
	}
	internal static class FavoriteOverlay
	{
		private const string OverlayName = "QuickStashFavorite";

		private const int SpriteSize = 32;

		private const int BorderThickness = 3;

		private static Sprite _border;

		private static bool _disabled;

		private static readonly Dictionary<InventoryElement, Image> Overlays = new Dictionary<InventoryElement, Image>();

		private static readonly HashSet<int> FavoritePositions = new HashSet<int>();

		private static int Pack(int x, int y)
		{
			return (y << 8) | (x & 0xFF);
		}

		public static void Refresh(InventoryGrid grid)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			if (_disabled || !PluginConfig.Enabled.Value || (Object)(object)grid == (Object)null || grid.m_elements == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || grid.m_inventory != ((Humanoid)localPlayer).GetInventory())
			{
				return;
			}
			FavoritePositions.Clear();
			List<ItemData> allItems = grid.m_inventory.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (FavoriteStore.IsFavoriteItem(val))
				{
					FavoritePositions.Add(Pack(val.m_gridPos.x, val.m_gridPos.y));
				}
			}
			List<InventoryElement> elements = grid.m_elements;
			for (int j = 0; j < elements.Count; j++)
			{
				InventoryElement val2 = elements[j];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Vector2i position = val2.Position;
				bool flag = FavoriteStore.IsFavoriteSlot(position.x, position.y);
				bool flag2 = FavoritePositions.Contains(Pack(position.x, position.y));
				if (!flag && !flag2)
				{
					if (Overlays.TryGetValue(val2, out var value) && (Object)(object)value != (Object)null && ((Behaviour)value).enabled)
					{
						((Behaviour)value).enabled = false;
					}
					continue;
				}
				Image orCreate = GetOrCreate(val2);
				if (!((Object)(object)orCreate == (Object)null))
				{
					((Graphic)orCreate).color = (flag ? PluginConfig.SlotColor : PluginConfig.FavColor);
					((Behaviour)orCreate).enabled = true;
				}
			}
		}

		private static Image GetOrCreate(InventoryElement element)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			Image value;
			bool flag = Overlays.TryGetValue(element, out value);
			if (flag && (Object)(object)value != (Object)null)
			{
				return value;
			}
			if (flag)
			{
				Overlays.Remove(element);
				PruneDestroyed();
			}
			GameObject val = new GameObject("QuickStashFavorite", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(((Component)element).transform, false);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			((Transform)val2).SetAsLastSibling();
			Image component = val.GetComponent<Image>();
			component.sprite = BorderSprite();
			component.type = (Type)1;
			((Graphic)component).raycastTarget = false;
			Overlays[element] = component;
			return component;
		}

		public static void DisableAfterFailure(Exception e)
		{
			if (!_disabled)
			{
				_disabled = true;
				Plugin.Log.LogError((object)("Se desactivaron los bordes de favoritos porque el dibujado fallo. " + $"El resto del mod sigue funcionando. Detalle: {e}"));
			}
		}

		private static void PruneDestroyed()
		{
			if (Overlays.Count < 128)
			{
				return;
			}
			List<InventoryElement> list = new List<InventoryElement>();
			foreach (KeyValuePair<InventoryElement, Image> overlay in Overlays)
			{
				if ((Object)(object)overlay.Key == (Object)null)
				{
					list.Add(overlay.Key);
				}
			}
			foreach (InventoryElement item in list)
			{
				Overlays.Remove(item);
			}
		}

		private static Sprite BorderSprite()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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)
			if ((Object)(object)_border != (Object)null)
			{
				return _border;
			}
			Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)61
			};
			Color32 val2 = default(Color32);
			((Color32)(ref val2))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0);
			Color32 val3 = default(Color32);
			((Color32)(ref val3))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
			Color32[] array = (Color32[])(object)new Color32[1024];
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					bool flag = j < 3 || i < 3 || j >= 29 || i >= 29;
					array[i * 32 + j] = (flag ? val3 : val2);
				}
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			_border = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(3f, 3f, 3f, 3f));
			((Object)_border).hideFlags = (HideFlags)61;
			return _border;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class InventoryGrid_UpdateGui_Patch
	{
		private static void Postfix(InventoryGrid __instance)
		{
			try
			{
				FavoriteOverlay.Refresh(__instance);
			}
			catch (Exception e)
			{
				FavoriteOverlay.DisableAfterFailure(e);
			}
		}
	}
	internal static class PullButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnClick;
		}

		private const float CooldownSeconds = 0.3f;

		private const float CheckIntervalSeconds = 0.2f;

		private const int MaxItemsInMessage = 4;

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

		private static readonly StringBuilder Summary = new StringBuilder(96);

		private static GameObject _root;

		private static RectTransform _rect;

		private static RectTransform _craftRect;

		private static TMP_Text _label;

		private static Button _button;

		private static Recipe _recipe;

		private static int _quality;

		private static int _multiplier;

		private static bool _hasSomething;

		private static float _nextCheckAt;

		private static float _nextAllowedAt;

		public static void Build(InventoryGui gui)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				_root = null;
				_rect = null;
				_craftRect = null;
				_label = null;
				_button = null;
				_recipe = null;
				_hasSomething = false;
				if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_craftButton == (Object)null)
				{
					Plugin.Log.LogWarning((object)"No se encontro el boton vanilla de craftear, no se puede crear el boton de traer material. El resto del crafteo desde cofres sigue funcionando.");
					return;
				}
				RectTransform component = ((Component)gui.m_craftButton).GetComponent<RectTransform>();
				if ((Object)(object)component == (Object)null)
				{
					return;
				}
				GameObject val = Object.Instantiate<GameObject>(((Component)gui.m_craftButton).gameObject, ((Transform)component).parent);
				((Object)val).name = "QuickStashPullButton";
				Localize[] componentsInChildren = val.GetComponentsInChildren<Localize>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Object.Destroy((Object)(object)componentsInChildren[i]);
				}
				UIGamePad[] componentsInChildren2 = val.GetComponentsInChildren<UIGamePad>(true);
				foreach (UIGamePad obj in componentsInChildren2)
				{
					obj.m_keyCode = (KeyCode)0;
					obj.m_zinputKey = string.Empty;
				}
				_button = val.GetComponent<Button>();
				if ((Object)(object)_button != (Object)null)
				{
					((UnityEventBase)_button.onClick).RemoveAllListeners();
					ButtonClickedEvent onClick = _button.onClick;
					object obj2 = <>O.<0>__OnClick;
					if (obj2 == null)
					{
						UnityAction val2 = OnClick;
						<>O.<0>__OnClick = val2;
						obj2 = (object)val2;
					}
					((UnityEvent)onClick).AddListener((UnityAction)obj2);
					((Selectable)_button).interactable = true;
				}
				UITooltip component2 = val.GetComponent<UITooltip>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.m_text = "Trae de los cofres cercanos el material de esta receta, aunque no alcance";
				}
				LayoutElement val3 = val.GetComponent<LayoutElement>();
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = val.AddComponent<LayoutElement>();
				}
				val3.ignoreLayout = true;
				_root = val;
				_rect = val.GetComponent<RectTransform>();
				_craftRect = component;
				_label = val.GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)_rect != (Object)null)
				{
					_rect.anchorMin = component.anchorMin;
					_rect.anchorMax = component.anchorMax;
					_rect.pivot = component.pivot;
					_rect.sizeDelta = component.sizeDelta;
				}
				val.SetActive(false);
				ApplyConfig();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"No se pudo crear el boton de traer material: {arg}");
				_root = null;
			}
		}

		public static void ApplyConfig()
		{
			if (!((Object)(object)_root == (Object)null))
			{
				if ((Object)(object)_label != (Object)null)
				{
					_label.text = PluginConfig.PullButtonLabel.Value;
				}
				Reposition();
			}
		}

		private static void Reposition()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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)
			if (!((Object)(object)_rect == (Object)null) && !((Object)(object)_craftRect == (Object)null))
			{
				Rect rect = _craftRect.rect;
				float num = ((Rect)(ref rect)).height;
				if (num < 1f)
				{
					num = _craftRect.sizeDelta.y;
				}
				if (num < 1f)
				{
					num = 40f;
				}
				_rect.anchoredPosition = _craftRect.anchoredPosition + new Vector2(0f, 0f - (num + 4f)) + new Vector2(PluginConfig.PullButtonOffsetX.Value, PluginConfig.PullButtonOffsetY.Value);
			}
		}

		public static void Refresh(InventoryGui gui, Player player)
		{
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			if (!PluginConfig.Enabled.Value || !PluginConfig.ShowPullButton.Value || !PluginConfig.CraftFromContainers.Value || (Object)(object)gui == (Object)null || (Object)(object)player == (Object)null)
			{
				Hide();
				return;
			}
			if (gui.m_craftTimer >= 0f)
			{
				Hide();
				return;
			}
			Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe;
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || (Object)(object)currentCraftingStation == (Object)null)
			{
				Hide();
				return;
			}
			ItemData itemData = ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData;
			int num = ((itemData == null) ? 1 : (itemData.m_quality + 1));
			if (num > recipe.m_item.m_itemData.m_shared.m_maxQuality && !currentCraftingStation.m_upgrader)
			{
				Hide();
				return;
			}
			int num2 = Multiplier(gui, itemData);
			float unscaledTime = Time.unscaledTime;
			if ((Object)(object)recipe != (Object)(object)_recipe || num != _quality || num2 != _multiplier)
			{
				_recipe = recipe;
				_quality = num;
				_multiplier = num2;
				_nextCheckAt = 0f;
			}
			if (unscaledTime >= _nextCheckAt)
			{
				_nextCheckAt = unscaledTime + 0.2f;
				_hasSomething = CraftFromContainers.PullableFromContainers(player, recipe.m_resources, num, num2, recipe.m_requireOnlyOneIngredient) > 0;
			}
			if (_root.activeSelf != _hasSomething)
			{
				if (_hasSomething)
				{
					Reposition();
				}
				_root.SetActive(_hasSomething);
			}
		}

		private static void Hide()
		{
			_recipe = null;
			_hasSomething = false;
			if ((Object)(object)_root != (Object)null && _root.activeSelf)
			{
				_root.SetActive(false);
			}
		}

		private static int Multiplier(InventoryGui gui, ItemData upgrade)
		{
			if (upgrade != null)
			{
				return 1;
			}
			if (!gui.m_touchMultiCrafting && !ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyLStick"))
			{
				return 1;
			}
			return Mathf.Max(1, gui.m_multiCraftAmount);
		}

		private static void OnClick()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				Recipe recipe = _recipe;
				if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)recipe == (Object)null) && !((Character)localPlayer).IsTeleporting() && CraftFromContainers.Active(localPlayer))
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					if (!(realtimeSinceStartup < _nextAllowedAt))
					{
						_nextAllowedAt = realtimeSinceStartup + 0.3f;
						Moved.Clear();
						bool inventoryFull;
						int total = CraftFromContainers.PullForRecipe(localPlayer, recipe.m_resources, _quality, _multiplier, recipe.m_requireOnlyOneIngredient, Moved, out inventoryFull);
						_nextCheckAt = 0f;
						Report(localPlayer, total, inventoryFull);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al traer material de los cofres: {arg}");
			}
		}

		private static void Report(Player player, int total, bool inventoryFull)
		{
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			if (total <= 0)
			{
				((Character)player).Message((MessageType)2, inventoryFull ? "Sin espacio en el inventario" : "No hay material en los cofres cercanos", 0, (Sprite)null, false);
				return;
			}
			Summary.Length = 0;
			Summary.Append("Traido: ");
			int num = 0;
			foreach (KeyValuePair<string, int> item in Moved)
			{
				if (num >= 4)
				{
					Summary.Append(", ...");
					break;
				}
				if (num > 0)
				{
					Summary.Append(", ");
				}
				Summary.Append(MoveLog.Localize(item.Key)).Append(" x").Append(item.Value);
				num++;
			}
			if (inventoryFull)
			{
				Summary.Append(" (inventario lleno)");
			}
			((Character)player).Message((MessageType)2, Summary.ToString(), 0, (Sprite)null, false);
			Summary.Length = 0;
			if ((Object)(object)InventoryGui.instance != (Object)null)
			{
				InventoryGui.instance.m_moveItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Awake")]
	internal static class InventoryGui_Awake_PullButton_Patch
	{
		private static void Postfix(InventoryGui __instance)
		{
			PullButton.Build(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
	internal static class InventoryGui_UpdateRecipe_Patch
	{
		private static void Postfix(InventoryGui __instance, Player player)
		{
			try
			{
				PullButton.Refresh(__instance, player);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al actualizar el boton de traer material: {arg}");
			}
		}
	}
	internal static class StashButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Run;
		}

		private static GameObject _root;

		private static RectTransform _rect;

		private static TMP_Text _label;

		public static void Build(InventoryGui gui)
		{
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			try
			{
				if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_player == (Object)null)
				{
					return;
				}
				if ((Object)(object)gui.m_stackAllButton == (Object)null)
				{
					Plugin.Log.LogWarning((object)"No se encontro el boton vanilla de apilar, no se puede crear el boton de guardado. Usa el atajo de teclado configurable en su lugar.");
					return;
				}
				GameObject val = Object.Instantiate<GameObject>(((Component)gui.m_stackAllButton).gameObject, (Transform)(object)gui.m_player);
				((Object)val).name = "QuickStashButton";
				Localize[] componentsInChildren = val.GetComponentsInChildren<Localize>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Object.Destroy((Object)(object)componentsInChildren[i]);
				}
				Button component = val.GetComponent<Button>();
				if ((Object)(object)component != (Object)null)
				{
					((UnityEventBase)component.onClick).RemoveAllListeners();
					ButtonClickedEvent onClick = component.onClick;
					object obj = <>O.<0>__Run;
					if (obj == null)
					{
						UnityAction val2 = StashService.Run;
						<>O.<0>__Run = val2;
						obj = (object)val2;
					}
					((UnityEvent)onClick).AddListener((UnityAction)obj);
				}
				_root = val;
				_rect = val.GetComponent<RectTransform>();
				_label = val.GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)_rect != (Object)null)
				{
					_rect.anchorMin = new Vector2(1f, 1f);
					_rect.anchorMax = new Vector2(1f, 1f);
					_rect.pivot = new Vector2(1f, 1f);
				}
				ApplyConfig();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"No se pudo crear el boton de guardado: {arg}");
			}
		}

		public static void ApplyConfig()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_root == (Object)null))
			{
				_root.SetActive(PluginConfig.Enabled.Value && PluginConfig.ShowButton.Value);
				if ((Object)(object)_label != (Object)null)
				{
					_label.text = PluginConfig.ButtonLabel.Value;
				}
				if ((Object)(object)_rect != (Object)null)
				{
					_rect.anchoredPosition = new Vector2(-12f + PluginConfig.ButtonOffsetX.Value, 36f + PluginConfig.ButtonOffsetY.Value);
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Awake")]
	internal static class InventoryGui_Awake_Patch
	{
		private static void Postfix(InventoryGui __instance)
		{
			StashButton.Build(__instance);
		}
	}
}
namespace QuickStash.Features
{
	internal static class AnimalFeeder
	{
		private const float ScanRange = 40f;

		private const float MinFeedIntervalSeconds = 10f;

		private const float ClosestEpsilon = 1.5f;

		private static readonly Collider[] ItemHits = (Collider[])(object)new Collider[256];

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

		private static readonly List<ItemData> ItemBuffer = new List<ItemData>(32);

		private static readonly Dictionary<MonsterAI, float> NextFeed = new Dictionary<MonsterAI, float>();

		private static readonly List<MonsterAI> PruneBuffer = new List<MonsterAI>(8);

		private static readonly HashSet<string> LoggedMenus = new HashSet<string>(StringComparer.Ordinal);

		private static readonly StringBuilder MenuBuilder = new StringBuilder(128);

		private static float _nextRun;

		private static int _itemMask;

		public static void Tick()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.Enabled.Value || !PluginConfig.FeedAnimals.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextRun)
			{
				return;
			}
			_nextRun = realtimeSinceStartup + PluginConfig.AnimalIntervalSeconds.Value;
			List<BaseAI> instances = BaseAI.m_instances;
			if (instances == null)
			{
				return;
			}
			PruneStale(realtimeSinceStartup);
			Vector3 position = ((Component)localPlayer).transform.position;
			float num = 1600f;
			long playerId = ContainerAccess.LocalPlayerId();
			int num2 = PluginConfig.AnimalMaxPerCycle.Value;
			for (int i = 0; i < instances.Count; i++)
			{
				if (num2 <= 0)
				{
					break;
				}
				BaseAI obj = instances[i];
				MonsterAI val = (MonsterAI)(object)((obj is MonsterAI) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Tameable tamable = ((BaseAI)val).m_tamable;
				if ((Object)(object)tamable == (Object)null || !tamable.IsTamed())
				{
					continue;
				}
				Character character = ((BaseAI)val).m_character;
				if (!((Object)(object)character == (Object)null) && !character.IsDead())
				{
					Vector3 position2 = ((Component)val).transform.position;
					Vector3 val2 = position2 - position;
					if (!(((Vector3)(ref val2)).sqrMagnitude > num) && (!NextFeed.TryGetValue(val, out var value) || !(realtimeSinceStartup < value)) && tamable.IsHungry() && val.m_consumeItems != null && val.m_consumeItems.Count != 0 && IsClosestPlayer(localPlayer, position2) && !HasFoodOnGround(val) && Feed(val, position2, playerId))
					{
						NextFeed[val] = realtimeSinceStartup + Mathf.Max(10f, PluginConfig.AnimalIntervalSeconds.Value);
						num2--;
					}
				}
			}
		}

		private static bool IsClosestPlayer(Player local, Vector3 animalPosition)
		{
			//IL_001a: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			List<Player> allPlayers = Player.GetAllPlayers();
			if (allPlayers == null || allPlayers.Count <= 1)
			{
				return true;
			}
			float num = Vector3.Distance(((Component)local).transform.position, animalPosition);
			long playerID = local.GetPlayerID();
			foreach (Player item in allPlayers)
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)local))
				{
					float num2 = Vector3.Distance(((Component)item).transform.position, animalPosition) - num;
					if (num2 < -1.5f)
					{
						return false;
					}
					if (Mathf.Abs(num2) <= 1.5f && item.GetPlayerID() < playerID)
					{
						return false;
					}
				}
			}
			return true;
		}

		private static bool HasFoodOnGround(MonsterAI ai)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (_itemMask == 0)
			{
				_itemMask = LayerMask.GetMask(new string[1] { "item" });
			}
			int num = Physics.OverlapSphereNonAlloc(((Component)ai).transform.position, ai.m_consumeSearchRange, ItemHits, _itemMask);
			if (num >= ItemHits.Length)
			{
				return true;
			}
			for (int i = 0; i < num; i++)
			{
				Collider val = ItemHits[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.attachedRigidbody == (Object)null))
				{
					ItemDrop component = ((Component)val.attachedRigidbody).GetComponent<ItemDrop>();
					if (!((Object)(object)component == (Object)null) && component.m_itemData?.m_shared != null && ai.CanConsume(component.m_itemData))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool Feed(MonsterAI ai, Vector3 animalPosition, long playerId)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			ContainerRegistry.Query(animalPosition, PluginConfig.AnimalFeedRange.Value, PluginConfig.MaxScanned.Value, playerId, Buffer);
			int num = 0;
			int num2 = 0;
			foreach (Container item in Buffer)
			{
				if ((Object)(object)item.m_nview == (Object)null || !item.m_nview.IsOwner())
				{
					continue;
				}
				num++;
				Inventory inventory = item.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				ItemData val = FindFood(ai, inventory);
				if (val == null)
				{
					continue;
				}
				num2++;
				if (DropOne(ai, item, inventory, val, animalPosition))
				{
					if (PluginConfig.DebugTiming.Value)
					{
						Plugin.Log.LogInfo((object)("[animal] " + Name(ai) + ": se le dejo " + MoveLog.Localize(val.m_shared.m_name) + " " + $"de un cofre a {Vector3.Distance(animalPosition, ((Component)item).transform.position):F1} m"));
					}
					return true;
				}
			}
			Explain(ai, num, num2);
			return false;
		}

		private static ItemData FindFood(MonsterAI ai, Inventory source)
		{
			ItemBuffer.Clear();
			ItemBuffer.AddRange(source.GetAllItems());
			try
			{
				foreach (ItemData item in ItemBuffer)
				{
					if (item?.m_shared != null && item.m_stack > 0 && !((Object)(object)item.m_dropPrefab == (Object)null) && !PluginConfig.IsExcluded(item) && !PluginConfig.IsExcludedForAnimals(item) && ai.CanConsume(item))
					{
						return item;
					}
				}
			}
			finally
			{
				ItemBuffer.Clear();
			}
			return null;
		}

		private static bool DropOne(MonsterAI ai, Container container, Inventory source, ItemData item, Vector3 animalPosition)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = item.Clone();
			val.m_stack = 1;
			string name = item.m_shared.m_name;
			if (!source.RemoveItem(item, 1))
			{
				return false;
			}
			Vector3 val2 = Random.insideUnitSphere * 0.3f;
			Vector3 val3 = animalPosition + new Vector3(val2.x, 0.4f, val2.z);
			try
			{
				ItemDrop.DropItem(val, 1, val3, Quaternion.identity);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Se perdio 1 x " + MoveLog.Localize(name) + " al dejarsela a " + Name(ai) + ": " + ex.Message));
				return false;
			}
			MoveLog.RecordAnimalFeed(container, name, 1);
			return true;
		}

		private static void PruneStale(float now)
		{
			if (NextFeed.Count < 64)
			{
				return;
			}
			PruneBuffer.Clear();
			foreach (KeyValuePair<MonsterAI, float> item in NextFeed)
			{
				if ((Object)(object)item.Key == (Object)null || now > item.Value + 300f)
				{
					PruneBuffer.Add(item.Key);
				}
			}
			foreach (MonsterAI item2 in PruneBuffer)
			{
				NextFeed.Remove(item2);
			}
			PruneBuffer.Clear();
		}

		private static void Explain(MonsterAI ai, int owned, int withFood)
		{
			if (PluginConfig.DebugTiming.Value)
			{
				string text = ((Buffer.Count == 0) ? $"no hay ningun cofre accesible a menos de {PluginConfig.AnimalFeedRange.Value:F0} m del animal" : ((owned == 0) ? ($"hay {Buffer.Count} cofre(s) cerca pero ninguno es tuyo " + "(en el servidor la propiedad del ZDO puede estar en otro jugador)") : ((withFood != 0) ? "se encontro comida pero no se pudo sacar del cofre" : $"hay {owned} cofre(s) tuyo(s) cerca, pero ninguno tiene comida que este animal acepte")));
				Plugin.Log.LogInfo((object)("[animal] " + Name(ai) + " tiene hambre y no comio: " + text + "."));
				LogMenuOnce(ai);
			}
		}

		private static void LogMenuOnce(MonsterAI ai)
		{
			string text = Name(ai);
			if (!LoggedMenus.Add(text))
			{
				return;
			}
			MenuBuilder.Length = 0;
			foreach (ItemDrop consumeItem in ai.m_consumeItems)
			{
				if (!((Object)(object)consumeItem == (Object)null) && consumeItem.m_itemData?.m_shared != null)
				{
					if (MenuBuilder.Length > 0)
					{
						MenuBuilder.Append(", ");
					}
					MenuBuilder.Append(MoveLog.Localize(consumeItem.m_itemData.m_shared.m_name));
				}
			}
			Plugin.Log.LogInfo((object)$"[animal] {text} come: {MenuBuilder}");
			MenuBuilder.Length = 0;
		}

		private static string Name(MonsterAI ai)
		{
			Character character = ((BaseAI)ai).m_character;
			if (!((Object)(object)character != (Object)null))
			{
				return ((Object)ai).name;
			}
			return MoveLog.Localize(character.m_name);
		}

		public static void Reset()
		{
			Buffer.Clear();
			ItemBuffer.Clear();
			NextFeed.Clear();
			PruneBuffer.Clear();
			LoggedMenus.Clear();
			_nextRun = 0f;
		}
	}
	internal static class BuildFromContainers
	{
		private const int BuildQualityLevel = 0;

		private const int BuildMultiplier = 1;

		public static void PullFor(Player player, Piece piece)
		{
			CraftFromContainers.PullMissing(player, piece.m_resources, 0, -1, 1, onlyOneIngredient: false);
		}

		public static bool CoveredByInventory(Player player, Piece piece)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_amount > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					if (inventory.CountItems(name, -1, true) < val.m_amount)
					{
						return false;
					}
				}
			}
			return true;
		}

		public static bool HaveWithContainers(Player player, Piece piece, RequirementMode mode)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Invalid comparison between Unknown and I4
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = inventory.GetEmptySlots();
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (!Object.op_Implicit((Object)(object)val.m_resItem) || val.m_amount <= 0)
				{
					continue;
				}
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				int num2 = inventory.CountItems(name, -1, true);
				int num3 = num2;
				if (num2 > 0 || num > 0)
				{
					num3 += CraftFromContainers.InContainers(player, name, -1);
					if (num2 == 0)
					{
						num--;
					}
				}
				int num4 = (((int)mode == 2) ? 1 : val.m_amount);
				if (num3 < num4)
				{
					return false;
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Piece),
		typeof(RequirementMode)
	})]
	internal static class Player_HaveRequirements_Piece_Patch
	{
		private static void Postfix(Player __instance, Piece piece, RequirementMode mode, ref bool __result)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (__result || (Object)(object)piece == (Object)null || (int)mode == 1 || !CraftFromContainers.BuildActive(__instance))
			{
				return;
			}
			try
			{
				if (HasStationAndDlc(__instance, piece, mode))
				{
					__result = BuildFromContainers.HaveWithContainers(__instance, piece, mode);
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al evaluar requisitos de construccion con cofres: {arg}");
			}
		}

		private static bool HasStationAndDlc(Player player, Piece piece, RequirementMode mode)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece.m_craftingStation != (Object)null)
			{
				bool num;
				if ((int)mode != 2)
				{
					if (Object.op_Implicit((Object)(object)CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)player).transform.position)))
					{
						goto IL_005f;
					}
					num = ZoneSystem.instance.GetGlobalKey((GlobalKeys)27);
				}
				else
				{
					num = player.m_knownStations.ContainsKey(piece.m_craftingStation.m_name);
				}
				if (!num)
				{
					return false;
				}
			}
			goto IL_005f;
			IL_005f:
			if (piece.m_dlc.Length <= 0)
			{
				return true;
			}
			if ((Object)(object)DLCMan.instance != (Object)null)
			{
				return DLCMan.instance.IsDLCInstalled(piece.m_dlc);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class Player_TryPlacePiece_Patch
	{
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece == (Object)null || !CraftFromContainers.BuildActive(__instance))
			{
				return true;
			}
			try
			{
				if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
				{
					return true;
				}
				if ((int)__instance.m_placementStatus != 0)
				{
					return true;
				}
				BuildFromContainers.PullFor(__instance, piece);
				if (!BuildFromContainers.CoveredByInventory(__instance, piece))
				{
					((Character)__instance).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
					__result = false;
					return false;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al traer material de los cofres para construir: {arg}");
			}
			return true;
		}
	}
	internal static class CraftFromContainers
	{
		public static readonly Color AvailableColor = new Color(0.55f, 0.85f, 1f);

		public static bool Active(Player player)
		{
			if (PluginConfig.Enabled.Value && PluginConfig.CraftFromContainers.Value && (Object)(object)player != (Object)null)
			{
				return (Object)(object)player.GetCurrentCraftingStation() != (Object)null;
			}
			return false;
		}

		public static bool IsBuilding(Player player)
		{
			if ((Object)(object)player != (Object)null && ((Character)player).InPlaceMode())
			{
				return !InventoryGui.IsVisible();
			}
			return false;
		}

		public static bool BuildActive(Player player)
		{
			if (PluginConfig.Enabled.Value && PluginConfig.BuildFromContainers.Value)
			{
				return IsBuilding(player);
			}
			return false;
		}

		private static float ActiveRange(Player player)
		{
			if (!IsBuilding(player))
			{
				return PluginConfig.CraftRange.Value;
			}
			return PluginConfig.BuildRange.Value;
		}

		private static void Refresh(Player player)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			ContainerIndex.EnsureFresh(((Component)player).transform.position, ActiveRange(player), ContainerAccess.LocalPlayerId());
		}

		public static int InContainers(Player player, string name, int quality)
		{
			Refresh(player);
			return ContainerIndex.Count(name, quality);
		}

		public static bool CanReceive(Inventory inventory, int alreadyInInventory)
		{
			if (alreadyInInventory <= 0)
			{
				return inventory.HaveEmptySlot();
			}
			return true;
		}

		public static int CountAvailable(Player player, string name, int quality)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (!CanReceive(inventory, inventory.CountItems(name, -1, true)))
			{
				return 0;
			}
			return InContainers(player, name, quality);
		}

		public static bool IsRelevant(Requirement requirement, CraftingStation station)
		{
			if ((Object)(object)requirement.m_resItem == (Object)null)
			{
				return false;
			}
			if ((Object)(object)station != (Object)null && station.m_upgrader != requirement.m_upgraderResource)
			{
				return false;
			}
			if (!((Object)(object)station != (Object)null))
			{
				return !requirement.m_upgraderResource;
			}
			return true;
		}

		public static bool CoveredInSingleQuality(Inventory inventory, Requirement requirement, int need)
		{
			string name = requirement.m_resItem.m_itemData.m_shared.m_name;
			int maxQuality = requirement.m_resItem.m_itemData.m_shared.m_maxQuality;
			for (int i = 0; i <= maxQuality; i++)
			{
				if (inventory.CountItems(name, i, true) >= need)
				{
					return true;
				}
			}
			return false;
		}

		public static bool AnyIngredientCovered(Player player, Requirement[] requirements, int qualityLevel, int multiplier)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			Inventory inventory = ((Humanoid)player).GetInventory();
			foreach (Requirement val in requirements)
			{
				if (IsRelevant(val, currentCraftingStation))
				{
					int num = val.GetAmount(qualityLevel) * multiplier;
					if (num > 0 && CoveredInSingleQuality(inventory, val, num))
					{
						return true;
					}
				}
			}
			return false;
		}

		public static void PullMissing(Player player, Recipe recipe, int qualityLevel, int itemQuality, int multiplier)
		{
			PullMissing(player, recipe.m_resources, qualityLevel, itemQuality, multiplier, recipe.m_requireOnlyOneIngredient);
		}

		public static void PullMissing(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, bool onlyOneIngredient)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			Inventory inventory = ((Humanoid)player).GetInventory();
			bool flag = false;
			if (onlyOneIngredient && AnyIngredientCovered(player, requirements, qualityLevel, multiplier))
			{
				return;
			}
			foreach (Requirement val in requirements)
			{
				if (!IsRelevant(val, currentCraftingStation))
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * multiplier;
				if (num <= 0)
				{
					continue;
				}
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				int num2 = num - inventory.CountItems(name, itemQuality, true);
				if (num2 > 0 && (!onlyOneIngredient || CountAvailable(player, name, itemQuality) >= num2) && Pull(player, inventory, name, itemQuality, num2, out var _) > 0)
				{
					flag = true;
					if (onlyOneIngredient)
					{
						break;
					}
				}
			}
			if (flag)
			{
				ContainerIndex.Invalidate();
			}
		}

		public static int PullableFromContainers(Player player, Requirement[] requirements, int qualityLevel, int multiplier, bool onlyOneIngredient)
		{
			bool inventoryFull;
			return Transfer(player, requirements, qualityLevel, multiplier, onlyOneIngredient, dryRun: true, null, out inventoryFull);
		}

		public static int PullForRecipe(Player player, Requirement[] requirements, int qualityLevel, int multiplier, bool onlyOneIngredient, Dictionary<string, int> moved, out bool inventoryFull)
		{
			return Transfer(player, requirements, qualityLevel, multiplier, onlyOneIngredient, dryRun: false, moved, out inventoryFull);
		}

		private static int Transfer(Player player, Requirement[] requirements, int qualityLevel, int multiplier, bool onlyOneIngredient, bool dryRun, Dictionary<string, int> moved, out bool inventoryFull)
		{
			inventoryFull = false;
			if ((Object)(object)player == (Object)null || requirements == null)
			{
				return 0;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			Inventory inventory = ((Humanoid)player).GetInventory();
			Requirement val = null;
			if (onlyOneIngredient)
			{
				if (AnyIngredientCovered(player, requirements, qualityLevel, multiplier))
				{
					return 0;
				}
				val = ChooseSingleIngredient(player, requirements, qualityLevel, multiplier, currentCraftingStation);
				if (val == null)
				{
					return 0;
				}
			}
			int num = 0;
			foreach (Requirement val2 in requirements)
			{
				if ((val != null && val2 != val) || !IsRelevant(val2, currentCraftingStation))
				{
					continue;
				}
				int num2 = val2.GetAmount(qualityLevel) * multiplier;
				if (num2 <= 0)
				{
					continue;
				}
				string name = val2.m_resItem.m_itemData.m_shared.m_name;
				int num3 = num2 - inventory.CountItems(name, -1, true);
				if (num3 <= 0)
				{
					continue;
				}
				int num4 = CountAvailable(player, name, -1);
				if (num4 <= 0)
				{
					continue;
				}
				if (dryRun)
				{
					num += Mathf.Min(num3, num4);
					continue;
				}
				bool inventoryFull2;
				int num5 = Pull(player, inventory, name, -1, num3, out inventoryFull2);
				if (num5 > 0)
				{
					num += num5;
					if (moved != null)
					{
						moved.TryGetValue(name, out var value);
						moved[name] = value + num5;
					}
				}
				if (inventoryFull2)
				{
					inventoryFull = true;
					break;
				}
			}
			if (!dryRun && num > 0)
			{
				ContainerIndex.Invalidate();
			}
			return num;
		}

		private static Requirement ChooseSingleIngredient(Player player, Requirement[] requirements, int qualityLevel, int multiplier, CraftingStation station)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			Requirement result = null;
			int num = 0;
			foreach (Requirement val in requirements)
			{
				if (!IsRelevant(val, station))
				{
					continue;
				}
				int num2 = val.GetAmount(qualityLevel) * multiplier;
				if (num2 > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					int num3 = Mathf.Min(num2, inventory.CountItems(name, -1, true) + CountAvailable(player, name, -1));
					if (num3 > num)
					{
						num = num3;
						result = val;
					}
				}
			}
			return result;
		}

		private static int Pull(Player player, Inventory playerInventory, string name, int itemQuality, int missing, out bool inventoryFull)
		{
			Refresh(player);
			inventoryFull = false;
			int num = 0;
			long playerId = ContainerAccess.LocalPlayerId();
			IReadOnlyList<Container> containers = ContainerIndex.Containers;
			for (int i = 0; i < 2; i++)
			{
				if (missing <= 0)
				{
					break;
				}
				bool flag = i == 0;
				for (int j = 0; j < containers.Count; j++)
				{
					if (missing <= 0)
					{
						break;
					}
					Container val = containers[j];
					if (!ContainerAccess.CanUse(val, playerId) || val.m_nview.IsOwner() != flag)
					{
						continue;
					}
					Inventory inventory = val.GetInventory();
					if (inventory != null && inventory.CountItems(name, itemQuality, true) > 0)
					{
						val.m_nview.ClaimOwnership();
						bool inventoryFull2;
						int num2 = PullFrom(val, inventory, playerInventory, name, itemQuality, missing, out inventoryFull2);
						missing -= num2;
						num += num2;
						if (inventoryFull2)
						{
							inventoryFull = true;
							return num;
						}
					}
				}
			}
			return num;
		}

		private static int PullFrom(Container owner, Inventory source, Inventory playerInventory, string name, int itemQuality, int missing, out bool inventoryFull)
		{
			inventoryFull = false;
			int num = 0;
			Action onChanged = source.m_onChanged;
			source.m_onChanged = null;
			try
			{
				foreach (ItemData item in new List<ItemData>(source.GetAllItems()))
				{
					if (missing <= 0)
					{
						break;
					}
					if (item?.m_shared != null && !(item.m_shared.m_name != name) && item.m_worldLevel >= Game.m_worldLevel && (itemQuality < 0 || item.m_quality == itemQuality))
					{
						int stack = Mathf.Min(missing, item.m_stack);
						ItemData val = item.Clone();
						val.m_stack = stack;
						int num2 = playerInventory.CountItems(name, itemQuality, true);
						bool num3 = playerInventory.AddItem(val);
						int num4 = playerInventory.CountItems(name, itemQuality, true) - num2;
						if (num4 > 0)
						{
							source.RemoveItem(item, num4);
							missing -= num4;
							num += num4;
						}
						if (!num3)
						{
							inventoryFull = true;
							break;
						}
					}
				}
			}
			finally
			{
				source.m_onChanged = onChanged;
			}
			if (num > 0)
			{
				source.Changed(false, false);
				MoveLog.RecordSingle(intoContainer: false, owner, name, num);
			}
			return num;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirementItems")]
	internal static class Player_HaveRequirementItems_Patch
	{
		private static void Postfix(Player __instance, Recipe piece, bool discover, int qualityLevel, int amount, ref bool __result)
		{
			if (__result || discover || (Object)(object)piece == (Object)null || !CraftFromContainers.Active(__instance))
			{
				return;
			}
			try
			{
				CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation();
				Inventory inventory = ((Humanoid)__instance).GetInventory();
				bool requireOnlyOneIngredient = piece.m_requireOnlyOneIngredient;
				Requirement[] resources = piece.m_resources;
				foreach (Requirement val in resources)
				{
					if (!CraftFromContainers.IsRelevant(val, currentCraftingStation))
					{
						continue;
					}
					int num = val.GetAmount(qualityLevel) * amount;
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					int num2 = (CraftFromContainers.CanReceive(inventory, inventory.CountItems(name, -1, true)) ? CraftFromContainers.InContainers(__instance, name, -1) : 0);
					int num3 = 0;
					for (int j = 1; j < val.m_resItem.m_itemData.m_shared.m_maxQuality + 1; j++)
					{
						int num4 = inventory.CountItems(name, j, true);
						if (num2 > 0)
						{
							num4 += CraftFromContainers.InContainers(__instance, name, j);
						}
						if (num4 > num3)
						{
							num3 = num4;
						}
					}
					if (requireOnlyOneIngredient)
					{
						if (num3 >= num)
						{
							__result = true;
							return;
						}
					}
					else if (num3 < num)
					{
						return;
					}
				}
				__result = !requireOnlyOneIngredient;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al evaluar requisitos con cofres: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	internal static class InventoryGui_SetupRequirement_Patch
	{
		private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			if (!__result || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)player == (Object)null || !PluginConfig.Enabled.Value)
			{
				return;
			}
			bool value = PluginConfig.ShowAvailable.Value;
			if (!value && !PluginConfig.ShowContainerTotals.Value)
			{
				return;
			}
			bool flag = (CraftFromContainers.IsBuilding(player) ? CraftFromContainers.BuildActive(player) : (craft && CraftFromContainers.Active(player)));
			try
			{
				int num = req.GetAmount(quality) * craftMultiplier;
				if (num <= 0)
				{
					return;
				}
				string name = req.m_resItem.m_itemData.m_shared.m_name;
				int num2 = ((Humanoid)player).GetInventory().CountItems(name, -1, true);
				int num3 = num2;
				if (flag)
				{
					num3 += CraftFromContainers.CountAvailable(player, name, -1);
				}
				Transform val = elementRoot.Find("res_amount");
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				TMP_Text component = ((Component)val).GetComponent<TMP_Text>();
				if (!((Object)(object)component == (Object)null))
				{
					if (value)
					{
						component.text = num3 + "/" + num;
					}
					if (PluginConfig.ShowContainerTotals.Value && flag && num2 < num && num3 >= num)
					{
						((Graphic)component).color = CraftFromContainers.AvailableColor;
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al pintar el requisito: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class InventoryGui_DoCrafting_Patch
	{
		private static bool Prefix(InventoryGui __instance, Player player)
		{
			if (!CraftFromContainers.Active(player) || (Object)(object)__instance.m_craftRecipe == (Object)null)
			{
				return true;
			}
			try
			{
				Recipe craftRecipe = __instance.m_craftRecipe;
				int qualityLevel = ((__instance.m_craftUpgradeItem == null) ? 1 : (__instance.m_craftUpgradeItem.m_quality + 1));
				int num = ((!__instance.m_multiCrafting) ? 1 : __instance.m_multiCraftAmount);
				if (__instance.m_craftUpgradeItem == null && !craftRecipe.m_requireOnlyOneIngredient && !((Humanoid)player).GetInventory().CanAddItem(((Component)craftRecipe.m_item).gameObject, craftRecipe.m_amount * num))
				{
					return true;
				}
				CraftFromContainers.PullMissing(player, craftRecipe, qualityLevel, -1, num);
				if (craftRecipe.m_requireOnlyOneIngredient && !CraftFromContainers.AnyIngredientCovered(player, craftRecipe.m_resources, qualityLevel, num))
				{
					((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
					return false;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al traer material de los cofres: {arg}");
			}
			return true;
		}
	}
	[Serializable]
	internal class FavoriteData
	{
		public List<string> items = new List<string>();

		public List<string> slots = new List<string>();
	}
	internal static class FavoriteStore
	{
		private const float SaveDelaySeconds = 2f;

		private const float RetryDelaySeconds = 30f;

		private const int MaxLoggedFailures = 3;

		private static readonly HashSet<string> FavoriteItems = new HashSet<string>(StringComparer.Ordinal);

		private static readonly HashSet<int> FavoriteSlots = new HashSet<int>();

		private static string _profileKey;

		private static bool _dirty;

		private static float _saveAt;

		private static int _saveFailures;

		private static string Folder => Path.Combine(Paths.ConfigPath, "QuickStash");

		private static string FilePath => Path.Combine(Folder, "favorites." + (_profileKey ?? "default") + ".json");

		private static int PackSlot(int x, int y)
		{
			return (y << 8) | (x & 0xFF);
		}

		public static void LoadForCurrentProfile()
		{
			string text = ResolveProfileKey();
			if (text == _profileKey)
			{
				return;
			}
			FlushIfDirty();
			_profileKey = text;
			FavoriteItems.Clear();
			FavoriteSlots.Clear();
			try
			{
				if (File.Exists(FilePath))
				{
					FavoriteData favoriteData = JsonUtility.FromJson<FavoriteData>(File.ReadAllText(FilePath));
					if (favoriteData != null)
					{
						if (favoriteData.items != null)
						{
							foreach (string item in favoriteData.items)
							{
								FavoriteItems.Add(item);
							}
						}
						if (favoriteData.slots != null)
						{
							foreach (string slot in favoriteData.slots)
							{
								string[] array = slot.Split(new char[1] { ',' });
								if (array.Length == 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2))
								{
									FavoriteSlots.Add(PackSlot(result, result2));
								}
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("No se pudieron leer los favoritos de '" + _profileKey + "': " + ex.Message));
			}
			_dirty = false;
			_saveFailures = 0;
		}

		private static string ResolveProfileKey()
		{
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (val == null)
			{
				return null;
			}
			string text = val.GetFilename();
			if (string.IsNullOrEmpty(text))
			{
				text = val.GetName();
			}
			if (string.IsNullOrEmpty(text))
			{
				return "default";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				text = text.Replace(oldChar, '_');
			}
			return text;
		}

		public static bool IsFavoriteItem(ItemData item)
		{
			if (item?.m_shared != null)
			{
				return FavoriteItems.Contains(item.m_shared.m_name);
			}
			return false;
		}

		public static bool IsFavoriteSlot(int x, int y)
		{
			return FavoriteSlots.Contains(PackSlot(x, y));
		}

		public static bool IsProtected(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			if (!IsFavoriteItem(item))
			{
				return IsFavoriteSlot(item.m_gridPos.x, item.m_gridPos.y);
			}
			return true;
		}

		public static bool ToggleItem(ItemData item)
		{
			if (item?.m_shared == null)
			{
				return false;
			}
			string name = item.m_shared.m_name;
			bool result;
			if (FavoriteItems.Contains(name))
			{
				FavoriteItems.Remove(name);
				result = false;
			}
			else
			{
				FavoriteItems.Add(name);
				result = true;
			}
			MarkDirty();
			return result;
		}

		public static bool ToggleSlot(int x, int y)
		{
			int item = PackSlot(x, y);
			bool result;
			if (FavoriteSlots.Contains(item))
			{
				FavoriteSlots.Remove(item);
				result = false;
			}
			else
			{
				FavoriteSlots.Add(item);
				result = true;
			}
			MarkDirty();
			return result;
		}

		private static void MarkDirty()
		{
			_dirty = true;
			_saveAt = Time.realtimeSinceStartup + 2f;
		}

		public static void Tick()
		{
			if (_dirty && Time.realtimeSinceStartup >= _saveAt)
			{
				Save();
			}
		}

		public static void FlushIfDirty()
		{
			if (_dirty)
			{
				Save();
			}
		}

		private static void Save()
		{
			if (_profileKey == null)
			{
				_dirty = false;
				return;
			}
			try
			{
				Directory.CreateDirectory(Folder);
				FavoriteData favoriteData = new FavoriteData();
				favoriteData.items.AddRange(FavoriteItems);
				foreach (int favoriteSlot in FavoriteSlots)
				{
					favoriteData.slots.Add((favoriteSlot & 0xFF) + "," + (favoriteSlot >> 8));
				}
				string text = FilePath + ".tmp";
				File.WriteAllText(text, JsonUtility.ToJson((object)favoriteData, true));
				if (File.Exists(FilePath))
				{
					File.Delete(FilePath);
				}
				File.Move(text, FilePath);
				_dirty = false;
				_saveFailures = 0;
			}
			catch (Exception ex)
			{
				_saveFailures++;
				_saveAt = Time.realtimeSinceStartup + 30f;
				if (_saveFailures <= 3)
				{
					Plugin.Log.LogWarning((object)("No se pudieron guardar los favoritos de '" + _profileKey + "': " + ex.Message));
				}
			}
		}
	}
	internal static class StashService
	{
		private struct Request
		{
			public int ActionId;

			public float ExpiresAt;
		}

		private const float ResponseTimeoutSeconds = 5f;

		private const float RequestGraceSeconds = 60f;

		private const float CooldownSeconds = 0.5f;

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

		private static readonly List<ItemData> ItemBuffer = new List<ItemData>(64);

		private static readonly Dictionary<Container, Request> Requested = new Dictionary<Container, Request>();

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

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

		private static int _actionId;

		private static int _awaiting;

		private static int _requested;

		private static int _matching;

		private static int _inRange;

		private static int _usable;

		private static bool _dispatching;

		private static bool _reported;

		private static bool _running;

		private static float _deadline;

		private static float _nextAllowedAt;

		private static int _movedTotal;

		private static int _containersTouched;

		private static int _denied;

		private static float _startedAt;

		private static float _syncMs;

		private static ItemData _dragged;

		public static void Run()
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.Enabled.Value)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_running || realtimeSinceStartup < _nextAllowedAt)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting())
			{
				return;
			}
			_nextAllowedAt = realtimeSinceStartup + 0.5f;
			_actionId++;
			_movedTotal = 0;
			_containersTouched = 0;
			_denied = 0;
			_awaiting = 0;
			_requested = 0;
			_matching = 0;
			_reported = false;
			_startedAt = realtimeSinceStartup;
			_running = true;
			_deadline = realtimeSinceStartup + 5f;
			_dragged = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_dragItem : null);
			_dispatching = true;
			try
			{
				long num = ContainerAccess.LocalPlayerId();
				Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
				if (PluginConfig.IncludeOpenContainer.Value && (Object)(object)val != (Object)null)
				{
					int num2 = StackInto(val, localPlayer);
					if (num2 > 0)
					{
						_movedTotal += num2;
						_containersTouched++;
					}
				}
				ContainerRegistry.Query(((Component)localPlayer).transform.position, PluginConfig.Range.Value, PluginConfig.MaxScanned.Value, num, Buffer);
				_inRange = ContainerRegistry.LastInRange;
				_usable = ContainerRegistry.LastUsable;
				int value = PluginConfig.MaxContainersPerAction.Value;
				_matching = 0;
				foreach (Container item in Buffer)
				{
					if (!((Object)(object)item == (Object)(object)val) && HasAnythingFor(item, localPlayer))
					{
						_matching++;
						if (_requested < value)
						{
							_requested++;
							_awaiting++;
							Requested[item] = new Request
							{
								ActionId = _actionId,
								ExpiresAt = realtimeSinceStartup + 60f
							};
							item.m_nview.InvokeRPC("RPC_RequestStack", new object[1] { num });
						}
					}
				}
				_syncMs = (Time.realtimeSinceStartup - realtimeSinceStartup) * 1000f;
			}
			finally
			{
				_dispatching = false;
			}
			if (_awaiting == 0)
			{
				Report();
			}
		}

		public static bool TryHandleResponse(Container container, bool granted)
		{
			if ((Object)(object)container == (Object)null || !Requested.TryGetValue(container, out var value))
			{
				return false;
			}
			Requested.Remove(container);
			if (value.ActionId != _actionId)
			{
				return true;
			}
			if (granted)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && (Object)(object)container.m_nview != (Object)null && container.m_nview.IsValid())
				{
					container.m_nview.ClaimOwnership();
					int num = StackInto(container, localPlayer);
					if (num > 0)
					{
						_movedTotal += num;
						_containersTouched++;
					}
				}
			}
			else
			{
				_denied++;
			}
			if (_awaiting > 0)
			{
				_awaiting--;
			}
			if (!_dispatching && _awaiting == 0)
			{
				Report();
			}
			return true;
		}

		public static void Tick()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_running && realtimeSinceStartup >= _deadline)
			{
				_awaiting = 0;
				Report();
			}
			PruneExpiredRequests(realtimeSinceStartup);
		}

		public static void Reset()
		{
			Requested.Clear();
			Buffer.Clear();
			ItemBuffer.Clear();
			_awaiting = 0;
			_requested = 0;
			_matching = 0;
			_dispatching = false;
			_running = false;
			_reported = true;
			_dragged = null;
		}

		private static void PruneExpiredRequests(float now)
		{
			if (Requested.Count == 0)
			{
				return;
			}
			PruneBuffer.Clear();
			foreach (KeyValuePair<Container, Request> item in Requested)
			{
				if ((Object)(object)item.Key == (Object)null || now >= item.Value.ExpiresAt)
				{
					PruneBuffer.Add(item.Key);
				}
			}
			foreach (Container item2 in PruneBuffer)
			{
				Requested.Remove(item2);
			}
			PruneBuffer.Clear();
		}

		private static bool HasAnythingFor(Container container, Player player)
		{
			Inventory inventory = container.GetInventory();
			Inventory inventory2 = ((Humanoid)player).GetInventory();
			if (inventory == null || inventory2 == null)
			{
				return false;
			}
			List<ItemData> allItems = inventory2.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (CanStash(val, player) && inventory.ContainsItemByName(val.m_shared.m_name))
				{
					return true;
				}
			}
			return false;
		}

		private static int StackInto(Container container, Player player)
		{
			ZNetView nview = container.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner())
			{
				return 0;
			}
			Inventory inventory = container.GetInventory();
			Inventory inventory2 = ((Humanoid)player).GetInventory();
			if (inventory == null || inventory2 == null)
			{
				return 0;
			}
			int num = 0;
			Action onChanged = inventory.m_onChanged;
			inventory.m_onChanged = null;
			try
			{
				ItemBuffer.Clear();
				ItemBuffer.AddRange(inventory2.GetAllItems());
				MovedBuffer.Clear();
				foreach (ItemData item in ItemBuffer)
				{
					if (CanStash(item, player) && inventory.ContainsItemByName(item.m_shared.m_name))
					{
						int stack = item.m_stack;
						int num2;
						if (inventory.AddItem(item))
						{
							inventory2.RemoveItem(item);
							num2 = stack;
						}
						else
						{
							num2 = stack - item.m_stack;
						}
						if (num2 > 0)
						{
							num += num2;
							string key = MoveLog.Localize(item.m_shared.m_name);
							MovedBuffer.TryGetValue(key, out var value);
							MovedBuffer[key] = value + num2;
						}
					}
				}
			}
			finally
			{
				ItemBuffer.Clear();
				inventory.m_onChanged = onChanged;
			}
			if (num > 0)
			{
				inventory.Changed(false, false);
				inventory2.Changed(false, false);
				MoveLog.Record(intoContainer: true, container, MovedBuffer);
			}
			MovedBuffer.Clear();
			return num;
		}

		private static bool CanStash(ItemData item, Player player)
		{
			if (item?.m_shared == null || item == _dragged)
			{
				return false;
			}
			if (((Humanoid)player).IsItemEquiped(item))
			{
				return false;
			}
			if (PluginConfig.ProtectHotbar.Value && item.m_gridPos.y == 0)
			{
				return false;
			}
			if (FavoriteStore.IsProtected(item))
			{
				return false;
			}
			return !PluginConfig.IsExcluded(item);
		}

		private static void Report()
		{
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			if (_reported)
			{
				return;
			}
			_reported = true;
			_running = false;
			_dragged = null;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			if (PluginConfig.DebugTiming.Value)
			{
				float num = (Time.realtimeSinceStartup - _startedAt) * 1000f;
				Plugin.Log.LogInfo((object)($"[stash] {_movedTotal} items -> {_containersTouched} cofres | " + $"en rango {_inRange} | utilizables {_usable} | " + $"con coincidencia {_matching} | pedidos {_requested} (tope {PluginConfig.MaxContainersPerAction.Value}) | " + $"sincrono {_syncMs:F2} ms | total {num:F0} ms | rechazados {_denied} | " + $"registro {ContainerRegistry.Count} cofres | peticiones vivas {Requested.Count}"));
			}
			if (_movedTotal > 0)
			{
				((Character)localPlayer).Message((MessageType)2, $"Guardado: {_movedTotal} en {_containersTouched} cofre(s)", 0, (Sprite)null, false);
				if ((Object)(object)InventoryGui.instance != (Object)null)
				{
					InventoryGui.instance.m_moveItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			else if (_denied > 0)
			{
				((Character)localPlayer).Message((MessageType)2, $"Sin guardar: {_denied} cofre(s) en uso por otro jugador", 0, (Sprite)null, false);
			}
			else
			{
				((Character)localPlayer).Message((MessageType)2, "Nada que guardar cerca", 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_StackResponse")]
	internal static class Container_RPC_StackResponse_Patch
	{
		private static bool Prefix(Container __instance, bool granted)
		{
			try
			{
				return !StashService.TryHandleResponse(__instance, granted);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al procesar la respuesta de apilado: {arg}");
				return false;
			}
		}
	}
	internal static class StationFeeder
	{
		private static readonly List<Container> Buffer = new List<Container>(16);

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

		private static readonly List<ItemData> ItemBuffer = new List<ItemData>(32);

		private static readonly Dictionary<Smelter, float> NextCheck = new Dictionary<Smelter, float>();

		private static readonly List<Smelter> PruneBuffer = new List<Smelter>(8);

		private static float _budgetWindowStart;

		private static int _stationsInWindow;

		public static void Tick(Smelter smelter)
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.Enabled.Value || !PluginConfig.FeedStations.Value)
			{
				return;
			}
			ZNetView nview = smelter.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (NextCheck.TryGetValue(smelter, out var value) && realtimeSinceStartup < value)
			{
				return;
			}
			NextCheck[smelter] = realtimeSinceStartup + PluginConfig.FeedIntervalSeconds.Value;
			PruneStale(realtimeSinceStartup);
			bool flag = (Object)(object)smelter.m_fuelItem != (Object)null && smelter.m_maxFuel > 0 && smelter.GetFuel() < (float)smelter.m_maxFuel;
			bool flag2 = smelter.GetQueueSize() < smelter.m_maxOre;
			bool flag3 = nview.IsOwner();
			if (!flag3 || (!flag && !flag2))
			{
				Diagnose(smelter, flag3, flag, flag2, 0L);
			}
			else
			{
				if (!TakeGlobalBudget(realtimeSinceStartup))
				{
					return;
				}
				long playerId = ContainerAccess.LocalPlayerId();
				ContainerRegistry.Query(((Component)smelter).transform.position, PluginConfig.FeedRange.Value, PluginConfig.MaxScanned.Value, playerId, Buffer);
				int num = PluginConfig.FeedMaxPerCycle.Value;
				foreach (Container item in Buffer)
				{
					if (num <= 0)
					{
						break;
					}
					if (!((Object)(object)item.m_nview == (Object)null) && item.m_nview.IsOwner())
					{
						num -= FeedFrom(smelter, item, num);
					}
				}
				if (num == PluginConfig.FeedMaxPerCycle.Value)
				{
					Diagnose(smelter, owner: true, flag, flag2, playerId);
				}
			}
		}

		private static void Diagnose(Smelter smelter, bool owner, bool wantsFuel, bool wantsOre, long playerId)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.DebugTiming.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			float num = Vector3.Distance(((Component)smelter).transform.position, ((Component)localPlayer).transform.position);
			if (num > 30f)
			{
				return;
			}
			string arg = MoveLog.Localize(smelter.m_name);
			if (!owner)
			{
				Plugin.Log.LogInfo((object)($"[horno] {arg} a {num:F1} m | dueno del ZDO: NO -> este cliente no lo alimenta " + "(lo tiene otro jugador o el servidor)"));
				return;
			}
			if (!wantsFuel && !wantsOre)
			{
				Plugin.Log.LogInfo((object)$"[horno] {arg} a {num:F1} m | lleno, no necesita nada");
				return;
			}
			ContainerRegistry.Query(((Component)smelter).transform.position, PluginConfig.FeedRange.Value, PluginConfig.MaxScanned.Value, playerId, DiagBuffer);
			int num2 = 0;
			foreach (Container item in DiagBuffer)
			{
				if ((Object)(object)item.m_nview != (Object)null && item.m_nview.IsOwner())
				{
					num2++;
				}
			}
			int lastInRange = ContainerRegistry.LastInRange;
			int lastUsable = ContainerRegistry.LastUsable;
			string text = ((lastInRange == 0) ? $"no hay ningun cofre a menos de {PluginConfig.FeedRange.Value:F0} m" : ((lastUsable < lastInRange) ? ($"de {lastInRange} cofre(s) cerca, {lastInRange - lastUsable} quedaron descartados por " + "permisos, ward, o por estar marcados EN USO (un cofre con la tapa levantada que nadie abrio tiene la marca trabada: rompelo y volvelo a poner)") : ((num2 != 0) ? $"hay {num2} cofre(s) tuyo(s) pero ninguno tiene material que este horno acepte" : ($"hay {lastUsable} cofre(s) utilizable(s) pero ninguno es tuyo " + "(en el servidor la propiedad del ZDO puede estar en otro jugador)"))));
			Plugin.Log.LogInfo((object)(string.Format("[horno] {0} a {1:F1} m | quiere {2}", arg, num, wantsFuel ? "combustible" : "") + ((wantsFuel && wantsOre) ? " y " : "") + (wantsOre ? "mineral" : "") + " | " + $"en rango {lastInRange} | utilizables {lastUsable} | tuyos {num2} -> {text}"));
		}

		private static bool TakeGlobalBudget(float now)
		{
			if (now - _budgetWindowStart >= 1f)
			{
				_budgetWindowStart = now;
				_stationsInWindow = 0;
			}
			if (_stationsInWindow >= PluginConfig.FeedMaxStationsPerSecond.Value)
			{
				return false;
			}
			_stationsInWindow++;
			return true;
		}

		private static int FeedFrom(Smelter smelter, Container container, int budget)
		{
			Inventory inventory = container.GetInventory();
			if (inventory == null)
			{
				return 0;
			}
			string text = (((Object)(object)smelter.m_fuelItem != (Object)null) ? ((Object)((Component)smelter.m_fuelItem).gameObject).name : null);
			int num = 0;
			Action onChanged = inventory.m_onChanged;
			inventory.m_onChanged = null;
			try
			{
				ItemBuffer.Clear();
				ItemBuffer.AddRange(inventory.GetAllItems());
				foreach (ItemData item in ItemBuffer)
				{
					if (num >= budget)
					{
						break;
					}
					if (item?.m_shared != null && !((Object)(object)item.m_dropPrefab == (Object)null) && item.m_stack > 0 && !PluginConfig.IsExcluded(item))
					{
						string name = ((Object)item.m_dropPrefab).name;
						bool flag = text != null && name == text;
						bool flag2 = !flag && smelter.IsItemAllowed(name);
						if (flag || flag2)
						{
							num += (flag ? FeedFuel(smelter, container, inventory, item, budget - num) : FeedOre(smelter, container, inventory, item, name, budget - num));
						}
					}
				}
			}
			finally
			{
				ItemBuffer.Clear();
				inventory.m_onChanged = onChanged;
				if (num > 0)
				{
					inventory.Changed(false, false);
				}
			}
			return num;
		}

		private static int FeedFuel(Smelter smelter, Container container, Inventory source, ItemData item, int budget)
		{
			int num = Mathf.Min(Mathf.Min(Mathf.FloorToInt((float)smelter.m_maxFuel - smelter.GetFuel()), item.m_stack), budget);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (!Move(smelter, source, item, 1))
				{
					break;
				}
				num2++;
				smelter.m_nview.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
			}
			if (num2 > 0)
			{
				MoveLog.RecordStationFeed(container, item.m_shared.m_name, num2);
			}
			return num2;
		}

		private static int FeedOre(Smelter smelter, Container container, Inventory source, ItemData item, string prefabName, int budget)
		{
			int num = Mathf.Min(Mathf.Min(smelter.m_maxOre - smelter.GetQueueSize(), item.m_stack), budget);
			bool cheated = item.m_cheated;
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (!Move(smelter, source, item, 1))
				{
					break;
				}
				num2++;
				smelter.m_nview.InvokeRPC("RPC_AddOre", new object[2] { prefabName, cheated });
			}
			if (num2 > 0)
			{
				MoveLog.RecordStationFeed(container, item.m_shared.m_name, num2);
			}
			return num2;
		}

		private static bool Move(Smelter smelter, Inventory source, ItemData item, int amount)
		{
			if ((Object)(object)smelter.m_nview == (Object)null || !smelter.m_nview.IsValid() || !smelter.m_nview.IsOwner())
			{
				return false;
			}
			return source.RemoveItem(item, amount);
		}

		private static void PruneStale(float now)
		{
			if (NextCheck.Count < 64)
			{
				return;
			}
			PruneBuffer.Clear();
			foreach (KeyValuePair<Smelter, float> item in NextCheck)
			{
				if ((Object)(object)item.Key == (Object)null || now > item.Value + 300f)
				{
					PruneBuffer.Add(item.Key);
				}
			}
			foreach (Smelter item2 in PruneBuffer)
			{
				NextCheck.Remove(item2);
			}
			PruneBuffer.Clear();
		}

		public static void Reset()
		{
			NextCheck.Clear();
			Buffer.Clear();
			DiagBuffer.Clear();
			ItemBuffer.Clear();
			_stationsInWindow = 0;
			_budgetWindowStart = 0f;
		}
	}
	[HarmonyPatch(typeof(Smelter), "UpdateSmelter")]
	internal static class Smelter_UpdateSmelter_Patch
	{
		private static void Postfix(Smelter __instance)
		{
			try
			{
				StationFeeder.Tick(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Fallo al cargar el horno desde los cofres: {arg}");
			}
		}
	}
}
namespace QuickStash.Core
{
	internal static class AnimalDiagnostics
	{
		private const float IntervalSeconds = 5f;

		private const float ScanRange = 25f;

		private const int MaxReported = 5;

		private static readonly StringBuilder Builder = new StringBuilder(160);

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

		private static float _nextRun;

		private static bool _reportedEmpty;

		public static void Tick()
		{
			//IL_0057: 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_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_00b3: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.DebugTiming.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextRun)
			{
				return;
			}
			_nextRun = realtimeSinceStartup + 5f;
			List<BaseAI> instances = BaseAI.m_instances;
			if (instances == null)
			{
				Plugin.Log.LogWarning((object)"[diag] no se pudo leer la lista de IAs del juego.");
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			float num = 625f;
			long playerId = ContainerAccess.LocalPlayerId();
			int num2 = 0;
			int num3 = 0;
			for (int i = 0; i < instances.Count; i++)
			{
				BaseAI val = instances[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Tameable tamable = val.m_tamable;
				if ((Object)(object)tamable == (Object)null || !tamable.IsTamed())
				{
					continue;
				}
				Vector3 val2 = ((Component)val).transform.position - position;
				if (!(((Vector3)(ref val2)).sqrMagnitude > num))
				{
					num3++;
					if (num2 < 5)
					{
						num2++;
						Report(val, tamable, position, playerId);
					}
				}
			}
			if (num3 == 0)
			{
				if (!_reportedEmpty)
				{
					_reportedEmpty = true;
					Plugin.Log.LogInfo((object)$"[diag] no hay animales domesticados a menos de {25f:F0} m.");
				}
			}
			else
			{
				_reportedEmpty = false;
				if (num3 > num2)
				{
					Plugin.Log.LogInfo((object)$"[diag] ... y {num3 - num2} animal(es) mas, no listados.");
				}
			}
		}

		private static void Report(BaseAI ai, Tameable tameable, Vector3 origin, long playerId)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			ZNetView nview = ai.m_nview;
			bool flag = (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner();
			Builder.Length = 0;
			Builder.Append("[diag] ").Append(NameOf(ai));
			Builder.Append(" a ").Append(Vector3.Distance(((Component)ai).transform.position, origin).ToString("F1")).Append(" m");
			Builder.Append(" | dueno del ZDO: ").Append(flag ? "SI" : "NO");
			Builder.Append(" | hambriento: ").Append(tameable.IsHungry() ? "si" : "no");
			MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null);
			if ((Object)(object)val == (Object)null)
			{
				Builder.Append(" | no es MonsterAI (no usa el sistema de comer del suelo)");
			}
			else if (val.m_consumeItems == null || val.m_consumeItems.Count == 0)
			{
				Builder.Append(" | LISTA DE COMIDA VACIA (el juego nunca busca comida para este bicho)");
			}
			else
			{
				Builder.Append(" | come ").Append(val.m_consumeItems.Count).Append(": ");
				for (int i = 0; i < val.m_consumeItems.Count; i++)
				{
					ItemDrop val2 = val.m_consumeItems[i];
					if (!((Object)(object)val2 == (Object)null) && val2.m_itemData?.m_shared != null)
					{
						if (i > 0)
						{
							Builder.Append(", ");
						}
						Builder.Append(MoveLog.Localize(val2.m_itemData.m_shared.m_name));
					}
				}
			}
			ContainerRegistry.Query(((Component)ai).transform.position, PluginConfig.AnimalFeedRange.Value, PluginConfig.MaxScanned.Value, playerId, ContainerBuffer);
			int num = 0;
			foreach (Container item in ContainerBuffer)
			{
				if ((Object)(object)item.m_nview != (Object)null && item.m_nview.IsOwner())
				{
					num++;
				}
			}
			Builder.Append(" | cofres accesibles: ").Append(ContainerBuffer.Count).Append(" (tuyos: ")
				.Append(num)
				.Append(')');
			Plugin.Log.LogInfo((object)Builder.ToString());
			Builder.Length = 0;
		}

		private static string NameOf(BaseAI ai)
		{
			Character character = ai.m_character;
			if (!((Object)(object)character != (Object)null))
			{
				return ((Object)ai).name;
			}
			return MoveLog.Localize(character.m_name);
		}

		public static void Reset()
		{
			_nextRun = 0f;
			_reportedEmpty = false;
			ContainerBuffer.Clear();
		}
	}
	internal static class ContainerAccess
	{
		public static bool CanUse(Container container, long playerId)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			ZNetView nview = container.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return false;
			}
			if (IsInUse(container, nview))
			{
				return false;
			}
			if (!container.CheckAccess(playerId))
			{
				return false;
			}
			if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
			{
				return false;
			}
			return true;
		}

		public static bool IsInUse(Container container, ZNetView nview)
		{
			if (nview.IsOwner())
			{
				return container.m_inUse;
			}
			ZDO zDO = nview.GetZDO();
			if (zDO != null)
			{
				return zDO.GetInt(ZDOVars.s_inUse, 0) == 1;
			}
			return false;
		}

		public static long LocalPlayerId()
		{
			if ((Object)(object)Game.instance == (Object)null)
			{
				return 0L;
			}
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			if (playerProfile != null)
			{
				return playerProfile.GetPlayerID();
			}
			return 0L;
		}
	}
	internal static class ContainerIndex
	{
		private readonly struct ItemKey : IEquatable<ItemKey>
		{
			public readonly string Name;

			public readonly int Quality;

			public ItemKey(string name, int quality)
			{
				Name = name;
				Quality = quality;
			}

			public bool Equals(ItemKey other)
			{
				if (Quality == other.Quality)
				{
					return Name == other.Name;
				}
				return false;
			}

			public override bool Equals(object obj)
			{
				if (obj is ItemKey other)
				{
					return Equals(other);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return ((Name?.GetHashCode() ?? 0) * 397) ^ Quality;
			}
		}

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

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

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

		private static Vector3 _point;

		private static float _range = -1f;

		private static float _time = float.NegativeInfinity;

		private static bool _valid;

		public static IReadOnlyList<Container> Containers => Nearby;

		public static void Invalidate()
		{
			_valid = false;
			Nearby.Clear();
			Counts.Clear();
			Totals.Clear();
		}

		public static void EnsureFresh(Vector3 point, float range, long playerId)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			float num = (float)PluginConfig.CacheMs.Value / 1000f;
			if (_valid && Mathf.Approximately(_range, range) && realtimeSinceStartup - _time <= num)
			{
				Vector3 val = point - _point;
				if (((Vector3)(ref val)).sqrMagnitude <= 1f)
				{
					return;
				}
			}
			ContainerRegistry.Query(point, range, PluginConfig.MaxScanned.Value, playerId, Nearby);
			Counts.Clear();
			Totals.Clear();
			foreach (Container item in Nearby)
			{
				Inventory inventory = item.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem?.m_shared != null && allItem.m_worldLevel >= Game.m_worldLevel)
					{
						ItemKey key = new ItemKey(allItem.m_shared.m_name, allItem.m_quality);
						Counts.TryGetValue(key, out var value);
						Counts[key] = value + allItem.m_stack;
						Totals.TryGetValue(allItem.m_shared.m_name, out var value2);
						Totals[allItem.m_shared.m_name] = value2 + allItem.m_stack;
					}
				}
			}
			_point = point;
			_range = range;
			_time = realtimeSinceStartup;
			_valid = true;
		}

		public static int Count(string name, int quality)
		{
			if (quality >= 0)
			{
				Counts.TryGetValue(new ItemKey(name, quality), out var value);
				return value;
			}
			Totals.TryGetValue(name, out var value2);
			return value2;
		}
	}
	internal static class ContainerRegistry
	{
		private struct Entry
		{
			public Container Container;

			public float SqrDistance;
		}

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

		private static readonly List<Entry> SortBuffer = new List<Entry>(64);

		private static readonly Comparison<Entry> ByDistance = (Entry a, Entry b) => a.SqrDistance.CompareTo(b.SqrDistance);

		public static int Count => All.Count;

		public static int LastInRange { get; private set; }

		public static int LastUsable { get; private set; }

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

		public static void Unregister(Container container)
		{
			All.Remove(container);
		}

		public static void Clear()
		{
			All.Clear();
		}

		public static void Query(Vector3 point, float range, int maxScanned, long playerId, List<Container> results)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			results.Clear();
			SortBuffer.Clear();
			LastInRange = 0;
			float num = range * range;
			for (int num2 = All.Count - 1; num2 >= 0; num2--)
			{
				Container val = All[num2];
				if ((Object)(object)val == (Object)null)
				{
					All.RemoveAt(num2);
				}
				else
				{
					Vector3 val2 = ((Component)val).transform.position - point;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (!(sqrMagnitude > num))
					{
						LastInRange++;
						if (ContainerAccess.CanUse(val, playerId))
						{
							SortBuffer.Add(new Entry
							{
								Container = val,
								SqrDistance = sqrMagnitude
							});
						}
					}
				}
			}
			LastUsable = SortBuffer.Count;
			SortBuffer.Sort(ByDistance);
			int num3 = Mathf.Min(maxScanned, SortBuffer.Count);
			for (int i = 0; i < num3; i++)
			{
				results.Add(SortBuffer[i].Container);
			}
			SortBuffer.Clear();
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake_Patch
	{
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class Container_OnDestroyed_Patch
	{
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Unregister(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class Player_OnSpawned_Patch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				FavoriteStore.LoadForCurrentProfile();
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Logout")]
	internal static class Game_Logout_Patch
	{
		private static void Prefix()
		{
			FavoriteStore.FlushIfDirty();
			ContainerRegistry.Clear();
			ContainerIndex.Invalidate();
			StashService.Reset();
			StationFeeder.Reset();
			AnimalFeeder.Reset();
			AnimalDiagnostics.Reset();
		}
	}
	internal static class MoveLog
	{
		private static readonly StringBuilder Builder = new StringBuilder(128);

		public static void Record(bool intoContainer, Container container, Dictionary<string, int> items)
		{
			if (!PluginConfig.LogMoves.Value || items == null || items.Count == 0)
			{
				return;
			}
			try
			{
				Builder.Length = 0;
				foreach (KeyValuePair<string, int> item in items)
				{
					if (Builder.Length > 0)
					{
						Builder.Append(", ");
					}
					Builder.Append(item.Key).Append(" x").Append(item.Value);
				}
				string text = (intoContainer ? "GUARDADO ->" : "SACADO <-");
				Plugin.Log.LogInfo((object)$"{text} {Describe(container)} | {DateTime.Now:HH:mm:ss} | {Builder}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("No se pudo registrar el movimiento: " + ex.Message));
			}
			finally
			{
				Builder.Length = 0;
			}
		}

		public static void RecordSingle(bool intoContainer, Container container, string itemName, int amount)
		{
			Write(intoContainer ? "GUARDADO ->" : "SACADO <-", container, itemName, amount);
		}

		public static void RecordStationFeed(Container container, string itemName, int amount)
		{
			if (PluginConfig.FeedLog.Value)
			{
				Write("HORNO <-", container, itemName, amount);
			}
		}

		public static void RecordAnimalFeed(Container container, string itemName, int amount)
		{
			if (PluginConfig.AnimalLog.Value)
			{
				Write("ANIMAL <-", container, itemName, amount);
			}
		}

		private static void Write(string label, Container container, string itemName, int amount)
		{
			if (!PluginConfig.LogMoves.Value || amount <= 0)
			{
				return;
			}
			try
			{
				Plugin.Log.LogInfo((object)$"{label} {Describe(container)} | {DateTime.Now:HH:mm:ss} | {Localize(itemName)} x{amount}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("No se pudo registrar el movimiento: " + ex.Message));
			}
		}

		private static string Describe(Container container)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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)
			if ((Object)(object)container == (Object)null)
			{
				return "cofre desconocido";
			}
			Vector3 position = ((Component)container).transform.position;
			return $"{Localize(container.m_name)} ({position.x:F0}, {position.y:F0}, {position.z:F0})";
		}

		public static string Localize(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "?";
			}
			if (Localization.instance == null)
			{
				return token;
			}
			return Localization.instance.Localize(token);
		}
	}
}
namespace Quick