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 ZenRecycle v1.2.4
plugins/ZenRecycle.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using Zen; using Zen.Lib; using Zen.Lib.Config; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ZenRecycle")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ZenRecycle")] [assembly: AssemblyCopyright("Copyright \ufffd 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ZenRecycle { [HarmonyPatch] internal static class DestroyInFire { private static bool IsDestroyInFireEnabled => ((List<string>)(object)Configs.DestroyAllowFires.Value).Count > 0; private static bool IsAllowedRule { get { if (Configs.DestroyRuleFire.Value == Configs.DestroyOptionFire.AdminOnly) { return PlayerExt.IsGodMode(Player.m_localPlayer); } return true; } } private static bool IsValidFire(this Fireplace? fire) { if (Object.op_Implicit((Object)(object)fire)) { return Configs.DestroyAllowFires.Value.Contains(GameObjectExt.GetPrefabName(((Component)fire).gameObject), true); } return false; } public static void InitPrefabs() { DestroyInFireInventory destroyInFireInventory = default(DestroyInFireInventory); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if (prefab.TryGetComponent<DestroyInFireInventory>(ref destroyInFireInventory)) { Object.Destroy((Object)(object)destroyInFireInventory); } if (Configs.DestroyAllowFires.Value.Contains(((Object)prefab).name, true) && Object.op_Implicit((Object)(object)prefab.GetComponent<Fireplace>())) { prefab.AddComponent<DestroyInFireInventory>(); } } } [HarmonyPostfix] [HarmonyPatch(typeof(Fireplace), "GetHoverText")] private static void Fireplace_GetHoverText(Fireplace __instance, ref string __result) { if (IsDestroyInFireEnabled && IsAllowedRule && __instance.IsValidFire() && __instance.IsBurning()) { __result = StringExt.Localize(UI.InsertInteractAlt(__result, "$prop_eldersummoningbowl_use")); } } [HarmonyPrefix] [HarmonyPatch(typeof(Fireplace), "Interact")] private static void Fireplace_Interact(Fireplace __instance, Humanoid user, bool hold, bool alt, ref bool __runOriginal) { if (IsDestroyInFireEnabled && IsAllowedRule && __instance.IsBurning() && alt) { if (alt) { __runOriginal = false; } if (!hold && !VirtualInventory.TryRequestOpenFrom(((Component)__instance).gameObject)) { Logging<Plugin>.Warning((object)("Unable to request open: " + ((Object)__instance).name + " is the VirtualInventory missing?"), 0); } } } } internal static class Configs { public enum DestroyOptionGround { Everything, ProtectImportant, ProtectImportantAskOther, AskImportant, AskEverything, AdminOnly } public enum DestroyOptionFire { Everything, ProtectImportant, AdminOnly } public static readonly ConfigEntry<float> RecyclePercent; public static readonly ConfigEntry<StringList> MeltConversion; public static readonly ConfigEntry<StringList> NoRecycleItems; public static readonly ConfigEntry<bool> NoRecycleSingleIngredientNoTeleport; public static readonly ConfigEntry<string> ItemRequiredPerUse; public static readonly ConfigEntry<int> ItemRequiredPerUseAmount; public static readonly ConfigEntry<StringList> CraftingRequirements; public static readonly ConfigEntry<bool> DestroyAllowGround; public static readonly ConfigEntry<StringList> DestroyAllowFires; public static readonly ConfigEntry<DestroyOptionGround> DestroyRuleGround; public static readonly ConfigEntry<DestroyOptionFire> DestroyRuleFire; public static readonly ConfigEntry<bool> DestroyRequireDrop; static Configs() { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_0104: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_0196: Expected O, but got Unknown //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Expected O, but got Unknown //IL_0201: Expected O, but got Unknown RecyclePercent = Config.Define<float>(true, "Recycle", "Recycle Percent", 1f, Config.AcceptRange<float>(0f, 1f), "Use the Obliterator/Deconstructor to salvage scraps from items.\r\nOnly a single ingredient will be recovered from each item in the machine. \r\nThe resource selected is chosen at random.\r\nAll levels of the item will be included in the total amount of the resource returned up to the max stack size.\r\n[What percentage of that total should be returned? Default: 100%]"); NoRecycleItems = Config.Define<StringList>(true, "Recycle", "No Recycle - Items", StringList.Empty, "List of items which will never be recycled.\r\nHowever, they will still be destroyed / obliterated."); ItemRequiredPerUse = Config.Define<string>(true, "Recycle", "Item Required Per Use", "Thunderstone", "This item must be in the machine for each use.\r\n[Vanilla: blank]"); ItemRequiredPerUseAmount = Config.Define<int>(true, "Recycle", "Item Required Per Use - Amount", 1, Config.AcceptRange<int>(1, 1000), "Amount needed of the " + ((ConfigEntryBase)ItemRequiredPerUse).Definition.Key + ".\r\n[For example it could be Coins x 250]\r\n[Has no effect if " + ((ConfigEntryBase)ItemRequiredPerUse).Definition.Key + " is blank]"); StringList val = new StringList(); ((List<string>)val).Add("Iron:8"); ((List<string>)val).Add("Copper:4"); CraftingRequirements = Config.Define<StringList>(true, "Recycle", "Crafting Requirements", val, "Obliterator crafting requirements.\r\nNOTE: Thunderstone was removed because the default configs require it to be consumed on each use.\r\n[Vanilla - Iron:8, Copper:4, Thunderstone:1]"); NoRecycleSingleIngredientNoTeleport = Config.Define<bool>(true, "Recycle", "No Recycle - Single Ingredient No Teleport", true, "Never recycle items that only have a single ingredient when that ingredient is not teleportable.\r\nSingle ingredient items can be abused to always get back the original ingredient and bypass portal restrictions.\r\nExample: BronzeNails, IronNails"); StringList val2 = new StringList(); ((List<string>)val2).Add("Tin:TinOre"); ((List<string>)val2).Add("Copper:CopperOre"); ((List<string>)val2).Add("Bronze:BronzeScrap"); ((List<string>)val2).Add("Iron:IronScrap"); ((List<string>)val2).Add("Silver:SilverOre"); ((List<string>)val2).Add("BlackMetal:BlackMetalScrap"); ((List<string>)val2).Add("FlametalNew:FlametalOreNew"); ((List<string>)val2).Add("Flametal:FlametalOre"); MeltConversion = Config.Define<StringList>(true, "Recycle", "Melt Conversion", val2, "Ingots are melted into ore/scrap automatically.\r\nThis is a map of items that are melted back into other items.\r\nBy default it's a conversion map of ingots back to ore.\r\nIt could map anything to anything, but it must be 1 item yields 1 ingredient.\r\nAdvanced Option:\r\nAppend %n to the end of a resource to force a yield % of that item diff than RecyclePercent\r\nDivide multiple resources with | to have an even chance of selecting any one of those resources.\r\nExample, this will give a 34% chance to convert bronze to TinOre, and a 67% chance to convert Bronze to CopperOre:\r\nBronze:TinOre%34|CopperOre%67"); DestroyAllowGround = Config.Define<bool>(true, "Trash", "Destroy - On Ground", false, "You can smash trash items on the ground.\r\n[Disabled by default because progression is nice without it, burn in fire instead]"); DestroyRequireDrop = Config.Define<bool>(true, "Trash", "Destroy - On Ground - Require Drop", true, "You can only destroy items on the ground that you have dropped from your inventory.\r\nIf you leave the area the item will no longer be able to be destroyed until you pick it up and drop it again.\r\nSafety feature to prevent accidental destruction of items that you drop."); StringList val3 = new StringList(); ((List<string>)val3).Add("bonfire"); ((List<string>)val3).Add("fire_pit_iron"); DestroyAllowFires = Config.Define<StringList>(true, "Trash", "Destroy - In Fires", val3, "You can burn trash in these fires.\r\n[Leave blank to disable this feature]\r\n[restart required for changes to take effect]"); DestroyRuleGround = Config.Define<DestroyOptionGround>(true, "Trash", "Destruction Rule - On Ground", DestroyOptionGround.ProtectImportantAskOther, $"What are the rules for destroying items?\r\n{DestroyOptionGround.Everything}: Any item can be destroyed, no restrictions.\r\n{DestroyOptionGround.ProtectImportant}: Important items can not be destroyed (no-teleport, or equipable).\r\n{DestroyOptionGround.ProtectImportantAskOther}: Important items can not be destroyed, confirm for anything else.\r\n{DestroyOptionGround.AskImportant}: Ask before destroying important items.\r\n{DestroyOptionGround.AskEverything}: Any item can be destroyed, but confirmation is required for everything.\r\n{DestroyOptionGround.AdminOnly}: Only Admin can destroy items (Must be in God mode)\r\n[Admin can always override by being in God mode and holding LeftAlt]"); DestroyRuleFire = Config.Define<DestroyOptionFire>(true, "Trash", "Destruction Rule - In Fire", DestroyOptionFire.ProtectImportant, $"What are the rules for destroying items?\r\n{DestroyOptionFire.Everything}: Any item can be destroyed, no restrictions.\r\n{DestroyOptionFire.ProtectImportant}: Important items can not be destroyed (no-teleport, or equipable).\r\n{DestroyOptionFire.AdminOnly}: Only Admin can destroy items (Must be in God mode)\r\n[Admin can always override by being in God mode and holding LeftAlt]"); } } internal static class DestroyTrash { public static bool IsImportant(this ItemDrop itemDrop) { return IsImportant(itemDrop.m_itemData, itemDrop.m_autoDestroy); } public static bool IsImportant(this ItemData itemData) { return IsImportant(itemData, ItemDataExt.GetPrefabItemDrop(itemData).m_autoDestroy); } private static bool IsImportant(ItemData itemData, bool isDropAutoDestroy) { if (PlayerExt.IsGodMode(Player.m_localPlayer) && ZInput.GetKey((KeyCode)308, true)) { return false; } if (!itemData.IsEquipable() && itemData.m_shared.m_teleportable) { return !isDropAutoDestroy; } return true; } } internal class DestroyInFireInventory : VirtualInventory { private Fireplace? _fire; private Coroutine? _burning; private Fireplace Fire { get { if (!Object.op_Implicit((Object)(object)_fire)) { return _fire = ((Component)this).GetComponent<Fireplace>(); } return _fire; } } protected override Inventory Inventory { get; } = new Inventory("$prompt_destroy", (Sprite)null, 3, 3); protected override bool IsOwnerRequired => false; protected override bool IsWarded() { return false; } protected override bool IsStackAllButtonVisible() { return true; } protected override ZNetView GetZNetView() { return Fire.m_nview; } protected override bool OnHoldToStack() { return true; } protected override void LoadInventory() { } public override void ClearInventory() { } protected override bool IsAddItemValid(ItemData item) { if (Configs.DestroyRuleFire.Value == Configs.DestroyOptionFire.ProtectImportant) { return !item.IsImportant(); } return true; } protected override void OnOpenAllow() { StopBurning(); } protected override void OnClose() { if (!((VirtualInventory)this).IsEmpty) { StartBurning(); if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, "$se_burning_name ...", 0, (Sprite)null); ((Humanoid)Player.m_localPlayer).DoInteractAnimation(((Component)Fire).gameObject); } } } private void StopBurning() { if (_burning != null) { ((MonoBehaviour)this).StopCoroutine(_burning); _burning = null; } } private void StartBurning() { StopBurning(); _burning = ((MonoBehaviour)this).StartCoroutine(BurnItems()); } private IEnumerator BurnItems() { yield return (object)new WaitForSeconds(5f); yield return (object)new WaitUntil((Func<bool>)Fire.IsBurning); if (!((VirtualInventory)this).IsEmpty) { Fire.AddFuel(float.Epsilon); ((VirtualInventory)this).Inventory.RemoveAll(); } } private void DisplayFlameFX() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)this).transform; Fire.m_fuelAddedEffects.Create(transform.position, transform.rotation, (Transform)null, 1f, -1); } } [HarmonyPatch(typeof(Incinerator))] internal static class IncineratorPatch { private const string PrefabName = "incinerator"; private static bool IsValidPrefab(Incinerator incinerator) { return Utils.GetPrefabName(((Object)incinerator).name) == "incinerator"; } public static void InitPrefab() { Piece prefab = GlobalStatic.GetPrefab<Piece>("incinerator", false); prefab.m_resources = Configs.CraftingRequirements.Value.ToRequirements(':'); if (Utility.IsNullOrWhiteSpace(prefab.m_description)) { prefab.m_description = "$piece_incinerator_description"; } } [HarmonyPostfix] [HarmonyPatch(typeof(Container), "Awake")] private static void IncineratorContainer_Awake(Container __instance) { if (!Object.op_Implicit((Object)(object)__instance.m_piece)) { Incinerator componentInParent = ((Component)__instance).GetComponentInParent<Incinerator>(); if (Object.op_Implicit((Object)(object)componentInParent)) { __instance.m_piece = ((Component)componentInParent).GetComponent<Piece>(); } } } [HarmonyPrefix] [HarmonyPatch(typeof(Incinerator), "OnIncinerate")] private static void Incinerator_OnIncinerate(Incinerator __instance, Humanoid user, ref bool __result, ref bool __runOriginal) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) ItemDrop prefab = GlobalStatic.GetPrefab<ItemDrop>(Configs.ItemRequiredPerUse.Value, true); if (!Object.op_Implicit((Object)(object)prefab)) { Logging<Plugin>.Info("No required item per use defined.", 0); return; } int num = __instance.m_container.GetInventory().CountItems(ItemDataExt.GetName(prefab), -1, true); int value = Configs.ItemRequiredPerUseAmount.Value; if (num >= value) { Logging<Plugin>.Info("Correct amount of the required item is available", 0); return; } bool flag = default(bool); InterpolatedString<Plugin> val = new InterpolatedString<Plugin>(36, 3, ref flag); if (flag) { val.AppendLiteral("Incorrect amount of "); val.AppendFormatted(ItemDataExt.GetName(prefab)); val.AppendLiteral(", need: "); val.AppendFormatted<int>(value); val.AppendLiteral(", have: "); val.AppendFormatted<int>(num); } Logging<Plugin>.Info(val, 0); StringBuilder stringBuilder = new StringBuilder("$msg_missingrequirement " + ItemDataExt.GetName(prefab)); if (value > 1) { stringBuilder.Append($" x{value}"); } ((Character)user).Message((MessageType)2, stringBuilder.ToString(), 0, (Sprite)null); __result = true; __runOriginal = false; } [HarmonyTranspiler] [HarmonyPatch(/*Could not decode attribute arguments.*/)] private static IEnumerable<CodeInstruction> Incinerate_Transpile(IEnumerable<CodeInstruction> codes) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "RemoveAll", (Type[])null, (Type[])null); Action<Inventory, Incinerator, List<ItemDrop>> action = IncineratorRemoveAllIntercept; return new CodeMatcher(codes, (ILGenerator)null).MatchStartForward((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Callvirt, (object)methodInfo, (string)null) }).ThrowIfInvalid("Unable to match IL").InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Ldloc_3, (object)null) }) .Set(OpCodes.Call, (object)action.Method) .InstructionEnumeration(); static void IncineratorRemoveAllIntercept(Inventory inventory, Incinerator incinerator, List<ItemDrop> vanillaToAdd) { if (!IsValidPrefab(incinerator)) { inventory.RemoveAll(); } else { List<ItemData> list = Recycle.Process(inventory); inventory.RemoveAll(); foreach (ItemData item in list) { inventory.AddItem(item, item.m_stack, item.m_gridPos.x, item.m_gridPos.y); } FitVanillaItems(inventory.GetEmptySlots(), vanillaToAdd); } } } private static void FitVanillaItems(int emptySlots, List<ItemDrop> vanillaToAdd) { Tally<ItemDrop> val = new Tally<ItemDrop>(); foreach (ItemDrop item in vanillaToAdd) { Tally<ItemDrop> val2 = val; ItemDrop val3 = item; val2[val3] += item.m_itemData.m_stack; } int num = 0; foreach (ItemDrop key in ((Dictionary<ItemDrop, int>)(object)val).Keys) { num += Mathf.CeilToInt((float)key.m_itemData.m_stack / (float)key.m_itemData.m_shared.m_maxStackSize); } if (num > emptySlots) { Logging<Plugin>.Warning((object)"No extra slots available, truncating coal", 0); vanillaToAdd.Clear(); } } } [BepInPlugin("ZenDragon.ZenRecycle", "ZenRecycle", "1.2.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Plugin : ZenMod<Plugin> { public const string PluginName = "ZenRecycle"; public const string PluginVersion = "1.2.4"; public const string PluginGUID = "ZenDragon.ZenRecycle"; protected override void Setup() { base.ConfigSync += IncineratorPatch.InitPrefab; base.ConfigSync += DestroyInFire.InitPrefabs; } protected override void TitleScene(bool isFirstBoot) { } protected override void WorldStart() { } protected override void Shutdown() { } protected override HelpInfo? GetHelpInfo() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown StringBuilder stringBuilder = new StringBuilder(); if (Configs.RecyclePercent.Value > 0f) { stringBuilder.AppendLine(HelpInfo.Format<float>(Configs.RecyclePercent, true, true, true)); } if (!Utility.IsNullOrWhiteSpace(Configs.ItemRequiredPerUse.Value)) { stringBuilder.AppendLine(HelpInfo.Format<string>(Configs.ItemRequiredPerUse, true, true, true)); if (Configs.ItemRequiredPerUseAmount.Value > 1) { stringBuilder.AppendLine(HelpInfo.Format<int>(Configs.ItemRequiredPerUseAmount, true, true, true)); } } if ((Configs.DestroyRuleGround.Value != Configs.DestroyOptionGround.AdminOnly || PlayerExt.IsGodMode(Player.m_localPlayer)) && Configs.DestroyAllowGround.Value) { stringBuilder.AppendLine(HelpInfo.Format<bool>(Configs.DestroyAllowGround, false, true, true)); if (Configs.DestroyRequireDrop.Value) { stringBuilder.AppendLine(HelpInfo.Format<bool>(Configs.DestroyRequireDrop, false, true, true)); } } if ((Configs.DestroyRuleFire.Value != Configs.DestroyOptionFire.AdminOnly || PlayerExt.IsGodMode(Player.m_localPlayer)) && ((List<string>)(object)Configs.DestroyAllowFires.Value).Count > 0) { stringBuilder.AppendLine(HelpInfo.Format<StringList>(Configs.DestroyAllowFires, true, true, true)); } return new HelpInfo("Recycle", stringBuilder.ToString()); } } internal static class Recycle { private static readonly Dictionary<string, Recipe> RecipeItemCache = new Dictionary<string, Recipe>(); private static void BuildRecipeCache() { Logging<Plugin>.Info("Create recipe cache", 0); RecipeItemCache.Clear(); foreach (Recipe item in from r in ObjectDB.instance.m_recipes where Object.op_Implicit((Object)(object)r.m_item) orderby r.m_amount select r) { string prefabName = ItemDataExt.GetPrefabName(item.m_item); if (!RecipeItemCache.ContainsKey(prefabName)) { RecipeItemCache.Add(prefabName, item); } } } public static List<ItemData> Process(Inventory inventory) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) bool flag = default(bool); InterpolatedString<Plugin> val = new InterpolatedString<Plugin>(21, 2, ref flag); if (flag) { val.AppendLiteral("Processing "); val.AppendFormatted<int>(inventory.NrOfItems()); val.AppendLiteral(" items in "); val.AppendFormatted(inventory.GetName()); } Logging<Plugin>.Info(val, 0); Random random = new Random(); List<ItemData> list = new List<ItemData>(); foreach (ItemData allItem in inventory.GetAllItems()) { if (ItemDataExt.IsItemType(allItem, (ItemType)2)) { val = new InterpolatedString<Plugin>(24, 1, ref flag); if (flag) { val.AppendFormatted(ItemDataExt.GetPrefabName(allItem)); val.AppendLiteral(" is consumable, burn it."); } Logging<Plugin>.Info(val, 0); continue; } ItemData[] array = Disassemble(allItem); if (array.Length != 0) { ItemData val2 = array[random.Next(array.Length)]; val2.m_gridPos = allItem.m_gridPos; val = new InterpolatedString<Plugin>(10, 2, ref flag); if (flag) { val.AppendLiteral("WINNER: "); val.AppendFormatted(ItemDataExt.GetPrefabName(val2)); val.AppendLiteral(" x"); val.AppendFormatted<int>(val2.m_stack); } Logging<Plugin>.Info(val, 0); list.Add(val2); } } return list; } private static ItemData[] Disassemble(ItemData item) { //IL_0072: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) if (RecipeItemCache.Count == 0) { BuildRecipeCache(); } string prefabName = ItemDataExt.GetPrefabName(item); bool flag = default(bool); InterpolatedString<Plugin> val; if (Configs.NoRecycleItems.Value.Contains(prefabName, true)) { val = new InterpolatedString<Plugin>(21, 1, ref flag); if (flag) { val.AppendLiteral("Recycle not allowed: "); val.AppendFormatted(prefabName); } Logging<Plugin>.Info(val, 0); return Array.Empty<ItemData>(); } val = new InterpolatedString<Plugin>(18, 2, ref flag); if (flag) { val.AppendLiteral("Dissasemble: "); val.AppendFormatted(prefabName); val.AppendLiteral(" lvl "); val.AppendFormatted<int>(item.m_quality); } Logging<Plugin>.Info(val, 0); if (item.IsMelty()) { return (ItemData[])(object)new ItemData[1] { Melt(item) }; } Recipe recipe; ItemData[] array = (RecipeItemCache.TryGetValue(prefabName, out recipe) ? (from req in recipe.m_resources select ReclaimFromRequirement(req, item, recipe.m_amount) into itm where itm.m_stack > 0 select itm).ToArray() : Array.Empty<ItemData>()); if (Configs.NoRecycleSingleIngredientNoTeleport.Value && array.Length == 1 && !array[0].m_shared.m_teleportable) { val = new InterpolatedString<Plugin>(46, 1, ref flag); if (flag) { val.AppendLiteral("Recycle not allowed, single item no-teleport: "); val.AppendFormatted(prefabName); } Logging<Plugin>.Info(val, 0); return Array.Empty<ItemData>(); } return array; } private static ItemData ReclaimFromRequirement(Requirement req, ItemData item, int recipeAmount) { ItemData val = req.m_resItem.m_itemData.Clone(); val.m_dropPrefab = ObjectDB.instance.GetItemPrefab(val.m_shared); val.m_worldLevel = Game.m_worldLevel; val.m_stack = ((req.m_resItem.m_itemData.m_shared.m_maxStackSize <= 1) ? 1 : SumAmount(req, item, recipeAmount)); val.m_stack = Math.Min(val.m_stack, val.m_shared.m_maxStackSize); if (val.m_stack == 0) { return val; } if (val.IsEquipable()) { val.m_quality = item.m_quality; } if (!val.IsMelty()) { return val; } return Melt(val); } private static int SumAmount(Requirement requirement, ItemData item, int recipeAmount) { float num = (float)item.m_stack / (float)recipeAmount; int num2 = 0; for (int num3 = item.m_quality; num3 > 0; num3--) { num2 += requirement.GetAmount(num3); } return (int)((float)Mathf.RoundToInt((float)num2 * Configs.RecyclePercent.Value) * num); } private static bool IsMelty(this ItemData item) { return Enumerable.Contains(Configs.MeltConversion.Value.ToDictionary(':').Keys, ItemDataExt.GetPrefabName(item)); } private static ItemData Melt(ItemData item) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) bool flag = default(bool); InterpolatedString<Plugin> val = new InterpolatedString<Plugin>(9, 1, ref flag); if (flag) { val.AppendLiteral("Melting: "); val.AppendFormatted(ItemDataExt.GetName(item)); } Logging<Plugin>.Info(val, 0); if (!Configs.MeltConversion.Value.ToDictionary(':').TryGetValue(ItemDataExt.GetPrefabName(item), out var value)) { return item; } (string, int) tuple = PreProcess(value, item.m_stack); return CreateItem(tuple.Item1, tuple.Item2); } private static (string Name, int Amount) PreProcess(string material, int stackSize) { List<(string, float)> list = new List<(string, float)>(); string[] array = material.Trim().Split(new char[1] { '|' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '%' }, 2); string item = array2[0].Trim(); float result = ((array2.Length <= 1 || !float.TryParse(array2[1].Trim(), out result)) ? 1f : (result / 100f)); list.Add((item, result)); } (string, float) tuple = list[new Random().Next(list.Count)]; return (Name: tuple.Item1, Amount: Mathf.RoundToInt(tuple.Item2 * (float)stackSize)); } private static ItemData CreateItem(string prefabName, int stackSize) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); if (!Object.op_Implicit((Object)(object)itemPrefab)) { Logging<Plugin>.Error((object)(prefabName + " is not in the ObjectDB. Check your config file for syntax errors. Here is some Coal instead."), (ushort)0); itemPrefab = ObjectDB.instance.GetItemPrefab("Coal"); } ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); val.m_dropPrefab = itemPrefab; val.m_stack = Math.Min(stackSize, val.m_shared.m_maxStackSize); val.m_worldLevel = Game.m_worldLevel; bool flag = default(bool); InterpolatedString<Plugin> val2 = new InterpolatedString<Plugin>(16, 2, ref flag); if (flag) { val2.AppendLiteral("Create: "); val2.AppendFormatted(prefabName); val2.AppendLiteral(" Stack: "); val2.AppendFormatted<int>(val.m_stack); } Logging<Plugin>.Info(val2, 0); return val; } } [HarmonyPatch] public static class DestroyOnGround { [CompilerGenerated] private static class <>O { public static PopupButtonCallback <0>__Pop; } private static bool _smashedOnce; private static bool IsAdminOverride { get { if (PlayerExt.IsGodMode(Player.m_localPlayer)) { return ZInput.GetKey((KeyCode)308, true); } return false; } } private static void Interact(ItemDrop itemDrop, Humanoid character, bool alt, bool repeat, ref bool runOriginal, ref bool result) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (!Configs.DestroyAllowGround.Value || !alt || itemDrop.IsPiece() || (_smashedOnce && repeat)) { return; } _smashedOnce = false; runOriginal = false; if (!repeat) { return; } if (itemDrop.m_itemData.m_shared.m_questItem && !((Character)character).InGodMode()) { ((Character)character).Message((MessageType)2, "Can not destroy quest items", 0, (Sprite)null); } else if (!Configs.DestroyRequireDrop.Value || !itemDrop.m_autoPickup) { result = false; if (WardAccessExt.CanAccessWard(((Component)itemDrop).transform.position, true) || ((Character)Player.m_localPlayer).InGodMode()) { DestroyItem(itemDrop, DestroyIt); } } void DestroyIt() { Player localPlayer = Player.m_localPlayer; ((Character)localPlayer).Message((MessageType)1, ItemDataExt.GetName(itemDrop) + " $caption_destroyed", itemDrop.m_itemData.m_stack, ItemDataExt.GetIcon(itemDrop)); ((Humanoid)localPlayer).DoInteractAnimation(((Component)itemDrop).gameObject); itemDrop.SmashIt(); _smashedOnce = true; } } private static void SmashIt(this ItemDrop itemDrop) { itemDrop.DisplaySmashFX(); itemDrop.m_nview.ClaimOwnership(); itemDrop.m_nview.Destroy(); } private static void DisplaySmashFX(this ItemDrop itemDrop) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) Fireplace val = default(Fireplace); if (!GlobalStatic.TryGetPrefab<Fireplace>("fire_pit", ref val, true)) { Logging<Plugin>.Warning((object)"Prefab missing: fire_pit", 0); return; } EffectList fuelAddedEffects = val.m_fuelAddedEffects; Transform transform = ((Component)itemDrop).transform; fuelAddedEffects.Create(transform.position, transform.rotation, (Transform)null, 1f, -1); } public static bool DestroyItem(ItemData itemData, Action onDestroy) { return DestroyItem(itemData, onDestroy, itemData.IsImportant()); } public static bool DestroyItem(ItemDrop itemDrop, Action onDestroy) { return DestroyItem(itemDrop.m_itemData, onDestroy, itemDrop.IsImportant()); } private static bool DestroyItem(ItemData itemData, Action onDestroy, bool isImportant) { switch (Configs.DestroyRuleGround.Value) { case Configs.DestroyOptionGround.Everything: onDestroy(); break; case Configs.DestroyOptionGround.ProtectImportant: if (!isImportant || IsAdminOverride) { onDestroy(); break; } DisplayProtected(); return false; case Configs.DestroyOptionGround.ProtectImportantAskOther: if (!isImportant || IsAdminOverride) { DisplayConfirm(); break; } DisplayProtected(); return false; case Configs.DestroyOptionGround.AskImportant: if (isImportant) { DisplayConfirm(); } else { onDestroy(); } break; case Configs.DestroyOptionGround.AskEverything: DisplayConfirm(); break; case Configs.DestroyOptionGround.AdminOnly: if (((Character)Player.m_localPlayer).InGodMode()) { onDestroy(); } break; default: throw new ArgumentOutOfRangeException(); } return true; void DisplayConfirm() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown int stack = itemData.m_stack; string text = ((stack > 1) ? $" x{stack}" : string.Empty); string text2 = $"{ItemDataExt.GetName(itemData)}{text}\n\n<size=-3><color={UIColor.MinorInfo}>{itemData.m_shared.m_description}</color></size>"; PopupButtonCallback val = delegate { onDestroy(); UnifiedPopup.Pop(); }; object obj = <>O.<0>__Pop; if (obj == null) { PopupButtonCallback val2 = UnifiedPopup.Pop; <>O.<0>__Pop = val2; obj = (object)val2; } UnifiedPopup.Push((PopupBase)new YesNoPopup("$prompt_destroy?", text2, val, (PopupButtonCallback)obj, true)); } static void DisplayProtected() { if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_cantremovenow", 0, (Sprite)null); } } } [HarmonyPrefix] [HarmonyPatch(typeof(ItemDrop), "Interact")] private static void ItemDrop_Interact(ItemDrop __instance, Humanoid character, bool alt, bool repeat, ref bool __runOriginal, ref bool __result) { Interact(__instance, character, alt, repeat, ref __runOriginal, ref __result); } [HarmonyPrefix] [HarmonyPatch(typeof(Fish), "Interact")] private static void Fish_Interact(Fish __instance, Humanoid character, bool alt, bool repeat, ref bool __runOriginal, ref bool __result) { if (__instance.IsOutOfWater() && Object.op_Implicit((Object)(object)__instance.m_itemDrop)) { Interact(__instance.m_itemDrop, character, alt, repeat, ref __runOriginal, ref __result); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemDrop), "GetHoverText")] [HarmonyPriority(300)] private static void ItemDrop_GetHoverText(ItemDrop __instance, ref string __result) { if (Configs.DestroyAllowGround.Value && !__instance.IsPiece() && (Configs.DestroyRuleGround.Value != Configs.DestroyOptionGround.AdminOnly || ((Character)Player.m_localPlayer).InGodMode()) && (!Configs.DestroyRequireDrop.Value || !__instance.m_autoPickup)) { bool flag = __instance.IsImportant(); if (flag) { Configs.DestroyOptionGround value = Configs.DestroyRuleGround.Value; bool flag2 = (uint)(value - 1) <= 1u; flag = flag2; } bool num = flag; string text = "$prompt_destroy ($ui_hold)"; if (num && !IsAdminOverride) { text = "<s>" + text + "</s>"; } __result += StringExt.Localize("\n" + UI.PromptInteractAlt + " " + text); } } } }