Decompiled source of ForgeReclaimer v1.0.1

BepInEx/plugins/Helgi.ForgeReclaimer/Helgi.ForgeReclaimer.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Helgi.ForgeReclaimer
{
	internal static class EpicLootIntegration
	{
		private static MethodInfo _tryGetRarity;

		private static MethodInfo _isMagicItem;

		private static bool _loggedAvailable;

		private static bool _loggedFailure;

		public static float GetExperienceMultiplier(ItemData item)
		{
			if (item == null || !EnsureApi())
			{
				return 1f;
			}
			try
			{
				object[] array = new object[2] { item, 0 };
				object obj = _tryGetRarity.Invoke(null, array);
				if (!(obj is bool) || !(bool)obj)
				{
					return 1f;
				}
				switch ((int)array[1])
				{
				case 0:
					return RecycleConfig.EpicLootMagicExperienceMultiplier.Value;
				case 1:
					return RecycleConfig.EpicLootRareExperienceMultiplier.Value;
				case 2:
					return RecycleConfig.EpicLootEpicExperienceMultiplier.Value;
				case 3:
					return RecycleConfig.EpicLootLegendaryExperienceMultiplier.Value;
				case 4:
					return RecycleConfig.EpicLootMythicExperienceMultiplier.Value;
				case 5:
					return RecycleConfig.EpicLootAncientExperienceMultiplier.Value;
				default:
					RecyclePlugin.Log.LogWarning((object)$"Epic Loot returned unknown item rarity {(int)array[1]}; using normal Recycling XP.");
					return 1f;
				}
			}
			catch (Exception ex)
			{
				if (!_loggedFailure)
				{
					_loggedFailure = true;
					RecyclePlugin.Log.LogWarning((object)("Epic Loot rarity lookup failed; using normal Recycling XP. " + ex.Message));
				}
				return 1f;
			}
		}

		public static bool IsMagicItem(ItemData item)
		{
			if (item == null || !EnsureApi() || _isMagicItem == null)
			{
				return false;
			}
			try
			{
				object obj = _isMagicItem.Invoke(null, new object[1] { item });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				if (!_loggedFailure)
				{
					_loggedFailure = true;
					RecyclePlugin.Log.LogWarning((object)("Epic Loot magic-item lookup failed; treating the item as non-magic. " + ex.Message));
				}
				return false;
			}
		}

		private static bool EnsureApi()
		{
			if (_tryGetRarity != null)
			{
				return true;
			}
			Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "EpicLoot")?.GetType("EpicLoot.API");
			_tryGetRarity = type?.GetMethod("TryGetRarity", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
			{
				typeof(ItemData),
				typeof(int).MakeByRefType()
			}, null);
			_isMagicItem = type?.GetMethod("IsMagicItem", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(ItemData) }, null);
			if (_tryGetRarity == null)
			{
				return false;
			}
			if (!_loggedAvailable)
			{
				_loggedAvailable = true;
				RecyclePlugin.Log.LogInfo((object)"Epic Loot detected: Recycling XP now uses its configured rarity multipliers.");
			}
			return true;
		}
	}
	internal static class RecycleConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> FailWeight;

		public static ConfigEntry<float> PoorWeight;

		public static ConfigEntry<float> NormalWeight;

		public static ConfigEntry<float> GoodWeight;

		public static ConfigEntry<float> JackpotWeight;

		public static ConfigEntry<string> PoorRange;

		public static ConfigEntry<string> NormalRange;

		public static ConfigEntry<string> GoodRange;

		public static ConfigEntry<float> SkillFailReduction;

		public static ConfigEntry<bool> EnableRecyclingSkill;

		public static ConfigEntry<string> SkillProgressionPreset;

		public static ConfigEntry<bool> DurabilityAffectsYield;

		public static ConfigEntry<float> SkillGain;

		public static ConfigEntry<float> SkillJackpotBonus;

		public static ConfigEntry<float> CraftingSkillGain;

		public static ConfigEntry<float> EpicLootMagicExperienceMultiplier;

		public static ConfigEntry<bool> AllowEpicLootMagicItemsToRecycle;

		public static ConfigEntry<float> EpicLootRareExperienceMultiplier;

		public static ConfigEntry<float> EpicLootEpicExperienceMultiplier;

		public static ConfigEntry<float> EpicLootLegendaryExperienceMultiplier;

		public static ConfigEntry<float> EpicLootMythicExperienceMultiplier;

		public static ConfigEntry<float> EpicLootAncientExperienceMultiplier;

		public static ConfigEntry<string> AllowedItemTypes;

		public static ConfigEntry<float> YieldMultiplier;

		public static ConfigEntry<bool> CapAtCraftingCost;

		public static ConfigEntry<string> BlockedItems;

		public static ConfigEntry<string> BlockedMaterials;

		public static ConfigEntry<string> MaterialMultipliers;

		private static HashSet<ItemType> _allowedTypes;

		private static HashSet<string> _blockedItems;

		private static HashSet<string> _blockedMaterials;

		private static Dictionary<string, float> _materialMultipliers;

		private static string _lastUnknownProgressionPreset;

		private static readonly Dictionary<ConfigEntry<string>, (float min, float max)> _ranges = new Dictionary<ConfigEntry<string>, (float, float)>();

		public static void Bind(ConfigFile cfg)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Expected O, but got Unknown
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Expected O, but got Unknown
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Expected O, but got Unknown
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Expected O, but got Unknown
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Expected O, but got Unknown
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Expected O, but got Unknown
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Expected O, but got Unknown
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Expected O, but got Unknown
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Expected O, but got Unknown
			//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Expected O, but got Unknown
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Expected O, but got Unknown
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Expected O, but got Unknown
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_0472: Expected O, but got Unknown
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a1: Expected O, but got Unknown
			//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f9: Expected O, but got Unknown
			//IL_051e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Expected O, but got Unknown
			//IL_054d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0557: Expected O, but got Unknown
			//IL_05de: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e8: Expected O, but got Unknown
			//IL_0609: Unknown result type (might be due to invalid IL or missing references)
			//IL_0613: Expected O, but got Unknown
			//IL_0638: Unknown result type (might be due to invalid IL or missing references)
			//IL_0642: Expected O, but got Unknown
			//IL_0667: Unknown result type (might be due to invalid IL or missing references)
			//IL_0671: Expected O, but got Unknown
			//IL_0696: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a0: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			Enabled = cfg.Bind<bool>("General", "Enabled", true, new ConfigDescription("Show the Recycle tab at crafting stations.", (AcceptableValueBase)null, new object[1] { val }));
			AcceptableValueRange<float> val2 = new AcceptableValueRange<float>(0f, 1000f);
			FailWeight = cfg.Bind<float>("Outcome", "FailWeight", 20f, new ConfigDescription("Weight of 'crumbled' (0% of the ingredient back).", (AcceptableValueBase)(object)val2, new object[1] { val }));
			PoorWeight = cfg.Bind<float>("Outcome", "PoorWeight", 30f, new ConfigDescription("Weight of 'poor' (25-50% back).", (AcceptableValueBase)(object)val2, new object[1] { val }));
			NormalWeight = cfg.Bind<float>("Outcome", "NormalWeight", 30f, new ConfigDescription("Weight of 'normal' (50-75% back).", (AcceptableValueBase)(object)val2, new object[1] { val }));
			GoodWeight = cfg.Bind<float>("Outcome", "GoodWeight", 15f, new ConfigDescription("Weight of 'good' (75-99% back).", (AcceptableValueBase)(object)val2, new object[1] { val }));
			JackpotWeight = cfg.Bind<float>("Outcome", "JackpotWeight", 5f, new ConfigDescription("Weight of 'jackpot' (100% back, ignores durability).", (AcceptableValueBase)(object)val2, new object[1] { val }));
			EnableRecyclingSkill = cfg.Bind<bool>("Skill", "EnableRecyclingSkill", true, new ConfigDescription("Enable the Recycling skill, its experience gain, and its outcome bonuses. Disabling it preserves existing skill levels but makes recycling use the base outcome weights.", (AcceptableValueBase)null, new object[1] { val }));
			SkillProgressionPreset = cfg.Bind<string>("Skill", "ProgressionPreset", "Balanced", new ConfigDescription("Recycling XP pace: Vanilla = 1, Slow = 5, Balanced = 10, Fast = 20, Custom = use SkillGain. This changes XP only; material returns and outcome odds are configured separately.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "Vanilla", "Slow", "Balanced", "Fast", "Custom" }), new object[1] { val }));
			SkillFailReduction = cfg.Bind<float>("Skill", "SkillFailReduction", 0.5f, new ConfigDescription("At Recycling skill 100 the fail weight is reduced by this fraction (scales linearly with level); the removed weight goes to 'good'.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { val }));
			SkillJackpotBonus = cfg.Bind<float>("Skill", "SkillJackpotBonus", 1f, new ConfigDescription("At Recycling skill 100 the jackpot weight is increased by this fraction (1 = doubled; scales linearly with level).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			SkillGain = cfg.Bind<float>("Skill", "SkillGain", 10f, new ConfigDescription("Base Recycling XP per item when ProgressionPreset is Custom. Final XP is this value x item quality x Epic Loot rarity multiplier (if applicable).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { val }));
			CraftingSkillGain = cfg.Bind<float>("Skill", "CraftingSkillGain", 0f, new ConfigDescription("Optional Crafting skill XP per recycled item (0 = recycling only trains Recycling).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { val }));
			AllowEpicLootMagicItemsToRecycle = cfg.Bind<bool>("EpicLoot", "AllowMagicItemsToRecycle", false, new ConfigDescription("Allow Epic Loot magic items in Recycle. They return only their normal recipe materials; use Epic Loot's Sacrifice/Disenchant systems for magical materials. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)null, new object[1] { val }));
			EpicLootMagicExperienceMultiplier = cfg.Bind<float>("EpicLoot", "MagicExperienceMultiplier", 1.25f, new ConfigDescription("Recycling XP multiplier for Epic Loot Magic items. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			EpicLootRareExperienceMultiplier = cfg.Bind<float>("EpicLoot", "RareExperienceMultiplier", 1.5f, new ConfigDescription("Recycling XP multiplier for Epic Loot Rare items. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			EpicLootEpicExperienceMultiplier = cfg.Bind<float>("EpicLoot", "EpicExperienceMultiplier", 2f, new ConfigDescription("Recycling XP multiplier for Epic Loot Epic items. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			EpicLootLegendaryExperienceMultiplier = cfg.Bind<float>("EpicLoot", "LegendaryExperienceMultiplier", 3f, new ConfigDescription("Recycling XP multiplier for Epic Loot Legendary items. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			EpicLootMythicExperienceMultiplier = cfg.Bind<float>("EpicLoot", "MythicExperienceMultiplier", 4f, new ConfigDescription("Recycling XP multiplier for Epic Loot Mythic items. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			EpicLootAncientExperienceMultiplier = cfg.Bind<float>("EpicLoot", "AncientExperienceMultiplier", 5f, new ConfigDescription("Recycling XP multiplier for Epic Loot Ancient items. Has no effect unless Epic Loot is installed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			DurabilityAffectsYield = cfg.Bind<bool>("Yield", "DurabilityAffectsYield", true, new ConfigDescription("Worn items give less: yield is scaled by 50% + 50% x durability.", (AcceptableValueBase)null, new object[1] { val }));
			AllowedItemTypes = cfg.Bind<string>("Yield", "AllowedItemTypes", "OneHandedWeapon,TwoHandedWeapon,TwoHandedWeaponLeft,Bow,Shield,Helmet,Chest,Legs,Shoulder,Utility,Tool,Trinket", new ConfigDescription("Item types that can be recycled (ItemDrop.ItemData.ItemType names). Stackable items are never recyclable.", (AcceptableValueBase)null, new object[1] { val }));
			AllowedItemTypes.SettingChanged += delegate
			{
				_allowedTypes = null;
			};
			PoorRange = cfg.Bind<string>("Outcome", "PoorRange", "0.25-0.5", new ConfigDescription("Fraction of the max returned on a 'poor' roll (min-max, 0-1).", (AcceptableValueBase)null, new object[1] { val }));
			NormalRange = cfg.Bind<string>("Outcome", "NormalRange", "0.5-0.75", new ConfigDescription("Fraction of the max returned on a 'normal' roll (min-max, 0-1).", (AcceptableValueBase)null, new object[1] { val }));
			GoodRange = cfg.Bind<string>("Outcome", "GoodRange", "0.75-0.99", new ConfigDescription("Fraction of the max returned on a 'good' roll (min-max, 0-1). Jackpot is always 1.", (AcceptableValueBase)null, new object[1] { val }));
			ConfigEntry<string>[] array = new ConfigEntry<string>[3] { PoorRange, NormalRange, GoodRange };
			for (int num = 0; num < array.Length; num++)
			{
				array[num].SettingChanged += delegate
				{
					_ranges.Clear();
				};
			}
			YieldMultiplier = cfg.Bind<float>("Yield", "YieldMultiplier", 1f, new ConfigDescription("Scales every amount recycling can return (2 = double, 0.5 = half).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { val }));
			CapAtCraftingCost = cfg.Bind<bool>("Yield", "CapAtCraftingCost", true, new ConfigDescription("Never return more of a material than the item cost to make (stops craft -> recycle duplication when YieldMultiplier > 1).", (AcceptableValueBase)null, new object[1] { val }));
			BlockedItems = cfg.Bind<string>("Blocking", "BlockedItems", "", new ConfigDescription("Item prefab names that can never be recycled, comma separated (e.g. SwordCheat, AxeJotunBane).", (AcceptableValueBase)null, new object[1] { val }));
			BlockedMaterials = cfg.Bind<string>("Blocking", "BlockedMaterials", "", new ConfigDescription("Material prefab names never given back, comma separated (e.g. DragonTear, Eitr). The item can still be recycled.", (AcceptableValueBase)null, new object[1] { val }));
			MaterialMultipliers = cfg.Bind<string>("Blocking", "MaterialMultipliers", "", new ConfigDescription("Per-material multiplier on top of YieldMultiplier, separated by semicolons or new lines, e.g. Iron:0.5; Silver:0.25 (0 = blocked; comma decimal marks are accepted).", (AcceptableValueBase)null, new object[1] { val }));
			BlockedItems.SettingChanged += delegate
			{
				_blockedItems = null;
			};
			BlockedMaterials.SettingChanged += delegate
			{
				_blockedMaterials = null;
			};
			MaterialMultipliers.SettingChanged += delegate
			{
				_materialMultipliers = null;
			};
		}

		private static IEnumerable<string> SplitList(string value)
		{
			return from s in value.Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim() into s
				where s.Length > 0
				select s;
		}

		private static IEnumerable<string> SplitPairs(string value)
		{
			return from s in value.Split(new char[3] { ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim() into s
				where s.Length > 0
				select s;
		}

		private static bool TryParseDecimal(string text, out float value)
		{
			if (float.TryParse((text ?? "").Trim().Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		public static bool IsItemBlocked(string prefabName)
		{
			if (_blockedItems == null)
			{
				_blockedItems = new HashSet<string>(SplitList(BlockedItems.Value), StringComparer.OrdinalIgnoreCase);
			}
			if (prefabName != null)
			{
				return _blockedItems.Contains(prefabName);
			}
			return false;
		}

		public static float RecyclingSkillGain()
		{
			switch ((SkillProgressionPreset.Value ?? "").Trim().ToLowerInvariant())
			{
			case "vanilla":
				return 1f;
			case "slow":
				return 5f;
			case "balanced":
				return 10f;
			case "fast":
				return 20f;
			case "custom":
				return SkillGain.Value;
			default:
			{
				string text = SkillProgressionPreset.Value ?? "";
				if (_lastUnknownProgressionPreset != text)
				{
					_lastUnknownProgressionPreset = text;
					RecyclePlugin.Log.LogWarning((object)$"ProgressionPreset '{text}' is unknown; using Custom SkillGain ({SkillGain.Value}).");
				}
				return SkillGain.Value;
			}
			}
		}

		public static float MaterialMultiplier(string prefabName)
		{
			if (_blockedMaterials == null)
			{
				_blockedMaterials = new HashSet<string>(SplitList(BlockedMaterials.Value), StringComparer.OrdinalIgnoreCase);
			}
			if (_blockedMaterials.Contains(prefabName))
			{
				return 0f;
			}
			if (_materialMultipliers == null)
			{
				_materialMultipliers = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
				foreach (string item in SplitPairs(MaterialMultipliers.Value))
				{
					string[] array = item.Split(new char[1] { ':' });
					if (array.Length == 2 && TryParseDecimal(array[1], out var value))
					{
						_materialMultipliers[array[0].Trim()] = Math.Max(0f, value);
					}
					else
					{
						RecyclePlugin.Log.LogWarning((object)("MaterialMultipliers: can't parse '" + item + "' (expected Name:0.5; Name:0,5 is also accepted)"));
					}
				}
			}
			if (!_materialMultipliers.TryGetValue(prefabName, out var value2))
			{
				return 1f;
			}
			return value2;
		}

		public static (float min, float max) Range(ConfigEntry<string> entry)
		{
			if (_ranges.TryGetValue(entry, out (float, float) value))
			{
				return value;
			}
			string[] array = entry.Value.Split(new char[1] { '-' });
			if (array.Length == 2 && TryParseDecimal(array[0], out var value2) && TryParseDecimal(array[1], out var value3))
			{
				value2 = Mathf.Clamp01(value2);
				value3 = Mathf.Clamp01(value3);
				value = (Math.Min(value2, value3), Math.Max(value2, value3));
			}
			else
			{
				RecyclePlugin.Log.LogWarning((object)$"{((ConfigEntryBase)entry).Definition.Key}: can't parse '{entry.Value}', using default {((ConfigEntryBase)entry).DefaultValue}");
				string[] array2 = ((string)((ConfigEntryBase)entry).DefaultValue).Split(new char[1] { '-' });
				TryParseDecimal(array2[0], out var value4);
				TryParseDecimal(array2[1], out var value5);
				value = (Mathf.Clamp01(Math.Min(value4, value5)), Mathf.Clamp01(Math.Max(value4, value5)));
			}
			_ranges[entry] = value;
			return value;
		}

		public static bool IsAllowedType(ItemType type)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (_allowedTypes == null)
			{
				_allowedTypes = new HashSet<ItemType>();
				string[] array = AllowedItemTypes.Value.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string text in array)
				{
					if (Enum.TryParse<ItemType>(text.Trim(), ignoreCase: true, out ItemType result) && Enum.IsDefined(typeof(ItemType), result))
					{
						_allowedTypes.Add(result);
					}
					else
					{
						RecyclePlugin.Log.LogWarning((object)("AllowedItemTypes: '" + text.Trim() + "' is not a defined ItemType and was ignored."));
					}
				}
			}
			return _allowedTypes.Contains(type);
		}
	}
	internal static class RecycleLocalization
	{
		private const string Prefix = "Translations.";

		public static void Register()
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected I4, but got Unknown
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			Assembly assembly = typeof(RecycleLocalization).Assembly;
			int num = 0;
			string[] manifestResourceNames = assembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				if (!text.StartsWith("Translations.") || !text.EndsWith(".json"))
				{
					continue;
				}
				string text2 = text.Substring("Translations.".Length, text.Length - "Translations.".Length - ".json".Length);
				using (Stream stream = assembly.GetManifestResourceStream(text))
				{
					using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
					string text3 = streamReader.ReadToEnd();
					localization.AddJsonFile(text2, text3);
					Match match = Regex.Match(text3, "\\\"hfr_skill\\\"\\s*:\\s*\\\"(?<value>(?:\\\\.|[^\\\"])*)\\\"");
					if (match.Success)
					{
						string text4 = $"skill_{(int)RecycleSkill.Type}";
						localization.AddTranslation(ref text2, ref text4, Regex.Unescape(match.Groups["value"].Value));
					}
				}
				num++;
			}
			RecyclePlugin.Log.LogInfo((object)$"Loaded {num} translations");
		}
	}
	[BepInPlugin("helgi.forgereclaimer", "Helgi's Forge Reclaimer", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class RecyclePlugin : BaseUnityPlugin
	{
		public const string Guid = "helgi.forgereclaimer";

		public const string Name = "Helgi's Forge Reclaimer";

		public const string Version = "1.0.1";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			RecycleConfig.Bind(((BaseUnityPlugin)this).Config);
			RecycleSkill.Register();
			RecycleLocalization.Register();
			new Harmony("helgi.forgereclaimer").PatchAll(typeof(RecyclePlugin).Assembly);
			Log.LogInfo((object)"Helgi's Forge Reclaimer 1.0.1 loaded");
		}
	}
	internal static class Recycler
	{
		public enum Outcome
		{
			Nothing,
			Poor,
			Normal,
			Good,
			Jackpot
		}

		public struct Yield
		{
			public ItemDrop Resource;

			public int Cost;

			public float MaxExact;

			public int Max => Mathf.Max(1, Mathf.CeilToInt(MaxExact - 0.0001f));
		}

		public static bool IsInUse(Player player, ItemData item)
		{
			if (!item.m_equipped && item != ((Humanoid)player).m_hiddenLeftItem)
			{
				return item == ((Humanoid)player).m_hiddenRightItem;
			}
			return true;
		}

		public static Recipe GetRecipe(ItemData item, CraftingStation station)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (item == null || (Object)(object)station == (Object)null || station.m_upgrader)
			{
				return null;
			}
			if (item.m_shared.m_maxStackSize > 1)
			{
				return null;
			}
			if (!RecycleConfig.IsAllowedType(item.m_shared.m_itemType))
			{
				return null;
			}
			if (!RecycleConfig.AllowEpicLootMagicItemsToRecycle.Value && EpicLootIntegration.IsMagicItem(item))
			{
				return null;
			}
			Recipe recipe = ObjectDB.instance.GetRecipe(item);
			if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || recipe.m_resources == null || recipe.m_resources.Length == 0)
			{
				return null;
			}
			if (recipe.m_requireOnlyOneIngredient)
			{
				return null;
			}
			if (recipe.m_amount > 1)
			{
				return null;
			}
			CraftingStation requiredStation = recipe.GetRequiredStation(item.m_quality);
			if ((Object)(object)requiredStation == (Object)null || requiredStation.m_name != station.m_name)
			{
				return null;
			}
			if (RecycleConfig.IsItemBlocked(((Object)((Component)recipe.m_item).gameObject).name))
			{
				return null;
			}
			if (GetYields(recipe, item).Count == 0)
			{
				return null;
			}
			return recipe;
		}

		public static bool StationLevelOk(Recipe recipe, ItemData item, CraftingStation station)
		{
			return station.GetLevel(true) >= recipe.GetRequiredStationLevel(item.m_quality);
		}

		public static List<Yield> GetYields(Recipe recipe, ItemData item)
		{
			List<Yield> list = new List<Yield>();
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if ((Object)(object)val.m_resItem == (Object)null || !val.m_recover || val.m_upgraderResource)
				{
					continue;
				}
				int num = 0;
				for (int j = 1; j <= item.m_quality; j++)
				{
					num += val.GetAmount(j);
				}
				if (num <= 0)
				{
					continue;
				}
				float num2 = RecycleConfig.YieldMultiplier.Value * RecycleConfig.MaterialMultiplier(((Object)((Component)val.m_resItem).gameObject).name);
				if (!(num2 <= 0f))
				{
					float num3 = (float)num * num2;
					if (RecycleConfig.CapAtCraftingCost.Value)
					{
						num3 = Mathf.Min(num3, (float)num);
					}
					list.Add(new Yield
					{
						Resource = val.m_resItem,
						Cost = num,
						MaxExact = num3
					});
				}
			}
			return list;
		}

		public static float DurabilityFactor(ItemData item)
		{
			if (!RecycleConfig.DurabilityAffectsYield.Value || !item.m_shared.m_useDurability)
			{
				return 1f;
			}
			float durabilityPercentage = item.GetDurabilityPercentage();
			if (float.IsNaN(durabilityPercentage) || float.IsInfinity(durabilityPercentage))
			{
				return 1f;
			}
			return 0.5f + 0.5f * Mathf.Clamp01(durabilityPercentage);
		}

		public static float[] GetWeights(Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			float num = ((RecycleConfig.EnableRecyclingSkill.Value && (Object)(object)player != (Object)null) ? ((Character)player).GetSkillFactor(RecycleSkill.Type) : 0f);
			float value = RecycleConfig.FailWeight.Value;
			float num2 = value * num * RecycleConfig.SkillFailReduction.Value;
			float[] array = new float[5]
			{
				value - num2,
				RecycleConfig.PoorWeight.Value,
				RecycleConfig.NormalWeight.Value,
				RecycleConfig.GoodWeight.Value + num2,
				RecycleConfig.JackpotWeight.Value * (1f + num * RecycleConfig.SkillJackpotBonus.Value)
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (!(array[i] > 0f))
				{
					array[i] = 0f;
				}
			}
			return array;
		}

		public static int[] GetPercentages(float[] weights)
		{
			float sum = Mathf.Max(0.0001f, weights.Sum());
			return weights.Select((float w) => Mathf.RoundToInt(100f * w / sum)).ToArray();
		}

		private static Outcome RollOutcome(float[] weights)
		{
			float num = weights.Sum();
			if (num <= 0f)
			{
				return Outcome.Nothing;
			}
			float num2 = Random.Range(0f, num);
			for (int i = 0; i < weights.Length; i++)
			{
				if (num2 < weights[i])
				{
					return (Outcome)i;
				}
				num2 -= weights[i];
			}
			return Outcome.Normal;
		}

		private static float RollFraction(Outcome outcome)
		{
			(float, float) tuple;
			switch (outcome)
			{
			case Outcome.Poor:
				tuple = RecycleConfig.Range(RecycleConfig.PoorRange);
				break;
			case Outcome.Normal:
				tuple = RecycleConfig.Range(RecycleConfig.NormalRange);
				break;
			case Outcome.Good:
				tuple = RecycleConfig.Range(RecycleConfig.GoodRange);
				break;
			case Outcome.Jackpot:
				return 1f;
			default:
				return 0f;
			}
			return Random.Range(tuple.Item1, tuple.Item2);
		}

		private static int StochasticRound(float value)
		{
			int num = Mathf.FloorToInt(value);
			return num + ((Random.value < value - (float)num) ? 1 : 0);
		}

		public static bool Recycle(Player player, CraftingStation station, ItemData item)
		{
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).GetInventory();
			Recipe recipe = GetRecipe(item, station);
			if ((Object)(object)recipe == (Object)null || !inventory.ContainsItem(item))
			{
				return false;
			}
			if (!StationLevelOk(recipe, item, station) || !station.CheckUsable(player, true))
			{
				return false;
			}
			if (IsInUse(player, item))
			{
				((Character)player).Message((MessageType)2, "$hfr_equipped", 0, (Sprite)null, false);
				return false;
			}
			List<Yield> yields = GetYields(recipe, item);
			float num = DurabilityFactor(item);
			float[] weights = GetWeights(player);
			int quality = item.m_quality;
			string name = item.m_shared.m_name;
			inventory.RemoveItem(item);
			Outcome outcome = Outcome.Nothing;
			float num2 = 0f;
			float num3 = 0f;
			foreach (Yield item2 in yields)
			{
				Outcome outcome2 = RollOutcome(weights);
				float num4 = RollFraction(outcome2);
				int num5 = Mathf.Clamp(StochasticRound((outcome2 == Outcome.Jackpot) ? item2.MaxExact : (item2.MaxExact * num4 * num)), 0, item2.Max);
				if (outcome2 > outcome)
				{
					outcome = outcome2;
				}
				num2 += (float)num5;
				num3 += item2.MaxExact;
				if (num5 > 0)
				{
					Give(player, item2.Resource, num5);
				}
			}
			Outcome outcome3 = ((!(num3 <= 0f) && !(num2 <= 0f)) ? ((outcome == Outcome.Jackpot && num2 >= num3) ? Outcome.Jackpot : ((num2 / num3 >= 0.75f) ? Outcome.Good : ((!(num2 / num3 >= 0.45f)) ? Outcome.Poor : Outcome.Normal))) : Outcome.Nothing);
			((Character)player).Message((MessageType)2, Localization.instance.Localize(outcome3 switch
			{
				Outcome.Nothing => "$hfr_result_nothing", 
				Outcome.Poor => "$hfr_result_poor", 
				Outcome.Good => "$hfr_result_good", 
				Outcome.Jackpot => "$hfr_result_jackpot", 
				_ => "$hfr_result_normal", 
			}, new string[1] { Localization.instance.Localize(name) }), 0, (Sprite)null, false);
			((outcome3 == Outcome.Nothing) ? station.m_craftItemDoneFailEffects : station.m_craftItemDoneEffects).Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			if (RecycleConfig.EnableRecyclingSkill.Value)
			{
				float experienceMultiplier = EpicLootIntegration.GetExperienceMultiplier(item);
				((Character)player).RaiseSkill(RecycleSkill.Type, RecycleConfig.RecyclingSkillGain() * (float)quality * experienceMultiplier);
			}
			if (RecycleConfig.CraftingSkillGain.Value > 0f)
			{
				((Character)player).RaiseSkill(station.m_craftingSkill, RecycleConfig.CraftingSkillGain.Value);
			}
			return true;
		}

		private static void Give(Player player, ItemDrop resource, int amount)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).GetInventory();
			string name = resource.m_itemData.m_shared.m_name;
			Sprite icon = resource.m_itemData.GetIcon();
			int num = Mathf.Max(1, resource.m_itemData.m_shared.m_maxStackSize);
			Transform transform = ((Component)player).transform;
			int num2 = amount;
			while (num2 > 0)
			{
				int num3 = Mathf.Min(num2, num);
				num2 -= num3;
				ItemData val = resource.m_itemData.Clone();
				val.m_stack = num3;
				val.m_worldLevel = Game.m_worldLevel;
				val.m_dropPrefab = ((Component)resource).gameObject;
				if (!inventory.CanAddItem(val, -1) || !inventory.AddItem(val))
				{
					ItemDrop.DropItem(val, num3, transform.position + transform.forward + Vector3.up, transform.rotation);
				}
			}
			((Character)player).Message((MessageType)1, "$msg_added " + name, amount, icon, false);
		}
	}
	[HarmonyPatch]
	internal static class RecycleSkill
	{
		public const string Identifier = "helgi.forgereclaimer.recycling";

		private static SkillConfig _config;

		public static SkillType Type { get; private set; }

		public static void Register()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0036: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			_config = new SkillConfig
			{
				Identifier = "helgi.forgereclaimer.recycling",
				Name = "$hfr_skill",
				Description = "$hfr_skill_desc",
				IncreaseStep = 1f
			};
			Type = SkillManager.Instance.AddSkill(_config);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static void SetIcon(ObjectDB __instance)
		{
			if (_config != null && !((Object)(object)_config.Icon != (Object)null))
			{
				GameObject itemPrefab = __instance.GetItemPrefab("Hammer");
				if ((Object)(object)itemPrefab != (Object)null)
				{
					_config.Icon = itemPrefab.GetComponent<ItemDrop>().m_itemData.GetIcon();
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui))]
	internal static class RecycleTab
	{
		private static Button _tab;

		private static bool _active;

		private static bool _recycling;

		private static TMP_Text _styleSource;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void CreateTab(InventoryGui __instance)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			Button tabUpgrade = __instance.m_tabUpgrade;
			GameObject val = Object.Instantiate<GameObject>(((Component)tabUpgrade).gameObject, ((Component)tabUpgrade).transform.parent);
			((Object)val).name = "TabRecycle";
			_active = false;
			_recycling = false;
			RectTransform val2 = (RectTransform)((Component)__instance.m_tabCraft).transform;
			RectTransform val3 = (RectTransform)((Component)tabUpgrade).transform;
			((RectTransform)val.transform).anchoredPosition = val3.anchoredPosition + (val3.anchoredPosition - val2.anchoredPosition);
			Component[] componentsInChildren = val.GetComponentsInChildren<Component>(true);
			foreach (Component val4 in componentsInChildren)
			{
				string name = ((object)val4).GetType().Name;
				if (name == "Localize" || name == "UIGamePad")
				{
					Object.Destroy((Object)(object)val4);
				}
			}
			_tab = val.GetComponent<Button>();
			_tab.onClick = new ButtonClickedEvent();
			((UnityEvent)_tab.onClick).AddListener((UnityAction)delegate
			{
				Activate(__instance);
			});
			((Selectable)_tab).interactable = true;
			_styleSource = ((Component)__instance.m_tabCraft).GetComponentInChildren<TMP_Text>(true);
			SetLabel();
		}

		private static void SetLabel()
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_tab == (Object)null)
			{
				return;
			}
			CultureInfo culture = ((Localization.instance.GetSelectedLanguage() == "Turkish") ? new CultureInfo("tr-TR") : CultureInfo.InvariantCulture);
			string text = Localization.instance.Localize("$hfr_tab").ToUpper(culture);
			TMP_Text[] componentsInChildren = ((Component)_tab).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if ((Object)(object)_styleSource != (Object)null)
				{
					val.font = _styleSource.font;
					val.fontSharedMaterial = _styleSource.fontSharedMaterial;
					val.fontSize = _styleSource.fontSize;
					val.fontStyle = _styleSource.fontStyle;
					val.enableAutoSizing = _styleSource.enableAutoSizing;
					val.characterSpacing = _styleSource.characterSpacing;
				}
				val.text = text;
			}
		}

		private static void Activate(InventoryGui gui)
		{
			gui.SetActiveGroup(gui.m_uiGroups[3], true);
			_active = true;
			((Selectable)gui.m_tabCraft).interactable = true;
			((Selectable)gui.m_tabUpgrade).interactable = true;
			((Selectable)_tab).interactable = false;
			SetLabel();
			gui.UpdateCraftingPanel(false);
		}

		private static void Deactivate()
		{
			_active = false;
			if ((Object)(object)_tab != (Object)null)
			{
				((Selectable)_tab).interactable = true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("OnTabCraftPressed")]
		private static void OnCraftTab()
		{
			Deactivate();
		}

		[HarmonyPrefix]
		[HarmonyPatch("OnTabUpgradePressed")]
		private static void OnUpgradeTab()
		{
			Deactivate();
		}

		[HarmonyPostfix]
		[HarmonyPatch("Hide")]
		private static void OnHide(InventoryGui __instance)
		{
			if (_active)
			{
				((Selectable)__instance.m_tabCraft).interactable = false;
				((Selectable)__instance.m_tabUpgrade).interactable = true;
			}
			Deactivate();
			_recycling = false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateCraftingPanel")]
		private static void UpdateTabVisibility(InventoryGui __instance)
		{
			if (!((Object)(object)_tab == (Object)null))
			{
				CraftingStation val = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Player.m_localPlayer.GetCurrentCraftingStation() : null);
				bool flag = RecycleConfig.Enabled.Value && (Object)(object)val != (Object)null && val.m_hasCraftTab && !val.m_upgrader;
				((Component)_tab).gameObject.SetActive(flag);
				if (!flag && _active)
				{
					((Selectable)__instance.m_tabCraft).interactable = false;
					((Selectable)__instance.m_tabUpgrade).interactable = true;
					Deactivate();
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateRecipeList")]
		private static bool BuildRecycleList(InventoryGui __instance)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!_active)
			{
				return true;
			}
			foreach (RecipeDataPair availableRecipe in __instance.m_availableRecipes)
			{
				RecipeDataPair current = availableRecipe;
				Object.Destroy((Object)(object)((RecipeDataPair)(ref current)).InterfaceElement);
			}
			__instance.m_availableRecipes.Clear();
			Player localPlayer = Player.m_localPlayer;
			CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
			List<(Recipe, ItemData, bool)> list = new List<(Recipe, ItemData, bool)>();
			foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems())
			{
				Recipe recipe = Recycler.GetRecipe(allItem, currentCraftingStation);
				if (!((Object)(object)recipe == (Object)null))
				{
					list.Add((recipe, allItem, !Recycler.IsInUse(localPlayer, allItem) && Recycler.StationLevelOk(recipe, allItem, currentCraftingStation)));
				}
			}
			foreach (var item in from e in list
				orderby e.can descending, Localization.instance.Localize(e.item.m_shared.m_name), e.item.m_quality
				select e)
			{
				__instance.AddRecipeToList(localPlayer, item.Item1, item.Item2, item.Item3);
			}
			float num = Mathf.Max(__instance.m_recipeListBaseSize, (float)__instance.m_availableRecipes.Count * __instance.m_recipeListSpace);
			__instance.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, num);
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("UpdateRecipe")]
		private static void ShowRecycleDetails(InventoryGui __instance, Player player)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			if (!_active)
			{
				return;
			}
			RecipeDataPair selectedRecipe = __instance.m_selectedRecipe;
			if ((Object)(object)((RecipeDataPair)(ref selectedRecipe)).Recipe == (Object)null || ((RecipeDataPair)(ref selectedRecipe)).ItemData == null)
			{
				((Selectable)__instance.m_craftButton).interactable = false;
				return;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			ItemData itemData = ((RecipeDataPair)(ref selectedRecipe)).ItemData;
			Localization instance = Localization.instance;
			((Component)__instance.m_itemCraftType).gameObject.SetActive(true);
			__instance.m_itemCraftType.text = instance.Localize("$hfr_crafttype", new string[2]
			{
				instance.Localize(itemData.m_shared.m_name),
				itemData.m_quality.ToString()
			});
			((Component)__instance.m_variantButton).gameObject.SetActive(false);
			int[] percentages = Recycler.GetPercentages(Recycler.GetWeights(player));
			float num = Recycler.DurabilityFactor(itemData);
			string text = "\n\n";
			if (RecycleConfig.EnableRecyclingSkill.Value)
			{
				int num2 = Mathf.FloorToInt(((Character)player).GetSkillLevel(RecycleSkill.Type));
				text = text + instance.Localize("$hfr_skill_line", new string[1] { num2.ToString() }) + "\n";
			}
			text += instance.Localize("$hfr_odds", percentages.Select((int p) => p.ToString()).ToArray());
			if (num < 0.999f)
			{
				text += instance.Localize("$hfr_worn", new string[1] { Mathf.RoundToInt(num * 100f).ToString() });
			}
			__instance.m_recipeDecription.text = instance.Localize(ItemData.GetTooltip(itemData, itemData.m_quality, false, (float)Game.m_worldLevel, -1, false)) + text;
			List<Recycler.Yield> yields = Recycler.GetYields(((RecipeDataPair)(ref selectedRecipe)).Recipe, itemData);
			GameObject[] recipeRequirementList = __instance.m_recipeRequirementList;
			int num3 = Mathf.Max(1, Mathf.CeilToInt((float)yields.Count / (float)recipeRequirementList.Length));
			int num4 = (int)Time.fixedTime % num3 * recipeRequirementList.Length;
			for (int num5 = 0; num5 < recipeRequirementList.Length; num5++)
			{
				Transform transform = recipeRequirementList[num5].transform;
				int num6 = num4 + num5;
				if (num6 >= yields.Count)
				{
					InventoryGui.HideRequirement(transform);
					continue;
				}
				ItemData itemData2 = yields[num6].Resource.m_itemData;
				Image component = ((Component)transform.Find("res_icon")).GetComponent<Image>();
				TMP_Text component2 = ((Component)transform.Find("res_name")).GetComponent<TMP_Text>();
				TMP_Text component3 = ((Component)transform.Find("res_amount")).GetComponent<TMP_Text>();
				((Component)component).gameObject.SetActive(true);
				((Component)component2).gameObject.SetActive(true);
				((Component)component3).gameObject.SetActive(true);
				component.sprite = itemData2.GetIcon();
				((Graphic)component).color = Color.white;
				component2.text = instance.Localize(itemData2.m_shared.m_name);
				component3.text = $"0-{yields[num6].Max}";
				((Graphic)component3).color = Color.white;
				((Component)transform).GetComponent<UITooltip>().m_text = component2.text;
			}
			int requiredStationLevel = ((RecipeDataPair)(ref selectedRecipe)).Recipe.GetRequiredStationLevel(itemData.m_quality);
			((Component)__instance.m_minStationLevelIcon).gameObject.SetActive(true);
			__instance.m_minStationLevelText.text = requiredStationLevel.ToString();
			((Graphic)__instance.m_minStationLevelText).color = (((Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.GetLevel(true) >= requiredStationLevel) ? __instance.m_minStationLevelBasecolor : Color.red);
			bool flag = (Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.CheckUsable(player, false);
			bool flag2 = Recycler.IsInUse(player, itemData);
			bool interactable = flag && !flag2 && Recycler.StationLevelOk(((RecipeDataPair)(ref selectedRecipe)).Recipe, itemData, currentCraftingStation) && ((Humanoid)player).GetInventory().ContainsItem(itemData);
			((Selectable)__instance.m_craftButton).interactable = interactable;
			((Component)__instance.m_craftButton).GetComponentInChildren<TMP_Text>().text = instance.Localize("$hfr_button");
			((Component)__instance.m_craftButton).GetComponent<UITooltip>().m_text = (flag2 ? instance.Localize("$hfr_equipped") : ((!flag) ? instance.Localize("$msg_missingstation") : ""));
		}

		[HarmonyPrefix]
		[HarmonyPatch("OnCraftPressed")]
		private static bool StartRecycle(InventoryGui __instance)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (!_active)
			{
				_recycling = false;
				return true;
			}
			RecipeDataPair selectedRecipe = __instance.m_selectedRecipe;
			if ((Object)(object)((RecipeDataPair)(ref selectedRecipe)).Recipe == (Object)null || ((RecipeDataPair)(ref selectedRecipe)).ItemData == null)
			{
				return false;
			}
			__instance.SetActiveGroup(__instance.m_uiGroups[3], true);
			__instance.m_craftRecipe = ((RecipeDataPair)(ref selectedRecipe)).Recipe;
			__instance.m_craftUpgradeItem = ((RecipeDataPair)(ref selectedRecipe)).ItemData;
			__instance.m_multiCrafting = false;
			__instance.m_craftTimer = 0f;
			_recycling = true;
			CraftingStation currentCraftingStation = Player.m_localPlayer.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null)
			{
				currentCraftingStation.m_craftItemEffects.Create(((Component)Player.m_localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("DoCrafting")]
		private static bool FinishRecycle(InventoryGui __instance, Player player)
		{
			if (!_recycling)
			{
				return true;
			}
			_recycling = false;
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			ItemData craftUpgradeItem = __instance.m_craftUpgradeItem;
			__instance.m_craftRecipe = null;
			__instance.m_craftUpgradeItem = null;
			if ((Object)(object)currentCraftingStation != (Object)null && craftUpgradeItem != null)
			{
				Recycler.Recycle(player, currentCraftingStation, craftUpgradeItem);
			}
			__instance.UpdateCraftingPanel(false);
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}