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 IdolObliterator v1.0.0
plugins\IdolObliterator.dll
Decompiled 10 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("IdolObliterator")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("IdolObliterator")] [assembly: AssemblyTitle("IdolObliterator")] [assembly: AssemblyVersion("1.0.0.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; } } } namespace IdolObliterator { [BepInPlugin("kairi.IdolObliterator", "IdolObliterator", "1.0.0")] public class Plugin : BaseUnityPlugin { private sealed class Proto { public Incinerator Incinerator; public int Width; public int Height; public List<IncineratorConversion> SortedConversions; } private sealed class Pending { public Proto Proto; public uint Revision; public Dictionary<string, int> Before; public Dictionary<string, int> ExpectedVanilla; public byte[] Result; public int IdolCount; public float Deadline; } public const string Guid = "kairi.IdolObliterator"; public const string ModName = "IdolObliterator"; public const string Version = "1.0.0"; internal static Plugin Instance; internal static ManualLogSource Log; private static readonly int LeverReturnHash = StringExtensionMethods.GetStableHashCode("RPC_AnimateLeverReturn"); private const float WaitForOwnerSeconds = 15f; private static readonly string[] TierNames = new string[8] { "Wooden", "Bronze", "Iron", "Silver", "Black metal", "Black marble", "Flametal", "Bloodgold" }; private static readonly string[] DefaultMaterials = new string[8] { "Wood, FineWood, RoundLog, YggdrasilWood, Blackwood, Frostwood", "CopperOre, TinOre, Copper, Tin, Bronze", "IronScrap, IronOre, Iron", "SilverOre, Silver", "BlackMetalScrap, BlackMetal", "BlackMarble", "FlametalOreNew, FlametalNew", "GoldOre, Gold" }; private ConfigEntry<bool> _enabled; private ConfigEntry<float> _battleChance; private ConfigEntry<int> _stackSize; private ConfigEntry<bool> _verbose; private readonly ConfigEntry<string>[] _tierMaterials = new ConfigEntry<string>[TierNames.Length]; private readonly Dictionary<string, int> _materialTier = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<ZDOID, Pending> _pending = new Dictionary<ZDOID, Pending>(); private readonly Dictionary<int, Proto> _protoCache = new Dictionary<int, Proto>(); private bool _validated; private readonly List<ZDOID> _keys = new List<ZDOID>(); private void Awake() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0131: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the idol conversion on or off."); _battleChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BattleIdolChance", 0.5f, new ConfigDescription("Chance (0-1) that a stack becomes a Battle Idol. Otherwise it becomes a Protection Idol.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _stackSize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StackSizeRequired", 0, "How many of a material make one idol. 0 = one full stack (the item's max stack size, e.g. 50 wood or 30 iron)."); _verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log every conversion to the BepInEx log."); for (int i = 0; i < TierNames.Length; i++) { _tierMaterials[i] = ((BaseUnityPlugin)this).Config.Bind<string>("Materials", TierNames[i].Replace(" ", ""), DefaultMaterials[i], "Comma-separated item prefab names that turn into a " + TierNames[i] + " Battle/Protection Idol."); } BuildMaterialTable(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { BuildMaterialTable(); _validated = false; }; new Harmony("kairi.IdolObliterator").PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"IdolObliterator 1.0.0 loaded. It only does anything on the server (or in single-player / when hosting)."); } private void BuildMaterialTable() { _materialTier.Clear(); for (int i = 0; i < _tierMaterials.Length; i++) { string[] array = _tierMaterials[i].Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < array.Length; j++) { string text = array[j].Trim(); if (text.Length != 0) { if (_materialTier.ContainsKey(text)) { Log.LogWarning((object)("'" + text + "' is listed under more than one tier; using " + TierNames[_materialTier[text]] + ".")); } else { _materialTier[text] = i; } } } } } private void ValidateOnce() { if (_validated || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ObjectDB.instance.GetItemPrefab("Wood") == (Object)null) { return; } _validated = true; foreach (KeyValuePair<string, int> item in _materialTier) { if ((Object)(object)ObjectDB.instance.GetItemPrefab(item.Key) == (Object)null) { Log.LogWarning((object)("Material '" + item.Key + "' (" + TierNames[item.Value] + ") is not a known item prefab name and will be ignored.")); } } for (int i = 0; i < TierNames.Length; i++) { bool[] array = new bool[2] { true, false }; foreach (bool battle in array) { if ((Object)(object)ObjectDB.instance.GetItemPrefab(IdolName(i, battle)) == (Object)null) { Log.LogError((object)("Idol prefab '" + IdolName(i, battle) + "' not found. " + TierNames[i] + " stacks will be left as vanilla coal.")); } } } } private static string IdolName(int tier, bool battle) { return string.Format("Upgrader{0}{1}", tier, battle ? "Weapon" : "Armor"); } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } internal void OnRoutedRpc(RoutedRPCData data) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (!_enabled.Value || data == null || data.m_methodHash != LeverReturnHash || !IsServer() || ((ZDOID)(ref data.m_targetZDO)).IsNone() || ZDOMan.instance == null) { return; } ZDO zDO = ZDOMan.instance.GetZDO(data.m_targetZDO); if (zDO == null) { return; } Proto proto = GetProto(zDO.GetPrefab()); if (proto == null) { return; } ValidateOnce(); byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null) { return; } Pending pending = BuildPlan(proto, byteArray); if (pending != null) { pending.Revision = zDO.DataRevision; pending.Deadline = Time.time + 15f; _pending[zDO.m_uid] = pending; if (_verbose.Value) { Log.LogInfo((object)$"Lightning at obliterator {zDO.m_uid}: {pending.IdolCount} idol(s) queued."); } } } private Proto GetProto(int prefabHash) { if (_protoCache.TryGetValue(prefabHash, out var value)) { return value; } Proto proto = null; GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabHash) : null); Incinerator val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren<Incinerator>(true) : null); if ((Object)(object)val2 != (Object)null) { Container val3 = (((Object)(object)val2.m_container != (Object)null) ? val2.m_container : val.GetComponentInChildren<Container>(true)); if ((Object)(object)val3 != (Object)null) { List<IncineratorConversion> list = new List<IncineratorConversion>(val2.m_conversions ?? new List<IncineratorConversion>()); list.Sort((IncineratorConversion a, IncineratorConversion b) => b.m_priority.CompareTo(a.m_priority)); proto = new Proto { Incinerator = val2, Width = val3.m_width, Height = val3.m_height, SortedConversions = list }; } } _protoCache[prefabHash] = proto; return proto; } private Pending BuildPlan(Proto proto, byte[] items) { //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Expected O, but got Unknown Inventory val = LoadInventory(proto, items); Inventory val2 = LoadInventory(proto, items); if (val == null || val2 == null) { return null; } Dictionary<string, int> before = Counts(val); List<int> list = new List<int>(); foreach (ItemData item in new List<ItemData>(val2.GetAllItems())) { string text = PrefabName(item); if (text == null || !_materialTier.TryGetValue(text, out var value)) { continue; } int num = ((_stackSize.Value > 0) ? _stackSize.Value : item.m_shared.m_maxStackSize); if (num > 0 && item.m_stack >= num && !((Object)(object)ObjectDB.instance.GetItemPrefab(IdolName(value, battle: true)) == (Object)null) && !((Object)(object)ObjectDB.instance.GetItemPrefab(IdolName(value, battle: false)) == (Object)null)) { int num2 = item.m_stack / num; val2.RemoveItem(item, num2 * num); for (int i = 0; i < num2; i++) { list.Add(value); } } } if (list.Count == 0) { return null; } SimulateVanilla(proto, val); SimulateVanilla(proto, val2); foreach (int item2 in list) { bool battle = Random.value < _battleChance.Value; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(IdolName(item2, battle)); if (!val2.AddItem(itemPrefab, 1)) { Log.LogWarning((object)"Obliterator is too full to hold the idols; leaving the vanilla result."); return null; } } ZPackage val3 = new ZPackage(); val2.Save(val3); return new Pending { Proto = proto, Before = before, ExpectedVanilla = Counts(val), Result = val3.GetArray(), IdolCount = list.Count }; } private static void SimulateVanilla(Proto proto, Inventory inv) { Incinerator incinerator = proto.Incinerator; List<ItemDrop> list = new List<ItemDrop>(); foreach (IncineratorConversion sortedConversion in proto.SortedConversions) { sortedConversion.AttemptCraft(inv, list); } if ((Object)(object)incinerator.m_defaultResult != (Object)null && incinerator.m_defaultCost > 0) { int num = inv.NrOfItemsIncludingStacks() / incinerator.m_defaultCost; for (int i = 0; i < num; i++) { list.Add(incinerator.m_defaultResult); } } inv.RemoveAll(); foreach (ItemDrop item in list) { inv.AddItem(((Component)item).gameObject, 1); } } private static Inventory LoadInventory(Proto proto, byte[] data) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0029: Expected O, but got Unknown if (data == null) { return null; } Inventory val = new Inventory("IdolObliterator", (Sprite)null, proto.Width, proto.Height); val.Load(new ZPackage(data)); return val; } private static string PrefabName(ItemData item) { if (item == null) { return null; } if ((Object)(object)item.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name; } GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item.m_shared) : null); if (!((Object)(object)val != (Object)null)) { return null; } return ((Object)val).name; } private static Dictionary<string, int> Counts(Inventory inv) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (ItemData allItem in inv.GetAllItems()) { string key = PrefabName(allItem) ?? allItem.m_shared.m_name; dictionary.TryGetValue(key, out var value); dictionary[key] = value + allItem.m_stack; } return dictionary; } private static bool SameCounts(Dictionary<string, int> a, Dictionary<string, int> b) { if (a.Count != b.Count) { return false; } foreach (KeyValuePair<string, int> item in a) { if (!b.TryGetValue(item.Key, out var value) || value != item.Value) { return false; } } return true; } private void Update() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) if (!_validated && IsServer()) { ValidateOnce(); } if (_pending.Count == 0) { return; } if (!IsServer() || ZDOMan.instance == null) { _pending.Clear(); return; } _keys.Clear(); _keys.AddRange(_pending.Keys); foreach (ZDOID key in _keys) { Pending pending = _pending[key]; ZDO zDO = ZDOMan.instance.GetZDO(key); if (zDO == null) { _pending.Remove(key); continue; } if (zDO.DataRevision == pending.Revision) { if (Time.time > pending.Deadline) { _pending.Remove(key); if (_verbose.Value) { Log.LogInfo((object)$"Obliterator {key} never converted (lever aborted?). Left as is."); } } continue; } pending.Revision = zDO.DataRevision; Inventory val = LoadInventory(pending.Proto, zDO.GetByteArray(ZDOVars.s_items, (byte[])null)); Dictionary<string, int> a = ((val != null) ? Counts(val) : new Dictionary<string, int>()); if (SameCounts(a, pending.ExpectedVanilla)) { zDO.Set(ZDOVars.s_items, pending.Result); _pending.Remove(key); if (_verbose.Value) { Log.LogInfo((object)$"Obliterator {key}: replaced the coal with {pending.IdolCount} idol(s)."); } } else if (!SameCounts(a, pending.Before)) { _pending.Remove(key); Log.LogWarning((object)$"Obliterator {key}: contents didn't match the expected result, so they were left alone."); } } } } [HarmonyPatch(typeof(ZRoutedRpc), "HandleRoutedRPC")] internal static class HandleRoutedRpcPatch { private static void Prefix(RoutedRPCData data) { try { Plugin.Instance?.OnRoutedRpc(data); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"IdolObliterator error: {arg}"); } } } } }