using System;
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 BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using Zen.Config;
using Zen.Lib;
using Zen.Logging;
[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]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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
{
internal static class Configs
{
public static readonly ConfigEntry<float> RecyclePercent;
public static readonly ConfigEntry<StringList> MeltConversion;
public static readonly ConfigEntry<bool> EnableSmash;
public static readonly ConfigEntry<SmashTrash.DestroyOption> DestroyRule;
static Configs()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Expected O, but got Unknown
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Expected O, but got Unknown
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Expected O, but got Unknown
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Expected O, but got Unknown
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Expected O, but got Unknown
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Expected O, but got Unknown
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected O, but got Unknown
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Expected O, but got Unknown
//IL_00a0: Expected O, but got Unknown
RecyclePercent = Config.Define<float>(true, "Recycle", "Recycle Percent", 1f, Config.AcceptRange<float>(0f, 1f), "When obliterating an item only a single ingredient will be recovered. It is chosen at random.\nAll levels of the item will be included in the total amount of the resource returned up to the max stack size.\nWhat percentage of that total should be returned? Default: 100%");
StringList val = new StringList();
((List<string>)val).Add("Tin:TinOre");
((List<string>)val).Add("Copper:CopperOre");
((List<string>)val).Add("Bronze:TinOre%34|CopperOre%67");
((List<string>)val).Add("Iron:IronScrap");
((List<string>)val).Add("Silver:SilverOre");
((List<string>)val).Add("BlackMetal:BlackMetalScrap");
((List<string>)val).Add("FlametalNew:FlametalOreNew");
((List<string>)val).Add("Flametal:FlametalOre");
MeltConversion = Config.Define<StringList>(true, "Recycle", "Melt Conversion", val, "Ingots are melted into ore automatically.\nThis is a map of items that are melted back into other items.\nBy default it's a conversion map of ingots back to ore.\nIt could map anything to anything, but it must be 1 item yields 1 ingredient.\nAdvanced Option:\nAppend %n to the end of a resource to force a yield % of that item diff than RecyclePercent\nDivide multiple resources with | to have an even chance of selecting any one of those resources.\nSee Bronze for an example advanced syntax.");
EnableSmash = Config.Define<bool>(true, "Smash Trash", "Enable Smash", true, "Destroy items on the ground via alt interaction");
DestroyRule = Config.Define<SmashTrash.DestroyOption>(true, "Smash Trash", "Destruction Rule", SmashTrash.DestroyOption.AdminOnly, "What are the rules for destroying items?\n" + $"{SmashTrash.DestroyOption.Everything}: Any item can be destroyed, no restrictions.\n" + $"{SmashTrash.DestroyOption.ProtectImportant}: Important items can not be destroyed (no-teleport, or equipable).\n" + $"{SmashTrash.DestroyOption.AskImportant}: Ask before destroying important items.\n" + $"{SmashTrash.DestroyOption.AskEverything}: Any item can be destroyed, but confirmation is required for everything.\n" + $"{SmashTrash.DestroyOption.AdminOnly}: Only Admin can destroy items (Must be in God mode)");
}
}
[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";
}
[HarmonyTranspiler]
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
private static IEnumerable<CodeInstruction> Incinerate_Transpile(IEnumerable<CodeInstruction> codes)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Expected O, but got Unknown
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Expected O, but got Unknown
MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "RemoveAll", (Type[])null, (Type[])null);
CodeMatcher val = new CodeMatcher(codes, (ILGenerator)null);
CodeMatch[] array = new CodeMatch[1];
object obj = methodInfo;
array[0] = new CodeMatch((OpCode?)null, obj, (string)null);
val.MatchStartForward((CodeMatch[])(object)array).SetAndAdvance(OpCodes.Pop, (object)null).Insert((CodeInstruction[])(object)new CodeInstruction[3]
{
new CodeInstruction(OpCodes.Ldloc_1, (object)null),
new CodeInstruction(OpCodes.Ldloc_3, (object)null),
CodeInstruction.Call(typeof(IncineratorPatch), "Incinerator_RemoveAll_Intercept", (Type[])null, (Type[])null)
});
return val.InstructionEnumeration();
}
private static void Incinerator_RemoveAll_Intercept(Incinerator incinerator, List<ItemDrop> vanillaToAdd)
{
Inventory inventory = incinerator.m_container.GetInventory();
if (!IsValidPrefab(incinerator))
{
inventory.RemoveAll();
return;
}
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)
{
Log.Warning((object)"No extra slots available, truncating coal", (ushort)0);
vanillaToAdd.Clear();
}
}
}
[BepInPlugin("ZenDragon.ZenRecycle", "ZenRecycle", "0.3.3")]
[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 = "0.3.3";
public const string PluginGUID = "ZenDragon.ZenRecycle";
protected override void Setup()
{
}
protected override void TitleScene(bool isFirstBoot)
{
}
protected override void WorldStart()
{
}
protected override void Shutdown()
{
}
}
public static class Recycle
{
private static readonly Dictionary<string, Recipe> RecipeItemCache = new Dictionary<string, Recipe>();
private static void BuildRecipeCache()
{
Log.Info((object)"Create recipe cache", (ushort)0);
RecipeItemCache.Clear();
foreach (Recipe item in ObjectDB.instance.m_recipes.Where((Recipe r) => Object.op_Implicit((Object)(object)r.m_item)))
{
string name = ItemDataExt.GetName(item.m_item);
if (!RecipeItemCache.ContainsKey(name))
{
RecipeItemCache.Add(name, item);
}
}
}
public static List<ItemData> Process(Inventory inventory)
{
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: 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_008f: Unknown result type (might be due to invalid IL or missing references)
Log.Info((object)$"Processing {inventory.NrOfItems()} items in {inventory.GetName()}", (ushort)0);
Random random = new Random();
List<ItemData> list = new List<ItemData>();
foreach (ItemData allItem in inventory.GetAllItems())
{
if (ItemDataExt.IsItemType(allItem, (ItemType)2))
{
Log.Info((object)(ItemDataExt.GetPrefabName(allItem) + " is consumable, skipping."), (ushort)0);
continue;
}
Vector2i gridPos = allItem.m_gridPos;
ItemData[] array = Disassemble(allItem);
if (array.Length != 0)
{
ItemData val = array[random.Next(array.Length)];
val.m_gridPos = gridPos;
Log.Info((object)$"WINNER: {ItemDataExt.GetPrefabName(val)} x{val.m_stack}", (ushort)0);
list.Add(val);
}
}
return list;
}
private static ItemData[] Disassemble(ItemData item)
{
ItemData item2 = item;
if (RecipeItemCache.Count == 0)
{
BuildRecipeCache();
}
Log.Info((object)$"Dissasemble: {ItemDataExt.GetPrefabName(item2)} lvl {item2.m_quality}", (ushort)0);
if (item2.IsMelty())
{
return (ItemData[])(object)new ItemData[1] { Melt(item2) };
}
if (!RecipeItemCache.TryGetValue(ItemDataExt.GetName(item2), out Recipe value))
{
return Array.Empty<ItemData>();
}
return value.m_resources.Select((Requirement req) => ReclaimFromRequirement(req, item2.m_quality)).ToArray();
}
private static ItemData ReclaimFromRequirement(Requirement req, int origItemQuality)
{
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, origItemQuality));
val.m_stack = Math.Min(val.m_stack, val.m_shared.m_maxStackSize);
if (val.IsEquipable())
{
val.m_quality = origItemQuality;
}
if (!val.IsMelty())
{
return val;
}
return Melt(val);
}
private static int SumAmount(Requirement requirement, int maxQuality)
{
int num = 0;
for (int num2 = maxQuality; num2 > 0; num2--)
{
num += requirement.GetAmount(num2);
}
return Mathf.RoundToInt((float)num * Configs.RecyclePercent.Value);
}
private static bool IsMelty(this ItemData item)
{
return Configs.MeltConversion.Value.ToDictionary(':').Keys.Contains(ItemDataExt.GetPrefabName(item));
}
private static ItemData Melt(ItemData item)
{
Log.Info((object)("Melting: " + ItemDataExt.GetName(item)), (ushort)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 (tuple.Item1, Mathf.RoundToInt(tuple.Item2 * (float)stackSize));
}
private static ItemData CreateItem(string prefabName, int stackSize)
{
GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
if (!Object.op_Implicit((Object)(object)itemPrefab))
{
Log.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;
Log.Info((object)$"Create: {prefabName} Stack: {val.m_stack}", (ushort)0);
return val;
}
}
[HarmonyPatch]
public static class SmashTrash
{
public enum DestroyOption
{
Everything,
ProtectImportant,
AskImportant,
AskEverything,
AdminOnly
}
[CompilerGenerated]
private static class <>O
{
public static PopupButtonCallback <0>__Pop;
}
private static bool _smashedOnce;
private static string? GetImportantReason(ItemDrop itemDrop)
{
if (!itemDrop.m_itemData.IsEquipable())
{
if (itemDrop.m_itemData.m_shared.m_teleportable)
{
return null;
}
return "$item_noteleport";
}
return "Equipable";
}
private static void Interact(ItemDrop itemDrop, Humanoid character, bool alt, bool repeat, ref bool runOriginal, ref bool result)
{
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
ItemDrop itemDrop2 = itemDrop;
if (!Configs.EnableSmash.Value || !alt || itemDrop2.IsPiece() || (_smashedOnce && repeat))
{
return;
}
_smashedOnce = false;
runOriginal = false;
if (!repeat)
{
return;
}
if (itemDrop2.m_itemData.m_shared.m_questItem)
{
((Character)character).Message((MessageType)2, "Can not destroy quest items", 0, (Sprite)null);
return;
}
result = true;
if (!WardAccessExt.CanAccessWard(((Component)itemDrop2).transform.position, true) && !((Character)Player.m_localPlayer).InGodMode())
{
return;
}
string importantReason = GetImportantReason(itemDrop2);
switch (Configs.DestroyRule.Value)
{
case DestroyOption.Everything:
DestroyIt();
break;
case DestroyOption.ProtectImportant:
if (importantReason == null || (((Character)Player.m_localPlayer).InGodMode() && ZInput.GetKey((KeyCode)304, true)))
{
DestroyIt();
}
else
{
result = false;
}
break;
case DestroyOption.AskImportant:
if (importantReason != null)
{
DisplayConfirm();
}
else
{
DestroyIt();
}
break;
case DestroyOption.AskEverything:
DisplayConfirm();
break;
case DestroyOption.AdminOnly:
if (((Character)Player.m_localPlayer).InGodMode())
{
DestroyIt();
}
break;
default:
throw new ArgumentOutOfRangeException();
}
void DestroyIt()
{
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
Player localPlayer = Player.m_localPlayer;
((Character)localPlayer).Message((MessageType)1, ItemDataExt.GetName(itemDrop2) + " $caption_destroyed", itemDrop2.m_itemData.m_stack, ItemDataExt.GetIcon(itemDrop2));
((Humanoid)localPlayer).DoInteractAnimation(((Component)itemDrop2).gameObject);
EffectList fuelAddedEffects = ZNetScene.instance.GetPrefab("fire_pit").GetComponent<Fireplace>().m_fuelAddedEffects;
Transform transform = ((Component)itemDrop2).transform;
fuelAddedEffects.Create(transform.position, transform.rotation, (Transform)null, 1f, -1);
itemDrop2.m_nview.ClaimOwnership();
itemDrop2.m_nview.Destroy();
_smashedOnce = true;
}
void DisplayConfirm()
{
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Expected O, but got Unknown
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Expected O, but got Unknown
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected O, but got Unknown
int stack = itemDrop2.m_itemData.m_stack;
string text = ((stack > 1) ? $" x{stack}" : "");
string text2 = ItemDataExt.GetName(itemDrop2) + text;
if (importantReason != null)
{
text2 = text2 + "\\n\\n(" + importantReason + ")";
}
string text3 = text2;
PopupButtonCallback val = delegate
{
DestroyIt();
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("Destroy?", text3, val, (PopupButtonCallback)obj, true));
}
}
[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 (!__instance.IsPiece() && (Configs.DestroyRule.Value != DestroyOption.AdminOnly || ((Character)Player.m_localPlayer).InGodMode()))
{
bool num = Configs.DestroyRule.Value == DestroyOption.ProtectImportant && GetImportantReason(__instance) != null;
string text = "Destroy ($ui_hold)";
if (num)
{
text = "<s>" + text + "</s>";
}
__result += Localization.instance.Localize("\n" + UI.PromptInteractAlt + " " + text);
}
}
}
}