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 CraftingStorageCompatibility v1.1.2
plugins/MultiCraftAzuCompatibility.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using AdventureBackpacks.API; using AzuCraftyBoxes; using AzuCraftyBoxes.IContainers; using AzuCraftyBoxes.Util.Functions; using BepInEx; using BepInEx.Bootstrap; using EpicLoot_UnityLib; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using MultiCraft; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("MultiCraftAzuCompatibility")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2")] [assembly: AssemblyProduct("MultiCraftAzuCompatibility")] [assembly: AssemblyTitle("MultiCraftAzuCompatibility")] [assembly: AssemblyVersion("1.1.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal static class BackpackAccess { private static readonly Func<bool> CraftEnabled = Bind<bool>("IsCraftFromBackpackEnabled"); private static readonly Func<bool> LeaveOne = Bind<bool>("IsLeaveOneItemInBackpackEnabled"); private static readonly Func<string> Priority = Bind<string>("GetConsumptionPriority"); private static readonly Action<string> Suppress = BindAction("SuppressNativeCrafting"); private static readonly Action<string> Unsuppress = BindAction("UnsuppressNativeCrafting"); [ThreadStatic] private static int paymentDepth; internal static bool Modern => CraftEnabled != null; internal static bool KeepOne => LeaveOne?.Invoke() ?? false; internal static bool First => Priority?.Invoke() == "BackpackFirst"; private static Action<string> BindAction(string name) { MethodInfo method = typeof(ABAPI).GetMethod(name, new Type[1] { typeof(string) }); if (!(method == null)) { return (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), method); } return null; } internal static void BeginCheckedPayment(out bool __state) { __state = RuneforgingApi.Active && ABAPI.IsLoaded() && Suppress != null && Unsuppress != null; if (__state && paymentDepth++ == 0) { Suppress("local.multicraft.azucraftyboxes.compatibility"); } } internal static void EndCheckedPayment(bool __state) { if (__state && --paymentDepth == 0) { Unsuppress("local.multicraft.azucraftyboxes.compatibility"); } } private static Func<T> Bind<T>(string name) { MethodInfo method = typeof(ABAPI).GetMethod(name, Type.EmptyTypes); if (!(method == null)) { return (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), method); } return null; } internal static bool TryGet(Player player, out Inventory inventory) { inventory = null; if (!Object.op_Implicit((Object)(object)player) || !ABAPI.IsLoaded() || !ABAPI.IsBackpackEquipped(player)) { return false; } if (CraftEnabled != null && !CraftEnabled()) { return false; } if (!ABAPI.TryGetEquippedBackpackInventory(player, out inventory) || inventory == null) { return false; } if (CraftEnabled != null) { return true; } Inventory candidate = inventory; if (Plugin.UseStorage(((Humanoid)player).GetInventory()) && API.GetNearbyContainers<Player>(player, AzuCraftyBoxesPlugin.mRange.Value).Any((IContainer c) => c != null && c.GetInventory() == candidate)) { return true; } inventory = null; return false; } } internal static class CraftPaymentGate { private static readonly FieldInfo RecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe"); private static readonly FieldInfo UpgradeField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem"); private static readonly FieldInfo MultiField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting"); private static readonly FieldInfo AmountField = AccessTools.Field(typeof(InventoryGui), "m_multiCraftAmount"); internal static void Insert(List<CodeInstruction> result, CodeInstruction selected, CodeInstruction amount, ILGenerator generator) { //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Expected O, but got Unknown //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Expected O, but got Unknown //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Expected O, but got Unknown //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Expected O, but got Unknown CodeInstruction[] array = result.Where((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, AccessTools.Field(typeof(InventoryGui), "m_craftVariant"), false)).ToArray(); CodeInstruction[] array2 = result.Where((CodeInstruction i) => CodeInstructionExtensions.Calls(i, AccessTools.Method(typeof(Player), "ConsumeResources", (Type[])null, (Type[])null))).ToArray(); CodeInstruction[] array3 = result.Where((CodeInstruction i) => CodeInstructionExtensions.Calls(i, AccessTools.Method(typeof(Inventory), "ItemCheated", new Type[3] { typeof(Requirement[]), typeof(int), typeof(bool) }, (Type[])null))).ToArray(); if (array.Length != 1 || array2.Length != 1 || array3.Length != 1 || result.Any((CodeInstruction i) => i.blocks.Count != 0)) { throw new InvalidOperationException("Unrecognized crafting transaction boundary"); } int index = result.IndexOf(array[0]) - 1; if (result[index].opcode != OpCodes.Ldarg_0) { throw new InvalidOperationException("Unrecognized crafting output boundary"); } array2[0].opcode = OpCodes.Call; array2[0].operand = AccessTools.Method(typeof(CraftPaymentGate), "FinishResources", (Type[])null, (Type[])null); array3[0].opcode = OpCodes.Call; array3[0].operand = AccessTools.Method(typeof(CraftPaymentGate), "MaterialsCheated", (Type[])null, (Type[])null); Label label = generator.DefineLabel(); CodeInstruction val = result[index]; CodeInstruction val2 = new CodeInstruction(OpCodes.Ldarg_0, (object)null); val2.labels.AddRange(val.labels); val.labels.Clear(); val.labels.Add(label); result.InsertRange(index, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[7] { val2, new CodeInstruction(OpCodes.Ldarg_1, (object)null), new CodeInstruction(selected.opcode, selected.operand), new CodeInstruction(amount.opcode, amount.operand), new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CraftPaymentGate), "PayBeforeOutput", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Brtrue, (object)label), new CodeInstruction(OpCodes.Ret, (object)null) }); } private static bool PayBeforeOutput(InventoryGui gui, Player player, ItemData selected, int amount) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown if (player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) { return true; } SingleIngredientPayment.Scope current = SingleIngredientPayment.Current; if (current == null || current.Paid) { throw new InvalidOperationException("Invalid crafting transaction scope"); } Recipe val = (Recipe)RecipeField.GetValue(gui); ItemData val2 = (ItemData)UpgradeField.GetValue(gui); CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); bool flag = Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader; if (flag && !val.m_resources.Any((Requirement r) => r.m_upgraderResource && Object.op_Implicit((Object)(object)r.m_resItem))) { return false; } int quality = ((val2 == null) ? 1 : (val2.m_quality + 1)); int multiplier = ((!(bool)MultiField.GetValue(gui)) ? 1 : ((int)AmountField.GetValue(gui))); IEnumerable<InventoryPaymentPlan.Cost> enumerable2; if (selected != null) { IEnumerable<InventoryPaymentPlan.Cost> enumerable = new InventoryPaymentPlan.Cost[1] { new InventoryPaymentPlan.Cost { Item = selected.m_dropPrefab.GetComponent<ItemDrop>(), Amount = amount, Quality = selected.m_quality } }; enumerable2 = enumerable; } else { enumerable2 = InventoryPaymentPlan.Requirements(val.m_resources, quality, multiplier, flag); } IEnumerable<InventoryPaymentPlan.Cost> costs = enumerable2; Inventory inventory; bool chestsOnly = selected != null && !((Humanoid)player).GetInventory().ContainsItem(selected) && (!BackpackAccess.Modern || !BackpackAccess.TryGet(player, out inventory)); InventoryPaymentPlan inventoryPaymentPlan = InventoryPaymentPlan.Create(player, costs, chestsOnly); current.Cheated = ((Humanoid)player).GetInventory().ItemCheated(val.m_resources, -1, true); if (inventoryPaymentPlan == null || !inventoryPaymentPlan.Commit(notify: false)) { return false; } current.Plan = inventoryPaymentPlan; current.Paid = true; return true; } private static bool MaterialsCheated(Inventory inventory, Requirement[] resources, int quality, bool worldLevel) { if (SingleIngredientPayment.Current?.Plan == null) { return inventory.ItemCheated(resources, quality, worldLevel); } return SingleIngredientPayment.Current.Cheated; } private static void FinishResources(Player player, Requirement[] resources, int quality, int itemQuality, int multiplier) { if (SingleIngredientPayment.Current?.Plan == null) { player.ConsumeResources(resources, quality, itemQuality, multiplier); } } } internal static class ImproveInteraction { private sealed class Selection { internal ItemData Item; internal float Scroll; } private static readonly FieldInfo Selected = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static readonly FieldInfo Available = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes"); private static readonly PropertyInfo Item = Selected.FieldType.GetProperty("ItemData"); private static readonly MethodInfo SetRecipe = AccessTools.Method(typeof(InventoryGui), "SetRecipe", new Type[2] { typeof(int), typeof(bool) }, (Type[])null); private static readonly FieldInfo Queued = AccessTools.Field(typeof(MultiCraft_Logic), "WasCrafting"); private static readonly FieldInfo[] Controls = new FieldInfo[3] { AccessTools.Field(typeof(MultiCraft_UI), "plusButton"), AccessTools.Field(typeof(MultiCraft_UI), "minusButton"), AccessTools.Field(typeof(MultiCraft_UI), "amountText") }; private static readonly Dictionary<GameObject, bool> Hidden = new Dictionary<GameObject, bool>(); internal static void Install(Harmony harmony) { //IL_0099: 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) //IL_00bb: Expected O, but got Unknown //IL_00bb: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown if (Selected == null || Available == null || Item == null || SetRecipe == null || Queued == null || Array.Exists(Controls, (FieldInfo f) => f == null)) { throw new MissingMemberException("Improve/MultiCraft UI contract changed"); } harmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.UiType, "BuildImproveList", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ImproveInteraction), "RememberSelection", (Type[])null), new HarmonyMethod(typeof(ImproveInteraction), "RestoreSelection", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.UiType, "Open", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ImproveInteraction), "Opened", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.StateType, "Reset", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ImproveInteraction), "RestoreControls", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); PatchPrefix(harmony, typeof(MultiCraft_UI), "RefreshCustomButtonStates", "RefreshControls"); PatchPrefix(harmony, typeof(MultiCraft_UI), "AreCustomButtonsUsable", "ButtonsUsable"); PatchPrefix(harmony, typeof(MultiCraft_UI), "OnCraftButtonPressed", "AllowBatch"); PatchPrefix(harmony, typeof(MultiCraft_Logic), "StartCraftingLogic", "AllowBatch"); PatchPrefix(harmony, typeof(MultiCraft_Logic), "Update", "AllowBatch"); } private static void PatchPrefix(Harmony h, Type type, string target, string patch) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown h.Patch((MethodBase)AccessTools.Method(type, target, (Type[])null, (Type[])null), new HarmonyMethod(typeof(ImproveInteraction), patch, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static void RememberSelection(InventoryGui gui, bool focusView, out Selection __state) { __state = null; if (!(!RuneforgingApi.Active || focusView) && !((Object)(object)gui == (Object)null)) { object? value = Item.GetValue(Selected.GetValue(gui), null); ItemData val = (ItemData)((value is ItemData) ? value : null); if (val != null) { __state = new Selection { Item = val, Scroll = (Object.op_Implicit((Object)(object)gui.m_recipeListScroll) ? gui.m_recipeListScroll.value : 0f) }; } } } private static void RestoreSelection(InventoryGui gui, Selection __state) { if (__state == null || !RuneforgingApi.Active || (Object)(object)gui == (Object)null) { return; } IList list = (IList)Available.GetValue(gui); int num = -1; for (int i = 0; i < list.Count; i++) { if (Item.GetValue(list[i], null) == __state.Item) { num = i; break; } } SetRecipe.Invoke(gui, new object[2] { num, false }); if (Object.op_Implicit((Object)(object)gui.m_recipeListScroll)) { gui.m_recipeListScroll.value = __state.Scroll; } } private static void SuspendBatch() { MultiCraft_Logic instance = MultiCraft_Logic.instance; Queued.SetValue(instance, false); if (instance.CurrentCraftAmount != 1) { instance.CurrentCraftAmount = 1; } } private static bool AllowBatch() { if (!RuneforgingApi.Active) { return true; } SuspendBatch(); return false; } private static bool ButtonsUsable(ref bool __result) { if (!RuneforgingApi.Active) { return true; } __result = false; return false; } private static void Opened() { if (RuneforgingApi.Active) { RefreshControls(MultiCraft_UI.instance); } } private static bool RefreshControls(MultiCraft_UI __instance) { if (!RuneforgingApi.Active) { RestoreControls(); return true; } SuspendBatch(); FieldInfo[] controls = Controls; for (int i = 0; i < controls.Length; i++) { object? value = controls[i].GetValue(__instance); RectTransform val = (RectTransform)((value is RectTransform) ? value : null); if (Object.op_Implicit((Object)(object)val)) { GameObject gameObject = ((Component)val).gameObject; if (!Hidden.ContainsKey(gameObject)) { Hidden.Add(gameObject, gameObject.activeSelf); } gameObject.SetActive(false); } } return false; } internal static void RestoreControls() { foreach (KeyValuePair<GameObject, bool> item in Hidden) { if (Object.op_Implicit((Object)(object)item.Key)) { item.Key.SetActive(item.Value); } } Hidden.Clear(); } } internal sealed class InventoryPaymentPlan { internal sealed class Cost { internal ItemDrop Item; internal int Amount; internal int Quality = -1; } private sealed class Source { internal Inventory Inventory; internal IContainer Container; internal bool Backpack; } private sealed class Debit { internal Source Source; internal ItemData Item; internal int Before; internal int Amount; internal string Name; internal int Quality; } private readonly List<Debit> debits = new List<Debit>(); private readonly Player player; private readonly bool storage; private bool committed; private bool notified; private readonly bool keepOne = BackpackAccess.KeepOne; private readonly int worldLevel = Game.m_worldLevel; private static readonly Func<int, int> Spendable = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), AccessTools.Method(typeof(Boxes), "CheckAndDecrement", (Type[])null, (Type[])null)); private static readonly Action<Inventory, bool, bool> Changed = (Action<Inventory, bool, bool>)Delegate.CreateDelegate(typeof(Action<Inventory, bool, bool>), AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null)); private InventoryPaymentPlan(Player player, bool storage) { this.player = player; this.storage = storage; } internal static bool Supported(IContainer source) { string fullName = ((object)source).GetType().FullName; if (!(fullName == "AzuCraftyBoxes.IContainers.VanillaContainer") && !(fullName == "AzuCraftyBoxes.IContainers.BackpackContainer")) { return fullName == "AzuCraftyBoxes.IContainers.GemBagContainer"; } return true; } private static List<Source> Sources(Player player, bool chestsOnly) { bool num = Plugin.UseStorage(((Humanoid)player).GetInventory()); List<Source> list = new List<Source>(); HashSet<Inventory> hashSet = new HashSet<Inventory> { ((Humanoid)player).GetInventory() }; if (!chestsOnly) { list.Add(new Source { Inventory = ((Humanoid)player).GetInventory() }); } if (BackpackAccess.TryGet(player, out var inventory) && hashSet.Add(inventory)) { list.Insert((!BackpackAccess.First) ? list.Count : 0, new Source { Inventory = inventory, Backpack = true }); } if (num) { foreach (IContainer nearbyContainer in API.GetNearbyContainers<Player>(player, AzuCraftyBoxesPlugin.mRange.Value)) { if (nearbyContainer != null && Supported(nearbyContainer)) { Inventory inventory2 = nearbyContainer.GetInventory(); if (inventory2 != null && hashSet.Add(inventory2)) { list.Add(new Source { Inventory = inventory2, Container = nearbyContainer }); } } } } return list; } private static int Stock(Source source, string name, int quality) { return (from i in source.Inventory.GetAllItems() where i.m_shared.m_name == name && (quality < 0 || i.m_quality == quality) && i.m_worldLevel >= Game.m_worldLevel && !i.m_equipped select i).Sum((ItemData i) => i.m_stack); } private static int Available(Source source, string name, int quality) { int num = Stock(source, name, quality); if (!source.Backpack) { if (source.Container != null) { return Spendable(num); } return num; } return Math.Max(0, num - (BackpackAccess.KeepOne ? 1 : 0)); } internal static int CountAvailable(Player player, string name) { int num = 0; foreach (Source item in Sources(player, chestsOnly: false)) { ItemData val = ((IEnumerable<ItemData>)item.Inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i.m_shared.m_name == name)); if (val != null && (item.Container == null || (Object.op_Implicit((Object)(object)val.m_dropPrefab) && API.CanItemBePulled(item.Container.GetPrefabName(), ((Object)val.m_dropPrefab).name)))) { num = checked(num + Available(item, name, -1)); } } return num; } internal static IEnumerable<Inventory> CandidateInventories(Player player) { return from s in Sources(player, chestsOnly: false) select s.Inventory; } internal static InventoryPaymentPlan Create(Player player, IEnumerable<Cost> costs, bool chestsOnly = false) { if (!Object.op_Implicit((Object)(object)player) || costs == null) { return null; } InventoryPaymentPlan inventoryPaymentPlan = new InventoryPaymentPlan(player, Plugin.UseStorage(((Humanoid)player).GetInventory())); List<Source> list = Sources(player, chestsOnly); foreach (Cost cost in costs.OrderByDescending((Cost c) => c.Quality >= 0)) { if (cost.Amount <= 0) { continue; } if (!Object.op_Implicit((Object)(object)cost.Item)) { return null; } int num = cost.Amount; string name = cost.Item.m_itemData.m_shared.m_name; foreach (Source source in list) { if (source.Container != null && !API.CanItemBePulled(source.Container.GetPrefabName(), ((Object)((Component)cost.Item).gameObject).name)) { continue; } int num2 = inventoryPaymentPlan.debits.Where((Debit d) => d.Source.Inventory == source.Inventory && d.Name == name && (cost.Quality < 0 || d.Quality == cost.Quality)).Sum((Debit d) => d.Amount); int val = Math.Max(0, Available(source, name, cost.Quality) - num2); int num3 = Math.Min(num, val); foreach (ItemData item in source.Inventory.GetAllItems()) { if (item.m_shared.m_name != name || (cost.Quality >= 0 && item.m_quality != cost.Quality) || item.m_worldLevel < Game.m_worldLevel || item.m_equipped) { continue; } Debit debit = inventoryPaymentPlan.debits.FirstOrDefault((Debit d) => d.Item == item && d.Source.Inventory == source.Inventory); int num4 = Math.Min(num3, item.m_stack - (debit?.Amount ?? 0)); if (num4 > 0) { if (debit == null) { debit = new Debit { Source = source, Item = item, Before = item.m_stack, Name = name, Quality = item.m_quality }; inventoryPaymentPlan.debits.Add(debit); } debit.Amount += num4; num -= num4; num3 -= num4; if (num3 == 0) { break; } } } if (num == 0) { break; } } if (num != 0) { return null; } } return inventoryPaymentPlan; } internal bool Commit(bool notify = true) { if (committed || !Object.op_Implicit((Object)(object)player) || worldLevel != Game.m_worldLevel || (storage && !Plugin.UseStorage(((Humanoid)player).GetInventory()))) { return false; } foreach (Debit debit in debits) { if (debit.Source.Backpack && (keepOne != BackpackAccess.KeepOne || !BackpackAccess.TryGet(player, out var inventory) || inventory != debit.Source.Inventory)) { return false; } if (!debit.Source.Inventory.ContainsItem(debit.Item) || debit.Item.m_stack != debit.Before || debit.Item.m_shared.m_name != debit.Name || debit.Item.m_quality != debit.Quality || debit.Item.m_worldLevel < worldLevel || debit.Item.m_equipped || (debit.Source.Container != null && debit.Source.Container.GetInventory() != debit.Source.Inventory)) { return false; } } foreach (var item2 in from d in debits group d by new { d.Source, d.Name }) { if (item2.Sum((Debit d) => d.Amount) > Available(item2.Key.Source, item2.Key.Name, -1)) { return false; } } committed = true; foreach (Debit debit2 in debits) { ItemData item = debit2.Item; item.m_stack -= debit2.Amount; if (debit2.Item.m_stack == 0) { debit2.Source.Inventory.GetAllItems().Remove(debit2.Item); } } if (notify) { Notify(); } return true; } internal void Notify() { if (!committed || notified) { return; } notified = true; foreach (Source item in from d in debits select d.Source into s group s by s.Inventory into g select g.First()) { try { if (item.Container != null) { item.Container.Save(); } else { Changed(item.Inventory, arg2: false, arg3: false); } } catch (Exception ex) { Debug.LogError((object)("[CraftingStorageCompatibility] Paid inventory notification failed: " + ex)); } } } internal static IEnumerable<Cost> Requirements(Requirement[] requirements, int quality, int multiplier, bool upgrader = false) { return from r in requirements where r != null && Object.op_Implicit((Object)(object)r.m_resItem) && r.m_upgraderResource == upgrader select new Cost { Item = r.m_resItem, Amount = checked(r.GetAmount(quality) * multiplier) }; } internal static IEnumerable<Cost> Improve(IList costs) { foreach (object cost in costs) { yield return new Cost { Item = RuneforgingApi.Resource(cost), Amount = RuneforgingApi.Amount(cost) }; } } } internal static class LegacyImprovePayment { internal static void Install(Harmony harmony) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.CostType, "PlayerHasCost", (Type[])null, (Type[])null), new HarmonyMethod(typeof(LegacyImprovePayment), "Have", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.CostType, "Consume", (Type[])null, (Type[])null), new HarmonyMethod(typeof(LegacyImprovePayment), "Consume", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.UiType, "CompleteImprove", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(LegacyImprovePayment), "RequirePayment", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } private static bool Have(Player player, IList cost, ref bool __result) { __result = InventoryPaymentPlan.Create(player, InventoryPaymentPlan.Improve(cost)) != null; return false; } internal static bool TryConsume(Player player, IList cost) { if (!Object.op_Implicit((Object)(object)player)) { return false; } if (player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) { return true; } return InventoryPaymentPlan.Create(player, InventoryPaymentPlan.Improve(cost))?.Commit() ?? false; } private static bool Consume(Player player, IList cost) { if (!TryConsume(player, cost)) { throw new InvalidOperationException("Improve payment could not be completed"); } return false; } internal static IEnumerable<CodeInstruction> RequirePayment(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); CodeInstruction[] array = list.Where((CodeInstruction i) => CodeInstructionExtensions.Calls(i, AccessTools.Method(RuneforgingApi.CostType, "Consume", (Type[])null, (Type[])null))).ToArray(); CodeInstruction[] array2 = list.Where((CodeInstruction i) => i.opcode == OpCodes.Ldstr && object.Equals(i.operand, "$msg_missingrequirement")).ToArray(); if (array.Length != 1 || array2.Length != 1) { throw new InvalidOperationException("Unrecognized legacy Improve completion"); } int num = list.IndexOf(array2[0]) - 2; if (num < 0 || list[num].opcode != OpCodes.Ldarg_1 || list[num + 1].opcode != OpCodes.Ldc_I4_2) { throw new InvalidOperationException("Unrecognized legacy missing-material branch"); } Label label = generator.DefineLabel(); list[num].labels.Add(label); int num2 = list.IndexOf(array[0]); array[0].opcode = OpCodes.Call; array[0].operand = AccessTools.Method(typeof(LegacyImprovePayment), "TryConsume", (Type[])null, (Type[])null); list.Insert(num2 + 1, new CodeInstruction(OpCodes.Brfalse, (object)label)); return list; } } internal static class ModernImprove { internal static readonly MethodInfo HaveMethod = AccessTools.Method(typeof(Player), "HaveRequirements", new Type[4] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) }, (Type[])null); private static readonly MethodInfo InvokeMethod = AccessTools.Method(typeof(MethodBase), "Invoke", new Type[2] { typeof(object), typeof(object[]) }, (Type[])null); private static readonly FieldInfo SelectedRecipe = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static PropertyInfo selectedItem; internal static void Install(Harmony harmony) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_00ec: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown if (HaveMethod == null || SelectedRecipe == null) { throw new MissingMethodException("Unsupported Valheim recipe API"); } Type type = RuneforgingApi.CostType.Assembly.GetType("BalrondItemRarity.Resources.ImproveResourceService", throwOnError: true); Patch(harmony, type, "ResolveExternalContract", "FixContractLookup"); Patch(harmony, type, "ExternalHasRequirements", "FixRequirementInvocation"); Patch(harmony, type, "ExternalConsume", "FixConsumptionInvocation"); string[] array = new string[3] { "UpdateRecipe", "UpdateCraftingPanel", "OnCraftPressed" }; foreach (string text in array) { harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), text, (Type[])null, (Type[])null), new HarmonyMethod(typeof(BackpackAccess), "BeginCheckedPayment", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BackpackAccess), "EndCheckedPayment", (Type[])null) { priority = 0 }, (HarmonyMethod)null); } array = new string[2] { "UpdateRecipe", "UpdateCraftingPanel" }; foreach (string text2 in array) { harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), text2, (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ModernImprove), "ShowCounts", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } AccessTools.Method(type, "RefreshExternalContract", (Type[])null, (Type[])null).Invoke(null, null); } private static void Patch(Harmony harmony, Type type, string method, string transpiler) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { throw new MissingMethodException(type.FullName, method); } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ModernImprove), transpiler, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } internal static IEnumerable<CodeInstruction> FixContractLookup(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); int num = 0; foreach (CodeInstruction item in list) { if (!(item.opcode != OpCodes.Ldsfld) && item.operand is FieldInfo fieldInfo && !(fieldInfo.Name != "HaveRequirementsMethod") && !(fieldInfo.DeclaringType?.FullName != "BalrondItemRarity.Resources.ImproveResourceService")) { item.operand = AccessTools.Field(typeof(ModernImprove), "HaveMethod"); num++; } } if (num != 3) { throw new InvalidOperationException("Unsupported Runeforging contract lookup: " + num); } return list; } internal static IEnumerable<CodeInstruction> FixRequirementInvocation(IEnumerable<CodeInstruction> instructions) { return ReplaceInvoke(instructions, "InvokeRequirements", 1); } internal static IEnumerable<CodeInstruction> FixConsumptionInvocation(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> result = ReplaceInvoke(instructions, "InvokeConsumption", 2).ToList(); foreach (int item in from i in Enumerable.Range(0, result.Count) where CodeInstructionExtensions.Calls(result[i], AccessTools.Method(typeof(ModernImprove), "InvokeConsumption", (Type[])null, (Type[])null)) select i) { if (result[item + 1].opcode != OpCodes.Pop || result[item + 2].opcode != OpCodes.Ldc_I4_1 || result[item + 1].labels.Count != 0 || result[item + 2].labels.Count != 0) { throw new InvalidOperationException("Unrecognized Improve payment result"); } result[item + 1].opcode = OpCodes.Nop; result[item + 2].opcode = OpCodes.Nop; } return result; } private static IEnumerable<CodeInstruction> ReplaceInvoke(IEnumerable<CodeInstruction> instructions, string replacement, int expected) { List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); int num = 0; foreach (CodeInstruction item in list) { if (CodeInstructionExtensions.Calls(item, InvokeMethod)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(ModernImprove), replacement, (Type[])null, (Type[])null); num++; } } if (num != expected) { throw new InvalidOperationException("Unsupported Runeforging invocation: " + replacement + " / " + num); } return list; } private static object InvokeRequirements(MethodBase method, object target, object[] args) { if (!(method != HaveMethod)) { Player val = (Player)((target is Player) ? target : null); if (val != null && args.Length == 3 && args[0] is Requirement[] requirements) { return InventoryPaymentPlan.Create(val, InventoryPaymentPlan.Requirements(requirements, 1, 1)) != null; } } throw new InvalidOperationException("Unexpected Improve requirements contract"); } private static bool InvokeConsumption(MethodBase method, object target, object[] args) { object obj = ((target is Player) ? target : null); if (obj == null || !(args[0] is Requirement[] requirements)) { throw new InvalidOperationException("Unexpected Improve consumption contract"); } return InventoryPaymentPlan.Create((Player)obj, InventoryPaymentPlan.Requirements(requirements, 1, 1))?.Commit() ?? false; } internal static int CountUncached(Player player, List<IContainer> containers, string name) { int num = ((Humanoid)player).GetInventory().CountItems(name, -1, true); HashSet<Inventory> hashSet = new HashSet<Inventory> { ((Humanoid)player).GetInventory() }; HashSet<IContainer> hashSet2 = new HashSet<IContainer>(); foreach (IContainer container in containers) { if (container != null && hashSet2.Add(container)) { Inventory inventory = container.GetInventory(); if (inventory == null || hashSet.Add(inventory)) { num = checked(num + container.ItemCount(name)); } } } return num; } private static void ShowCounts(InventoryGui __instance) { Player localPlayer = Player.m_localPlayer; if (!RuneforgingApi.Active || RuneforgingApi.IsImproving || (Object)(object)localPlayer == (Object)null || (Object)(object)__instance == (Object)null || !((Behaviour)__instance.m_recipeIcon).enabled) { return; } object value = SelectedRecipe.GetValue(__instance); if (value == null) { return; } if (selectedItem == null) { selectedItem = value.GetType().GetProperty("ItemData"); } object? obj = selectedItem?.GetValue(value, null); ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { IList cost = RuneforgingApi.GetCost(val); for (int i = 0; i < cost.Count && i < __instance.m_recipeRequirementList.Length; i++) { Plugin.ShowImproveCount(__instance.m_recipeRequirementList[i].transform, localPlayer, cost[i]); } } } internal static void Cleanup() { } } [BepInPlugin("local.multicraft.azucraftyboxes.compatibility", "Crafting Storage Compatibility", "1.1.2")] [BepInDependency("maximods.valheim.multicraft", "1.5.0")] [BepInDependency("Azumatt.AzuCraftyBoxes", "1.8.19")] [BepInDependency("balrond.astafaraios.Runeforging", "1.0.3")] [BepInDependency("randyknapp.mods.epicloot", "0.14.13")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "local.multicraft.azucraftyboxes.compatibility"; private static Func<bool> shouldPrevent; private Harmony harmony; private Harmony improveHarmony; private Harmony interactionHarmony; private Harmony paymentHarmony; private Harmony stationHarmony; private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Expected O, but got Unknown //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Expected O, but got Unknown try { stationHarmony = new Harmony("local.multicraft.azucraftyboxes.compatibility.station"); if (ReforgedStationGuard.Install(stationHarmony)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Corrected ReforgedPotential nullable crafting-station accesses."); } } catch (Exception ex) { Harmony obj = stationHarmony; if (obj != null) { obj.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogWarning((object)("ReforgedPotential station guard was not installed: " + ex.Message)); } try { Type type = typeof(AzuCraftyBoxesPlugin).Assembly.GetType("AzuCraftyBoxes.Util.Functions.MiscFunctions", throwOnError: true); shouldPrevent = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), AccessTools.Method(type, "ShouldPrevent", (Type[])null, (Type[])null)); MethodInfo methodInfo = AccessTools.Method(((object)Chainloader.PluginInfos["maximods.valheim.multicraft"].Instance).GetType().Assembly.GetType("MultiCraft.Helper", throwOnError: true), "GetAvailableItems", new Type[1] { typeof(string) }, (Type[])null); if (methodInfo == null) { throw new MissingMethodException("MultiCraft.Helper.GetAvailableItems"); } harmony = new Harmony("local.multicraft.azucraftyboxes.compatibility"); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "CountAvailable", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Enabled MultiCraft storage counts."); } catch (Exception ex2) { Harmony obj2 = harmony; if (obj2 != null) { obj2.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not enable MultiCraft storage counts. " + ex2)); return; } try { if (Chainloader.PluginInfos["Azumatt.AzuCraftyBoxes"].Metadata.Version == new Version(1, 8, 19)) { paymentHarmony = new Harmony("local.multicraft.azucraftyboxes.compatibility.payment"); SingleIngredientPayment.Install(paymentHarmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Enabled checked crafting payment before output creation; recipe previews cannot queue charges."); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Azu version not verified for the single-ingredient payment fix; payment patches skipped."); } } catch (Exception ex3) { Harmony obj3 = paymentHarmony; if (obj3 != null) { obj3.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not enable single-ingredient payment protection; all payment patches removed. " + ex3)); } try { Version version = Chainloader.PluginInfos["balrond.astafaraios.Runeforging"].Metadata.Version; if (version == new Version(1, 1, 5) || version == new Version(1, 0, 3)) { RuneforgingApi.Bind(((object)Chainloader.PluginInfos["balrond.astafaraios.Runeforging"].Instance).GetType().Assembly); } improveHarmony = new Harmony("local.multicraft.azucraftyboxes.compatibility.improve"); if (version == new Version(1, 1, 5)) { ModernImprove.Install(improveHarmony); InstallInteraction(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Enabled Runeforging 1.1.5 vanilla-contract adapter and Improve counts; protected Improve methods untouched."); } else if (version != new Version(1, 0, 3)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Runeforging " + version?.ToString() + " has not been verified. Keeping MultiCraft support; leaving Improve to Runeforging.")); } else { LegacyImprovePayment.Install(improveHarmony); PatchImprove("SetupCostRequirement", RuneforgingApi.UiType); improveHarmony.Patch((MethodBase)AccessTools.Method(RuneforgingApi.UiType, "SetupCostRequirement", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ShowImproveCount", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Enabled MultiCraft stock counts and Runeforging Improve storage support. Radius/access/toggle inherited from AzuCraftyBoxes."); InstallInteraction(); } } catch (Exception ex4) { Harmony obj4 = improveHarmony; if (obj4 != null) { obj4.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not enable Improve compatibility; MultiCraft support remains enabled. " + ex4)); } } private void InstallInteraction() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { interactionHarmony = new Harmony("local.multicraft.azucraftyboxes.compatibility.interaction"); ImproveInteraction.Install(interactionHarmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Improve keeps the selected item and uses one improvement per click; MultiCraft batches remain in Craft."); } catch (Exception ex) { Harmony obj = interactionHarmony; if (obj != null) { obj.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not enable Improve UI fixes; storage integration remains enabled. " + ex)); } } private void PatchImprove(string method, Type type) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown improveHarmony.Patch((MethodBase)AccessTools.Method(type, method, (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "RedirectImproveInventory", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } private static IEnumerable<CodeInstruction> RedirectImproveInventory(IEnumerable<CodeInstruction> instructions, MethodBase original) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { list.Add(new CodeInstruction(instruction)); } MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "CountItems", new Type[3] { typeof(string), typeof(int), typeof(bool) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Inventory), "RemoveItem", new Type[4] { typeof(string), typeof(int), typeof(int), typeof(bool) }, (Type[])null); int num = 0; foreach (CodeInstruction item in list) { string text = (CodeInstructionExtensions.Calls(item, methodInfo) ? "CountImproveMaterial" : (CodeInstructionExtensions.Calls(item, methodInfo2) ? "RemoveImproveMaterial" : null)); if (text != null) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(Plugin), text, (Type[])null, (Type[])null); num++; } } if (num != 1) { throw new InvalidOperationException("Unexpected Improve method: " + original.Name + " (" + num + " inventory calls)"); } return list; } internal static bool UseStorage(Inventory inventory) { if ((Object)(object)Player.m_localPlayer != (Object)null && inventory == ((Humanoid)Player.m_localPlayer).GetInventory()) { return !shouldPrevent(); } return false; } private static int CountImproveMaterial(Inventory inventory, string name, int quality, bool matchWorldLevel) { if (!UseStorage(inventory) && !BackpackAccess.TryGet(Player.m_localPlayer, out var _)) { return inventory.CountItems(name, quality, matchWorldLevel); } return InventoryPaymentPlan.CountAvailable(Player.m_localPlayer, name); } private static void RemoveImproveMaterial(Inventory inventory, string name, int amount, int quality, bool worldLevelBased) { if (UseStorage(inventory)) { InventoryManagement.Instance.RemoveItem(name, amount); } else { inventory.RemoveItem(name, amount, quality, worldLevelBased); } } internal static void ShowImproveCount(Transform elementRoot, Player player, object entry) { if ((Object)(object)player == (Object)null || entry == null) { return; } ItemDrop val = RuneforgingApi.Resource(entry); if (!((Object)(object)val == (Object)null)) { Transform obj = elementRoot.Find("res_amount"); TMP_Text val2 = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (!((Object)(object)val2 == (Object)null)) { int num = CountImproveMaterial(((Humanoid)player).GetInventory(), val.m_itemData.m_shared.m_name, -1, matchWorldLevel: true); val2.text = RuneforgingApi.Amount(entry) + "/" + num; val2.enableAutoSizing = true; val2.fontSizeMin = 10f; val2.fontSizeMax = 16f; } } } private static bool CountAvailable(string itemName, ref int __result) { if ((Object)(object)Player.m_localPlayer == (Object)null || (shouldPrevent() && !BackpackAccess.TryGet(Player.m_localPlayer, out var _))) { return true; } __result = InventoryPaymentPlan.CountAvailable(Player.m_localPlayer, itemName); return false; } private void OnDestroy() { Harmony obj = stationHarmony; if (obj != null) { obj.UnpatchSelf(); } Harmony obj2 = paymentHarmony; if (obj2 != null) { obj2.UnpatchSelf(); } Harmony obj3 = interactionHarmony; if (obj3 != null) { obj3.UnpatchSelf(); } ImproveInteraction.RestoreControls(); Harmony obj4 = improveHarmony; if (obj4 != null) { obj4.UnpatchSelf(); } Harmony obj5 = harmony; if (obj5 != null) { obj5.UnpatchSelf(); } ModernImprove.Cleanup(); } } internal static class ReforgedStationGuard { internal static bool Install(Harmony harmony) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown if (!Chainloader.PluginInfos.TryGetValue("akuichi.ReforgedPotential", out var value)) { return false; } if (value.Metadata.Version != new Version(2, 0, 4)) { return false; } MethodInfo methodInfo = AccessTools.Method(((object)value.Instance).GetType().Assembly.GetType("ReforgedPotential.ReforgedPotential+InventoryGuiPatch", throwOnError: true), "DoCraftingPrefix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo.ReturnType != typeof(bool) || methodInfo.GetParameters().Length != 3) { throw new MissingMethodException("ReforgedPotential crafting contract changed"); } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ReforgedStationGuard), "NullableStation", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); return true; } private static bool IsUpgrader(CraftingStation station) { if (Object.op_Implicit((Object)(object)station)) { return station.m_upgrader; } return false; } private static string StationName(Object station) { if (!Object.op_Implicit(station)) { return "(hand crafting)"; } return station.name; } internal static IEnumerable<CodeInstruction> NullableStation(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); CodeInstruction[] array = list.Where((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, AccessTools.Field(typeof(CraftingStation), "m_upgrader"), false)).ToArray(); if (array.Length != 1) { throw new InvalidOperationException("Unrecognized ReforgedPotential station access"); } int num = list.IndexOf(array[0]); int num2 = list.FindIndex(num + 1, (CodeInstruction i) => CodeInstructionExtensions.Calls(i, AccessTools.PropertyGetter(typeof(Object), "name"))); if (num2 < 1 || num2 - num > 30 || !CodeInstructionExtensions.IsLdloc(list[num2 - 1], (LocalBuilder)null) || list[num2 - 1].opcode != list[num - 1].opcode || !object.Equals(list[num2 - 1].operand, list[num - 1].operand)) { throw new InvalidOperationException("Unrecognized ReforgedPotential station diagnostic"); } array[0].opcode = OpCodes.Call; array[0].operand = AccessTools.Method(typeof(ReforgedStationGuard), "IsUpgrader", (Type[])null, (Type[])null); list[num2].opcode = OpCodes.Call; list[num2].operand = AccessTools.Method(typeof(ReforgedStationGuard), "StationName", (Type[])null, (Type[])null); return list; } } internal static class RuneforgingApi { internal static Type CostType; internal static Type UiType; internal static Type StateType; private static Func<bool> active; private static Func<bool> improving; private static Func<ItemData, IList> costs; private static Func<object, ItemDrop> resource; private static Func<object, int> amount; internal static bool Active { get { if (active != null) { return active(); } return false; } } internal static bool IsImproving { get { if (improving != null) { return improving(); } return false; } } internal static IList GetCost(ItemData item) { return costs(item); } internal static ItemDrop Resource(object entry) { return resource(entry); } internal static int Amount(object entry) { return amount(entry); } internal static void Bind(Assembly assembly) { CostType = assembly.GetType("BalrondItemRarity.UI.ImproveCost", throwOnError: true); UiType = assembly.GetType("BalrondItemRarity.UI.ImproveUI", throwOnError: true); StateType = assembly.GetType("BalrondItemRarity.UI.ImproveUIState", throwOnError: true); Type type = assembly.GetType("BalrondItemRarity.UI.ImproveCostEntry", throwOnError: true); active = ReadFlag("Active"); improving = ReadFlag("IsImproving"); ParameterExpression parameterExpression = Expression.Parameter(typeof(ItemData), "item"); costs = Expression.Lambda<Func<ItemData, IList>>(Expression.Convert(Expression.Call(AccessTools.Method(CostType, "GetCost", new Type[1] { typeof(ItemData) }, (Type[])null), parameterExpression), typeof(IList)), new ParameterExpression[1] { parameterExpression }).Compile(); ParameterExpression parameterExpression2 = Expression.Parameter(typeof(object), "entry"); UnaryExpression expression = Expression.Convert(parameterExpression2, type); resource = Expression.Lambda<Func<object, ItemDrop>>(Expression.Field(expression, "Item"), new ParameterExpression[1] { parameterExpression2 }).Compile(); amount = Expression.Lambda<Func<object, int>>(Expression.Field(expression, "Amount"), new ParameterExpression[1] { parameterExpression2 }).Compile(); } private static Func<bool> ReadFlag(string name) { return Expression.Lambda<Func<bool>>(Expression.Field(null, AccessTools.Field(StateType, name)), Array.Empty<ParameterExpression>()).Compile(); } } internal static class SingleIngredientPayment { internal sealed class Scope { internal Scope Parent; internal bool Paid; internal InventoryPaymentPlan Plan; internal bool Cheated; } private static readonly MethodInfo Remove = AccessTools.Method(typeof(Inventory), "RemoveItem", new Type[4] { typeof(string), typeof(int), typeof(int), typeof(bool) }, (Type[])null); private static readonly MethodInfo Add = AccessTools.Method(typeof(ConcurrentBag<LaterConsumption>), "Add", (Type[])null, (Type[])null); [ThreadStatic] private static Scope current; internal static Scope Current => current; internal static void Install(Harmony harmony) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00d7: Expected O, but got Unknown //IL_00d7: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0176: Expected O, but got Unknown //IL_0176: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Expected O, but got Unknown //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Expected O, but got Unknown Assembly assembly = typeof(AzuCraftyBoxesPlugin).Assembly; MethodInfo methodInfo = AccessTools.Method(assembly.GetType("AzuCraftyBoxes.Patches.CheckNearbyForOneIngredientItems", throwOnError: true), "Postfix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(assembly.GetType("AzuCraftyBoxes.Patches.ConsumeLaterConsumptionItemsInventoryGuiDoCraftingPatch", throwOnError: true), "Postfix", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(InventoryGui), "DoCrafting", new Type[1] { typeof(Player) }, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo3 == null) { throw new MissingMethodException("Azu single-ingredient payment contract changed"); } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SingleIngredientPayment), "BeforeLookup", (Type[])null), new HarmonyMethod(typeof(SingleIngredientPayment), "AfterLookup", (Type[])null), new HarmonyMethod(typeof(SingleIngredientPayment), "ScopeEnqueue", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(SingleIngredientPayment), "BackpackLookup", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(SingleIngredientPayment), "Begin", (Type[])null) { priority = 800 }, (HarmonyMethod)null, new HarmonyMethod(typeof(SingleIngredientPayment), "PaymentPoint", (Type[])null) { priority = 0 }, new HarmonyMethod(typeof(SingleIngredientPayment), "End", (Type[])null) { priority = 0 }, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(SingleIngredientPayment), "SkipDelayedPayment", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Recipe), "GetAmount", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SingleIngredientPayment), "GuardMissingIngredient", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "HaveRequirements", new Type[4] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(SingleIngredientPayment), "CheckSelectedIngredient", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ClearQueue(); } internal static void Begin(out Scope __state) { __state = new Scope { Parent = current }; ClearQueue(); current = __state; } internal static void End(Scope __state) { if (__state != null) { __state.Plan?.Notify(); ClearQueue(); current = __state.Parent; } } private static bool SkipDelayedPayment() { return false; } private static void CheckSelectedIngredient(Player __instance, Recipe recipe, bool discover, int qualityLevel, int amount, ref bool __result) { if (!(!__result || discover) && Object.op_Implicit((Object)(object)recipe)) { if (recipe.m_requireOnlyOneIngredient) { int num = default(int); int num2 = default(int); __result = __instance.GetFirstRequiredItem(((Humanoid)__instance).GetInventory(), recipe, qualityLevel, ref num, ref num2, amount) != null; return; } CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation(); __result = InventoryPaymentPlan.Create(__instance, InventoryPaymentPlan.Requirements(recipe.m_resources, qualityLevel, amount, Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader)) != null; } } private static void ClearQueue() { LaterConsumption result; while (ConsumptionManager.PendingConsumptions.TryTake(out result)) { } } private static void Enqueue(ConcurrentBag<LaterConsumption> queue, LaterConsumption entry) { } private static void BeforeLookup(ItemData __7, out bool __state) { __state = __7 == null; } private static void BackpackLookup(Player __instance, Inventory inventory, Recipe recipe, int qualityLevel, ref int amount, ref int extraAmount, int craftMultiplier, ref ItemData __result) { if (inventory != ((Humanoid)__instance).GetInventory() || !BackpackAccess.TryGet(__instance, out var _)) { return; } CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation(); Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (!Object.op_Implicit((Object)(object)val.m_resItem) || val.m_upgraderResource != (Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader)) { continue; } int num = checked(val.GetAmount(qualityLevel) * craftMultiplier); if (num <= 0) { continue; } HashSet<int> hashSet = new HashSet<int>(); foreach (Inventory item in InventoryPaymentPlan.CandidateInventories(__instance)) { foreach (ItemData allItem in item.GetAllItems()) { if (allItem.m_shared.m_name != val.m_resItem.m_itemData.m_shared.m_name || allItem.m_equipped || !hashSet.Add(allItem.m_quality)) { continue; } if (!BackpackAccess.Modern && item == inventory && inventory.CountItems(allItem.m_shared.m_name, allItem.m_quality, true) < num) { hashSet.Remove(allItem.m_quality); continue; } bool chestsOnly = !BackpackAccess.Modern && !inventory.ContainsItem(allItem); if (InventoryPaymentPlan.Create(__instance, new InventoryPaymentPlan.Cost[1] { new InventoryPaymentPlan.Cost { Item = val.m_resItem, Amount = num, Quality = allItem.m_quality } }, chestsOnly) != null) { __result = allItem; amount = num; extraAmount = val.m_extraAmountOnlyOneIngredient; return; } } } } __result = null; amount = 0; extraAmount = 0; } private static List<IContainer> Inventories(Player player) { HashSet<Inventory> hashSet = new HashSet<Inventory>(); List<IContainer> list = new List<IContainer>(); foreach (IContainer nearbyContainer in API.GetNearbyContainers<Player>(player, AzuCraftyBoxesPlugin.mRange.Value)) { Inventory val2 = ((nearbyContainer != null) ? nearbyContainer.GetInventory() : null); if (val2 != null && InventoryPaymentPlan.Supported(nearbyContainer) && val2 != ((Humanoid)player).GetInventory() && hashSet.Add(val2)) { list.Add(nearbyContainer); } } return list; } private static void AfterLookup(Player __0, Inventory __1, Recipe __2, int __3, ref int __4, ref int __5, int __6, ref ItemData __7, bool __state) { if (BackpackAccess.TryGet(__0, out var _) || !__state || !Plugin.UseStorage(__1)) { return; } List<IContainer> list = Inventories(__0); ItemData val = null; CraftingStation currentCraftingStation = __0.GetCurrentCraftingStation(); Requirement[] resources = __2.m_resources; foreach (Requirement val2 in resources) { if (!Object.op_Implicit((Object)(object)val2.m_resItem) || val2.m_upgraderResource != (Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader)) { continue; } int num = val2.GetAmount(__3) * __6; if (num <= 0) { continue; } string name = val2.m_resItem.m_itemData.m_shared.m_name; HashSet<int> hashSet = new HashSet<int>(); foreach (IContainer item in list) { foreach (ItemData allItem in item.GetInventory().GetAllItems()) { if (!(allItem.m_shared.m_name != name) && hashSet.Add(allItem.m_quality) && InventoryPaymentPlan.Create(__0, new InventoryPaymentPlan.Cost[1] { new InventoryPaymentPlan.Cost { Item = val2.m_resItem, Amount = num, Quality = allItem.m_quality } }, chestsOnly: true) != null) { val = allItem; __4 = num; __5 = val2.m_extraAmountOnlyOneIngredient; break; } } if (val != null) { break; } } if (val != null) { break; } } __7 = val; if (val == null) { __4 = 0; __5 = 0; } } internal static IEnumerable<CodeInstruction> ScopeEnqueue(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); List<CodeInstruction> list2 = list.Where((CodeInstruction i) => CodeInstructionExtensions.Calls(i, Add)).ToList(); if (list2.Count != 1) { throw new InvalidOperationException("Unexpected Azu deferred enqueue shape"); } list2[0].opcode = OpCodes.Call; list2[0].operand = AccessTools.Method(typeof(SingleIngredientPayment), "Enqueue", (Type[])null, (Type[])null); return list; } private static int SelectedQuality(ItemData item) { return item?.m_quality ?? 1; } internal static IEnumerable<CodeInstruction> GuardMissingIngredient(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); FieldInfo quality = AccessTools.Field(typeof(ItemData), "m_quality"); CodeInstruction[] array = list.Where((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, quality, false)).ToArray(); if (array.Length != 1 || list.Count((CodeInstruction i) => CodeInstructionExtensions.Calls(i, AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null))) != 1) { throw new InvalidOperationException("Unrecognized single-ingredient amount expression"); } array[0].opcode = OpCodes.Call; array[0].operand = AccessTools.Method(typeof(SingleIngredientPayment), "SelectedQuality", (Type[])null, (Type[])null); return list; } internal static IEnumerable<CodeInstruction> PaymentPoint(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Expected O, but got Unknown List<CodeInstruction> result = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList(); int[] array = (from i in Enumerable.Range(0, result.Count) where CodeInstructionExtensions.Calls(result[i], Remove) select i).ToArray(); if (array.Length != 1) { throw new InvalidOperationException("Ambiguous single-ingredient payment point"); } int num = array[0]; FieldInfo fieldInfo = AccessTools.Field(typeof(ItemData), "m_quality"); if (num < 3 || result[num - 1].opcode != OpCodes.Ldc_I4_1 || !CodeInstructionExtensions.LoadsField(result[num - 2], fieldInfo, false) || !CodeInstructionExtensions.IsLdloc(result[num - 3], (LocalBuilder)null) || result[num].blocks.Count != 0 || result[num - 3].labels.Count != 0 || result[num - 2].labels.Count != 0 || result[num - 1].labels.Count != 0) { throw new InvalidOperationException("Unrecognized selected-ingredient payment expression"); } CodeInstruction val = new CodeInstruction(result[num - 3].opcode, result[num - 3].operand); val.labels.AddRange(result[num].labels); result[num].labels.Clear(); result[num].opcode = OpCodes.Call; result[num].operand = AccessTools.Method(typeof(SingleIngredientPayment), "Pay", (Type[])null, (Type[])null); CodeInstruction val2 = new CodeInstruction(result[num - 4].opcode, result[num - 4].operand); if (!CodeInstructionExtensions.IsLdloc(val2, (LocalBuilder)null)) { throw new InvalidOperationException("Unrecognized ingredient amount"); } result.Insert(num, val); CraftPaymentGate.Insert(result, val, val2, generator); return result; } internal static void Pay(Inventory inventory, string name, int amount, int quality, bool matchWorldLevel, ItemData selected) { if (current == null) { throw new InvalidOperationException("Missing crafting payment scope"); } if (current.Plan != null) { return; } current.Paid = true; ClearQueue(); if (inventory.ContainsItem(selected)) { inventory.RemoveItem(name, amount, quality, matchWorldLevel); return; } InventoryPaymentPlan inventoryPaymentPlan = InventoryPaymentPlan.Create(Player.m_localPlayer, new InventoryPaymentPlan.Cost[1] { new InventoryPaymentPlan.Cost { Item = selected.m_dropPrefab.GetComponent<ItemDrop>(), Amount = amount, Quality = quality } }, chestsOnly: true); if (inventoryPaymentPlan != null && inventoryPaymentPlan.Commit()) { return; } throw new InvalidOperationException("Selected ingredient cannot fund this payment"); } } namespace AdventureBackpacks.API { [PublicAPI] internal class ABAPI { public struct Backpack { public string Name; public ItemData ItemData; public Inventory Inventory; public BackpackDefinition Definition; } public class BackpackDefinition { public GameObject BackPackGo; public AssetBundle AssetBundle; public string PrefabName; public string ItemName; public string ConfigSection = ""; public string CraftingTable; public int StationLevel; public int MaxRequiredStationLevel; public readonly List<RecipeIngredient> RecipeIngredients = new List<RecipeIngredient>(); public readonly List<RecipeIngredient> UpgradeIngredients = new List<RecipeIngredient>(); public readonly List<DropTarget> DropsFrom = new List<DropTarget>(); public readonly Dictionary<BackpackBiomes, KeyValuePair<StatusEffect, int>> EffectsToApply = new Dictionary<BackpackBiomes, KeyValuePair<StatusEffect, int>>(); public Dictionary<int, Vector2> BackpackSizeByQuality = new Dictionary<int, Vector2>(); public StatusEffect ItemSetStatusEffect; public float WeightMultiplier; public int CarryBonus; public int ArmorPerLevel = 1; public float SpeedMod; public bool ScaleSpeedModByQuality = true; public bool EnableFreezing; public BackpackBiomes BackpackBiome; public BackpackDefinition() { } public BackpackDefinition(GameObject backPackGo) { BackPackGo = backPackGo; } public BackpackDefinition(AssetBundle assetBundle, string prefabName) { AssetBundle = assetBundle; PrefabName = prefabName; } } public struct DropTarget { public string Creature; public int Min; public int? Max; public float Chance; public DropTarget(string creature, float chance, int min = 1, int? max = null) { Creature = creature; Chance = chance; Min = min; Max = max; } } public struct EffectDefinition { public readonly string Name; public readonly string LocalizedName; public readonly string EffectName; public readonly string Description; public readonly StatusEffect StatusEffect; public EffectDefinition(string name, string localizedName, string effectName, string description, StatusEffect statusEffect) { Name = name; LocalizedName = localizedName; EffectName = effectName; Description = description; StatusEffect = statusEffect; } } public struct RecipeIngredient { public string ItemPrefabName; public int Quantity; public RecipeIngredient(string itemPrefabName, int quantity) { ItemPrefabName = itemPrefabName; Quantity = quantity; } } public static bool IsLoaded() { return false; } public static bool IsBackpack(ItemData itemData) { return false; } public static bool IsBackpackEquipped(Player player) { return false; } public static bool CanOpenBackpack(Player player) { return false; } public static bool IsThisBackpackEquipped(Player player, ItemData itemData) { return false; } public static Backpack? GetEquippedBackpack(Player player) { return null; } public static Backpack? GetBackpack(ItemData itemData) { return null; } public static Inventory GetEquippedBackpackInventory(Player player) { return null; } public static bool TryGetEquippedBackpackInventory(Player player, out Inventory inventory) { inventory = GetEquippedBackpackInventory(player); return inventory != null; } public static Inventory GetBackpackInventory(ItemData itemData) { return null; } public static bool TryGetBackpackInventory(ItemData itemData, out Inventory inventory) { inventory = GetBackpackInventory(itemData); return inventory != null; } public static HashSet<StatusEffect> GetActiveBackpackStatusEffects() { return null; } public static HashSet<StatusEffect> GetRegisterdStatusEffects() { return null; } public static void OpenBackpack(Player player, InventoryGui gui) { } public static void RegisterEffect(EffectDefinition effectDefinition) { } public static void RegisterBackpack(BackpackDefinition definition) { } public static bool IsCraftFromBackpackEnabled() { return false; } public static bool IsCraftingContextActive() { return false; } public static string GetConsumptionPriority() { return "PlayerInventoryFirst"; } public static bool IsLeaveOneItemInBackpackEnabled() { return false; } public static bool IsDisplayTotalIngredientCountEnabled() { return false; } public static void SuppressNativeCrafting(string modIdentifier) { } public static void UnsuppressNativeCrafting(string modIdentifier) { } } [Flags] internal enum BackpackBiomes : uint { None = 0u, Meadows = 1u, BlackForest = 2u, Swamp = 4u, Mountains = 8u, Plains = 0x10u, Mistlands = 0x20u, Ashlands = 0x40u, DeepNorth = 0x80u, EffectBiome1 = 0x100000u, EffectBiome2 = 0x200000u, EffectBiome3 = 0x400000u, EffectBiome4 = 0x800000u, EffectBiome5 = 0x1000000u } }