Decompiled source of EquivalentExchange v0.39.0

EquivalentExchange.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EquivalentExchange.Commands;
using EquivalentExchange.Core;
using EquivalentExchange.Drops;
using EquivalentExchange.Economy;
using EquivalentExchange.InventoryUi;
using EquivalentExchange.Portals;
using EquivalentExchange.World;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("EquivalentExchange")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.39.0.0")]
[assembly: AssemblyInformationalVersion("0.39.0+786e3d5178fc1ed5ea3bb704df11654f7e08d736")]
[assembly: AssemblyProduct("EquivalentExchange")]
[assembly: AssemblyTitle("EquivalentExchange")]
[assembly: AssemblyVersion("0.39.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EquivalentExchange
{
	internal static class Patches
	{
		internal static void Apply(Harmony harmony, ManualLogSource log)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			Patch(harmony, log, typeof(ZNet), "SaveWorld");
			Patch(harmony, log, typeof(ZNet), "SaveWorldAndPlayerProfiles");
			Patch(harmony, log, typeof(ZNet), "Save");
			Patch(harmony, log, typeof(ZNet), "RPC_SavePlayerProfile");
			PatchSavePrefix(harmony, log);
			MethodInfo methodInfo = AccessTools.Method(typeof(ObjectDB), "Awake", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnObjectDbReady", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				log.LogWarning((object)"Harmony: ObjectDB.Awake не найден — автопрайсинг рецептов отключён.");
			}
			PatchPostfix(harmony, log, typeof(InventoryGui), "Awake", typeof(EmcInventoryUi), "OnInventoryGuiAwake");
			PatchPostfix(harmony, log, typeof(InventoryGui), "Show", typeof(EmcInventoryUi), "OnInventoryGuiShow");
			PatchPostfix(harmony, log, typeof(InventoryGui), "Show", typeof(EmcInventoryUi), "OnShowContainerPostfix");
			PatchPostfix(harmony, log, typeof(InventoryGui), "Hide", typeof(EmcInventoryUi), "OnInventoryGuiHide");
			PatchPrefix(harmony, log, typeof(InventoryGui), "OnSelectedItem", typeof(EmcInventoryUi), "OnSelectedItemPrefix");
			PatchPrefix(harmony, log, typeof(InventoryGui), "UpdateContainer", typeof(EmcInventoryUi), "UpdateContainerPrefix");
			PatchPostfix(harmony, log, typeof(Inventory), "GetTotalWeight", typeof(EmcInventoryUi), "GetTotalWeightPostfix");
			PatchPostfix(harmony, log, typeof(InventoryGrid), "UpdateGui", typeof(EmcInventoryUi), "UpdateGuiPostfix");
			PatchPostfix(harmony, log, typeof(InventoryGui), "UpdateRecipe", typeof(EmcInventoryUi), "UpdateRecipePostfix");
			PatchPostfix(harmony, log, typeof(InventoryGui), "OnCraftPressed", typeof(EmcInventoryUi), "OnCraftPressedPostfix");
			PatchFinalizer(harmony, log, typeof(InventoryGui), "OnCraftPressed", typeof(EmcInventoryUi), "OnCraftPressedFinalizer");
			PatchPrefix(harmony, log, typeof(InventoryGui), "OnCraftCancelPressed", typeof(EmcInventoryUi), "OnCraftCancelPressedPrefix");
			PatchPrefix(harmony, log, typeof(InventoryGui), "DoCrafting", typeof(EmcInventoryUi), "DoCraftingPrefix");
			PatchPostfix(harmony, log, typeof(InventoryGui), "DoCrafting", typeof(EmcInventoryUi), "DoCraftingPostfix");
			PatchFinalizer(harmony, log, typeof(InventoryGui), "DoCrafting", typeof(EmcInventoryUi), "DoCraftingFinalizer");
			PatchPrefix(harmony, log, typeof(Player), "ConsumeResources", typeof(EmcInventoryUi), "ConsumeResourcesPrefix");
			PatchPrefix(harmony, log, typeof(Player), "UpdatePlacement", typeof(EmcInventoryUi), "UpdatePlacementPrefix");
			PatchFinalizer(harmony, log, typeof(Player), "UpdatePlacement", typeof(EmcInventoryUi), "UpdatePlacementFinalizer");
			PatchPostfix(harmony, log, typeof(Hud), "SetupPieceInfo", typeof(EmcInventoryUi), "SetupPieceInfoPostfix");
			PatchPostfix(harmony, log, typeof(TeleportWorld), "GetHoverText", typeof(PortalHoverPatch), "OnGetHoverTextPostfix");
			PatchPrefix(harmony, log, typeof(TeleportWorld), "Teleport", typeof(PortalTeleportPatch), "OnPortalTeleportPrefix");
			PatchFinalizer(harmony, log, typeof(TeleportWorld), "Teleport", typeof(PortalTeleportPatch), "OnPortalTeleportFinalizer");
			PatchPrefix(harmony, log, typeof(Player), "TeleportTo", typeof(PortalTeleportPatch), "OnPlayerTeleportToPrefix");
			PatchPostfix(harmony, log, typeof(Player), "TeleportTo", typeof(PortalTeleportPatch), "OnPlayerTeleportToPostfix");
			PatchPrefix(harmony, log, typeof(Inventory), "RemoveItem", typeof(EmcInventoryUi), "RemoveItemPrefix", new Type[4]
			{
				typeof(string),
				typeof(int),
				typeof(int),
				typeof(bool)
			});
			PatchPrefix(harmony, log, typeof(InventoryGui), "RepairOneItem", typeof(EmcRepair), "BlockVanillaRepair");
			PatchPostfix(harmony, log, typeof(InventoryGui), "UpdateRepair", typeof(EmcRepair), "OnUpdateRepairPostfix");
			PatchPrefix(harmony, log, typeof(Player), "Repair", typeof(EmcRepair), "StructureRepairPrefix");
			PatchFinalizer(harmony, log, typeof(Player), "Repair", typeof(EmcRepair), "StructureRepairFinalizer");
			PatchPostfix(harmony, log, typeof(WearNTear), "Repair", typeof(EmcRepair), "PieceRepairPostfix");
			PatchPostfix(harmony, log, typeof(Hud), "UpdateCrosshair", typeof(EmcRepairHud), "OnUpdateCrosshairPostfix");
			PatchPrefix(harmony, log, typeof(Character), "OnDeath", typeof(MobLootDrop), "OnCharacterDeathPrefix");
			PatchPostfix(harmony, log, typeof(Character), "OnDeath", typeof(MobLootDrop), "OnCharacterDeathPostfix");
			PatchPostfix(harmony, log, typeof(ItemDrop), "Awake", typeof(DropParticlesPatch), "OnItemDropAwakePostfix");
			PatchPrefix(harmony, log, typeof(TombStone), "UpdateDespawn", typeof(TombstonePinCleanup), "OnTombstoneUpdateDespawnPrefix");
		}

		private static void PatchPrefix(Harmony harmony, ManualLogSource log, Type type, string methodName, Type patchType, string prefixName, Type[] paramTypes = null)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(type, methodName, paramTypes, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(patchType, prefixName, (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				log.LogWarning((object)("Harmony: " + type.Name + "." + methodName + " или " + patchType.Name + "." + prefixName + " не найдены."));
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				log.LogInfo((object)("Harmony: " + type.Name + "." + methodName + " перехвачен (" + patchType.Name + "." + prefixName + ")."));
			}
		}

		private static void PatchFinalizer(Harmony harmony, ManualLogSource log, Type type, string methodName, Type patchType, string finalizerName)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(patchType, finalizerName, (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				log.LogWarning((object)("Harmony: " + type.Name + "." + methodName + " или " + patchType.Name + "." + finalizerName + " не найдены."));
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null);
				log.LogInfo((object)("Harmony: " + type.Name + "." + methodName + " финалайзер (" + patchType.Name + "." + finalizerName + ")."));
			}
		}

		private static void PatchPostfix(Harmony harmony, ManualLogSource log, Type type, string methodName, Type patchType, string postfixName)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(patchType, postfixName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				log.LogWarning((object)("Harmony: " + type.Name + "." + methodName + " не найден."));
			}
			else if (methodInfo2 == null)
			{
				log.LogWarning((object)("Harmony: постфикс " + patchType.Name + "." + postfixName + " не найден."));
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				log.LogInfo((object)("Harmony: " + type.Name + "." + methodName + " -> " + patchType.Name + "." + postfixName));
			}
		}

		private static void OnObjectDbReady()
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			int num = ValuablePricing.Apply(instance.Registry, instance.CoinEmcValue);
			if (num > 0)
			{
				ManualLogSource log = instance.Log;
				if (log != null)
				{
					log.LogInfo((object)$"ValuablePricing: {num} ценностей оценены по курсу 1 монета = {instance.CoinEmcValue} EMC.");
				}
			}
			int num2 = RecipePricing.ComputeFromRecipes();
			if (num2 > 0)
			{
				ManualLogSource log2 = instance.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"RecipePricing: рассчитано {num2} новых предметов по рецептам. Всего в реестре: {instance.Registry.Count}");
				}
			}
		}

		private static void Patch(Harmony harmony, ManualLogSource log, Type type, string methodName)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				log.LogWarning((object)("Harmony: " + type.Name + "." + methodName + " не найден — часть моментов сохранения может не покрываться снимком данных."));
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnGameSave", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				log.LogInfo((object)("Harmony: снимок данных привязан к " + type.Name + "." + methodName));
			}
		}

		private static void PatchSavePrefix(Harmony harmony, ManualLogSource log)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "Save", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				log.LogWarning((object)"Harmony: Player.Save не найден — таймеры не будут попадать в сейв.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Patches), "OnGameSave", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			log.LogInfo((object)"Harmony: снимок данных привязан к Player.Save (prefix)");
		}

		private static void OnGameSave()
		{
			Plugin.Instance?.Data?.WriteSnapshot();
		}
	}
	[BepInPlugin("equivalentexchange", "EquivalentExchange", "0.39.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "equivalentexchange";

		public const string PluginName = "EquivalentExchange";

		public const string PluginVersion = "0.39.0";

		private static Plugin _instance;

		private Harmony _harmony;

		private ConfigEntry<long> _legacyBalance;

		private ConfigEntry<float> _pieceRepairPerHealth;

		private ConfigEntry<float> _buyPriceMultiplier;

		private ConfigEntry<float> _sellPriceMultiplier;

		private ConfigEntry<bool> _allowDirectBuy;

		private ConfigEntry<int> _coinEmcValue;

		private float _nextLanguageCheck;

		private readonly HashSet<string> _customValueNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static Plugin Instance => _instance;

		internal ManualLogSource Log => ((BaseUnityPlugin)this).Logger;

		public EmcRegistry Registry { get; private set; }

		public PlayerEmcData Data { get; private set; }

		public long EmcBalance => Data.Balance;

		public bool AllowDirectBuy => _allowDirectBuy != null && _allowDirectBuy.Value;

		public float PieceRepairEmcPerHealth => _pieceRepairPerHealth.Value;

		public double BuyPriceMultiplier => (_buyPriceMultiplier != null) ? Math.Max(1.0, _buyPriceMultiplier.Value) : 1.0;

		public double SellPriceMultiplier => (_sellPriceMultiplier != null) ? Math.Min(1.0, Math.Max(0.0, _sellPriceMultiplier.Value)) : 1.0;

		public int CoinEmcValue => (_coinEmcValue == null) ? 1 : Math.Max(1, _coinEmcValue.Value);

		public bool HasCustomValue(string prefabName)
		{
			return !string.IsNullOrEmpty(prefabName) && _customValueNames.Contains(prefabName);
		}

		public void AddEmc(long amount)
		{
			Data.AddEmc(amount);
		}

		public bool TrySpendEmc(long amount, out long remaining)
		{
			return Data.TrySpendEmc(amount, out remaining);
		}

		private void Awake()
		{
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			_instance = this;
			Loc.Init(ResolveGameLanguage);
			Loc.LanguageChanged += OnLanguageChanged;
			Registry = new EmcRegistry();
			Registry.RegisterDefaults();
			_legacyBalance = ((BaseUnityPlugin)this).Config.Bind<long>("Economy", "EmcBalance", 0L, "Легаси (v0.2.x): баланс EMC из конфига. При первом входе персонажа переносится в его данные и обнуляется.");
			_pieceRepairPerHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Economy", "PieceRepairEmcPerHealth", 0.02f, "Запасная цена ремонта строений, если EMC-стоимость постройки неизвестна (не оценён хотя бы один ресурс): недостающая прочность × это значение, минимум 1 EMC. Основной расчёт — потерянная доля EMC-стоимости постройки.");
			_buyPriceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Economy", "BuyPriceMultiplier", 1f, "Наценка ee_buy относительно реестровой цены. 1 — курс 1:1; >1 — покупка дороже.");
			_sellPriceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Economy", "SellPriceMultiplier", 1f, "Доля реестровой цены для ee_sell и ячейки. 1 — курс 1:1; <1 — продажа дешевле.");
			_allowDirectBuy = ((BaseUnityPlugin)this).Config.Bind<bool>("Economy", "AllowDirectBuy", false, "Разрешить ee_buy (прямая покупка предметов за EMC). По умолчанию выключено: EMC тратится только на крафт, постройку, ремонт и порталы.");
			_coinEmcValue = ((BaseUnityPlugin)this).Config.Bind<int>("Economy", "CoinEmcValue", 1, "Сколько EMC стоит 1 монета. Ценности (янтарь, жемчуг, рубины, ожерелья, самоцветы и новые ценности 1.0) оцениваются как торговая стоимость × это значение.");
			PortalEmcConfig.Bind(((BaseUnityPlugin)this).Config);
			MobLootConfig.Bind(((BaseUnityPlugin)this).Config);
			Data = new PlayerEmcData(_legacyBalance, Log);
			ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("Economy", "CustomValues", string.Empty, "Дополнительные предметы в формате Имя=EMC, через ';' (например: Resin=8;BoneFragments=16).");
			ApplyCustomValues(val.Value);
			RegisterCommands();
			_harmony = new Harmony("equivalentexchange");
			Patches.Apply(_harmony, Log);
			Log.LogInfo((object)string.Format("{0} {1} загружен. Предметов в реестре EMC: {2}", "EquivalentExchange", "0.39.0", Registry.Count));
		}

		private void Update()
		{
			if (Time.unscaledTime >= _nextLanguageCheck)
			{
				_nextLanguageCheck = Time.unscaledTime + 5f;
				Loc.Refresh();
			}
			Data.TrackPlayer(Player.m_localPlayer);
			EmcInventoryUi.Tick();
			EmcPassiveCycle.Tick();
		}

		private static string ResolveGameLanguage()
		{
			try
			{
				Localization instance = Localization.instance;
				if (instance != null)
				{
					return instance.GetSelectedLanguage() ?? "English";
				}
			}
			catch
			{
			}
			try
			{
				return PlatformPrefs.GetString("language", "English") ?? "English";
			}
			catch
			{
			}
			return "English";
		}

		private static void OnLanguageChanged()
		{
			CommandTexts.ApplyLanguage();
			EmcInventoryUi.OnLanguageChanged();
		}

		private void ApplyCustomValues(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return;
			}
			string[] array = raw.Split(new char[1] { ';' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { '=' });
				if (array2.Length != 2 || string.IsNullOrWhiteSpace(array2[0]) || !long.TryParse(array2[1].Trim(), out var result))
				{
					Log.LogWarning((object)("Не удалось разобрать запись CustomValues: '" + text + "'"));
					continue;
				}
				string text2 = array2[0].Trim();
				try
				{
					Registry.Register(text2, result);
					_customValueNames.Add(text2);
				}
				catch (ArgumentException ex)
				{
					Log.LogWarning((object)("CustomValues '" + text + "': " + ex.Message));
				}
			}
		}

		private void RegisterCommands()
		{
			ValueCommand.Register();
			BalanceCommand.Register();
			AddEmcCommand.Register();
			SpendEmcCommand.Register();
			SellCommand.Register();
			BuyCommand.Register();
			InventoryCommand.Register();
			MissingCommand.Register();
			TrinketsCommand.Register();
		}
	}
}
namespace EquivalentExchange.World
{
	public static class TombstonePinCleanup
	{
		private const float PinRadius = 10f;

		private static FieldInfo _containerField;

		private static FieldInfo _pinsField;

		public static void OnTombstoneUpdateDespawnPrefix(TombStone __instance)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Minimap instance = Minimap.instance;
				if (!((Object)(object)instance == (Object)null) && IsEmptied(__instance))
				{
					RemoveDeathPinNear(instance, ((Component)__instance).transform.position);
				}
			}
			catch (Exception ex)
			{
				Plugin instance2 = Plugin.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogWarning((object)("Метка смерти: " + ex.Message));
				}
			}
		}

		private static bool IsEmptied(TombStone tombstone)
		{
			if ((Object)(object)tombstone == (Object)null)
			{
				return false;
			}
			if (_containerField == null)
			{
				_containerField = AccessTools.Field(typeof(TombStone), "m_container");
			}
			object? obj = _containerField?.GetValue(tombstone);
			Container val = (Container)((obj is Container) ? obj : null);
			return val != null && !val.IsInUse() && val.GetInventory().NrOfItems() <= 0;
		}

		private static void RemoveDeathPinNear(Minimap minimap, Vector3 position)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Invalid comparison between Unknown and I4
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (_pinsField == null)
			{
				_pinsField = AccessTools.Field(typeof(Minimap), "m_pins");
			}
			if (!(_pinsField?.GetValue(minimap) is List<PinData> list))
			{
				return;
			}
			PinData val = null;
			float num = 10f;
			foreach (PinData item in list)
			{
				if (item != null && (int)item.m_type == 4)
				{
					float num2 = Utils.DistanceXZ(position, item.m_pos);
					if (!(num2 >= num))
					{
						num = num2;
						val = item;
					}
				}
			}
			if (val != null)
			{
				minimap.RemovePin(val);
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					instance.Log.LogInfo((object)"Метка смерти убрана с карты: надгробие опустошено.");
				}
			}
		}
	}
}
namespace EquivalentExchange.Portals
{
	public static class PortalCostCalculator
	{
		public static long Calculate(float distance, float weight)
		{
			return PortalCost.Calculate(distance, weight, PortalEmcConfig.BaseCost, PortalEmcConfig.CostPerMeter, PortalEmcConfig.WeightFactor, PortalEmcConfig.MinimumCost);
		}

		public static float GetCarriedWeight(Player player)
		{
			Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return 0f;
			}
			float num = 0f;
			foreach (ItemData allItem in val.GetAllItems())
			{
				if (allItem != null)
				{
					num += allItem.GetWeight(-1);
				}
			}
			return num + EmcCell.TotalWeight;
		}
	}
	public static class PortalDistance
	{
		public static bool TryGetDestination(TeleportWorld portal, out Vector3 position)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			if ((Object)(object)portal == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			ZDOID connectionZDOID = zDO.GetConnectionZDOID((ConnectionType)1);
			if (connectionZDOID == ZDOID.None)
			{
				return false;
			}
			ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(connectionZDOID) : null);
			if (val == null)
			{
				return false;
			}
			position = val.GetPosition();
			return true;
		}

		public static float Between(Vector3 from, Vector3 to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(from, to);
		}
	}
	public static class PortalEmcConfig
	{
		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<float> _baseCost;

		private static ConfigEntry<float> _costPerMeter;

		private static ConfigEntry<float> _weightFactor;

		private static ConfigEntry<long> _minimumCost;

		public static bool Enabled => _enabled != null && _enabled.Value;

		public static float BaseCost => (_baseCost != null) ? Math.Max(0f, _baseCost.Value) : 10f;

		public static float CostPerMeter => (_costPerMeter != null) ? Math.Max(0f, _costPerMeter.Value) : 0.05f;

		public static float WeightFactor => (_weightFactor != null) ? Math.Max(0f, _weightFactor.Value) : 0.01f;

		public static long MinimumCost => (_minimumCost != null) ? Math.Max(0L, _minimumCost.Value) : 10;

		public static void Bind(ConfigFile config)
		{
			_enabled = config.Bind<bool>("Portals", "Enabled", true, "Плата за переход через портал: цена показывается в подсказке и списывается перед телепортацией.");
			_baseCost = config.Bind<float>("Portals", "BaseCost", 10f, "Базовая стоимость перехода, EMC.");
			_costPerMeter = config.Bind<float>("Portals", "CostPerMeter", 0.05f, "Стоимость одного метра расстояния между порталами, EMC.");
			_weightFactor = config.Bind<float>("Portals", "WeightFactor", 0.01f, "Влияние веса снаряжения: множитель цены = 1 + вес × значение. Вес — весь инвентарь игрока, включая надетую экипировку.");
			_minimumCost = config.Bind<long>("Portals", "MinimumCost", 10L, "Минимальная стоимость перехода, EMC.");
		}
	}
	public static class PortalHoverPatch
	{
		public static void OnGetHoverTextPostfix(TeleportWorld __instance, ref string __result)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			if (!PortalEmcConfig.Enabled)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)32)))
			{
				return;
			}
			Plugin instance = Plugin.Instance;
			if (instance != null && instance.Data?.IsAvailable == true && PortalDistance.TryGetDestination(__instance, out var position))
			{
				float num = PortalDistance.Between(((Component)__instance).transform.position, position);
				float carriedWeight = PortalCostCalculator.GetCarriedWeight(localPlayer);
				long num2 = PortalCostCalculator.Calculate(num, carriedWeight);
				long emcBalance = instance.EmcBalance;
				string text = __instance.GetText();
				StringBuilder stringBuilder = new StringBuilder(__result);
				stringBuilder.Append('\n').Append(Loc.Tr("Пункт назначения:", "Destination:")).Append(' ')
					.Append(string.IsNullOrEmpty(text) ? "—" : text);
				stringBuilder.Append('\n').Append(Loc.Fmt($"Расстояние: {num:0} м", $"Distance: {num:0} m"));
				stringBuilder.Append('\n').Append(Loc.Fmt($"Вес с ячейкой: {carriedWeight:0.#}", $"Weight incl. cell: {carriedWeight:0.#}"));
				stringBuilder.Append('\n').Append(Loc.Fmt($"Стоимость перемещения: {num2} EMC", $"Travel cost: {num2} EMC"));
				if (emcBalance < num2)
				{
					stringBuilder.Append('\n').Append(Loc.Fmt($"<color=#ff8080>Недостаточно EMC! Ваш баланс: {emcBalance} EMC</color>", $"<color=#ff8080>Not enough EMC! Your balance: {emcBalance} EMC</color>"));
				}
				__result = stringBuilder.ToString();
			}
		}
	}
	public static class PortalTeleportPatch
	{
		private sealed class PendingTeleport
		{
			public TeleportWorld Portal;

			public Player Player;

			public Vector3 Destination;

			public long Cost;

			public long BalanceAfter;

			public bool Charged;

			public bool Settled;
		}

		private static PendingTeleport _pending;

		public static bool OnPortalTeleportPrefix(TeleportWorld __instance, Player player)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			if (!PortalEmcConfig.Enabled)
			{
				return true;
			}
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (!PortalDistance.TryGetDestination(__instance, out var position))
			{
				return true;
			}
			Plugin instance = Plugin.Instance;
			if (instance == null || instance.Data?.IsAvailable != true)
			{
				return true;
			}
			float distance = PortalDistance.Between(((Component)__instance).transform.position, position);
			float carriedWeight = PortalCostCalculator.GetCarriedWeight(player);
			long num = PortalCostCalculator.Calculate(distance, carriedWeight);
			if (num <= 0)
			{
				return true;
			}
			if (instance.EmcBalance < num)
			{
				Message(Loc.Fmt($"Портал: нужно {num} EMC — недостаточно (баланс {instance.EmcBalance})", $"Portal: {num} EMC required — not enough (balance {instance.EmcBalance})"));
				return false;
			}
			_pending = new PendingTeleport
			{
				Portal = __instance,
				Player = player,
				Destination = position,
				Cost = num
			};
			return true;
		}

		public static bool OnPlayerTeleportToPrefix(Player __instance, bool distantTeleport)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			PendingTeleport pending = _pending;
			if (pending == null || (Object)(object)pending.Player != (Object)(object)__instance || !distantTeleport)
			{
				return true;
			}
			Plugin instance = Plugin.Instance;
			if (instance == null || instance.Data?.IsAvailable != true)
			{
				pending.Settled = true;
				return true;
			}
			float distance = PortalDistance.Between(((Component)pending.Portal).transform.position, pending.Destination);
			float carriedWeight = PortalCostCalculator.GetCarriedWeight(__instance);
			pending.Cost = PortalCostCalculator.Calculate(distance, carriedWeight);
			if (pending.Cost <= 0)
			{
				pending.Settled = true;
				return true;
			}
			if (instance.EmcBalance < pending.Cost)
			{
				Message(Loc.Fmt($"Портал: нужно {pending.Cost} EMC — недостаточно (баланс {instance.EmcBalance})", $"Portal: {pending.Cost} EMC required — not enough (balance {instance.EmcBalance})"));
				pending.Settled = true;
				return false;
			}
			if (!instance.TrySpendEmc(pending.Cost, out var remaining))
			{
				Message(Loc.Fmt($"Портал: не удалось списать {pending.Cost} EMC", $"Portal: failed to charge {pending.Cost} EMC"));
				pending.Settled = true;
				return false;
			}
			pending.Charged = true;
			pending.BalanceAfter = remaining;
			return true;
		}

		public static void OnPlayerTeleportToPostfix(Player __instance, bool __result)
		{
			PendingTeleport pending = _pending;
			if (pending == null || (Object)(object)pending.Player != (Object)(object)__instance || pending.Settled)
			{
				return;
			}
			pending.Settled = true;
			if (pending.Charged)
			{
				if (__result)
				{
					Message(Loc.Fmt($"Портал: −{pending.Cost} EMC (баланс {pending.BalanceAfter})", $"Portal: −{pending.Cost} EMC (balance {pending.BalanceAfter})"));
					return;
				}
				if (!((Character)__instance).IsOwner())
				{
					Message(Loc.Fmt($"Портал: −{pending.Cost} EMC (через RPC, баланс {pending.BalanceAfter})", $"Portal: −{pending.Cost} EMC (via RPC, balance {pending.BalanceAfter})"));
					return;
				}
				Plugin.Instance?.AddEmc(pending.Cost);
				Message(Loc.Fmt($"Портал: телепорт не начался — возвращено {pending.Cost} EMC", $"Portal: teleport did not start — {pending.Cost} EMC refunded"));
			}
		}

		public static void OnPortalTeleportFinalizer(TeleportWorld __instance)
		{
			PendingTeleport pending = _pending;
			if (pending != null && !((Object)(object)pending.Portal != (Object)(object)__instance))
			{
				_pending = null;
				if (pending.Charged && !pending.Settled)
				{
					Plugin.Instance?.AddEmc(pending.Cost);
					Message(Loc.Fmt($"Портал: телепорт не выполнен — возвращено {pending.Cost} EMC", $"Portal: teleport failed — {pending.Cost} EMC refunded"));
				}
			}
		}

		private static void Message(string text)
		{
			try
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
				}
			}
			catch
			{
			}
			Plugin instance2 = Plugin.Instance;
			if (instance2 != null)
			{
				instance2.Log.LogInfo((object)text);
			}
		}
	}
}
namespace EquivalentExchange.InventoryUi
{
	public static class EmcCell
	{
		public enum CellBlock
		{
			Feed,
			Repair,
			Convert
		}

		private const float ConvertInterval = 30f;

		private const float RepairInterval = 60f;

		private const int RepairCostPerUnit = 1;

		private const float RepairDurabilityPerCycle = 1f;

		private const float DamagedEpsilon = 0.01f;

		private static readonly Inventory[] _blocks = (Inventory[])(object)new Inventory[3];

		private static Player _owner;

		private static float _nextConvert;

		private static float _nextRepair;

		private static bool _restorePending;

		private static float _nextRestoreAttempt;

		private static bool _syncPaused;

		private static bool _enforcing;

		private static int _lastSyncedStacks = -1;

		private static MethodInfo _inventoryChanged;

		private static bool _addItemAtMissingLogged;

		private static readonly MethodInfo AddItemAtMethod = AccessTools.Method(typeof(Inventory), "AddItem", new Type[5]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		}, (Type[])null);

		private static readonly MethodInfo AddItemAtPosMethod = AccessTools.Method(typeof(Inventory), "AddItem", new Type[2]
		{
			typeof(ItemData),
			typeof(Vector2i)
		}, (Type[])null);

		public static Inventory FeedInventory => GetInventory(CellBlock.Feed);

		public static Inventory RepairInventory => GetInventory(CellBlock.Repair);

		public static Inventory ConvertInventory => GetInventory(CellBlock.Convert);

		public static float TotalWeight
		{
			get
			{
				float num = 0f;
				foreach (CellBlock value in Enum.GetValues(typeof(CellBlock)))
				{
					num += GetInventory(value).GetTotalWeight();
				}
				return num;
			}
		}

		public static Inventory GetInventory(CellBlock block)
		{
			return _blocks[(int)block] ?? (_blocks[(int)block] = CreateBlockInventory(block));
		}

		public static CellBlock? BlockOf(Inventory inv)
		{
			int num = Array.IndexOf(_blocks, inv);
			return (num >= 0) ? new CellBlock?((CellBlock)num) : ((CellBlock?)null);
		}

		public static string BlockName(CellBlock block)
		{
			return block switch
			{
				CellBlock.Feed => Loc.Tr("Кормление", "Feed"), 
				CellBlock.Repair => Loc.Tr("Ремонт", "Repair"), 
				_ => Loc.Tr("Извлечение EMC", "EMC Extraction"), 
			};
		}

		public static string BlockHint(CellBlock block)
		{
			return block switch
			{
				CellBlock.Feed => Loc.Tr("Раз в минуту съедает одну единицу еды из блока за 1 EMC.", "Eats one food item from the block once a minute for 1 EMC."), 
				CellBlock.Repair => Loc.Tr("Раз в минуту чинит снаряжение из блока и всё надетое: +1 прочность за 1 EMC.", "Once a minute repairs block gear and everything equipped: +1 durability for 1 EMC."), 
				_ => Loc.Tr("Раз в 30 секунд извлекает EMC из одного предмета блока. Работает и при закрытой панели.", "Extracts EMC from one item in the block every 30 seconds. Works while the panel is closed."), 
			};
		}

		private static Inventory CreateBlockInventory(CellBlock block)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			Inventory val = new Inventory(BlockName(block), (Sprite)null, 2, 2);
			FieldInfo fieldInfo = AccessTools.Field(typeof(Inventory), "m_onChanged");
			Action a = fieldInfo?.GetValue(val) as Action;
			fieldInfo?.SetValue(val, (Action)Delegate.Combine(a, (Action)delegate
			{
				OnBlockInventoryChanged(block);
			}));
			return val;
		}

		public static bool IsFeedable(ItemData item)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			return item?.m_shared != null && (int)item.m_shared.m_itemType == 2 && (item.m_shared.m_food > 0f || item.m_shared.m_foodStamina > 0f || item.m_shared.m_foodEitr > 0f);
		}

		public static bool IsRepairable(ItemData item)
		{
			return item?.m_shared != null && item.m_shared.m_useDurability && item.GetMaxDurability() > 0f;
		}

		public static bool IsConvertible(ItemData item)
		{
			if (item?.m_shared == null || item.m_shared.m_questItem)
			{
				return false;
			}
			EmcRegistry emcRegistry = Plugin.Instance?.Registry;
			long emc;
			return emcRegistry != null && emcRegistry.TryGetValue(TransmutationService.PrefabName(item), out emc) && PricingPolicy.IsTradable(emc);
		}

		public static bool Fits(CellBlock block, ItemData item)
		{
			return block switch
			{
				CellBlock.Feed => IsFeedable(item), 
				CellBlock.Repair => IsRepairable(item), 
				_ => IsConvertible(item), 
			};
		}

		public static string DescribeRejection(CellBlock block, ItemData item)
		{
			string text = TransmutationService.PrefabName(item);
			if (item?.m_shared != null && item.m_shared.m_questItem)
			{
				return Loc.Fmt(text + " — квестовый предмет, вложение запрещено", text + " is a quest item — depositing is not allowed");
			}
			return block switch
			{
				CellBlock.Feed => Loc.Fmt(text + ": в блок «Кормление» кладётся только еда", text + ": the Feed block accepts only food"), 
				CellBlock.Repair => Loc.Fmt(text + ": в блок «Ремонт» — только снаряжение с прочностью", text + ": the Repair block accepts only gear with durability"), 
				_ => Loc.Fmt(text + " не имеет EMC и не может быть вложен", text + " has no EMC value and cannot be deposited"), 
			};
		}

		public static bool HasFeedItems()
		{
			foreach (ItemData allItem in FeedInventory.GetAllItems())
			{
				if (IsFeedable(allItem))
				{
					return true;
				}
			}
			return false;
		}

		public static bool MoveItemFromPlayer(Inventory playerInv, CellBlock block, ItemData item, bool announce = true)
		{
			if (playerInv == null || item == null)
			{
				return false;
			}
			if (!Fits(block, item))
			{
				if (announce)
				{
					Message(DescribeRejection(block, item));
				}
				return false;
			}
			Inventory inventory = GetInventory(block);
			string b = TransmutationService.PrefabName(item);
			int num = Math.Max(1, item.m_shared.m_maxStackSize);
			bool flag = false;
			foreach (ItemData item2 in inventory.GetAllItems().ToList())
			{
				if (item.m_stack <= 0)
				{
					break;
				}
				if (item2.m_stack < num && string.Equals(TransmutationService.PrefabName(item2), b, StringComparison.OrdinalIgnoreCase) && item2.m_quality == item.m_quality && item2.m_worldLevel == item.m_worldLevel)
				{
					int num2 = Math.Min(num - item2.m_stack, item.m_stack);
					if (!playerInv.RemoveItem(item, num2))
					{
						break;
					}
					item2.m_stack += num2;
					flag = true;
				}
			}
			if (item.m_stack > 0)
			{
				bool flag2 = false;
				for (int i = 0; i < 2; i++)
				{
					if (flag2)
					{
						break;
					}
					for (int j = 0; j < 2; j++)
					{
						if (flag2)
						{
							break;
						}
						if (inventory.GetItemAt(j, i) == null)
						{
							int stack = item.m_stack;
							if (!playerInv.RemoveItem(item, stack))
							{
								break;
							}
							item.m_stack = stack;
							if (AddItemAtSlot(inventory, item, j, i) || inventory.AddItem(item))
							{
								flag2 = true;
								flag = true;
							}
							else
							{
								playerInv.AddItem(item);
							}
						}
					}
				}
				if (!flag2 && !flag && announce)
				{
					Message(Loc.Fmt("В блоке «" + BlockName(block) + "» нет свободного места.", "No free space in the \"" + BlockName(block) + "\" block."));
				}
			}
			if (flag)
			{
				SyncCellToData();
			}
			return flag;
		}

		private static void ReturnToPlayer(CellBlock block, ItemData item)
		{
			Player localPlayer = Player.m_localPlayer;
			Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null);
			if (val != null)
			{
				val.MoveItemToThis(GetInventory(block), item);
			}
		}

		private static void EnforceBlockRules(CellBlock block)
		{
			Player localPlayer = Player.m_localPlayer;
			Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null);
			if (val == null)
			{
				return;
			}
			Inventory inventory = GetInventory(block);
			foreach (ItemData item in inventory.GetAllItems().ToList())
			{
				if (!Fits(block, item))
				{
					string text = TransmutationService.PrefabName(item);
					val.MoveItemToThis(inventory, item);
					if (!inventory.ContainsItem(item))
					{
						Message(Loc.Fmt(text + ": предмет не подходит блоку «" + BlockName(block) + "» — возвращён", text + ": item does not belong to the \"" + BlockName(block) + "\" block — returned"));
					}
				}
			}
		}

		private static void OnBlockInventoryChanged(CellBlock block)
		{
			if (_syncPaused)
			{
				return;
			}
			if (!_enforcing && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				_enforcing = true;
				_syncPaused = true;
				try
				{
					EnforceBlockRules(block);
				}
				finally
				{
					_syncPaused = false;
					_enforcing = false;
				}
			}
			SyncCellToData();
		}

		private static void ResetTimers()
		{
			_nextConvert = Time.time + 30f;
			_nextRepair = Time.time + 60f;
		}

		public static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				return;
			}
			if ((Object)(object)_owner != (Object)(object)localPlayer)
			{
				if (Plugin.Instance?.Data?.IsAvailable != true)
				{
					return;
				}
				_owner = localPlayer;
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					instance.Log.LogInfo((object)"Ячейка: перезаход — восстановление содержимого из файла.");
				}
				_syncPaused = true;
				try
				{
					ClearAllBlocks();
				}
				finally
				{
					_syncPaused = false;
				}
				if (!ApplyPending(Plugin.Instance?.Data?.CellContents))
				{
					_restorePending = true;
					_nextRestoreAttempt = Time.time + 5f;
					Plugin instance2 = Plugin.Instance;
					if (instance2 != null)
					{
						instance2.Log.LogWarning((object)"Ячейка: восстановление отложено — данные сохранены.");
					}
					return;
				}
				_restorePending = false;
				EnforceAllBlocks();
				SyncCellToData();
				ResetTimers();
			}
			else if (_restorePending && Time.time >= _nextRestoreAttempt)
			{
				if (AllBlocksEmpty() && !ApplyPending(Plugin.Instance?.Data?.CellContents))
				{
					return;
				}
				_restorePending = false;
			}
			if (_restorePending)
			{
				return;
			}
			Plugin instance3 = Plugin.Instance;
			if (instance3?.Data == null || !instance3.Data.IsAvailable)
			{
				return;
			}
			float time = Time.time;
			if (time >= _nextRepair)
			{
				_nextRepair = time + 60f;
				RepairGear(instance3);
			}
			if (time < _nextConvert)
			{
				return;
			}
			_nextConvert = time + 30f;
			List<ItemData> list = ConvertInventory.GetAllItems().ToList();
			if (list.Count == 0)
			{
				return;
			}
			ItemData val = list[Random.Range(0, list.Count)];
			string text = TransmutationService.PrefabName(val);
			if (!instance3.Registry.TryGetValue(text, out var emc) || !PricingPolicy.IsTradable(emc) || val.m_shared.m_questItem)
			{
				ReturnToPlayer(CellBlock.Convert, val);
				return;
			}
			long num = ItemPricing.SellValue(val, instance3.Registry, instance3.SellPriceMultiplier);
			if (num <= 0)
			{
				ReturnToPlayer(CellBlock.Convert, val);
				return;
			}
			ConvertInventory.RemoveItem(val, 1);
			instance3.AddEmc(num);
			Message($"{text} → +{num} EMC");
		}

		private static bool AllBlocksEmpty()
		{
			foreach (CellBlock value in Enum.GetValues(typeof(CellBlock)))
			{
				if (GetInventory(value).GetAllItems().Count > 0)
				{
					return false;
				}
			}
			return true;
		}

		private static void ClearAllBlocks()
		{
			foreach (CellBlock value in Enum.GetValues(typeof(CellBlock)))
			{
				Inventory inventory = GetInventory(value);
				foreach (ItemData item in inventory.GetAllItems().ToList())
				{
					inventory.RemoveItem(item, item.m_stack);
				}
			}
		}

		private static void EnforceAllBlocks()
		{
			foreach (CellBlock value in Enum.GetValues(typeof(CellBlock)))
			{
				EnforceBlockRules(value);
			}
		}

		private static void RepairGear(Plugin plugin)
		{
			int num = 0;
			bool flag = false;
			long remaining;
			foreach (ItemData item in RepairInventory.GetAllItems().ToList())
			{
				if (!IsRepairable(item))
				{
					continue;
				}
				float maxDurability = item.GetMaxDurability();
				if (!(item.m_durability >= maxDurability - 0.01f))
				{
					if (!plugin.TrySpendEmc(1L, out remaining))
					{
						flag = true;
						break;
					}
					item.m_durability = Mathf.Min(maxDurability, item.m_durability + 1f);
					num++;
				}
			}
			if (num > 0)
			{
				SyncCellToData();
			}
			int num2 = 0;
			Player localPlayer = Player.m_localPlayer;
			Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null);
			if (!flag && val != null)
			{
				foreach (ItemData equippedItem in val.GetEquippedItems())
				{
					if (equippedItem?.m_shared == null || !equippedItem.m_shared.m_useDurability)
					{
						continue;
					}
					float maxDurability2 = equippedItem.GetMaxDurability();
					if (!(equippedItem.m_durability >= maxDurability2 - 0.01f))
					{
						if (!plugin.TrySpendEmc(1L, out remaining))
						{
							break;
						}
						equippedItem.m_durability = Mathf.Min(maxDurability2, equippedItem.m_durability + 1f);
						num2++;
					}
				}
				if (num2 > 0)
				{
					RefreshInventoryUi(val);
				}
			}
			int num3 = num + num2;
			if (num3 > 0)
			{
				Message(Loc.Fmt($"Ремонт: +1 прочность у {num3} предм. (−{num3} EMC)", $"Repair: +1 durability on {num3} item(s) (−{num3} EMC)"));
			}
		}

		private static void RefreshInventoryUi(Inventory inventory)
		{
			try
			{
				if (_inventoryChanged == null)
				{
					_inventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);
				}
				_inventoryChanged?.Invoke(inventory, new object[2] { false, false });
			}
			catch (Exception ex)
			{
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					instance.Log.LogWarning((object)("Не удалось обновить UI после ремонта: " + ex.Message));
				}
			}
		}

		private static bool ApplyPending(List<CellItem> pending)
		{
			if (pending == null || pending.Count == 0)
			{
				return true;
			}
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Plugin instance2 = Plugin.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogWarning((object)"Ячейка: ObjectDB ещё не готова — восстановление отложено.");
				}
				return false;
			}
			List<(CellItem, GameObject)> list = new List<(CellItem, GameObject)>();
			foreach (CellItem item in pending)
			{
				if (item == null || string.IsNullOrEmpty(item.Prefab) || item.Stack <= 0)
				{
					continue;
				}
				GameObject itemPrefab = instance.GetItemPrefab(item.Prefab);
				if ((Object)(object)itemPrefab == (Object)null)
				{
					Plugin instance3 = Plugin.Instance;
					if (instance3 != null)
					{
						instance3.Log.LogWarning((object)("Ячейка: префаб '" + item.Prefab + "' не найден — восстановление отложено, данные сохранены."));
					}
					return false;
				}
				list.Add((item, itemPrefab));
			}
			_syncPaused = true;
			bool flag = true;
			try
			{
				foreach (var entry in list)
				{
					ItemDrop component = entry.Item2.GetComponent<ItemDrop>();
					int maxStackSize = ((!((Object)(object)component != (Object)null)) ? 1 : Math.Max(1, component.m_itemData.m_shared.m_maxStackSize));
					int num = ItemGrant.AddAll(entry.Item1.Stack, maxStackSize, (int batch) => AddClone(entry.Item2, entry.Item1, batch));
					if (num != entry.Item1.Stack)
					{
						flag = false;
						break;
					}
				}
				if (!flag)
				{
					ClearAllBlocks();
				}
			}
			finally
			{
				_syncPaused = false;
			}
			if (!flag)
			{
				Plugin instance4 = Plugin.Instance;
				if (instance4 != null)
				{
					instance4.Log.LogWarning((object)"Ячейка: не удалось восстановить содержимое — данные сохранены.");
				}
				return false;
			}
			SyncCellToData();
			ResetTimers();
			Plugin instance5 = Plugin.Instance;
			if (instance5 != null)
			{
				instance5.Log.LogInfo((object)"Ячейка: содержимое восстановлено по блокам.");
			}
			return true;
		}

		private static bool AddClone(GameObject prefab, CellItem source, int stack)
		{
			ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			ItemData val2 = val.m_itemData.Clone();
			val2.m_dropPrefab = prefab;
			val2.m_stack = stack;
			val2.m_quality = Math.Max(1, source.Quality);
			val2.m_variant = Math.Max(0, source.Variant);
			float maxDurability = val2.GetMaxDurability();
			val2.m_durability = ((source.Durability < 0f) ? maxDurability : Mathf.Clamp(source.Durability, 0f, maxDurability));
			val2.m_worldLevel = source.WorldLevel;
			val2.m_crafterID = source.CrafterId;
			val2.m_crafterName = source.CrafterName ?? "";
			val2.m_pickedUp = source.PickedUp;
			val2.m_cheated = source.Cheated;
			val2.m_equipped = false;
			val2.m_customData = ((source.CustomData != null) ? new Dictionary<string, string>(source.CustomData) : new Dictionary<string, string>());
			CellLayout.Resolve(source.GridX, source.GridY, out var block, out var localX, out var localY);
			Inventory inventory = GetInventory((CellBlock)block);
			if (localX >= 0 && localY >= 0 && inventory.GetItemAt(localX, localY) == null && AddItemAtSlot(inventory, val2, localX, localY))
			{
				return true;
			}
			if (AddFirstFree(inventory, val2))
			{
				return true;
			}
			return block != 2 && AddFirstFree(ConvertInventory, val2);
		}

		private static bool AddFirstFree(Inventory inv, ItemData item)
		{
			for (int i = 0; i < 2; i++)
			{
				for (int j = 0; j < 2; j++)
				{
					if (inv.GetItemAt(j, i) == null && AddItemAtSlot(inv, item, j, i))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool AddItemAtSlot(Inventory inv, ItemData item, int x, int y)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if (AddItemAtMethod != null)
			{
				try
				{
					object obj = AddItemAtMethod.Invoke(inv, new object[5] { item, item.m_stack, x, y, true });
					bool flag = default(bool);
					int num;
					if (obj is bool)
					{
						flag = (bool)obj;
						num = 1;
					}
					else
					{
						num = 0;
					}
					return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				}
				catch (Exception ex)
				{
					Plugin instance = Plugin.Instance;
					if (instance != null)
					{
						instance.Log.LogWarning((object)$"Ячейка: не удалось положить предмет в слот {x},{y}: {ex.Message}");
					}
					return false;
				}
			}
			if (AddItemAtPosMethod != null)
			{
				try
				{
					object obj = AddItemAtPosMethod.Invoke(inv, new object[2]
					{
						item,
						(object)new Vector2i(x, y)
					});
					bool flag2 = default(bool);
					int num2;
					if (obj is bool)
					{
						flag2 = (bool)obj;
						num2 = 1;
					}
					else
					{
						num2 = 0;
					}
					return (byte)((uint)num2 & (flag2 ? 1u : 0u)) != 0;
				}
				catch (Exception ex2)
				{
					Plugin instance2 = Plugin.Instance;
					if (instance2 != null)
					{
						instance2.Log.LogWarning((object)$"Ячейка: не удалось положить предмет в слот {x},{y}: {ex2.Message}");
					}
					return false;
				}
			}
			if (!_addItemAtMissingLogged)
			{
				_addItemAtMissingLogged = true;
				Plugin instance3 = Plugin.Instance;
				if (instance3 != null)
				{
					instance3.Log.LogWarning((object)"Ячейка: позиционная укладка Inventory.AddItem не найдена — слоты заполняются автоматически.");
				}
			}
			return false;
		}

		private static void SyncCellToData()
		{
			if (_syncPaused)
			{
				return;
			}
			Plugin instance = Plugin.Instance;
			if (instance?.Data == null)
			{
				return;
			}
			List<CellItem> list = new List<CellItem>();
			foreach (CellBlock value in Enum.GetValues(typeof(CellBlock)))
			{
				int num = CellLayout.StartColumn((int)value);
				foreach (ItemData allItem in GetInventory(value).GetAllItems())
				{
					list.Add(new CellItem
					{
						Prefab = TransmutationService.PrefabName(allItem),
						Stack = allItem.m_stack,
						Quality = allItem.m_quality,
						Variant = allItem.m_variant,
						Durability = allItem.m_durability,
						WorldLevel = (byte)Math.Max(0, Math.Min(255, allItem.m_worldLevel)),
						CrafterId = allItem.m_crafterID,
						CrafterName = (allItem.m_crafterName ?? ""),
						PickedUp = allItem.m_pickedUp,
						Cheated = allItem.m_cheated,
						GridX = num + allItem.m_gridPos.x,
						GridY = allItem.m_gridPos.y,
						CustomData = ((allItem.m_customData != null) ? new Dictionary<string, string>(allItem.m_customData) : new Dictionary<string, string>())
					});
				}
			}
			instance.Data.CellContents = list;
			if (list.Count != _lastSyncedStacks)
			{
				_lastSyncedStacks = list.Count;
				Plugin instance2 = Plugin.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogInfo((object)$"Ячейка: содержимое синхронизировано ({list.Count} стеков).");
				}
			}
		}

		private static void Message(string text)
		{
			try
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
				}
			}
			catch
			{
			}
			Plugin instance2 = Plugin.Instance;
			if (instance2 != null)
			{
				instance2.Log.LogInfo((object)text);
			}
		}
	}
	public static class EmcInventoryUi
	{
		private sealed class CellTab
		{
			public EmcCell.CellBlock Block;

			public Button Button;

			public Image ButtonImage;

			public Color ButtonBaseColor = Color.white;

			public TMP_Text Label;

			public TMP_Text Badge;

			public GameObject Panel;

			public RectTransform SlideRt;

			public float SlideHeight;

			public InventoryGrid Grid;

			public float Anim;

			public bool TargetOpen;

			public bool LastActive;

			public int LastCount = -1;
		}

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

		private static bool _emcBuildActive;

		private static Recipe _paidRecipe;

		private static ItemData _paidUpgrade;

		private static int _paidMultiplier = 1;

		private static MethodInfo _haveRequirements;

		private static object _haveRequirementsMode;

		private static bool _haveRequirementsEnumMode;

		private static bool _haveRequirementsHasAmount;

		private static bool _haveRequirementsResolved;

		private static Button _emcCraftButton;

		private static TMP_Text _emcCraftLabel;

		private static TMP_Text _vanillaCraftLabel;

		private static RectTransform _vanillaCraftRt;

		private static RectTransform _emcCraftRt;

		private static Vector2 _craftOrigSize;

		private static Vector2 _craftOrigPos;

		private static float _craftOrigWidth;

		private static float _craftOrigPivotX;

		private static float _craftSplitVanillaWidth;

		private static bool _craftGeomCaptured;

		private static bool _craftSplitApplied;

		private static bool _craftUnderLayoutGroup;

		private const float CraftButtonGap = 6f;

		private const float CraftVanillaShare = 0.4f;

		private static float _emcLabelBaseFont = -1f;

		private static string _fittedText;

		private static float _fittedWidth;

		private static float _fittedSize;

		private static string _vanillaCraftAuthor = string.Empty;

		private static bool _emcCraftRequested;

		private static bool _emcCraftActive;

		private static long _prepaidCraftCost;

		private static bool _prepaidCraftConsumed;

		private static float _craftStoppedAt = -1f;

		private static readonly FieldInfo CraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");

		private static readonly FieldInfo CraftUpgradeItemField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem");

		private static readonly FieldInfo CraftTimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer");

		private static readonly FieldInfo MultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting");

		private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

		private static readonly PropertyInfo PairRecipeProperty = AccessTools.Property(AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair"), "Recipe");

		private static readonly PropertyInfo PairItemDataProperty = AccessTools.Property(AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair"), "ItemData");

		private static readonly FieldInfo TouchMultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_touchMultiCrafting");

		private static readonly FieldInfo TextMarginWidthField = AccessTools.Field(typeof(TMP_Text), "m_marginWidth");

		private static float _nextTextRefresh;

		private static readonly FieldInfo ElementsField = AccessTools.Field(typeof(InventoryGrid), "m_elements");

		private static readonly PropertyInfo ElementPositionProperty = AccessTools.Property(typeof(InventoryElement), "Position");

		private static readonly Dictionary<GameObject, (GameObject Root, TMP_Text Label, RectTransform Backing)> _emcLabels = new Dictionary<GameObject, (GameObject, TMP_Text, RectTransform)>();

		private static TMP_FontAsset _emcFont;

		private static bool _emcLabelDiagLogged;

		private static Sprite _pillSprite;

		private static bool _built;

		private static bool _iconApplied;

		private static GameObject _statBox;

		private static TMP_Text _statText;

		private static Image _statIconImage;

		private static Image _vanillaIconImage;

		private static RectTransform _playerPanel;

		private static bool _repositionPending;

		private const float TabHeight = 24f;

		private const float TabGap = 8f;

		private const float PanelPad = 14f;

		private const float PanelTopPad = 20f;

		private const float HandleTopMargin = 2f;

		private const float HandleBottomMargin = 12f;

		private const float OpenLift = 4f;

		private const float FrameOverhang = 10f;

		private const float OpenDuration = 0.25f;

		private const float CloseDuration = 0.2f;

		private const float ContainerGap = 12f;

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

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

		private static readonly CellTab[] _cellTabs = new CellTab[3];

		private static bool _cellTabsBuilt;

		private static int _activeCellTab = -1;

		private static readonly FieldInfo DragItemField = AccessTools.Field(typeof(InventoryGui), "m_dragItem");

		private static readonly FieldInfo CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");

		private static readonly Color FeedColor = new Color(0.7f, 0.95f, 0.6f);

		private static readonly Color RepairColor = new Color(1f, 0.72f, 0.42f);

		private static readonly Color ConvertColor = new Color(1f, 0.85f, 0.4f);

		private static float TabStripHeight => 48f;

		internal static bool AnyCellPanelOpen => _activeCellTab >= 0;

		internal static EmcCell.CellBlock? ActiveCellBlock => (_activeCellTab >= 0) ? new EmcCell.CellBlock?((EmcCell.CellBlock)_activeCellTab) : ((EmcCell.CellBlock?)null);

		public static bool ConsumeResourcesPrefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			if (__instance.NoCostCheat())
			{
				return true;
			}
			if (_emcCraftActive)
			{
				if (_prepaidCraftCost <= 0)
				{
					return true;
				}
				_prepaidCraftConsumed = true;
				return false;
			}
			if (!_emcBuildActive)
			{
				return true;
			}
			Plugin instance = Plugin.Instance;
			if (instance?.Registry == null || instance.Data == null || !instance.Data.IsAvailable)
			{
				return true;
			}
			long? num = ComputeCost(requirements, qualityLevel, multiplier, __instance.GetCurrentCraftingStation(), instance.Registry);
			if (!num.HasValue || num.Value <= 0 || instance.EmcBalance < num.Value)
			{
				return true;
			}
			instance.TrySpendEmc(num.Value, out var _);
			ShowMessage(Loc.Fmt($"Постройка за {num.Value} EMC — ресурсы сохранены", $"Built for {num.Value} EMC — resources preserved"));
			return false;
		}

		public static void UpdatePlacementPrefix(bool takeInput)
		{
			_emcBuildActive = takeInput;
		}

		public static void UpdatePlacementFinalizer()
		{
			_emcBuildActive = false;
		}

		public static void SetupPieceInfoPostfix(Hud __instance, Piece piece)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece == (Object)null)
			{
				return;
			}
			TMP_Text buildSelection = __instance.m_buildSelection;
			if ((Object)(object)buildSelection == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !localPlayer.NoCostCheat() && (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())))
			{
				long? pieceCost = GetPieceCost(piece);
				string text = ((!pieceCost.HasValue) ? buildSelection.text : ((Plugin.Instance.EmcBalance < pieceCost.Value) ? $"{buildSelection.text} <color=#FF6A6A>({pieceCost.Value} EMC)</color>" : $"{buildSelection.text} ({pieceCost.Value} EMC)"));
				if (buildSelection.text != text)
				{
					buildSelection.text = text;
				}
			}
		}

		internal static long? GetPieceCost(Piece piece)
		{
			Plugin instance = Plugin.Instance;
			if (instance?.Registry == null || instance.Data == null || !instance.Data.IsAvailable)
			{
				return null;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || piece.m_resources == null)
			{
				return null;
			}
			long? num = ComputeCost(piece.m_resources, 0, 1, localPlayer.GetCurrentCraftingStation(), instance.Registry);
			return (num.HasValue && num.Value > 0) ? num : ((long?)null);
		}

		private static void RememberPaidCraft(Recipe recipe, ItemData upgrade, int multiplier)
		{
			_paidRecipe = recipe;
			_paidUpgrade = upgrade;
			_paidMultiplier = Mathf.Max(1, multiplier);
		}

		private static void ForgetPaidCraft()
		{
			_paidRecipe = null;
			_paidUpgrade = null;
			_paidMultiplier = 1;
		}

		private static int GetStartedCraftMultiplier(InventoryGui gui)
		{
			return (MultiCraftingField?.GetValue(gui) as bool? != true) ? 1 : Mathf.Max(1, gui.m_multiCraftAmount);
		}

		private static bool HaveCraftResources(Player player, Recipe recipe, ItemData upgrade, int multiplier)
		{
			if ((Object)(object)player == (Object)null || recipe?.m_resources == null)
			{
				return false;
			}
			if (player.NoCostCheat())
			{
				return true;
			}
			int multiplier2 = Mathf.Max(1, multiplier);
			int qualityLevel = ((upgrade == null) ? 1 : (upgrade.m_quality + 1));
			if (upgrade == null && TryVanillaRequirementCheck(player, recipe, qualityLevel, multiplier2, out var result))
			{
				return result;
			}
			return CountIngredients(player, recipe, qualityLevel, multiplier2);
		}

		private static bool TryVanillaRequirementCheck(Player player, Recipe recipe, int qualityLevel, int multiplier, out bool result)
		{
			result = false;
			ResolveHaveRequirements();
			if (_haveRequirements == null)
			{
				return false;
			}
			try
			{
				object[] parameters = (_haveRequirementsEnumMode ? new object[3] { recipe, _haveRequirementsMode, multiplier } : ((!_haveRequirementsHasAmount) ? new object[3] { recipe, false, qualityLevel } : new object[4] { recipe, false, qualityLevel, multiplier }));
				object obj = _haveRequirements.Invoke(player, parameters);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				result = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				return true;
			}
			catch (Exception ex)
			{
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					instance.Log.LogWarning((object)("Крафт за EMC: HaveRequirements бросил исключение (" + ex.Message + ") — считаем по инвентарю."));
				}
				_haveRequirements = null;
				return false;
			}
		}

		private static void ResolveHaveRequirements()
		{
			if (_haveRequirementsResolved)
			{
				return;
			}
			_haveRequirementsResolved = true;
			MethodInfo[] methods = typeof(Player).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name != "HaveRequirements" || methodInfo.ReturnType != typeof(bool))
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length < 3 || parameters.Length > 4 || parameters[0].ParameterType != typeof(Recipe))
				{
					continue;
				}
				if (parameters[1].ParameterType == typeof(bool) && parameters[2].ParameterType == typeof(int))
				{
					if (parameters.Length == 3)
					{
						_haveRequirementsHasAmount = false;
					}
					else
					{
						if (!(parameters[3].ParameterType == typeof(int)))
						{
							continue;
						}
						_haveRequirementsHasAmount = true;
					}
					_haveRequirementsEnumMode = false;
					_haveRequirements = methodInfo;
					Plugin instance = Plugin.Instance;
					if (instance != null)
					{
						instance.Log.LogInfo((object)"Крафт за EMC: проверка ресурсов через Player.HaveRequirements(Recipe, bool discover, ...).");
					}
					return;
				}
				if (parameters.Length != 3 || !parameters[1].ParameterType.IsEnum || !(parameters[2].ParameterType == typeof(int)))
				{
					continue;
				}
				Type parameterType = parameters[1].ParameterType;
				string[] names = Enum.GetNames(parameterType);
				string value = Array.Find(names, (string n) => n.IndexOf("CanCraft", StringComparison.OrdinalIgnoreCase) >= 0) ?? names[0];
				_haveRequirementsMode = Enum.Parse(parameterType, value);
				_haveRequirementsEnumMode = true;
				_haveRequirementsHasAmount = false;
				_haveRequirements = methodInfo;
				Plugin instance2 = Plugin.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogInfo((object)$"Крафт за EMC: проверка ресурсов через Player.HaveRequirements({parameterType.Name}={_haveRequirementsMode}).");
				}
				return;
			}
			Plugin instance3 = Plugin.Instance;
			if (instance3 != null)
			{
				instance3.Log.LogWarning((object)"Крафт за EMC: Player.HaveRequirements не найден — наличие ингредиентов считается по инвентарю.");
			}
		}

		private static bool CountIngredients(Player player, Recipe recipe, int qualityLevel, int multiplier)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool flag = (Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader;
			List<CraftStock> list = new List<CraftStock>();
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val?.m_resItem == (Object)null) && (recipe.m_requireOnlyOneIngredient || ((!((Object)(object)currentCraftingStation != (Object)null) || flag == val.m_upgraderResource) && (!((Object)(object)currentCraftingStation == (Object)null) || !val.m_upgraderResource))))
				{
					int amount = val.GetAmount(qualityLevel);
					if (amount > 0)
					{
						string text = val.m_resItem.m_itemData?.m_shared?.m_name;
						int num = ((!string.IsNullOrEmpty(text)) ? inventory.CountItems(text, -1, true) : 0);
						list.Add(new CraftStock((long)amount * (long)multiplier, num));
					}
				}
			}
			return recipe.m_requireOnlyOneIngredient ? CraftResourceGate.HasAny(list) : CraftResourceGate.HasAll(list);
		}

		private static void GuardPrepaidCraft()
		{
			if (_prepaidCraftCost <= 0)
			{
				return;
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null)
			{
				RefundPrepaidCraft();
				return;
			}
			if (!IsCraftRunning(instance))
			{
				if (_craftStoppedAt < 0f)
				{
					_craftStoppedAt = Time.time;
				}
				else if (Time.time - _craftStoppedAt >= 0.5f)
				{
					RefundPrepaidCraft();
				}
				return;
			}
			_craftStoppedAt = -1f;
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)_paidRecipe == (Object)null) && !HaveCraftResources(localPlayer, _paidRecipe, _paidUpgrade, _paidMultiplier))
			{
				RefundPrepaidCraft();
				AbortCraft(instance, Loc.Tr("Крафт за EMC отменён: ингредиентов больше нет в инвентаре", "EMC craft cancelled: ingredients are no longer in the inventory"));
			}
		}

		private static void EnsureEmcCraftButton(InventoryGui gui)
		{
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			if ((Object)(object)_emcCraftButton != (Object)null)
			{
				return;
			}
			Button craftButton = gui.m_craftButton;
			if ((Object)(object)craftButton == (Object)null)
			{
				return;
			}
			try
			{
				Transform parent = ((Component)craftButton).transform.parent;
				GameObject val = Object.Instantiate<GameObject>(((Component)craftButton).gameObject, parent);
				((Object)val).name = "EEV_CraftEmcButton";
				val.transform.SetSiblingIndex(((Component)craftButton).transform.GetSiblingIndex() + 1);
				Button component = val.GetComponent<Button>();
				if ((Object)(object)component == (Object)null)
				{
					Object.Destroy((Object)(object)val);
					return;
				}
				Component component2 = val.GetComponent("UITooltip");
				if ((Object)(object)component2 != (Object)null)
				{
					Object.Destroy((Object)(object)component2);
				}
				try
				{
					object obj = AccessTools.Field(typeof(UnityEventBase), "m_PersistentCalls")?.GetValue(component.onClick);
					obj?.GetType().GetMethod("Clear", Type.EmptyTypes)?.Invoke(obj, null);
				}
				catch
				{
				}
				((UnityEventBase)component.onClick).RemoveAllListeners();
				ButtonClickedEvent onClick = component.onClick;
				object obj3 = <>O.<0>__OnEmcCraftButtonClicked;
				if (obj3 == null)
				{
					UnityAction val2 = OnEmcCraftButtonClicked;
					<>O.<0>__OnEmcCraftButtonClicked = val2;
					obj3 = (object)val2;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj3);
				Navigation navigation = ((Selectable)component).navigation;
				((Navigation)(ref navigation)).mode = (Mode)3;
				((Selectable)component).navigation = navigation;
				_emcCraftButton = component;
				_emcCraftLabel = val.GetComponentInChildren<TMP_Text>(true);
				_vanillaCraftLabel = ((Component)craftButton).GetComponentInChildren<TMP_Text>(true);
				_vanillaCraftRt = ((Component)craftButton).GetComponent<RectTransform>();
				_emcCraftRt = val.GetComponent<RectTransform>();
				_craftUnderLayoutGroup = (Object)(object)parent != (Object)null && (Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null;
				_craftGeomCaptured = false;
				_craftSplitApplied = false;
				_vanillaCraftAuthor = string.Empty;
				_emcLabelBaseFont = -1f;
				_fittedText = null;
				val.SetActive(false);
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					instance.Log.LogInfo((object)(_craftUnderLayoutGroup ? "EmcInventoryUi: кнопка «за EMC» встала в layout-группу ванильной кнопки." : "EmcInventoryUi: кнопка «за EMC» создана справа от ванильной."));
				}
			}
			catch (Exception ex)
			{
				Plugin instance2 = Plugin.Instance;
				if (instance2 != null)
				{
					instance2.Log.LogWarning((object)("EmcInventoryUi: не удалось создать кнопку крафта за EMC: " + ex.Message));
				}
			}
		}

		private static void CaptureCraftGeometry()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!_craftGeomCaptured && !((Object)(object)_vanillaCraftRt == (Object)null))
			{
				Rect rect = _vanillaCraftRt.rect;
				float width = ((Rect)(ref rect)).width;
				if (!(width <= 1f))
				{
					_craftOrigSize = _vanillaCraftRt.sizeDelta;
					_craftOrigPos = _vanillaCraftRt.anchoredPosition;
					_craftOrigWidth = width;
					_craftOrigPivotX = _vanillaCraftRt.pivot.x;
					_craftGeomCaptured = true;
				}
			}
		}

		private static void LayoutCraftButtons(bool split)
		{
			//IL_002b: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			if (!_craftGeomCaptured || _craftUnderLayoutGroup)
			{
				return;
			}
			if (_craftSplitApplied == split)
			{
				if (!split)
				{
					return;
				}
				Rect rect = _vanillaCraftRt.rect;
				if (Mathf.Abs(((Rect)(ref rect)).width - _craftSplitVanillaWidth) < 0.5f)
				{
					return;
				}
			}
			_craftSplitApplied = split;
			if (!split)
			{
				_vanillaCraftRt.sizeDelta = _craftOrigSize;
				_vanillaCraftRt.anchoredPosition = _craftOrigPos;
				return;
			}
			float craftOrigWidth = _craftOrigWidth;
			float craftOrigPivotX = _craftOrigPivotX;
			float num = Mathf.Max(40f, (craftOrigWidth - 6f) * 0.4f);
			float num2 = Mathf.Max(60f, craftOrigWidth - 6f - num);
			_craftSplitVanillaWidth = num;
			_vanillaCraftRt.sizeDelta = _craftOrigSize - new Vector2(craftOrigWidth - num, 0f);
			_vanillaCraftRt.anchoredPosition = _craftOrigPos - new Vector2((craftOrigWidth - num) * craftOrigPivotX, 0f);
			_emcCraftRt.sizeDelta = _craftOrigSize - new Vector2(craftOrigWidth - num2, 0f);
			_emcCraftRt.anchoredPosition = _craftOrigPos + new Vector2(num + 6f + craftOrigPivotX * num2 - craftOrigPivotX * craftOrigWidth, 0f);
		}

		public static void UpdateRecipePostfix(InventoryGui __instance)
		{
			RefreshEmcCraftButton(__instance);
		}

		private static void RefreshEmcCraftButton(InventoryGui gui)
		{
			if ((Object)(object)gui == (Object)null)
			{
				return;
			}
			Button craftButton = gui.m_craftButton;
			if ((Object)(object)craftButton == (Object)null)
			{
				return;
			}
			EnsureEmcCraftButton(gui);
			if ((Object)(object)_emcCraftButton == (Object)null)
			{
				return;
			}
			CaptureCraftGeometry();
			if ((Object)(object)_vanillaCraftLabel == (Object)null)
			{
				_vanillaCraftLabel = ((Component)craftButton).GetComponentInChildren<TMP_Text>(true);
			}
			string text = (((Object)(object)_vanillaCraftLabel != (Object)null) ? (_vanillaCraftLabel.text ?? string.Empty) : string.Empty);
			if (!CraftButtonLabel.IsOwn(text))
			{
				_vanillaCraftAuthor = CraftButtonLabel.StripPrice(text);
			}
			ItemData upgrade;
			Recipe selectedRecipe = GetSelectedRecipe(gui, out upgrade);
			int craftMultiplier = GetCraftMultiplier(gui, upgrade);
			bool flag = ((Component)craftButton).gameObject.activeInHierarchy && ((Object)(object)selectedRecipe != (Object)null || !string.IsNullOrWhiteSpace(text)) && !IsCraftRunning(gui) && !IsFreeCraft();
			if (((Component)_emcCraftButton).gameObject.activeSelf != flag)
			{
				((Component)_emcCraftButton).gameObject.SetActive(flag);
			}
			LayoutCraftButtons(flag);
			if (!flag)
			{
				if ((Object)(object)_vanillaCraftLabel != (Object)null && !string.IsNullOrEmpty(_vanillaCraftAuthor) && _vanillaCraftLabel.text != _vanillaCraftAuthor)
				{
					_vanillaCraftLabel.text = _vanillaCraftAuthor;
				}
				return;
			}
			long? labelCost = GetLabelCost(gui);
			bool flag2 = labelCost.HasValue && Plugin.Instance.EmcBalance >= labelCost.Value;
			((Selectable)_emcCraftButton).interactable = ((Selectable)craftButton).interactable && flag2;
			string text2 = CraftButtonLabel.Count(craftMultiplier);
			if ((Object)(object)_vanillaCraftLabel != (Object)null && _vanillaCraftLabel.text != text2)
			{
				_vanillaCraftLabel.text = text2;
			}
			string text3 = CraftButtonLabel.CountWithPrice(craftMultiplier, labelCost, flag2);
			if ((Object)(object)_emcCraftLabel != (Object)null && _emcCraftLabel.text != text3)
			{
				_emcCraftLabel.text = text3;
			}
			ApplyCraftLabelFont();
		}

		private static bool IsCraftRunning(InventoryGui gui)
		{
			return CraftTimerField?.GetValue(gui) is float num && num >= 0f;
		}

		private static bool IsFreeCraft()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return true;
			}
			if (localPlayer.NoCostCheat())
			{
				return true;
			}
			return (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)25);
		}

		private static Recipe GetSelectedRecipe(InventoryGui gui, out ItemData upgrade)
		{
			upgrade = null;
			object obj = SelectedRecipeField?.GetValue(gui);
			if (obj == null)
			{
				return null;
			}
			object? obj2 = PairItemDataProperty?.GetValue(obj);
			upgrade = (ItemData)((obj2 is ItemData) ? obj2 : null);
			object? obj3 = PairRecipeProperty?.GetValue(obj);
			return (Recipe)((obj3 is Recipe) ? obj3 : null);
		}

		private static void OnEmcCraftButtonClicked()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || IsCraftRunning(instance))
			{
				return;
			}
			Button craftButton = instance.m_craftButton;
			if ((Object)(object)craftButton == (Object)null || !((Selectable)craftButton).interactable || !((Component)craftButton).gameObject.activeInHierarchy)
			{
				return;
			}
			Plugin instance2 = Plugin.Instance;
			if (instance2?.Data == null || !instance2.Data.IsAvailable)
			{
				return;
			}
			long? labelCost = GetLabelCost(instance);
			if (!labelCost.HasValue)
			{
				ShowMessage(Loc.Tr("Рецепт не оценён в EMC — крафтуйте за ресурсы", "The recipe has no EMC price — craft with resources"));
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			ItemData upgrade;
			Recipe selectedRecipe = GetSelectedRecipe(instance, out upgrade);
			if ((Object)(object)selectedRecipe == (Object)null || !HaveCraftResources(localPlayer, selectedRecipe, upgrade, GetCraftMultiplier(instance, upgrade)))
			{
				ShowMessage(Loc.Tr("Крафт за EMC недоступен: не хватает ингредиентов", "EMC craft unavailable: missing ingredients"));
				return;
			}
			if (instance2.EmcBalance < labelCost.Value)
			{
				ShowMessage(Loc.Fmt($"Недостаточно EMC: нужно {labelCost.Value}, есть {instance2.EmcBalance}", $"Not enough EMC: {labelCost.Value} needed, you have {instance2.EmcBalance}"));
				return;
			}
			_emcCraftRequested = true;
			try
			{
				((UnityEvent)craftButton.onClick).Invoke();
			}
			catch (Exception ex)
			{
				_emcCraftRequested = false;
				Plugin instance3 = Plugin.Instance;
				if (instance3 != null)
				{
					instance3.Log.LogWarning((object)("Крафт за EMC: ошибка запуска — " + ex.Message));
				}
			}
		}

		public static void OnCraftPressedPostfix(InventoryGui __instance)
		{
			bool emcCraftRequested = _emcCraftRequested;
			_emcCraftRequested = false;
			RefundPrepaidCraft();
			if (!emcCraftRequested || !IsCraftRunning(__instance))
			{
				return;
			}
			Plugin instance = Plugin.Instance;
			if (instance?.Data == null || !instance.Data.IsAvailable)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			object? obj = CraftRecipeField?.GetValue(__instance);
			Recipe val = (Recipe)((obj is Recipe) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				AbortCraft(__instance, Loc.Tr("Крафт за EMC: рецепт не выбран", "EMC craft: no recipe selected"));
				return;
			}
			object? obj2 = CraftUpgradeItemField?.GetValue(__instance);
			ItemData upgrade = (ItemData)((obj2 is ItemData) ? obj2 : null);
			int startedCraftMultiplier = GetStartedCraftMultiplier(__instance);
			if (!HaveCraftResources(localPlayer, val, upgrade, startedCraftMultiplier))
			{
				AbortCraft(__instance, Loc.Tr("Крафт за EMC отменён: не хватает ингредиентов", "EMC craft cancelled: missing ingredients"));
				return;
			}
			long? num = ComputeCraftCost(localPlayer, val, upgrade, startedCraftMultiplier);
			if (!num.HasValue || num.Value <= 0)
			{
				AbortCraft(__instance, Loc.Tr("Крафт за EMC отменён: рецепт не оценён", "EMC craft cancelled: the recipe is not priced"));
				return;
			}
			if (!instance.TrySpendEmc(num.Value, out var _))
			{
				AbortCraft(__instance, Loc.Fmt($"Крафт за EMC отменён: нужно {num.Value} EMC", $"EMC craft cancelled: {num.Value} EMC required"));
				return;
			}
			_prepaidCraftCost = num.Value;
			_prepaidCraftConsumed = false;
			_craftStoppedAt = -1f;
			RememberPaidCraft(val, upgrade, startedCraftMultiplier);
			ShowMessage(Loc.Fmt($"Крафт за {num.Value} EMC — списано сразу", $"Craft for {num.Value} EMC — charged upfront"));
		}

		public static void OnCraftPressedFinalizer()
		{
			_emcCraftRequested = false;
		}

		private static void AbortCraft(InventoryGui gui, string reason)
		{
			try
			{
				CraftTimerField?.SetValue(gui, -1f);
				MultiCraftingField?.SetValue(gui, false);
			}
			catch
			{
			}
			ShowMessage(reason);
		}

		public static void OnCraftCancelPressedPrefix()
		{
			RefundPrepaidCraft();
		}

		public static bool DoCraftingPrefix(InventoryGui __instance)
		{
			_emcCraftActive = true;
			if (_prepaidCraftCost <= 0)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)_paidRecipe == (Object)null || !HaveCraftResources(localPlayer, _paidRecipe, _paidUpgrade, _paidMultiplier))
			{
				RefundPrepaidCraft();
				AbortCraft(__instance, Loc.Tr("Крафт за EMC отменён: ингредиентов больше нет в инвентаре", "EMC craft cancelled: ingredients are no longer in the inventory"));
				return false;
			}
			return true;
		}

		public static void DoCraftingFinalizer()
		{
			_emcCraftActive = false;
		}

		public static void DoCraftingPostfix()
		{
			if (_prepaidCraftCost > 0)
			{
				if (_prepaidCraftConsumed)
				{
					_prepaidCraftCost = 0L;
					_prepaidCraftConsumed = false;
					ForgetPaidCraft();
				}
				else
				{
					RefundPrepaidCraft();
				}
			}
		}

		private static void RefundPrepaidCraft()
		{
			_craftStoppedAt = -1f;
			ForgetPaidCraft();
			if (_prepaidCraftCost > 0)
			{
				long prepaidCraftCost = _prepaidCraftCost;
				_prepaidCraftCost = 0L;
				if (_prepaidCraftConsumed)
				{
					_prepaidCraftConsumed = false;
					return;
				}
				Plugin.Instance?.AddEmc(prepaidCraftCost);
				ShowMessage(Loc.Fmt($"Крафт отменён — возвращено {prepaidCraftCost} EMC", $"Craft cancelled — {prepaidCraftCost} EMC refunded"));
			}
		}

		public static bool RemoveItemPrefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			if (!_emcCraftActive || _prepaidCraftCost <= 0)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || __instance != ((Humanoid)localPlayer).GetInventory())
			{
				return true;
			}
			_prepaidCraftConsumed = true;
			return false;
		}

		private static void ApplyCraftLabelFont()
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Invalid comparison between Unknown and I4
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_emcCraftLabel == (Object)null)
			{
				return;
			}
			if (_emcLabelBaseFont <= 0f)
			{
				float num = (_emcCraftLabel.enableAutoSizing ? _emcCraftLabel.fontSizeMax : _emcCraftLabel.fontSize);
				if (num <= 0f)
				{
					return;
				}
				_emcLabelBaseFont = num;
			}
			if (_emcCraftLabel.enableAutoSizing)
			{
				_emcCraftLabel.enableAutoSizing = false;
			}
			if ((int)_emcCraftLabel.textWrappingMode > 0)
			{
				_emcCraftLabel.textWrappingMode = (TextWrappingModes)0;
			}
			float num2 = _emcLabelBaseFont;
			float craftLabelWidth = GetCraftLabelWidth();
			string text = _emcCraftLabel.text ?? string.Empty;
			if (text == _fittedText && Mathf.Abs(craftLabelWidth - _fittedWidth) < 0.5f && Mathf.Abs(_emcCraftLabel.fontSize - _fittedSize) < 0.01f)
			{
				return;
			}
			if (Mathf.Abs(_emcCraftLabel.fontSize - _emcLabelBaseFont) > 0.01f)
			{
				_emcCraftLabel.fontSize = _emcLabelBaseFont;
			}
			if (craftLabelWidth > 0f)
			{
				float num3 = craftLabelWidth * 0.95f;
				Vector2 preferredValues = _emcCraftLabel.GetPreferredValues(text);
				if (preferredValues.x > num3)
				{
					num2 = Mathf.Max(4f, _emcLabelBaseFont * num3 / preferredValues.x);
				}
			}
			_fittedText = text;
			_fittedWidth = craftLabelWidth;
			_fittedSize = num2;
			if (Mathf.Abs(_emcCraftLabel.fontSize - num2) > 0.01f)
			{
				_emcCraftLabel.fontSize = num2;
			}
		}

		private static float GetCraftLabelWidth()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			float num = ((TextMarginWidthField?.GetValue(_emcCraftLabel) is float num2 && num2 > 0f) ? num2 : 0f);
			if (num <= 0f && (Object)(object)_emcCraftLabel != (Object)null)
			{
				Rect rect = _emcCraftLabel.rectTransform.rect;
				num = ((Rect)(ref rect)).width;
			}
			return (num > 0f) ? num : 0f;
		}

		private static long? GetLabelCost(InventoryGui gui)
		{
			Plugin instance = Plugin.Instance;
			if (instance?.Registry == null || instance.Data == null || !instance.Data.IsAvailable)
			{
				return null;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || IsFreeCraft())
			{
				return null;
			}
			ItemData upgrade;
			Recipe selectedRecipe = GetSelectedRecipe(gui, out upgrade);
			if (selectedRecipe?.m_resources == null)
			{
				return null;
			}
			return ComputeCraftCost(localPlayer, selectedRecipe, upgrade, GetCraftMultiplier(gui, upgrade));
		}

		private static long? ComputeCraftCost(Player player, Recipe recipe, ItemData upgrade, int multiplier)
		{
			Plugin instance = Plugin.Instance;
			if (instance?.Registry == null || recipe?.m_resources == null)
			{
				return null;
			}
			int num = ((upgrade == null) ? 1 : (upgrade.m_quality + 1));
			if (recipe.m_requireOnlyOneIngredient)
			{
				int num2 = default(int);
				int num3 = default(int);
				ItemData firstRequiredItem = player.GetFirstRequiredItem(((Humanoid)player).GetInventory(), recipe, num, ref num2, ref num3, multiplier);
				if (firstRequiredItem != null)
				{
					string prefabName = TransmutationService.PrefabName(firstRequiredItem);
					if (!instance.Registry.TryGetValue(prefabName, out var emc) || !PricingPolicy.IsTradable(emc))
					{
						return null;
					}
					return num2 * emc;
				}
				Requirement[] resources = recipe.m_resources;
				foreach (Requirement val in resources)
				{
					if ((Object)(object)val?.m_resItem == (Object)null)
					{
						continue;
					}
					int amount = val.GetAmount(num);
					if (amount > 0)
					{
						string prefabName2 = RecipePricing.PrefabNameOf(val.m_resItem);
						if (!instance.Registry.TryGetValue(prefabName2, out var emc2) || !PricingPolicy.IsTradable(emc2))
						{
							return null;
						}
						return (long)amount * (long)multiplier * emc2;
					}
				}
				return null;
			}
			long? num4 = ComputeCost(recipe.m_resources, num, multiplier, player.GetCurrentCraftingStation(), instance.Registry);
			return (num4.HasValue && num4.Value > 0) ? num4 : ((long?)null);
		}

		private static int GetCraftMultiplier(InventoryGui gui, ItemData upgrade)
		{
			if (upgrade != null)
			{
				return 1;
			}
			bool valueOrDefault = TouchMultiCraftingField?.GetValue(gui) as bool? == true;
			if (!(ZInput.GetButton("AltPlace") || ZInput.GetButton("JoyLStick") || valueOrDefault))
			{
				return 1;
			}
			return Mathf.Max(1, gui.m_multiCraftAmount);
		}

		public static void OnInventoryGuiShow(InventoryGui __instance)
		{
			ApplyEmcIcon();
			EnsureCellTabs(__instance);
			UpdateStatText();
			_repositionPending = true;
		}

		public static void OnInventoryGuiHide(InventoryGui __instance)
		{
			CloseCellPanels(immediate: true);
			_emcCraftActive = false;
			_emcCraftRequested = false;
			RefundPrepaidCraft();
		}

		public static void Tick()
		{
			EmcCell.Tick();
			TickCellTabs();
			if (!(Time.time < _nextTextRefresh))
			{
				_nextTextRefresh = Time.time + 0.25f;
				GuardPrepaidCraft();
				ApplyPendingPosition();
				UpdateStatText();
				SyncEmcIconColor();
				RefreshEmcCraftButton(InventoryGui.instance);
			}
		}

		private static long? ComputeCost(Requirement[] requirements, int qualityLevel, int multiplier, CraftingStation station, EmcRegistry registry)
		{
			bool flag = (Object)(object)station != (Object)null && station.m_upgrader;
			List<CraftRequirement> list = new List<CraftRequirement>();
			foreach (Requirement val in requirements)
			{
				if (!((Object)(object)val?.m_resItem == (Object)null) && (!((Object)(object)station != (Object)null) || flag == val.m_upgraderResource) && (!((Object)(object)station == (Object)null) || !val.m_upgraderResource))
				{
					int amount = val.GetAmount(qualityLevel);
					if (amount > 0)
					{
						list.Add(new CraftRequirement(registry.TryGetValue(RecipePricing.PrefabNameOf(val.m_resItem), out var emc) ? new long?(emc) : ((long?)null), amount));
					}
				}
			}
			return CraftCost.Compute(list, multiplier);
		}

		private static void ShowMessage(string text)
		{
			try
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
				}
			}
			catch
			{
			}
			Plugin instance2 = Plugin.Instance;
			if (instance2 != null)
			{
				instance2.Log.LogInfo((object)text);
			}
		}

		public static void UpdateGuiPostfix(InventoryGrid __instance, Player player)
		{
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			if (!(ElementsField?.GetValue(__instance) is List<InventoryElement> list))
			{
				return;
			}
			EmcRegistry emcRegistry = Plugin.Instance?.Registry;
			Inventory val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Humanoid)Player.m_localPlayer).GetInventory() : null);
			Inventory inventory = __instance.GetInventory();
			if (!AnyCellPanelOpen || emcRegistry == null || inventory == null || (inventory != val && !EmcCell.BlockOf(inventory).HasValue))
			{
				foreach (KeyValuePair<GameObject, (GameObject, TMP_Text, RectTransform)> emcLabel2 in _emcLabels)
				{
					if ((Object)(object)emcLabel2.Value.Item1 != (Object)null && emcLabel2.Value.Item1.activeSelf && emcLabel2.Value.Item1.transform.IsChildOf(((Component)__instance).transform))
					{
						emcLabel2.Value.Item1.SetActive(false);
					}
				}
				return;
			}
			foreach (InventoryElement item2 in list)
			{
				GameObject val2 = (((Object)(object)item2?.m_button != (Object)null) ? ((Component)item2.m_button).gameObject : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				TMP_Text emcLabel = GetEmcLabel(val2, item2);
				if ((Object)(object)emcLabel == (Object)null)
				{
					break;
				}
				(GameObject, TMP_Text, RectTransform) tuple = _emcLabels[val2];
				GameObject item = tuple.Item1;
				ItemData val3 = null;
				if (item2.m_used && ElementPositionProperty != null)
				{
					Vector2i val4 = (Vector2i)ElementPositionProperty.GetValue(item2);
					val3 = inventory.GetItemAt(val4.x, val4.y);
				}
				if (val3 == null || !emcRegistry.TryGetValue(TransmutationService.PrefabName(val3), out var _))
				{
					if (item.activeSelf)
					{
						item.SetActive(false);
					}
					continue;
				}
				if (!item.activeSelf)
				{
					item.SetActive(true);
				}
				string text = ItemPricing.SellValue(val3, emcRegistry, Plugin.Instance.SellPriceMultiplier).ToString();
				if (emcLabel.text != text)
				{
					emcLabel.text = text;
					float num = Mathf.Max(26f, 14f + (float)text.Length * 7.5f);
					tuple.Item3.sizeDelta = new Vector2(num, 18f);
				}
			}
		}

		private static Sprite GetPillSprite()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_pillSprite != (Object)null)
			{
				return _pillSprite;
			}
			Texture2D val = new Texture2D(48, 20, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1
			};
			for (int i = 0; i < 20; i++)
			{
				for (int j = 0; j < 48; j++)
				{
					int num = Mathf.Clamp(j, 9, 38);
					int num2 = Mathf.Clamp(i, 9, 10);
					int num3 = j - num;
					int num4 = i - num2;
					float num5 = Mathf.Sqrt((float)(num3 * num3 + num4 * num4));
					float num6 = ((num5 <= 8f) ? 1f : ((num5 < 9f) ? (9f - num5) : 0f));
					val.SetPixel(j, i, new Color(1f, 1f, 1f, num6));
				}
			}
			val.Apply();
			_pillSprite = Sprite.Create(val, new Rect(0f, 0f, 48f, 20f), new Vector2(0.5f, 0.5f));
			return _pillSprite;
		}

		private static TMP_Text GetEmcLabel(GameObject slot, InventoryElement el)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_00a2: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: 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)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Expected O, but got Unknown
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Expected O, but got Unknown
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ad: Unknown result type (might be due to invalid IL or missing references)
			if (_emcLabels.TryGetValue(slot, out (GameObject, TMP_Text, RectTransform) value) && (Object)(object)value.Item2 != (Object)null)
			{
				return value.Item2;
			}
			TMP_FontAsset val = ResolveEmcFont(el);
			if ((Object)(object)val == (Object)nul