Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ForgeReclaimer v1.0.1
BepInEx/plugins/Helgi.ForgeReclaimer/Helgi.ForgeReclaimer.dll
Decompiled 2 weeks agousing 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) { } } }