Decompiled source of VeiledRecipes v1.1.5

VeiledRecipes.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("VeiledRecipes")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("VeiledRecipes")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.1.5")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.5.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace VeiledRecipes
{
	[BepInPlugin("sighsorry.VeiledRecipes", "VeiledRecipes", "1.1.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class VeiledRecipesPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal const string ModName = "VeiledRecipes";

		internal const string ModVersion = "1.1.5";

		internal const string Author = "sighsorry";

		public const string ModGUID = "sighsorry.VeiledRecipes";

		private static string ConfigFileName = "sighsorry.VeiledRecipes.cfg";

		private static string ConfigFileFullPath;

		private readonly Harmony _harmony = new Harmony("sighsorry.VeiledRecipes");

		public static readonly ManualLogSource PluginLogger;

		private static readonly ConfigSync ConfigSync;

		private FileSystemWatcher? _watcher;

		private readonly object _reloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private string? _lastConfigFileText;

		private static readonly TimeSpan ConfigReloadDebounce;

		private static ConfigEntry<Toggle> _serverConfigLocked;

		internal static ConfigEntry<Toggle> ShowUnknownCraftingRecipes;

		internal static ConfigEntry<Toggle> ShowUnknownBuildPieces;

		internal static ConfigEntry<Toggle> RequireStationLevelForUnknownCraftingRecipes;

		internal static ConfigEntry<Toggle> RequireStationKnowledgeForUnknownBuildPieces;

		internal static ConfigEntry<Toggle> RequireStationInteractionForRecipeUnlock;

		internal static ConfigEntry<Toggle> EnableStationProximityDiscovery;

		internal static ConfigEntry<string> RecipePreviewPrefabBlacklist;

		internal static ConfigEntry<string> PiecePreviewPrefabBlacklist;

		internal static ConfigEntry<string> RequirementPreviewPrefabBlacklist;

		internal static ConfigEntry<string> UnknownNameText;

		internal static ConfigEntry<string> UnknownDescriptionText;

		internal static ConfigEntry<string> UnknownRequirementText;

		internal static ConfigEntry<bool> GroupUnknownRecipePreviewsBelowKnownRecipes;

		internal static ConfigEntry<bool> ShowRecipeUnlockNotifications;

		internal static ConfigEntry<bool> ShowPieceUnlockNotifications;

		internal static ConfigEntry<bool> ShowSkillLevelUpNotificationAndEffect;

		public void Awake()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				_serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
				ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
				ShowUnknownCraftingRecipes = config("2 - Veiled Recipes", "Show Unknown Crafting Recipes", Toggle.On, "Shows crafting recipes at the relevant crafting station before the recipe is fully unlocked.");
				ShowUnknownBuildPieces = config("2 - Veiled Recipes", "Show Unknown Build Pieces", Toggle.On, "Shows build pieces in build-piece tables before the piece is fully unlocked.");
				RequireStationLevelForUnknownCraftingRecipes = config("2 - Veiled Recipes", "Require Station Level For Unknown Crafting Recipes", Toggle.On, "If on, unknown crafting recipe previews are shown only when the current crafting station meets the recipe's required station level.");
				RequireStationKnowledgeForUnknownBuildPieces = config("2 - Veiled Recipes", "Require Station Knowledge For Unknown Build Pieces", Toggle.On, "If on, unknown build-piece previews that require a crafting station are shown only after the player knows that station.");
				RequireStationInteractionForRecipeUnlock = config("2 - Veiled Recipes", "Require Station Interaction For Recipe Unlock", Toggle.On, "If on, recipes that require a crafting station unlock only after the player has interacted with the required station level. If off, Valheim's normal station discovery behavior is used for recipe station knowledge.");
				EnableStationProximityDiscovery = config("2 - Veiled Recipes", "Enable Station Proximity Discovery", Toggle.On, "If on, Valheim's normal crafting station discovery radius is used. If off, walking near a crafting station does not discover it; interacting with the station is required.");
				RecipePreviewPrefabBlacklist = config("2 - Veiled Recipes", "Recipe Preview Prefab Blacklist", "", "Comma-separated item prefab names whose unknown crafting recipe previews should never be shown. This does not hide recipes after they are actually unlocked. Example: ArmorIronLegs, SwordIron");
				PiecePreviewPrefabBlacklist = config("2 - Veiled Recipes", "Piece Preview Prefab Blacklist", "", "Comma-separated piece prefab names whose unknown build piece previews should never be shown. This does not hide pieces after they are actually unlocked. Example: piece_workbench_ext1, piece_chest");
				RequirementPreviewPrefabBlacklist = config("2 - Veiled Recipes", "Requirement Preview Prefab Blacklist", "SwordCheat, SledgeCheat", "Comma-separated ingredient/resource prefab names that prevent unknown crafting recipe and build-piece previews from being shown when required by that recipe or piece. This does not hide anything after it is actually unlocked.");
				UnknownNameText = config("3 - Display", "Unknown Name Text", "???", "Text shown for unknown recipe and piece names.");
				UnknownDescriptionText = config("3 - Display", "Unknown Description Text", "Not enough info", "Text shown for unknown recipe and piece descriptions.");
				UnknownRequirementText = config("3 - Display", "Unknown Requirement Text", "?", "Text shown for unknown requirement names, amounts, and station levels.");
				GroupUnknownRecipePreviewsBelowKnownRecipes = config("3 - Display", "Group Unknown Recipe Previews Below Known Recipes", value: true, "If enabled, unknown crafting recipe previews are grouped below actually unlocked recipes in crafting station recipe lists.", synchronizedSetting: false);
				ShowRecipeUnlockNotifications = config("4 - Client Notifications", "Show Recipe Unlock Notifications", value: true, "Shows Valheim's unlock popup when a crafting recipe is learned.", synchronizedSetting: false);
				ShowPieceUnlockNotifications = config("4 - Client Notifications", "Show Piece Unlock Notifications", value: false, "Shows Valheim's unlock popup when a build piece or dish is learned.", synchronizedSetting: false);
				ShowSkillLevelUpNotificationAndEffect = config("4 - Client Notifications", "Show Skill Level Up Notification/Effect", value: true, "Shows the skill level-up message and VFX/SFX. When disabled, all $msg_skillup alarms and Player.OnSkillLevelup effects are hidden.", synchronizedSetting: false);
				VeiledRecipeInfinityHammerCompat.RegisterKnownPieceOverrides();
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				_harmony.PatchAll(executingAssembly);
				VeiledRecipeAaaCraftingCompat.Initialize();
				((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
				SetupWatcher();
				((BaseUnityPlugin)this).Config.Save();
				_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			try
			{
				SaveWithRespectToConfigSet();
			}
			finally
			{
				_watcher?.Dispose();
			}
		}

		private static void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			VeiledRecipeBuildUiRefreshPatch.Invalidate();
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			if (now - _lastConfigReloadTime < ConfigReloadDebounce)
			{
				return;
			}
			_lastConfigReloadTime = now;
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					PluginLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					string b = File.ReadAllText(ConfigFileFullPath);
					if (!string.Equals(_lastConfigFileText, b, StringComparison.Ordinal))
					{
						PluginLogger.LogDebug((object)"Reloading configuration...");
						SaveWithRespectToConfigSet(reload: true);
						_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
						PluginLogger.LogInfo((object)"Configuration reload complete.");
					}
				}
				catch (Exception ex)
				{
					PluginLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
		}

		private static string? ReadFileTextIfExists(string path)
		{
			if (!File.Exists(path))
			{
				return null;
			}
			return File.ReadAllText(path);
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				if (reload)
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				((BaseUnityPlugin)this).Config.Save();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), (AcceptableValueBase)null, Array.Empty<object>());
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		static VeiledRecipesPlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			PluginLogger = Logger.CreateLogSource("VeiledRecipes");
			ConfigSync = new ConfigSync("sighsorry.VeiledRecipes")
			{
				DisplayName = "VeiledRecipes",
				CurrentVersion = "1.1.5",
				MinimumRequiredVersion = "1.1.5"
			};
			ConfigReloadDebounce = TimeSpan.FromSeconds(1.0);
			_serverConfigLocked = null;
			ShowUnknownCraftingRecipes = null;
			ShowUnknownBuildPieces = null;
			RequireStationLevelForUnknownCraftingRecipes = null;
			RequireStationKnowledgeForUnknownBuildPieces = null;
			RequireStationInteractionForRecipeUnlock = null;
			EnableStationProximityDiscovery = null;
			RecipePreviewPrefabBlacklist = null;
			PiecePreviewPrefabBlacklist = null;
			RequirementPreviewPrefabBlacklist = null;
			UnknownNameText = null;
			UnknownDescriptionText = null;
			UnknownRequirementText = null;
			GroupUnknownRecipePreviewsBelowKnownRecipes = null;
			ShowRecipeUnlockNotifications = null;
			ShowPieceUnlockNotifications = null;
			ShowSkillLevelUpNotificationAndEffect = null;
		}
	}
	internal static class VeiledRecipeConstants
	{
		internal const string StationInteractionPrefix = "VeiledRecipes.InteractedStation.";

		internal const string UnknownNameFallback = "???";

		internal const string UnknownDescriptionFallback = "Not enough info";

		internal const string UnknownRequirementFallback = "?";

		internal const string NewRecipeMessage = "$msg_newrecipe";

		internal const string NewPieceMessage = "$msg_newpiece";

		internal const string NewDishMessage = "$msg_newdish";

		internal const string SkillUpMessagePrefix = "$msg_skillup ";

		internal const string MissingRequirementMessage = "$msg_missingrequirement";

		internal const string MenuNoneMessage = "$menu_none";

		internal const string CloneSuffix = "(Clone)";

		internal const string RecipeIconChild = "icon";

		internal const string RecipeNameChild = "name";

		internal const string DurabilityChild = "Durability";

		internal const string QualityLevelChild = "QualityLevel";

		internal const string RequirementIconChild = "res_icon";

		internal const string RequirementNameChild = "res_name";

		internal const string RequirementAmountChild = "res_amount";

		internal static readonly char[] PrefabBlacklistSeparators = new char[5] { ',', ';', '|', '\n', '\r' };
	}
	internal static class VeiledRecipeAaaCraftingCompat
	{
		internal const string PluginGuid = "Azumatt.AzuAntiArthriticCrafting";

		private const string Namespace = "AzuAntiArthriticCrafting.";

		private static bool _loaded;

		private static PropertyInfo? _trackedRecipe;

		private static FieldInfo? _trackedPanel;

		private static readonly HashSet<GameObject> HiddenTrackedPanels = new HashSet<GameObject>();

		private static GameObject? _tooltipRoot;

		private static TMP_Text? _tooltipTopic;

		private static TMP_Text? _tooltipText;

		private static bool _tooltipMasked;

		internal static void Initialize()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			if (_loaded || !Chainloader.PluginInfos.TryGetValue("Azumatt.AzuAntiArthriticCrafting", out var value) || (Object)(object)value.Instance == (Object)null)
			{
				return;
			}
			Harmony val = new Harmony("sighsorry.VeiledRecipes.AAA");
			try
			{
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				Type type = assembly.GetType("AzuAntiArthriticCrafting.Handlers.RecipeHoverTooltip", throwOnError: true);
				Type type2 = assembly.GetType("AzuAntiArthriticCrafting.RecipeTracking.RecipeTrackerUI", throwOnError: true);
				Type type3 = assembly.GetType("AzuAntiArthriticCrafting.RecipeTracking.RecipeUI", throwOnError: true);
				_trackedRecipe = type3.GetProperty("Recipe") ?? throw new MissingMemberException(type3.FullName, "Recipe");
				_trackedPanel = type3.GetField("recipeStub") ?? throw new MissingFieldException(type3.FullName, "recipeStub");
				if (_trackedRecipe.PropertyType != typeof(Recipe) || _trackedPanel.FieldType != typeof(GameObject))
				{
					throw new InvalidOperationException("Unsupported recipe tracker fields.");
				}
				Patch(val, type, "UpdateTextElements", Type.EmptyTypes, "TooltipPostfix");
				Patch(val, type, "LateUpdate", Type.EmptyTypes, "TooltipLateUpdatePostfix");
				Patch(val, type2, "AddSelectedRecipe", new Type[1] { typeof(Recipe) }, "AddTrackedRecipePrefix", prefix: true);
				Patch(val, type2, "ToggleUI", Type.EmptyTypes, "TrackerPostfix");
				_loaded = true;
				VeiledRecipesPlugin.PluginLogger.LogInfo((object)"AAA Crafting compatibility enabled.");
				InitializePagination(assembly);
			}
			catch (Exception ex)
			{
				val.UnpatchSelf();
				VeiledRecipesPlugin.PluginLogger.LogWarning((object)("AAA Crafting compatibility could not be enabled: " + ex.Message));
			}
		}

		private static void Patch(Harmony harmony, Type type, string method, Type[] parameters, string handler, bool prefix = false)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, method, parameters, (Type[])null) ?? throw new MissingMethodException(type.FullName, method);
			HarmonyMethod val = new HarmonyMethod(typeof(VeiledRecipeAaaCraftingCompat), handler, (Type[])null);
			val.after = new string[1] { "Azumatt.AzuAntiArthriticCrafting" };
			val.priority = 0;
			HarmonyMethod val2 = val;
			harmony.Patch((MethodBase)methodInfo, prefix ? val2 : null, prefix ? null : val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void InitializePagination(Assembly assembly)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			Harmony val = new Harmony("sighsorry.VeiledRecipes.AAA.Paging");
			try
			{
				Type type = assembly.GetType("AzuAntiArthriticCrafting.Patches.PaginatorPatches+InventoryGuiUpdateRecipeListPatch", throwOnError: true);
				Type[] array = new Type[2]
				{
					typeof(InventoryGui),
					typeof(List<Recipe>).MakeByRefType()
				};
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Prefix", array, (Type[])null) ?? throw new MissingMethodException(type.FullName, "Prefix");
				MethodInfo methodInfo2 = AccessTools.DeclaredMethod(type, "Postfix", array, (Type[])null) ?? throw new MissingMethodException(type.FullName, "Postfix");
				HarmonyMethod val2 = new HarmonyMethod(typeof(VeiledRecipeAaaCraftingCompat), "GroupBeforePagination", (Type[])null);
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null);
				VeiledRecipesPlugin.PluginLogger.LogInfo((object)"AAA Crafting grouping before pagination enabled.");
			}
			catch (Exception ex)
			{
				val.UnpatchSelf();
				VeiledRecipesPlugin.PluginLogger.LogWarning((object)("AAA Crafting full-list grouping could not be enabled; keeping page-local grouping: " + ex.Message));
			}
		}

		private static IEnumerable<CodeInstruction> GroupBeforePagination(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			bool flag = __originalMethod.Name == "Prefix";
			Type type = (flag ? typeof(Recipe) : typeof(RecipeDataPair));
			MethodInfo operand = AccessTools.Method(typeof(VeiledRecipeAaaCraftingCompat), flag ? "SkipCraftRecipes" : "SkipRecipePairs", (Type[])null, (Type[])null);
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (!(item.opcode != OpCodes.Call) && item.operand is MethodInfo methodInfo && !(methodInfo.DeclaringType != typeof(Enumerable)) && !(methodInfo.Name != "Skip") && methodInfo.IsGenericMethod && !(methodInfo.GetGenericArguments()[0] != type) && methodInfo.GetParameters().Length == 2 && !(methodInfo.GetParameters()[1].ParameterType != typeof(int)))
				{
					item.operand = operand;
					num++;
				}
			}
			int num2 = ((!flag) ? 1 : 2);
			if (num != num2)
			{
				throw new InvalidOperationException($"Unsupported AAA {__originalMethod.Name} pagination: expected {num2} Skip calls, found {num}.");
			}
			return list;
		}

		private static IEnumerable<Recipe> SkipCraftRecipes(IEnumerable<Recipe> recipes, int count)
		{
			return SkipGrouped(recipes, count, (Player player, Recipe recipe) => VeiledRecipeState.IsUnknownRecipePreview(player, recipe));
		}

		private static IEnumerable<RecipeDataPair> SkipRecipePairs(IEnumerable<RecipeDataPair> pairs, int count)
		{
			return SkipGrouped(pairs, count, (Player player, RecipeDataPair pair) => VeiledRecipeState.GetRecipeVisibilityState(player, ((RecipeDataPair)(ref pair)).Recipe, ((RecipeDataPair)(ref pair)).ItemData) == VeiledRecipeVisibilityState.UnknownPreview);
		}

		private static IEnumerable<T> SkipGrouped<T>(IEnumerable<T> source, int count, Func<Player, T, bool> isUnknown)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !VeiledRecipeState.GroupUnknownRecipePreviewsBelowKnownRecipes)
			{
				return source.Skip(count);
			}
			List<T> list = new List<T>();
			List<T> list2 = new List<T>();
			foreach (T item in source)
			{
				(isUnknown(localPlayer, item) ? list2 : list).Add(item);
			}
			list.AddRange(list2);
			return list.Skip(count);
		}

		internal static void MaskRecipeList(InventoryGui gui, Player player)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (!_loaded)
			{
				return;
			}
			foreach (RecipeDataPair availableRecipe in gui.m_availableRecipes)
			{
				RecipeDataPair current = availableRecipe;
				if ((Object)(object)((RecipeDataPair)(ref current)).InterfaceElement != (Object)null && VeiledRecipeState.ShouldMaskRecipe(player, ((RecipeDataPair)(ref current)).Recipe, ((RecipeDataPair)(ref current)).ItemData))
				{
					InventoryGuiAddRecipeToListPatch.MaskRecipeListElement(((RecipeDataPair)(ref current)).InterfaceElement, ((RecipeDataPair)(ref current)).Recipe);
				}
			}
		}

		private static bool ShouldMaskTooltip(Component tooltip)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)tooltip == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			foreach (RecipeDataPair availableRecipe in instance.m_availableRecipes)
			{
				RecipeDataPair current = availableRecipe;
				if ((Object)(object)((RecipeDataPair)(ref current)).InterfaceElement == (Object)(object)tooltip.gameObject)
				{
					return VeiledRecipeState.ShouldMaskRecipe(localPlayer, ((RecipeDataPair)(ref current)).Recipe, ((RecipeDataPair)(ref current)).ItemData);
				}
			}
			return false;
		}

		private static void TooltipPostfix(Component __instance, GameObject ___m_tooltip, string ___m_topic, string ___m_text)
		{
			UpdateTooltip(__instance, ___m_tooltip, ___m_topic, ___m_text);
		}

		private static void TooltipLateUpdatePostfix(Component __instance, Component ___m_current, GameObject ___m_tooltip, string ___m_topic, string ___m_text)
		{
			if ((Object)(object)__instance == (Object)(object)___m_current)
			{
				UpdateTooltip(__instance, ___m_tooltip, ___m_topic, ___m_text);
			}
		}

		private static void UpdateTooltip(Component owner, GameObject root, string? topic, string? text)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			if ((Object)(object)_tooltipRoot != (Object)(object)root)
			{
				_tooltipRoot = root;
				Transform obj = Utils.FindChild(root.transform, "Topic", (IterativeSearchType)0);
				_tooltipTopic = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
				Transform obj2 = Utils.FindChild(root.transform, "Text", (IterativeSearchType)0);
				_tooltipText = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null);
				_tooltipMasked = false;
			}
			bool flag = ShouldMaskTooltip(owner);
			if (flag || _tooltipMasked)
			{
				if ((Object)(object)_tooltipTopic != (Object)null)
				{
					_tooltipTopic.text = (flag ? VeiledRecipeState.UnknownNameText : Localization.instance.Localize(topic ?? ""));
				}
				if ((Object)(object)_tooltipText != (Object)null)
				{
					_tooltipText.text = (flag ? VeiledRecipeState.UnknownDescriptionText : Localization.instance.Localize(text ?? ""));
				}
				_tooltipMasked = flag;
			}
		}

		private static bool AddTrackedRecipePrefix(Recipe recipe)
		{
			Player localPlayer = Player.m_localPlayer;
			InventoryGui instance = InventoryGui.instance;
			ItemData targetItem = (((Object)(object)instance != (Object)null && (Object)(object)((RecipeDataPair)(ref instance.m_selectedRecipe)).Recipe == (Object)(object)recipe) ? ((RecipeDataPair)(ref instance.m_selectedRecipe)).ItemData : null);
			if ((Object)(object)localPlayer == (Object)null || !VeiledRecipeState.ShouldMaskRecipe(localPlayer, recipe, targetItem))
			{
				return true;
			}
			((Character)localPlayer).Message((MessageType)2, VeiledRecipeState.UnknownDescriptionText, 0, (Sprite)null, false);
			return false;
		}

		private static void TrackerPostfix(IEnumerable ___RecipeUIs)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || _trackedRecipe == null || _trackedPanel == null)
			{
				return;
			}
			HiddenTrackedPanels.RemoveWhere((GameObject panel) => (Object)(object)panel == (Object)null);
			foreach (object ___RecipeUI in ___RecipeUIs)
			{
				Component val = (Component)((___RecipeUI is Component) ? ___RecipeUI : null);
				if (val == null || (Object)(object)val == (Object)null)
				{
					continue;
				}
				object? value = _trackedRecipe.GetValue(___RecipeUI);
				Recipe val2 = (Recipe)((value is Recipe) ? value : null);
				if (val2 == null)
				{
					continue;
				}
				object? value2 = _trackedPanel.GetValue(___RecipeUI);
				GameObject val3 = (GameObject)((value2 is GameObject) ? value2 : null);
				if (val3 == null || (Object)(object)val3 == (Object)null)
				{
					continue;
				}
				if (VeiledRecipeState.RequiresRecipeKnowledge(localPlayer, val2))
				{
					if (val3.activeSelf)
					{
						HiddenTrackedPanels.Add(val3);
						val3.SetActive(false);
					}
				}
				else if (HiddenTrackedPanels.Remove(val3))
				{
					val3.SetActive(true);
				}
			}
		}
	}
	internal static class VeiledRecipeInfinityHammerCompat
	{
		private const string PluginGuid = "infinity_hammer";

		private const string HammerSelectTypeName = "InfinityHammer.HammerSelect";

		private const string SelectionTypeName = "InfinityHammer.Selection";

		private const string BaseSelectionTypeName = "InfinityHammer.BaseSelection";

		private static bool _initialized;

		private static bool _loaded;

		private static MethodInfo? _getSelectionMethod;

		private static MethodInfo? _getSelectedPieceMethod;

		private static PropertyInfo? _isToolProperty;

		private static object? _activeCommandSelection;

		internal static void RegisterKnownPieceOverrides()
		{
			VeiledRecipeState.RegisterKnownPieceOverride(IsKnownActiveSelectionPiece);
		}

		private static bool IsKnownActiveSelectionPiece(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return false;
			}
			EnsureInitialized();
			if (!_loaded || _getSelectionMethod == null || _getSelectedPieceMethod == null || _isToolProperty == null)
			{
				return false;
			}
			try
			{
				object obj = _getSelectionMethod.Invoke(null, null);
				if (obj == null)
				{
					_activeCommandSelection = null;
					return false;
				}
				object? obj2 = _getSelectedPieceMethod.Invoke(obj, null);
				Piece val = (Piece)((obj2 is Piece) ? obj2 : null);
				if ((Object)(object)val == (Object)null || val != piece)
				{
					return false;
				}
				if (IsToolSelection(obj) || obj == _activeCommandSelection)
				{
					return true;
				}
				if (!IsHammerSelectCommandOnStack())
				{
					return false;
				}
				_activeCommandSelection = obj;
				return true;
			}
			catch (Exception ex)
			{
				VeiledRecipesPlugin.PluginLogger.LogDebug((object)("Infinity Hammer active selection check failed: " + ex.Message));
				return false;
			}
		}

		private static bool IsToolSelection(object? selection)
		{
			if (selection != null)
			{
				object obj = _isToolProperty?.GetValue(selection);
				if (obj is bool)
				{
					return (bool)obj;
				}
				return false;
			}
			return false;
		}

		private static bool IsHammerSelectCommandOnStack()
		{
			StackFrame[] frames = new StackTrace().GetFrames();
			for (int i = 0; i < frames.Length; i++)
			{
				string? obj = frames[i].GetMethod()?.DeclaringType?.FullName;
				if (obj != null && obj.StartsWith("InfinityHammer.HammerSelect", StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		private static void EnsureInitialized()
		{
			if (!_initialized)
			{
				_initialized = true;
				if (Chainloader.PluginInfos.TryGetValue("infinity_hammer", out var value))
				{
					Assembly obj = ((object)value.Instance)?.GetType().Assembly;
					Type type = obj?.GetType("InfinityHammer.Selection", throwOnError: false);
					Type? obj2 = obj?.GetType("InfinityHammer.BaseSelection", throwOnError: false);
					_getSelectionMethod = type?.GetMethod("Get", BindingFlags.Static | BindingFlags.Public);
					_getSelectedPieceMethod = obj2?.GetMethod("GetSelectedPiece", BindingFlags.Instance | BindingFlags.Public);
					_isToolProperty = obj2?.GetProperty("IsTool", BindingFlags.Instance | BindingFlags.Public);
					_loaded = _getSelectionMethod != null && _getSelectedPieceMethod != null && _isToolProperty != null;
				}
			}
		}
	}
	internal static class VeiledRecipeState
	{
		private static readonly HashSet<string> RegisteredBuildPiecePrefabNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static ObjectDB? _registeredPieceObjectDb;

		private static int _registeredPieceItemCount = -1;

		private static readonly List<Func<Piece, bool>> KnownPieceOverrides = new List<Func<Piece, bool>>();

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

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

		private static string _recipePreviewPrefabBlacklistRaw = "";

		private static string _piecePreviewPrefabBlacklistRaw = "";

		private static string _requirementPreviewPrefabBlacklistRaw = "";

		private static HashSet<string> _recipePreviewPrefabBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static HashSet<string> _piecePreviewPrefabBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static HashSet<string> _requirementPreviewPrefabBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static bool ShowUnknownCraftingRecipes => IsOn(VeiledRecipesPlugin.ShowUnknownCraftingRecipes);

		internal static bool ShowUnknownBuildPieces => IsOn(VeiledRecipesPlugin.ShowUnknownBuildPieces);

		internal static bool RequireStationLevelForUnknownCraftingRecipes => IsOn(VeiledRecipesPlugin.RequireStationLevelForUnknownCraftingRecipes);

		internal static bool RequireStationKnowledgeForUnknownBuildPieces => IsOn(VeiledRecipesPlugin.RequireStationKnowledgeForUnknownBuildPieces);

		internal static bool RequireStationInteractionForRecipeUnlock => IsOn(VeiledRecipesPlugin.RequireStationInteractionForRecipeUnlock);

		internal static bool EnableStationProximityDiscovery => IsOn(VeiledRecipesPlugin.EnableStationProximityDiscovery);

		internal static string UnknownNameText => SafeText(VeiledRecipesPlugin.UnknownNameText, "???");

		internal static string UnknownDescriptionText => SafeText(VeiledRecipesPlugin.UnknownDescriptionText, "Not enough info");

		internal static string UnknownRequirementText => SafeText(VeiledRecipesPlugin.UnknownRequirementText, "?");

		internal static bool GroupUnknownRecipePreviewsBelowKnownRecipes => VeiledRecipesPlugin.GroupUnknownRecipePreviewsBelowKnownRecipes?.Value ?? true;

		internal static bool ShowRecipeUnlockNotifications => VeiledRecipesPlugin.ShowRecipeUnlockNotifications?.Value ?? true;

		internal static bool ShowPieceUnlockNotifications => VeiledRecipesPlugin.ShowPieceUnlockNotifications?.Value ?? false;

		internal static bool ShowSkillLevelUpNotificationAndEffect => VeiledRecipesPlugin.ShowSkillLevelUpNotificationAndEffect?.Value ?? true;

		internal static bool IsRecipeActuallyKnown(Player player, Recipe recipe)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return false;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)28))
			{
				return true;
			}
			if (player.m_noPlacementCost || player.NoCostCheat())
			{
				return true;
			}
			return player.IsRecipeKnown(recipe.m_item.m_itemData.m_shared.m_name);
		}

		internal static bool IsPieceActuallyKnown(Player player, Piece piece)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null)
			{
				return false;
			}
			if (piece.m_repairPiece || piece.m_removePiece)
			{
				return true;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)26))
			{
				return true;
			}
			if (player.m_noPlacementCost || player.NoCostCheat())
			{
				return true;
			}
			if (!player.IsRecipeKnown(piece.m_name))
			{
				return HasKnownPieceOverride(piece);
			}
			return true;
		}

		internal static bool RequiresRecipeKnowledge(Player player, Recipe recipe)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && !IsRecipeActuallyKnown(player, recipe))
			{
				return HasDiscoverableCraftingRecipe(player, recipe);
			}
			return false;
		}

		internal static bool ShouldMaskRecipe(Player player, Recipe recipe, ItemData? targetItem = null)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || IsRecipeActuallyKnown(player, recipe))
			{
				return false;
			}
			if (HasDiscoverableCraftingRecipe(player, recipe))
			{
				return true;
			}
			if (targetItem == null)
			{
				return !IsBackedByOwnedItem(player, recipe);
			}
			return false;
		}

		internal static bool ShouldMaskPiece(Player player, Piece piece)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null && !IsPieceActuallyKnown(player, piece))
			{
				return HasRegisteredBuildPiece(piece);
			}
			return false;
		}

		internal static void RegisterBuildPieceTable(PieceTable table)
		{
			if ((Object)(object)table == (Object)null)
			{
				return;
			}
			ResetRegisteredBuildPiecesIfObjectDbChanged();
			foreach (GameObject piece in table.m_pieces)
			{
				if (!((Object)(object)piece == (Object)null))
				{
					string prefabName = Utils.GetPrefabName(piece);
					if (!string.IsNullOrEmpty(prefabName))
					{
						RegisteredBuildPiecePrefabNames.Add(prefabName);
					}
				}
			}
		}

		private static bool HasDiscoverableCraftingRecipe(Player player, Recipe recipe)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return false;
			}
			string name = recipe.m_item.m_itemData.m_shared.m_name;
			foreach (Recipe recipe2 in ObjectDB.instance.m_recipes)
			{
				if ((Object)(object)recipe2 != (Object)null && (Object)(object)recipe2.m_item != (Object)null && recipe2.m_item.m_itemData.m_shared.m_name == name && IsRecipeEnabledForPlayer(player, recipe2))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsBackedByOwnedItem(Player player, Recipe recipe)
		{
			ItemData itemData = recipe.m_item.m_itemData;
			if (itemData != null)
			{
				return ((Humanoid)player).GetInventory().ContainsItem(itemData);
			}
			return false;
		}

		private static bool HasRegisteredBuildPiece(Piece piece)
		{
			RefreshRegisteredBuildPieces();
			string prefabName = Utils.GetPrefabName(((Component)piece).gameObject);
			if (!string.IsNullOrEmpty(prefabName))
			{
				return RegisteredBuildPiecePrefabNames.Contains(prefabName);
			}
			return false;
		}

		private static void RefreshRegisteredBuildPieces()
		{
			ObjectDB instance = ObjectDB.instance;
			ResetRegisteredBuildPiecesIfObjectDbChanged();
			if ((Object)(object)instance == (Object)null || _registeredPieceItemCount == instance.m_items.Count)
			{
				return;
			}
			foreach (GameObject item in instance.m_items)
			{
				ItemDrop val = (((Object)(object)item == (Object)null) ? null : item.GetComponent<ItemDrop>());
				if ((Object)(object)val?.m_itemData.m_shared.m_buildPieces != (Object)null)
				{
					RegisterBuildPieceTable(val.m_itemData.m_shared.m_buildPieces);
				}
			}
			_registeredPieceItemCount = instance.m_items.Count;
		}

		private static void ResetRegisteredBuildPiecesIfObjectDbChanged()
		{
			ObjectDB instance = ObjectDB.instance;
			if (_registeredPieceObjectDb != instance)
			{
				_registeredPieceObjectDb = instance;
				_registeredPieceItemCount = -1;
				RegisteredBuildPiecePrefabNames.Clear();
			}
		}

		internal static bool IsMaterialKnown(Player player, Requirement requirement)
		{
			if ((Object)(object)player == (Object)null || requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return false;
			}
			return player.IsKnownMaterial(requirement.m_resItem.m_itemData.m_shared.m_name);
		}

		private static bool DlcInstalled(string dlc)
		{
			if (!string.IsNullOrEmpty(dlc) && !((Object)(object)DLCMan.instance == (Object)null))
			{
				return DLCMan.instance.IsDLCInstalled(dlc);
			}
			return true;
		}

		internal static void RegisterKnownPieceOverride(Func<Piece, bool> predicate)
		{
			if (predicate != null && !KnownPieceOverrides.Contains(predicate))
			{
				KnownPieceOverrides.Add(predicate);
				VeiledRecipeBuildUiRefreshPatch.Invalidate();
			}
		}

		internal static void UnregisterKnownPieceOverride(Func<Piece, bool> predicate)
		{
			if (predicate != null && KnownPieceOverrides.Remove(predicate))
			{
				VeiledRecipeBuildUiRefreshPatch.Invalidate();
			}
		}

		internal static void RegisterKnownPiecePrefabOverride(string prefabName)
		{
			if (!string.IsNullOrWhiteSpace(prefabName) && KnownPiecePrefabOverrides.Add(prefabName.Trim()))
			{
				VeiledRecipeBuildUiRefreshPatch.Invalidate();
			}
		}

		internal static void UnregisterKnownPiecePrefabOverride(string prefabName)
		{
			if (!string.IsNullOrWhiteSpace(prefabName) && KnownPiecePrefabOverrides.Remove(prefabName.Trim()))
			{
				VeiledRecipeBuildUiRefreshPatch.Invalidate();
			}
		}

		internal static void RegisterKnownPieceTypeOverride(string typeName)
		{
			if (!string.IsNullOrWhiteSpace(typeName) && KnownPieceTypeOverrides.Add(typeName.Trim()))
			{
				VeiledRecipeBuildUiRefreshPatch.Invalidate();
			}
		}

		internal static void UnregisterKnownPieceTypeOverride(string typeName)
		{
			if (!string.IsNullOrWhiteSpace(typeName) && KnownPieceTypeOverrides.Remove(typeName.Trim()))
			{
				VeiledRecipeBuildUiRefreshPatch.Invalidate();
			}
		}

		private static bool HasKnownPieceOverride(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return false;
			}
			string prefabName = Utils.GetPrefabName(((Component)piece).gameObject);
			if (!string.IsNullOrEmpty(prefabName) && KnownPiecePrefabOverrides.Contains(prefabName))
			{
				return true;
			}
			string fullName = ((object)piece).GetType().FullName;
			if (!string.IsNullOrEmpty(fullName) && KnownPieceTypeOverrides.Contains(fullName))
			{
				return true;
			}
			foreach (Func<Piece, bool> knownPieceOverride in KnownPieceOverrides)
			{
				try
				{
					if (knownPieceOverride(piece))
					{
						return true;
					}
				}
				catch (Exception ex)
				{
					VeiledRecipesPlugin.PluginLogger.LogDebug((object)("Known piece override failed for '" + prefabName + "': " + ex.Message));
				}
			}
			return false;
		}

		private static bool IsOn(ConfigEntry<VeiledRecipesPlugin.Toggle> entry)
		{
			if (entry != null)
			{
				return entry.Value == VeiledRecipesPlugin.Toggle.On;
			}
			return false;
		}

		private static string SafeText(ConfigEntry<string> entry, string fallback)
		{
			if (entry == null || string.IsNullOrEmpty(entry.Value))
			{
				return fallback;
			}
			return entry.Value;
		}

		private static bool IsRecipePreviewBlacklisted(Recipe recipe)
		{
			HashSet<string> recipePreviewPrefabBlacklist = GetRecipePreviewPrefabBlacklist();
			if (recipePreviewPrefabBlacklist.Count > 0)
			{
				string[] obj = new string[4]
				{
					((Object)recipe).name,
					null,
					null,
					null
				};
				ItemDrop item = recipe.m_item;
				obj[1] = ((item != null) ? ((Object)item).name : null);
				ItemDrop item2 = recipe.m_item;
				object obj2;
				if (item2 == null)
				{
					obj2 = null;
				}
				else
				{
					GameObject gameObject = ((Component)item2).gameObject;
					obj2 = ((gameObject != null) ? ((Object)gameObject).name : null);
				}
				obj[2] = (string)obj2;
				ItemDrop item3 = recipe.m_item;
				object obj3;
				if (item3 == null)
				{
					obj3 = null;
				}
				else
				{
					GameObject dropPrefab = item3.m_itemData.m_dropPrefab;
					obj3 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				obj[3] = (string)obj3;
				return ContainsPrefabName(recipePreviewPrefabBlacklist, obj);
			}
			return false;
		}

		private static bool IsPiecePreviewBlacklisted(Piece piece)
		{
			HashSet<string> piecePreviewPrefabBlacklist = GetPiecePreviewPrefabBlacklist();
			if (piecePreviewPrefabBlacklist.Count > 0)
			{
				string[] obj = new string[2]
				{
					((Object)piece).name,
					null
				};
				GameObject gameObject = ((Component)piece).gameObject;
				obj[1] = ((gameObject != null) ? ((Object)gameObject).name : null);
				return ContainsPrefabName(piecePreviewPrefabBlacklist, obj);
			}
			return false;
		}

		private static HashSet<string> GetRecipePreviewPrefabBlacklist()
		{
			return GetPrefabBlacklist(VeiledRecipesPlugin.RecipePreviewPrefabBlacklist, ref _recipePreviewPrefabBlacklistRaw, ref _recipePreviewPrefabBlacklist);
		}

		private static HashSet<string> GetPiecePreviewPrefabBlacklist()
		{
			return GetPrefabBlacklist(VeiledRecipesPlugin.PiecePreviewPrefabBlacklist, ref _piecePreviewPrefabBlacklistRaw, ref _piecePreviewPrefabBlacklist);
		}

		private static bool HasPreviewBlacklistedRequirement(Requirement[] requirements)
		{
			HashSet<string> requirementPreviewPrefabBlacklist = GetRequirementPreviewPrefabBlacklist();
			if (requirementPreviewPrefabBlacklist.Count == 0 || requirements == null)
			{
				return false;
			}
			foreach (Requirement val in requirements)
			{
				if (!((Object)(object)val?.m_resItem == (Object)null) && val.GetAmount(1) > 0)
				{
					string[] obj = new string[3]
					{
						((Object)val.m_resItem).name,
						null,
						null
					};
					GameObject gameObject = ((Component)val.m_resItem).gameObject;
					obj[1] = ((gameObject != null) ? ((Object)gameObject).name : null);
					GameObject dropPrefab = val.m_resItem.m_itemData.m_dropPrefab;
					obj[2] = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
					if (ContainsPrefabName(requirementPreviewPrefabBlacklist, obj))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static HashSet<string> GetRequirementPreviewPrefabBlacklist()
		{
			return GetPrefabBlacklist(VeiledRecipesPlugin.RequirementPreviewPrefabBlacklist, ref _requirementPreviewPrefabBlacklistRaw, ref _requirementPreviewPrefabBlacklist);
		}

		private static HashSet<string> GetPrefabBlacklist(ConfigEntry<string> entry, ref string cachedRaw, ref HashSet<string> cached)
		{
			string text = entry?.Value ?? "";
			if (string.Equals(text, cachedRaw, StringComparison.Ordinal))
			{
				return cached;
			}
			cachedRaw = text;
			cached = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = text.Split(VeiledRecipeConstants.PrefabBlacklistSeparators, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = NormalizePrefabName(array[i]);
				if (!string.IsNullOrEmpty(text2))
				{
					cached.Add(text2);
				}
			}
			return cached;
		}

		private static bool ContainsPrefabName(HashSet<string> blacklist, params string?[] names)
		{
			for (int i = 0; i < names.Length; i++)
			{
				string text = NormalizePrefabName(names[i]);
				if (!string.IsNullOrEmpty(text) && blacklist.Contains(text))
				{
					return true;
				}
			}
			return false;
		}

		private static string NormalizePrefabName(string? name)
		{
			string text = name?.Trim() ?? "";
			if (text.Length == 0)
			{
				return "";
			}
			if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - "(Clone)".Length).Trim();
			}
			return text;
		}

		internal static VeiledRecipeVisibilityState GetRecipeVisibilityState(Player player, Recipe recipe, ItemData? targetItem = null)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (!ShouldMaskRecipe(player, recipe, targetItem))
			{
				return VeiledRecipeVisibilityState.Known;
			}
			if (!ShowUnknownCraftingRecipes || !IsRecipeEnabledForPlayer(player, recipe))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (IsRecipePreviewBlacklisted(recipe))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (HasPreviewBlacklistedRequirement(recipe.m_resources))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (!DlcInstalled(recipe.m_item.m_itemData.m_shared.m_dlc))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (!PassesCraftFilter(recipe))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			bool flag = RequireStationLevelForUnknownCraftingRecipes && (Object)(object)recipe.GetRequiredStation(1) != (Object)null;
			if (!player.RequiredCraftingStation(recipe, 1, flag))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			return VeiledRecipeVisibilityState.UnknownPreview;
		}

		internal static VeiledRecipeVisibilityState GetPieceVisibilityState(Player player, Piece piece)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null)
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (!ShouldMaskPiece(player, piece))
			{
				return VeiledRecipeVisibilityState.Known;
			}
			if (!ShowUnknownBuildPieces || !IsPieceEnabledForPlayer(player, piece))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (IsPiecePreviewBlacklisted(piece))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (HasPreviewBlacklistedRequirement(piece.m_resources))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (RequireStationKnowledgeForUnknownBuildPieces && !KnowsPieceStationRequirement(player, piece))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			if (!DlcInstalled(piece.m_dlc))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			return VeiledRecipeVisibilityState.UnknownPreview;
		}

		internal static bool IsUnknownRecipePreview(Player player, Recipe recipe)
		{
			return GetRecipeVisibilityState(player, recipe) == VeiledRecipeVisibilityState.UnknownPreview;
		}

		internal static bool IsUnknownPiecePreview(Player player, Piece piece)
		{
			return GetPieceVisibilityState(player, piece) == VeiledRecipeVisibilityState.UnknownPreview;
		}

		internal static bool CanDiscoverRecipe(Player player, Recipe recipe)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return false;
			}
			if ((Object)(object)recipe.m_craftingStation != (Object)null && !HasKnownRecipeStationLevel(player, recipe.m_craftingStation.m_name, recipe.m_minStationLevel))
			{
				return false;
			}
			if (!DlcInstalled(recipe.m_item.m_itemData.m_shared.m_dlc))
			{
				return false;
			}
			return HasDiscoveredRecipeMaterials(player, recipe);
		}

		private static bool HasDiscoveredRecipeMaterials(Player player, Recipe recipe)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool flag = false;
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val?.m_resItem == (Object)null) && val.m_amount > 0 && (!((Object)(object)currentCraftingStation == (Object)null) || !val.m_upgraderResource))
				{
					bool flag2 = IsMaterialKnown(player, val);
					if (recipe.m_requireOnlyOneIngredient)
					{
						flag = flag || flag2;
					}
					else if (!flag2)
					{
						return false;
					}
				}
			}
			return !recipe.m_requireOnlyOneIngredient || flag;
		}

		internal static bool IsRecipeRequirementActiveAtCurrentStation(Player player, Requirement requirement)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool flag = (Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader;
			return requirement.m_upgraderResource == flag;
		}

		private static bool IsRecipeEnabledForPlayer(Player player, Recipe recipe)
		{
			bool flag = (Object)(object)player.CurrentSeason != (Object)null && player.CurrentSeason.Recipes.Contains(recipe);
			return recipe.m_enabled || flag;
		}

		private static bool IsPieceEnabledForPlayer(Player player, Piece piece)
		{
			bool flag = (Object)(object)player.CurrentSeason != (Object)null && player.CurrentSeason.Pieces.Contains(((Component)piece).gameObject);
			return piece.m_enabled || flag;
		}

		private static bool PassesCraftFilter(Recipe recipe)
		{
			if (Player.s_FilterCraft.Count == 0)
			{
				return true;
			}
			string text = ((Object)recipe.m_item).name.ToLowerInvariant();
			string text2 = recipe.m_item.m_itemData.m_shared.m_name.ToLowerInvariant();
			string text3 = Localization.instance.Localize(recipe.m_item.m_itemData.m_shared.m_name).ToLowerInvariant();
			foreach (string item in Player.s_FilterCraft)
			{
				if (!string.IsNullOrWhiteSpace(item))
				{
					string value = item.ToLowerInvariant();
					if (text.Contains(value) || text2.Contains(value) || text3.Contains(value))
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static void RecordStationInteraction(Player player, CraftingStation station)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)station == (Object)null)
			{
				return;
			}
			if (!EnableStationProximityDiscovery)
			{
				player.AddKnownStation(station);
			}
			int num = Math.Max(1, station.GetLevel(true));
			string key = StationInteractionKey(station.m_name);
			if (player.m_customData.TryGetValue(key, out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result >= num)
			{
				return;
			}
			player.m_customData[key] = num.ToString(CultureInfo.InvariantCulture);
			try
			{
				player.UpdateKnownRecipesList();
			}
			catch (Exception ex)
			{
				VeiledRecipesPlugin.PluginLogger.LogDebug((object)("Could not refresh known recipes after station interaction: " + ex.Message));
			}
		}

		internal static bool KnowsRecipeStationRequirement(Player player, Recipe recipe, int quality)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null)
			{
				return false;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader)
			{
				return HasKnownRecipeStationLevel(player, currentCraftingStation.m_name, 1);
			}
			CraftingStation requiredStation = recipe.GetRequiredStation(quality);
			if (!((Object)(object)requiredStation == (Object)null))
			{
				return HasKnownRecipeStationLevel(player, requiredStation.m_name, recipe.GetRequiredStationLevel(quality));
			}
			return true;
		}

		internal static bool KnowsPieceStationRequirement(Player player, Piece piece)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)piece.m_craftingStation == (Object)null)
			{
				return true;
			}
			return GetKnownPieceStationLevel(player, piece.m_craftingStation.m_name) > 0;
		}

		private static int GetKnownRecipeStationLevel(Player player, string stationName)
		{
			if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(stationName))
			{
				return 0;
			}
			if (RequireStationInteractionForRecipeUnlock)
			{
				return GetInteractedStationLevel(player, stationName);
			}
			return GetKnownPieceStationLevel(player, stationName);
		}

		private static int GetKnownPieceStationLevel(Player player, string stationName)
		{
			if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(stationName))
			{
				return 0;
			}
			if (!player.m_knownStations.TryGetValue(stationName, out var value))
			{
				return 0;
			}
			return value;
		}

		private static bool HasKnownRecipeStationLevel(Player player, string stationName, int requiredLevel)
		{
			return GetKnownRecipeStationLevel(player, stationName) >= Math.Max(1, requiredLevel);
		}

		private static int GetInteractedStationLevel(Player player, string stationName)
		{
			if (player.m_customData.TryGetValue(StationInteractionKey(stationName), out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 0;
		}

		private static string StationInteractionKey(string stationName)
		{
			return "VeiledRecipes.InteractedStation." + stationName;
		}
	}
	[HarmonyPatch(typeof(MessageHud), "QueueUnlockMsg")]
	internal static class MessageHudQueueUnlockMsgPatch
	{
		private static bool Prefix(MessageHud __instance, string topic, string description)
		{
			if (ShouldShowUnlockNotification(topic))
			{
				return true;
			}
			__instance.AddLog(topic + ": " + description);
			return false;
		}

		private static bool ShouldShowUnlockNotification(string topic)
		{
			switch ((topic ?? "").Trim())
			{
			case "$msg_newrecipe":
				return VeiledRecipeState.ShowRecipeUnlockNotifications;
			case "$msg_newpiece":
			case "$msg_newdish":
				return VeiledRecipeState.ShowPieceUnlockNotifications;
			default:
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSkillLevelup")]
	internal static class PlayerSkillLevelUpEffectsPatch
	{
		private static bool Prefix()
		{
			return VeiledRecipeState.ShowSkillLevelUpNotificationAndEffect;
		}
	}
	[HarmonyPatch(typeof(Player), "Message")]
	internal static class PlayerSkillNotificationAlarmPatch
	{
		private static bool Prefix(string msg)
		{
			if (!VeiledRecipeState.ShowSkillLevelUpNotificationAndEffect)
			{
				return !(msg ?? "").Trim().StartsWith("$msg_skillup ", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Piece),
		typeof(RequirementMode)
	})]
	internal static class PlayerPieceRequirementsPatch
	{
		private static bool Prefix(Player __instance, Piece piece, RequirementMode mode, ref bool __result)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			if ((Object)(object)piece == (Object)null)
			{
				return true;
			}
			if ((int)mode != 1 && VeiledRecipeState.ShouldMaskPiece(__instance, piece))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(PieceTable), "UpdateAvailable")]
	internal static class PieceTableUpdateAvailablePatch
	{
		private static void Prefix(PieceTable __instance, List<List<Piece>> ___m_availablePiecesByCategory)
		{
			VeiledRecipeState.RegisterBuildPieceTable(__instance);
			EnsureAvailablePieceBuckets(__instance, ___m_availablePiecesByCategory);
		}

		private static void Postfix(PieceTable __instance, Player player, List<List<Piece>> ___m_availablePiecesByCategory)
		{
			if (!VeiledRecipeState.ShowUnknownBuildPieces || (Object)(object)player == (Object)null)
			{
				return;
			}
			HashSet<string> availablePiecePrefabNames = GetAvailablePiecePrefabNames(__instance);
			foreach (GameObject piece in __instance.m_pieces)
			{
				Piece val = (((Object)(object)piece == (Object)null) ? null : piece.GetComponent<Piece>());
				string text = (((Object)(object)val == (Object)null) ? "" : Utils.GetPrefabName(((Component)val).gameObject));
				if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(text) && !availablePiecePrefabNames.Contains(text) && VeiledRecipeState.GetPieceVisibilityState(player, val) == VeiledRecipeVisibilityState.UnknownPreview)
				{
					AddAvailablePiece(__instance, val, ___m_availablePiecesByCategory);
					availablePiecePrefabNames.Add(text);
				}
			}
		}

		private static void EnsureAvailablePieceBuckets(PieceTable table, List<List<Piece>> categories)
		{
			int requiredCategoryBucketCount = GetRequiredCategoryBucketCount(table);
			while (categories.Count < requiredCategoryBucketCount)
			{
				categories.Add(new List<Piece>());
			}
		}

		private static int GetRequiredCategoryBucketCount(PieceTable table)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Invalid comparison between Unknown and I4
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected I4, but got Unknown
			int num = 9;
			foreach (GameObject piece in table.m_pieces)
			{
				Piece val = (((Object)(object)piece == (Object)null) ? null : piece.GetComponent<Piece>());
				if (!((Object)(object)val == (Object)null) && (int)val.m_category != 100)
				{
					int num2 = (int)val.m_category;
					if (num2 >= 0)
					{
						num = Math.Max(num, num2 + 1);
					}
				}
			}
			return num;
		}

		private static HashSet<string> GetAvailablePiecePrefabNames(PieceTable table)
		{
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Piece availablePiece in table.m_availablePieces)
			{
				if ((Object)(object)availablePiece != (Object)null)
				{
					hashSet.Add(Utils.GetPrefabName(((Component)availablePiece).gameObject));
				}
			}
			return hashSet;
		}

		private static void AddAvailablePiece(PieceTable table, Piece piece, List<List<Piece>> categories)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected I4, but got Unknown
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			int num = (int)piece.m_category;
			if (((int)piece.m_category != 100 && (num < 0 || num >= categories.Count)) || !table.m_availablePieces.Add(piece))
			{
				return;
			}
			if ((int)piece.m_category == 100)
			{
				foreach (List<Piece> category in categories)
				{
					category.Add(piece);
				}
				return;
			}
			categories[num].Add(piece);
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdatePieceList")]
	internal static class HudUpdatePieceListPatch
	{
		private static void Postfix(Hud __instance, Player player)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			List<Piece> buildPieces = player.GetBuildPieces();
			if (buildPieces == null)
			{
				return;
			}
			for (int i = 0; i < __instance.m_pieceIcons.Count && i < buildPieces.Count; i++)
			{
				Piece val = buildPieces[i];
				if (!((Object)(object)val == (Object)null) && VeiledRecipeState.ShouldMaskPiece(player, val))
				{
					PieceIconData obj = __instance.m_pieceIcons[i];
					((Behaviour)obj.m_icon).enabled = true;
					obj.m_icon.sprite = val.m_icon;
					((Graphic)obj.m_icon).color = Color.black;
					obj.m_tooltip.m_text = VeiledRecipeState.UnknownNameText;
					obj.m_upgrade.SetActive(false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Hud), "SetupPieceInfo")]
	internal static class HudSetupPieceInfoPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(Hud __instance, Piece piece)
		{
			TryMaskPieceInfo(__instance, Player.m_localPlayer, piece);
		}

		internal static bool TryMaskPieceInfo(Hud hud, Player player, Piece piece)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hud == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!VeiledRecipeState.ShouldMaskPiece(player, piece))
			{
				((Graphic)hud.m_buildIcon).color = Color.white;
				return false;
			}
			MaskPieceInfo(hud, player, piece);
			return true;
		}

		private static void MaskPieceInfo(Hud hud, Player player, Piece piece)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Invalid comparison between Unknown and I4
			hud.m_buildSelection.text = VeiledRecipeState.UnknownNameText;
			hud.m_pieceDescription.text = VeiledRecipeState.UnknownDescriptionText;
			((Behaviour)hud.m_buildIcon).enabled = true;
			hud.m_buildIcon.sprite = piece.m_icon;
			((Graphic)hud.m_buildIcon).color = Color.black;
			((Behaviour)hud.m_snappingIcon).enabled = false;
			int i = 0;
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (i >= hud.m_requirementItems.Length)
				{
					break;
				}
				if (!((Object)(object)val?.m_resItem == (Object)null) && val.m_amount > 0)
				{
					hud.m_requirementItems[i].SetActive(true);
					VeiledRecipeRequirementUi.SetupRequirement(hud.m_requirementItems[i].transform, val, player, (int)piece.FreeBuildKey() == 25, 0);
					i++;
				}
			}
			if ((Object)(object)piece.m_craftingStation != (Object)null && i < hud.m_requirementItems.Length)
			{
				hud.m_requirementItems[i].SetActive(true);
				VeiledRecipeRequirementUi.SetupPieceStationRequirement(hud.m_requirementItems[i].transform, player, piece);
				i++;
			}
			for (; i < hud.m_requirementItems.Length; i++)
			{
				hud.m_requirementItems[i].SetActive(false);
			}
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateBuild")]
	internal static class HudUpdateBuildMaskPieceInfoPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(Hud __instance, Player player)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)player == (Object)null) && ((Character)player).InPlaceMode())
			{
				Piece val = __instance.m_hoveredPiece;
				if (!Object.op_Implicit((Object)(object)val))
				{
					val = player.GetSelectedPiece();
				}
				if (Object.op_Implicit((Object)(object)val))
				{
					HudSetupPieceInfoPatch.TryMaskPieceInfo(__instance, player, val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetupPlacementGhost")]
	internal static class PlayerSetupPlacementGhostPatch
	{
		private static void Postfix(Player __instance)
		{
			Piece selectedPiece = __instance.GetSelectedPiece();
			if (!((Object)(object)selectedPiece == (Object)null) && VeiledRecipeState.ShouldMaskPiece(__instance, selectedPiece))
			{
				if ((Object)(object)__instance.m_placementGhost != (Object)null)
				{
					Object.Destroy((Object)(object)__instance.m_placementGhost);
					__instance.m_placementGhost = null;
				}
				if ((Object)(object)__instance.m_placementMarkerInstance != (Object)null)
				{
					__instance.m_placementMarkerInstance.SetActive(false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class PlayerTryPlacePiecePatch
	{
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			if ((Object)(object)piece != (Object)null && VeiledRecipeState.ShouldMaskPiece(__instance, piece))
			{
				((Character)__instance).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(BuildUiPieceButton), "Setup")]
	internal static class BuildUiPieceSetupPatch
	{
		private static void Prefix(BuildUiPieceButton __instance)
		{
			Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(__instance.RefreshSearchTerm));
		}
	}
	[HarmonyPatch(typeof(BuildUiPieceButton), "UpdateRequirements")]
	internal static class BuildUiPieceRequirementsPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(BuildUiPieceButton __instance, Image ___m_icon, Image ___m_upgradeArrow)
		{
			ApplyAppearance(__instance, Player.m_localPlayer, ___m_icon, ___m_upgradeArrow);
		}

		private static void ApplyAppearance(BuildUiPieceButton button, Player? player, Image icon, Image upgradeArrow)
		{
			Piece piece = button.Piece;
			bool num = (Object)(object)player != (Object)null && (Object)(object)piece != (Object)null && VeiledRecipeState.ShouldMaskPiece(player, piece);
			VeiledBuildPieceIconGuard veiledBuildPieceIconGuard = ((Component)button).GetComponent<VeiledBuildPieceIconGuard>();
			if (!num)
			{
				veiledBuildPieceIconGuard?.Configure(icon, veiled: false);
				return;
			}
			if ((Object)(object)veiledBuildPieceIconGuard == (Object)null)
			{
				veiledBuildPieceIconGuard = ((Component)button).gameObject.AddComponent<VeiledBuildPieceIconGuard>();
			}
			veiledBuildPieceIconGuard.Configure(icon, veiled: true);
			((Component)upgradeArrow).gameObject.SetActive(false);
		}
	}
	internal sealed class VeiledBuildPieceIconGuard : MonoBehaviour
	{
		private Image? _veil;

		internal Image? Veil => _veil;

		internal void Configure(Image icon, bool veiled)
		{
			//IL_00b2: 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_0051: 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_0067: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_veil == (Object)null)
			{
				Image val = (_veil = new GameObject("VeiledRecipes Piece Veil", new Type[1] { typeof(RectTransform) }).AddComponent<Image>());
				((Component)val).transform.SetParent(((Component)icon).transform, false);
				RectTransform rectTransform = ((Graphic)val).rectTransform;
				rectTransform.anchorMin = Vector2.zero;
				rectTransform.anchorMax = Vector2.one;
				rectTransform.offsetMin = Vector2.zero;
				rectTransform.offsetMax = Vector2.zero;
				((Transform)rectTransform).localScale = Vector3.one;
			}
			Image veil = _veil;
			veil.sprite = icon.sprite;
			veil.preserveAspect = icon.preserveAspect;
			((Graphic)veil).raycastTarget = ((Graphic)icon).raycastTarget;
			((Graphic)veil).color = Color.black;
			((Behaviour)icon).enabled = !veiled;
			((Component)veil).gameObject.SetActive(veiled);
			if (veiled)
			{
				((Component)veil).transform.SetAsLastSibling();
			}
		}
	}
	[HarmonyPatch(typeof(BuildUiPieceButton), "RefreshSearchTerm")]
	internal static class BuildUiPieceSearchPatch
	{
		private static bool Prefix(BuildUiPieceButton __instance)
		{
			Player localPlayer = Player.m_localPlayer;
			Piece piece = __instance.Piece;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)piece == (Object)null || !VeiledRecipeState.ShouldMaskPiece(localPlayer, piece))
			{
				return true;
			}
			List<string> localizedSearchTerms = __instance.LocalizedSearchTerms;
			localizedSearchTerms.Clear();
			localizedSearchTerms.Add(Normalize(VeiledRecipeState.UnknownNameText));
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if ((Object)(object)val?.m_resItem != (Object)null && VeiledRecipeState.IsMaterialKnown(localPlayer, val))
				{
					localizedSearchTerms.Add(Normalize(Localization.instance.Localize(val.m_resItem.m_itemData.m_shared.m_name)));
				}
			}
			return false;
		}

		private static string Normalize(string text)
		{
			return text.ToLowerInvariant().Replace(" ", "");
		}
	}
	[HarmonyPatch(typeof(ByMaterialPieceList), "GetTagDisplayName")]
	internal static class BuildUiMaterialTagPatch
	{
		private static void Postfix(ByMaterialPieceList __instance, int index, ref string __result)
		{
			Player localPlayer = Player.m_localPlayer;
			Requirement tagById = __instance.GetTagById(__instance.GetTagIdByIndex(index));
			if ((Object)(object)localPlayer != (Object)null && tagById != null && !VeiledRecipeState.IsMaterialKnown(localPlayer, tagById))
			{
				__result = VeiledRecipeState.UnknownRequirementText;
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "Update")]
	internal static class VeiledRecipeBuildUiRefreshPatch
	{
		private sealed class Snapshot
		{
			public Player? Player;

			public PieceTable? Table;

			public int Revision;

			public bool FreePlacement;

			public bool NoCostCheat;

			public float NextCheck;

			public readonly HashSet<string> Recipes = new HashSet<string>();

			public readonly HashSet<string> Materials = new HashSet<string>();

			public readonly Dictionary<string, int> Stations = new Dictionary<string, int>();

			public bool SameStations(Dictionary<string, int> stations)
			{
				if (Stations.Count != stations.Count)
				{
					return false;
				}
				foreach (KeyValuePair<string, int> station in stations)
				{
					if (!Stations.TryGetValue(station.Key, out var value) || station.Value != value)
					{
						return false;
					}
				}
				return true;
			}
		}

		private static int _revision;

		private static readonly ConditionalWeakTable<BuildUi, Snapshot> Snapshots = new ConditionalWeakTable<BuildUi, Snapshot>();

		private static readonly FieldRef<Player, HashSet<string>> KnownRecipes = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownRecipes");

		private static readonly FieldRef<Player, HashSet<string>> KnownMaterials = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownMaterial");

		private static readonly FieldRef<Player, Dictionary<string, int>> KnownStations = AccessTools.FieldRefAccess<Player, Dictionary<string, int>>("m_knownStations");

		private static readonly FieldRef<Player, bool> NoPlacementCost = AccessTools.FieldRefAccess<Player, bool>("m_noPlacementCost");

		private static readonly Action<BuildUi, bool> RefreshTags = AccessTools.MethodDelegate<Action<BuildUi, bool>>(AccessTools.Method(typeof(BuildUi), "UpdateTagButtons", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<Player> RefreshGhost = AccessTools.MethodDelegate<Action<Player>>(AccessTools.Method(typeof(Player), "SetupPlacementGhost", (Type[])null, (Type[])null), (object)null, true);

		internal static void Invalidate()
		{
			Interlocked.Increment(ref _revision);
		}

		private static void Postfix(BuildUi __instance, PieceTable ___m_currentBuildTool)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)___m_currentBuildTool == (Object)null || !((Component)__instance).gameObject.activeInHierarchy || !((Character)localPlayer).InPlaceMode())
			{
				return;
			}
			Snapshot orCreateValue = Snapshots.GetOrCreateValue(__instance);
			if (Time.unscaledTime < orCreateValue.NextCheck)
			{
				return;
			}
			orCreateValue.NextCheck = Time.unscaledTime + 0.25f;
			HashSet<string> hashSet = KnownRecipes.Invoke(localPlayer);
			HashSet<string> hashSet2 = KnownMaterials.Invoke(localPlayer);
			Dictionary<string, int> dictionary = KnownStations.Invoke(localPlayer);
			bool flag = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)26);
			bool flag2 = NoPlacementCost.Invoke(localPlayer) || flag;
			bool flag3 = localPlayer.NoCostCheat();
			int num = Volatile.Read(in _revision);
			if (!((Object)(object)orCreateValue.Player != (Object)(object)localPlayer) && !((Object)(object)orCreateValue.Table != (Object)(object)___m_currentBuildTool) && orCreateValue.Revision == num && orCreateValue.FreePlacement == flag2 && orCreateValue.NoCostCheat == flag3 && orCreateValue.Recipes.SetEquals(hashSet) && orCreateValue.Materials.SetEquals(hashSet2) && orCreateValue.SameStations(dictionary))
			{
				return;
			}
			orCreateValue.Player = localPlayer;
			orCreateValue.Table = ___m_currentBuildTool;
			orCreateValue.Revision = num;
			orCreateValue.FreePlacement = flag2;
			orCreateValue.NoCostCheat = flag3;
			orCreateValue.Recipes.Clear();
			orCreateValue.Recipes.UnionWith(hashSet);
			orCreateValue.Materials.Clear();
			orCreateValue.Materials.UnionWith(hashSet2);
			orCreateValue.Stations.Clear();
			foreach (KeyValuePair<string, int> item in dictionary)
			{
				orCreateValue.Stations.Add(item.Key, item.Value);
			}
			___m_currentBuildTool.UpdateAvailable(hashSet, localPlayer, false, flag2);
			RefreshTags(__instance, arg2: true);
			RefreshGhost(localPlayer);
		}

		internal static void Forget(BuildUi ui)
		{
			Snapshots.Remove(ui);
		}
	}
	[HarmonyPatch(typeof(BuildUi), "OnDestroy")]
	internal static class BuildUiDestroyPatch
	{
		private static void Prefix(BuildUi __instance, List<BuildUiPieceButton> ___m_pieceButtons, Stack<BuildUiPieceButton> ___m_pieceButtonPool, Stack<BuildUiPieceButton> ___m_previousPieceButtonsTemp, BuildUiPieceButton ___m_specialPieceButton)
		{
			VeiledRecipeBuildUiRefreshPatch.Forget(__instance);
			foreach (BuildUiPieceButton ___m_pieceButton in ___m_pieceButtons)
			{
				Unsubscribe(___m_pieceButton);
			}
			foreach (BuildUiPieceButton item in ___m_pieceButtonPool)
			{
				Unsubscribe(item);
			}
			foreach (BuildUiPieceButton item2 in ___m_previousPieceButtonsTemp)
			{
				Unsubscribe(item2);
			}
			Unsubscribe(___m_specialPieceButton);
		}

		internal static void Unsubscribe(BuildUiPieceButton button)
		{
			if ((Object)(object)button != (Object)null)
			{
				Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(button.RefreshSearchTerm));
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "UpdatePieceButtons")]
	internal static class BuildUiSpecialPieceCleanupPatch
	{
		private static void Prefix(BuildUiPieceButton ___m_specialPieceButton)
		{
			BuildUiDestroyPatch.Unsubscribe(___m_specialPieceButton);
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class PlayerRecipeRequirementsPatch
	{
		private static bool Prefix(Player __instance, Recipe recipe, bool discover, ref bool __result)
		{
			if ((Object)(object)recipe == (Object)null)
			{
				return true;
			}
			if (discover && VeiledRecipeState.RequireStationInteractionForRecipeUnlock)
			{
				__result = VeiledRecipeState.CanDiscoverRecipe(__instance, recipe);
				return false;
			}
			if (!discover && VeiledRecipeState.RequiresRecipeKnowledge(__instance, recipe))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "GetAvailableRecipes")]
	internal static class PlayerGetAvailableRecipesPatch
	{
		private static void Postfix(Player __instance, ref List<Recipe> available)
		{
			if (!VeiledRecipeState.ShowUnknownCraftingRecipes || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			HashSet<Recipe> hashSet = new HashSet<Recipe>(available);
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (!hashSet.Contains(recipe) && VeiledRecipeState.GetRecipeVisibilityState(__instance, recipe) == VeiledRecipeVisibilityState.UnknownPreview)
				{
					available.Add(recipe);
					hashSet.Add(recipe);
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")]
	internal static class InventoryGuiAddRecipeToListPatch
	{
		private static void Prefix(Player player, Recipe recipe, ItemData item, ref bool canCraft, out bool __state)
		{
			__state = VeiledRecipeState.ShouldMaskRecipe(player, recipe, item);
			if (__state)
			{
				canCraft = false;
			}
		}

		private static void Postfix(InventoryGui __instance, Recipe recipe, ItemData item, bool __state)
		{
			if (__state)
			{
				GameObject val = FindRecipeElement(__instance, recipe, item);
				if ((Object)(object)val != (Object)null)
				{
					MaskRecipeListElement(val, recipe);
				}
			}
		}

		private static GameObject? FindRecipeElement(InventoryGui gui, Recipe recipe, ItemData item)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			for (int num = gui.m_availableRecipes.Count - 1; num >= 0; num--)
			{
				RecipeDataPair val = gui.m_availableRecipes[num];
				if ((Object)(object)((RecipeDataPair)(ref val)).Recipe == (Object)(object)recipe && ((RecipeDataPair)(ref val)).ItemData == item)
				{
					return ((RecipeDataPair)(ref val)).InterfaceElement;
				}
			}
			return null;
		}

		internal static void MaskRecipeListElement(GameObject element, Recipe recipe)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			Image val = VeiledRecipeRequirementUi.FindComponent<Image>(element.transform, "icon");
			if ((Object)(object)val != (Object)null)
			{
				((Behaviour)val).enabled = true;
				if ((Object)(object)recipe?.m_item != (Object)null)
				{
					val.sprite = recipe.m_item.m_itemData.GetIcon();
				}
				((Graphic)val).color = Color.black;
			}
			TMP_Text val2 = VeiledRecipeRequirementUi.FindComponent<TMP_Text>(element.transform, "name");
			if ((Object)(object)val2 != (Object)null)
			{
				val2.text = VeiledRecipeState.UnknownNameText;
				((Graphic)val2).color = Color.white;
			}
			GuiBar val3 = VeiledRecipeRequirementUi.FindComponent<GuiBar>(element.transform, "Durability");
			if ((Object)(object)val3 != (Object)null)
			{
				((Component)val3).gameObject.SetActive(false);
			}
			TMP_Text val4 = VeiledRecipeRequirementUi.FindComponent<TMP_Text>(element.transform, "QualityLevel");
			if ((Object)(object)val4 != (Object)null)
			{
				((Component)val4).gameObject.SetActive(false);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList", new Type[] { typeof(List<Recipe>) })]
	internal static class InventoryGuiUpdateRecipeListPatch
	{
		[HarmonyAfter(new string[] { "Azumatt.AzuAntiArthriticCrafting" })]
		private static void Postfix(InventoryGui __instance)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			VeiledRecipeAaaCraftingCompat.MaskRecipeList(__instance, localPlayer);
			if (!VeiledRecipeState.GroupUnknownRecipePreviewsBelowKnownRecipes || __instance.m_availableRecipes.Count <= 1)
			{
				return;
			}
			List<RecipeDataPair> list = new List<RecipeDataPair>();
			List<RecipeDataPair> list2 = new List<RecipeDataPair>();
			foreach (RecipeDataPair availableRecipe in __instance.m_availableRecipes)
			{
				RecipeDataPair current = availableRecipe;
				if (VeiledRecipeState.GetRecipeVisibilityState(localPlayer, ((RecipeDataPair)(ref current)).Recipe, ((RecipeDataPair)(ref current)).ItemData) == VeiledRecipeVisibilityState.UnknownPreview)
				{
					list2.Add(current);
				}
				else
				{
					list.Add(current);
				}
			}
			if (list.Count == 0 || list2.Count == 0)
			{
				return;
			}
			__instance.m_availableRecipes.Clear();
			__instance.m_availableRecipes.AddRange(list);
			__instance.m_availableRecipes.AddRange(list2);
			LayoutGroup component = ((Component)__instance.m_recipeListRoot).GetComponent<LayoutGroup>();
			bool flag = (Object)(object)component != (Object)null && ((Behaviour)component).isActiveAndEnabled;
			for (int i = 0; i < __instance.m_availableRecipes.Count; i++)
			{
				RecipeDataPair val = __instance.m_availableRecipes[i];
				GameObject interfaceElement = ((RecipeDataPair)(ref val)).InterfaceElement;
				if ((Object)(object)interfaceElement == (Object)null)
				{
					continue;
				}
				if (flag)
				{
					interfaceElement.transform.SetSiblingIndex(i);
					continue;
				}
				Transform transform = interfaceElement.transform;
				RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (val2 != null)
				{
					val2.anchoredPosition = new Vector2(0f, (float)i * (0f - __instance.m_recipeListSpace));
				}
			}
			if (flag)
			{
				LayoutRebuilder.MarkLayoutForRebuild(__instance.m_recipeListRoot);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
	internal static class InventoryGuiUpdateRecipePatch
	{
		[HarmonyAfter(new string[] { "Azumatt.AzuAntiArthriticCrafting" })]
		private static void Postfix(InventoryGui __instance, Player player)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			Recipe recipe = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe;
			if (!((Object)(object)recipe == (Object)null))
			{
				if (!VeiledRecipeState.ShouldMaskRecipe(player, recipe, ((RecipeDataPair)(ref __instance.m_selectedRecipe)).ItemData))
				{
					((Graphic)__instance.m_recipeIcon).color = Color.white;
				}
				else
				{
					MaskSelectedRecipe(__instance, player, recipe);
				}
			}
		}

		private static void MaskSelectedRecipe(InventoryGui gui, Player player, Recipe recipe)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			ItemData itemData = ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData;
			int num = ((itemData == null) ? 1 : (itemData.m_quality + 1));
			int craftMultiplier = ((itemData != null || (!ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyLStick"))) ? 1 : gui.m_multiCraftAmount);
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool flag = (Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader;
			bool allowedQuality = (Object)(object)recipe.m_item != (Object)null && (flag || num <= recipe.m_item.m_itemData.m_shared.m_maxQuality);
			((Behaviour)gui.m_recipeIcon).enabled = true;
			if ((Object)(object)recipe.m_item != (Object)null)
			{
				gui.m_recipeIcon.sprite = recipe.m_item.m_itemData.GetIcon();
			}
			((Graphic)gui.m_recipeIcon).color = Color.black;
			((Behaviour)gui.m_recipeName).enabled = true;
			gui.m_recipeName.text = VeiledRecipeState.UnknownNameText;
			((Behaviour)gui.m_recipeDecription).enabled = true;
			gui.m_recipeDecription.text = VeiledRecipeState.UnknownDescriptionText;
			((Component)gui.m_variantButton).gameObject.SetActive(false);
			((Component)gui.m_itemCraftType).gameObject.SetActive(false);
			((Component)gui.m_qualityPanel).gameObject.SetActive(false);
			((Selectable)gui.m_craftButton).interactable = false;
			((Component)gui.m_craftButton).GetComponent<UITooltip>().m_text = VeiledRecipeState.UnknownDescriptionText;
			SetupRecipeRequirements(gui, player, recipe, num, allowedQuality, craftMultiplier);
			SetupRecipeStationLevel(gui, player, recipe, num, allowedQuality);
		}

		private static void SetupRecipeStationLevel(InventoryGui gui, Player player, Recipe recipe, int quality, bool allowedQuality)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)recipe.GetRequiredStation(quality) == (Object)null || !allowedQuality)
			{
				((Component)gui.m_minStationLevelIcon).gameObject.SetActive(false);
				return;
			}
			((Component)gui.m_minStationLevelIcon).gameObject.SetActive(true);
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			int num = (((Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader) ? 1 : recipe.GetRequiredStationLevel(quality));
			gui.m_minStationLevelText.text = (VeiledRecipeState.KnowsRecipeStationRequirement(player, recipe, quality) ? num.ToString() : VeiledRecipeState.UnknownRequirementText);
			((Graphic)gui.m_minStationLevelText).color = gui.m_minStationLevelBasecolor;
		}

		private static void SetupRecipeRequirements(InventoryGui gui, Player player, Recipe recipe, int quality, bool allowedQuality, int craftMultiplier)
		{
			int i = 0;
			Requirement[] resources = recipe.m_resources;
			if (allowedQuality && resources != null && gui.m_recipeRequirementList.Length != 0)
			{
				int num = 0;
				Requirement[] array = resources;
				foreach (Requirement val in array)
				{
					if ((Object)(object)val?.m_resItem != (Object)null && VeiledRecipeState.IsRecipeRequirementActiveAtCurrentStation(player, val) && val.GetAmount(quality) > 0)
					{
						num++;
					}
				}
				int num2 = VeiledRecipeRequirementUi.GetCyclingStart(num, gui.m_recipeRequirementList.Length);
				array = resources;
				foreach (Requirement val2 in array)
				{
					if (i >= gui.m_recipeRequirementList.Length)
					{
						break;
					}
					if (!((Object)(object)val2?.m_resItem == (Object)null) && VeiledRecipeState.IsRecipeRequirementActiveAtCurrentStation(player, val2) && val2.GetAmount(quality) > 0)
					{
						if (num2 > 0)
						{
							num2--;
							continue;
						}
						VeiledRecipeRequirementUi.SetupRequirement(gui.m_recipeRequirementList[i].transform, val2, player, craft: true, quality, craftMultiplier);
						i++;
					}
				}
			}
			for (; i < gui.m_recipeRequirementList.Length; i++)
			{
				InventoryGui.HideRequirement(gui.m_recipeRequirementList[i].transform);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	internal static class InventoryGuiOnCraftPressedPatch
	{
		private static bool Prefix(InventoryGui __instance)
		{
			Recipe recipe = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)recipe != (Object)null && (Object)(object)localPlayer != (Object)null && VeiledRecipeState.ShouldMaskRecipe(localPlayer, recipe, ((RecipeDataPair)(ref __instance.m_selectedRecipe)).ItemData))
			{
				((Character)localPlayer).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class InventoryGuiDoCraftingPatch
	{
		private static bool Prefix(InventoryGui __instance, Player player)
		{
			Recipe craftRecipe = __instance.m_craftRecipe;
			if ((Object)(object)craftRecipe != (Object)null && VeiledRecipeState.ShouldMaskRecipe(player, craftRecipe, __instance.m_craftUpgradeItem))
			{
				__instance.m_craftTimer = -1f;
				((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Interact")]
	internal static class CraftingStationInteractPatch
	{
		private static void Prefix(CraftingStation __instance, Humanoid user, bool repeat)
		{
			if (!repeat && !((Object)(object)user != (Object)(object)Player.m_localPlayer))
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && __instance.InUseDistance(user) && __instance.CheckUsable(val, false))
				{
					VeiledRecipeState.RecordStationInteraction(val, __instance);
				}
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "UpdateKnownStationsInRange")]
	internal static class CraftingStationUpdateKnownStationsInRangePatch
	{
		private static bool Prefix()
		{
			return VeiledRecipeState.EnableStationProximityDiscovery;
		}
	}
	public enum VeiledRecipeVisibilityState
	{
		Hidden,
		Known,
		UnknownPreview
	}
	internal static class VeiledRecipeRequirementUi
	{
		internal static void SetupRequirement(Transform root, Requirement? requirement, Player player, bool craft, int quality, int multiplier = 1)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null || requirement.GetAmount(quality) <= 0)
			{
				InventoryGui.HideRequirement(root);
			}
			else if (VeiledRecipeState.IsMaterialKnown(player, requirement))
			{
				InventoryGui.SetupRequirement(root, requirement, player, craft, quality, multiplier);
			}
			else
			{
				SetupMaskedRequirement(root, requirement);
			}
		}

		internal static void SetupMaskedRequirement(Transform root, Requirement requirement)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			Image val = VeiledRecipeRequirementUi.FindComponent<Image>(root, "res_icon");
			TMP_Text val2 = VeiledRecipeRequirementUi.FindComponent<TMP_Text>(root, "res_name");
			TMP_Text val3 = VeiledRecipeRequirementUi.FindComponent<TMP_Text>(root, "res_amount");
			UITooltip component = ((Component)root).GetComponent<UITooltip>();
			if ((Object)(object)val != (Object)null)
			{
				((Component)val).gameObject.SetActive(true);
				((Behaviour)val).enabled = true;
				val.sprite = requirement.m_resItem.m_itemData.GetIcon();
				((Graphic)val).color = Color.black;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				((Component)val2).gameObject.SetActive(true);
				val2.text = VeiledRecipeState.UnknownNameText;
				((Graphic)val2).color = Color.white;
			}
			if ((Object)(object)val3 != (Object)null)
			{
				((Component)val3).gameObject.SetActive(true);
				val3.text = VeiledRecipeState.UnknownRequirementText;
				((Graphic)val3).color = Color.white;
			}
			if ((Object)(object)component != (Object)null)
			{
				component.m_text = VeiledRecipeState.UnknownNameText;
			}
		}

		internal static void SetupPieceStationRequirement(Transform root, Player player, Piece piece)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece.m_craftingStation == (Object)null)
			{
				InventoryGui.HideRequirement(root);
				return;
			}
			Image val = VeiledRecipeRequirementUi.FindComponent<Image>(root, "res_icon");
			TMP_Text val2 = VeiledRecipeRequirementUi.FindComponent<TMP_Text>(root, "res_name");
			TMP_Text val3 = VeiledRecipeRequirementUi.FindComponent<TMP_Text>(root, "res_amount");
			UITooltip component = ((Component)root).GetComponent<UITooltip>();
			bool flag = VeiledRecipeState.KnowsPieceStationRequirement(player, piece);
			if ((Object)(object)val != (Object)null)
			{
				((Component)val).gameObject.SetActive(true);
				((Behaviour)val).enabled = true;
				val.sprite = piece.m_craftingStation.m_icon;
				((Graphic)val).color = (flag ? Color.white : Color.black);
			}
			if ((Object)(object)val2 != (Object)null)
			{
				((Component)val2).gameObject.SetActive(true);
				val2.text = (flag ? Localization.instance.Localize(piece.m_craftingStation.m_name) : VeiledRecipeState.UnknownNameText);
				((Graphic)val2).color = Color.white;
			}
			if ((Object)(object)val3 != (Object)null)
			{
				((Component)val3).gameObject.SetActive(true);
				if (flag)
				{
					CraftingStation val4 = CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)player).transform.position);
					if ((Object)(object)val4 != (Object)null)
					{
						val4.ShowAreaMarker();
						val3.text = "";
					}
					else
					{
						val3.text = Localization.instance.Localize("$menu_none");
					}
				}
				else
				{
					val3.text = VeiledRecipeState.UnknownRequirementText;
				}
				((Graphic)val3).color = Color.white;
			}
			if ((Object)(object)component != (Object)null)
			{
				component.m_text = (flag ? piece.m_craftingStation.m_name : VeiledRecipeState.UnknownNameText);
			}
		}

		internal static int GetCyclingStart(int requirementCount, int slotCount)
		{
			if (slotCount <= 0 || requirementCount <= slotCount)
			{
				return 0;
			}
			int num = Mathf.CeilToInt((float)requirementCount / (float)slotCount);
			return (int)Time.fixedTime % num * slotCount;
		}

		internal static T? FindComponent<T>(Transform root, string childName) where T : Component
		{
			Transform val = root.Find(childName);
			if (!((Object)(object)val == (Object)null))
			{
				return ((Component)val).GetComponent<T>();
			}
			return default(T);
		}
	}
	public static class VeiledRecipesCompat
	{
		public const string PluginGuid = "sighsorry.VeiledRecipes";

		public const string PluginName = "VeiledRecipes";

		public const string PluginVersion = "1.1.5";

		public const string Author = "sighsorry";

		public static string UnknownNameText => VeiledRecipeState.UnknownNameText;

		public static string UnknownDescriptionText => VeiledRecipeState.UnknownDescriptionText;

		public static string UnknownRequirementText => VeiledRecipeState.UnknownRequirementText;

		public static bool GroupUnknownRecipePreviewsBelowKnownRecipes => VeiledRecipeState.GroupUnknownRecipePreviewsBelowKnownRecipes;

		[Obsolete("Admin bypass is no longer supported; this member remains for binary compatibility.")]
		public static bool IsAdminBypassActive => false;

		public static VeiledRecipeVisibilityState GetRecipeVisibilityState(Recipe recipe)
		{
			return GetRecipeVisibilityState(Player.m_localPlayer, recipe);
		}

		public static VeiledRecipeVisibilityState GetRecipeVisibilityState(Player player, Recipe recipe)
		{
			if (!((Object)(object)player != (Object)null) || !((Object)(object)recipe != (Object)null))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			return VeiledRecipeState.GetRecipeVisibilityState(player, recipe);
		}

		public static bool IsUnknownRecipePreview(Recipe recipe)
		{
			return IsUnknownRecipePreview(Player.m_localPlayer, recipe);
		}

		public static bool IsUnknownRecipePreview(Player player, Recipe recipe)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null)
			{
				return VeiledRecipeState.IsUnknownRecipePreview(player, recipe);
			}
			return false;
		}

		public static VeiledRecipeVisibilityState GetPieceVisibilityState(Piece piece)
		{
			return GetPieceVisibilityState(Player.m_localPlayer, piece);
		}

		public static VeiledRecipeVisibilityState GetPieceVisibilityState(Player player, Piece piece)
		{
			if (!((Object)(object)player != (Object)null) || !((Object)(object)piece != (Object)null))
			{
				return VeiledRecipeVisibilityState.Hidden;
			}
			return VeiledRecipeState.GetPieceVisibilityState(player, piece);
		}

		public static bool IsUnknownPiecePreview(Piece piece)
		{
			return IsUnknownPiecePreview(Player.m_localPlayer, piece);
		}

		public static bool IsUnknownPiecePreview(Player player, Piece piece)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null)
			{
				return VeiledRecipeState.IsUnknownPiecePreview(player, piece);
			}
			return false;
		}

		public static bool ShouldMaskRecipe(Recipe recipe)
		{
			return ShouldMaskRecipe(Player.m_localPlayer, recipe);
		}

		public static bool ShouldMaskRecipe(Player player, Recipe recipe)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null)
			{
				return VeiledRecipeState.ShouldMaskRecipe(player, recipe);
			}
			return false;
		}

		public static bool ShouldMaskRecipePair(RecipeDataPair pair)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return ShouldMaskRecipePair(Player.m_localPlayer, pair);
		}

		public static bool ShouldMaskRecipePair(Player player, RecipeDataPair pair)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)((RecipeDataPair)(ref pair)).Recipe != (Object)null)
			{
				return VeiledRecipeState.ShouldMaskRecipe(player, ((RecipeDataPair)(ref pair)).Recipe, ((RecipeDataPair)(ref pair)).ItemData);
			}
			return false;
		}

		public static bool IsRecipeActuallyKnown(Player player, Recipe recipe)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null)
			{
				return VeiledRecipeState.IsRecipeActuallyKnown(player, recipe);
			}
			return false;
		}

		public static bool ShouldMaskPiece(Piece piece)
		{
			return ShouldMaskPiece(Player.m_localPlayer, piece);
		}

		public static bool ShouldMaskPiece(Player player, Piece piece)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null)
			{
				return VeiledRecipeState.ShouldMaskPiece(player, piece);
			}
			return false;
		}

		public static void RegisterKnownPieceOverride(Func<Piece, bool> predicate)
		{
			VeiledRecipeState.RegisterKnownPieceOverride(predicate);
		}

		public static void UnregisterKnownPieceOverride(Func<Piece, bool> predicate)
		{
			VeiledRecipeState.UnregisterKnownPieceOverride(predicate);
		}

		public static void RegisterKnownPiecePrefabOverride(string prefabName)
		{
			VeiledRecipeState.RegisterKnownPiecePrefabOverride(prefabName);
		}

		public static void UnregisterKnownPiecePrefabOverride(string prefabName)
		{
			VeiledRecipeState.UnregisterKnownPiecePrefabOverride(prefabName);
		}

		public static void RegisterKnownPieceTypeOverride(string typeName)
		{
			VeiledRecipeState.RegisterKnownPieceTypeOverride(typeName);
		}

		public static void UnregisterKnownPieceTypeOverride(string typeName)
		{
			VeiledRecipeState.UnregisterKnownPieceTypeOverride(typeName);
		}

		public static bool IsPieceActuallyKnown(Player player, Piece piece)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null)
			{
				return VeiledRecipeState.IsPieceActuallyKnown(player, piece);
			}
			return false;
		}

		public static bool IsMaterialKnown(Requirement requirement)
		{
			return IsMaterialKnown(Player.m_localPlayer, requirement);
		}

		public static bool IsMaterialKnown(Player player, Requirement requirement)
		{
			if ((Object)(object)player != (Object)null && requirement != null)
			{
				return VeiledRecipeState.IsMaterialKnown(player, requirement);
			}
			return false;
		}

		public static bool KnowsRecipeStationRequirement(Recipe recipe, int quality)
		{
			return KnowsRecipeStationRequirement(Player.m_localPlayer, recipe, quality);
		}

		public static bool KnowsRecipeStationRequirement(Player player, Recipe recipe, int quality)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null)
			{
				return VeiledRecipeState.KnowsRecipeStationRequirement(player, recipe, quality);
			}
			return false;
		}

		public static bool KnowsPieceStationRequirement(Piece piece)
		{
			return KnowsPieceStationRequirement(Player.m_localPlayer, piece);
		}

		public static bool KnowsPieceStationRequirement(Player player, Piece piece)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null)
			{
				return VeiledRecipeState.KnowsPieceStationRequirement(player, piece);
			}
			return false;
		}
	}
}
namespace ServerSync
{
	[PublicAPI]
	internal abstract class OwnConfigEntryBase
	{
		public object? LocalBaseValue;

		public bool SynchronizedConfig = true;

		public abstract Conf