using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace ElementalBounty; [BepInPlugin("com.spencer4792.elementalbounty", "ElementalBounty", "1.0.0")] public class ElementalBountyPlugin : BaseUnityPlugin { public const string GUID = "com.spencer4792.elementalbounty"; public const string NAME = "ElementalBounty"; public const string VERSION = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry NameMatch; internal static ConfigEntry DropChanceMultiplier; internal static ConfigEntry EmptyChanceDivisor; internal static ConfigEntry LogBoosts; internal static ConfigEntry GuaranteeDrops; internal static string[] Matchers = new string[0]; private void Awake() { //IL_0129: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; NameMatch = ((BaseUnityPlugin)this).Config.Bind("General", "NameMatch", "Particle", "Comma-separated, case-insensitive substrings. Drop-table entries whose item name contains any of these get boosted. Default targets elemental particles."); DropChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind("General", "DropChanceMultiplier", 4f, "Multiplier applied to matching items' drop weight (1 = vanilla)."); EmptyChanceDivisor = ((BaseUnityPlugin)this).Config.Bind("General", "EmptyChanceDivisor", 2f, "Tables containing a matching item get their 'nothing drops' weight divided by this (1 = vanilla)."); LogBoosts = ((BaseUnityPlugin)this).Config.Bind("General", "LogBoosts", true, "Log each boosted drop entry (useful to verify what's being affected)."); GuaranteeDrops = ((BaseUnityPlugin)this).Config.Bind("General", "GuaranteeDrops", true, "Guarantee matching drops: tables containing a matching item lose their 'nothing drops' roll entirely and the matching item overwhelms other entries (~99.99%). Overrides DropChanceMultiplier for matched items."); string[] array = NameMatch.Value.Split(new char[1] { ',' }); List list = new List(); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.Length > 0) { list.Add(text2.ToLowerInvariant()); } } Matchers = list.ToArray(); new Harmony("com.spencer4792.elementalbounty").PatchAll(); Log.LogMessage((object)("ElementalBounty 1.0.0 ready. Items matching [" + string.Join(", ", Matchers) + "]: " + ((!GuaranteeDrops.Value) ? ("x" + DropChanceMultiplier.Value + " weight, empty chance /" + EmptyChanceDivisor.Value) : "GUARANTEED drops") + ". Home key = drop-table dump.")); } private void Update() { if (!InputHelper.GetKeyDown((KeyCode)278)) { return; } try { DropTable[] array = Object.FindObjectsOfType(typeof(DropTable)) as DropTable[]; Log.LogMessage((object)("=== Drop-table dump (v1.0.0): " + ((array != null) ? array.Length : 0) + " table(s) in scene ===")); if (array == null) { return; } FieldInfo field = typeof(DropTable).GetField("m_itemDrops", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(DropTable).GetField("m_emptyDropChance", BindingFlags.Instance | BindingFlags.NonPublic); DropTable[] array2 = array; foreach (DropTable val in array2) { if ((Object)(object)val == (Object)null) { continue; } List list = ((!(field != null)) ? null : (field.GetValue(val) as List)); int num = ((!(field2 != null)) ? (-1) : Convert.ToInt32(field2.GetValue(val))); Log.LogMessage((object)("TABLE '" + ((Object)((Component)val).gameObject).name + "' (root '" + ((Object)((Component)val).transform.root).name + "') entries=" + (list?.Count ?? 0) + " empty=" + num)); if (list == null) { continue; } foreach (ItemDropChance item in list) { if (item != null) { Item droppedItem = ((BasicItemDrop)item).DroppedItem; Log.LogMessage((object)(" '" + ((!((Object)(object)droppedItem != (Object)null)) ? "(null)" : droppedItem.Name) + "' weight=" + item.DropChance + " count=" + ((BasicItemDrop)item).MinDropCount + "-" + ((BasicItemDrop)item).MaxDropCount)); } } } Log.LogMessage((object)"=== end dump ==="); } catch (Exception ex) { Log.LogError((object)("Drop dump: " + ex)); } } internal static bool NameMatches(string itemName) { if (string.IsNullOrEmpty(itemName)) { return false; } string text = itemName.ToLowerInvariant(); string[] matchers = Matchers; foreach (string value in matchers) { if (text.Contains(value)) { return true; } } return false; } } internal static class InputHelper { private static readonly Type s_inputType = Type.GetType("UnityEngine.Input, UnityEngine.InputLegacyModule"); private static readonly MethodInfo s_getKeyDown = ((!(s_inputType != null)) ? null : s_inputType.GetMethod("GetKeyDown", new Type[1] { typeof(KeyCode) })); public static bool GetKeyDown(KeyCode k) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (s_getKeyDown == null) { return false; } try { return (bool)s_getKeyDown.Invoke(null, new object[1] { k }); } catch { return false; } } } [HarmonyPatch(typeof(DropTable), "CalculateChances")] internal static class CalculateChancesPatch { private static readonly HashSet s_boosted = new HashSet(); private static readonly FieldInfo s_itemDropsField = typeof(DropTable).GetField("m_itemDrops", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo s_emptyChanceField = typeof(DropTable).GetField("m_emptyDropChance", BindingFlags.Instance | BindingFlags.NonPublic); private static void Prefix(DropTable __instance) { try { if ((Object)(object)__instance == (Object)null || s_boosted.Contains(__instance)) { return; } List list = ((!(s_itemDropsField != null)) ? null : (s_itemDropsField.GetValue(__instance) as List)); if (list == null || list.Count == 0) { return; } bool flag = false; foreach (ItemDropChance item in list) { if (item == null) { continue; } Item droppedItem = ((BasicItemDrop)item).DroppedItem; if (!((Object)(object)droppedItem == (Object)null) && ElementalBountyPlugin.NameMatches(droppedItem.Name)) { flag = true; int dropChance = item.DropChance; int num = (item.DropChance = ((!ElementalBountyPlugin.GuaranteeDrops.Value) ? Mathf.Max(1, Mathf.RoundToInt((float)dropChance * ElementalBountyPlugin.DropChanceMultiplier.Value)) : (Mathf.Max(1, dropChance) * 10000))); if (ElementalBountyPlugin.LogBoosts.Value) { ElementalBountyPlugin.Log.LogMessage((object)("Boosted '" + droppedItem.Name + "' weight " + dropChance + " -> " + num + " in table '" + ((Object)((Component)__instance).gameObject).name + "'.")); } } } if (flag && s_emptyChanceField != null && (ElementalBountyPlugin.EmptyChanceDivisor.Value > 1f || ElementalBountyPlugin.GuaranteeDrops.Value)) { int num2 = Convert.ToInt32(s_emptyChanceField.GetValue(__instance)); if (num2 > 0) { int num3 = ((!ElementalBountyPlugin.GuaranteeDrops.Value) ? Mathf.Max(0, Mathf.RoundToInt((float)num2 / ElementalBountyPlugin.EmptyChanceDivisor.Value)) : 0); s_emptyChanceField.SetValue(__instance, num3); if (ElementalBountyPlugin.LogBoosts.Value) { ElementalBountyPlugin.Log.LogMessage((object)("Empty chance " + num2 + " -> " + num3 + " in table '" + ((Object)((Component)__instance).gameObject).name + "'.")); } } } s_boosted.Add(__instance); } catch (Exception ex) { ElementalBountyPlugin.Log.LogWarning((object)("CalculateChances patch: " + ex.Message)); } } }