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 IdolDrops v1.0.0
IdolDrops.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("IdolDrops")] [assembly: AssemblyDescription("Enemies in Valheim drop Forge of Potential idols of their own biome's tier")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: InternalsVisibleTo("IdolCheck")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [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 IdolDrops { internal static class AutoRules { private class Info { public string Skip; public bool Boss; public float Health; public int SpawnTier = -1; public float Rate; } private static readonly FieldInfo SpawnInstances = AccessTools.Field(typeof(SpawnSystem), "m_instances"); private static readonly Dictionary<string, Info> Cache = new Dictionary<string, Info>(StringComparer.Ordinal); private static readonly HashSet<string> Written = new HashSet<string>(StringComparer.Ordinal); private static HashSet<string> _excluded = new HashSet<string>(StringComparer.Ordinal); internal static void SetExcluded(string value) { _excluded = DropRules.ParseNames(value); Cache.Clear(); } internal static bool TryGet(Character character, string name, out Rule rule, out string source) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected I4, but got Unknown rule = default(Rule); source = null; if (!Plugin.AutoAddCreatures.Value || _excluded.Contains(name)) { return false; } if (!Cache.TryGetValue(name, out var value)) { value = Learn(character, name); if (value == null) { return false; } Cache[name] = value; } if (value.Skip != null) { return false; } int num = value.SpawnTier; source = "spawn list"; if (num < 0) { num = DropRules.BiomeTier((int)Heightmap.FindBiome(((Component)character).transform.position)); source = "death biome"; } if (num < 0) { return false; } if (value.Boss) { num = Math.Max(num, DropRules.BossHealthTier(value.Health)); rule = new Rule { Tier = num, Percent = 100f, Boss = true }; Write(name, $"Bosses: {name}:{num}", value, source); return true; } int num2 = DropRules.AutoClass(value.Health, num, value.Rate); rule = new Rule { Tier = num, Percent = Plugin.ClassPercent(num2) }; Write(name, $"{DropRules.Classes[num2]}: {name}:{num}", value, source); return true; } private static Info Learn(Character character, string name) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Expected I4, but got Unknown Info info = new Info { Boss = character.IsBoss(), Health = character.m_health }; Faction faction = character.m_faction; if ((int)faction == 0 || (int)faction == 1 || (int)faction == 11 || (int)faction == 12) { info.Skip = $"faction {faction}"; } else { BaseAI baseAI = character.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val == null) { info.Skip = "no monster AI (an animal)"; } else if (((BaseAI)val).m_passiveAggresive && !info.Boss) { info.Skip = "passive until attacked"; } else if (Object.op_Implicit((Object)(object)((Component)character).GetComponent<Growup>())) { info.Skip = "a baby that grows up"; } else { Humanoid val2 = (Humanoid)(object)((character is Humanoid) ? character : null); if (val2 != null && !info.Boss && Count(val2.m_defaultItems) + Count(val2.m_randomWeapon) + Count(val2.m_randomSets) == 0) { info.Skip = "no attacks"; } } } if (info.Skip != null) { if (Plugin.LogDrops.Value) { Plugin.Log.LogInfo((object)(name + ": not auto-added, " + info.Skip)); } return info; } SpawnSystem val3 = (SpawnInstances?.GetValue(null) as List<SpawnSystem>)?.FirstOrDefault((Func<SpawnSystem, bool>)((SpawnSystem s) => Object.op_Implicit((Object)(object)s))); if (!Object.op_Implicit((Object)(object)val3)) { return null; } int num = 0; foreach (SpawnSystemList spawnList in val3.m_spawnLists) { if (!Object.op_Implicit((Object)(object)spawnList)) { continue; } foreach (SpawnData spawner in spawnList.m_spawners) { if (spawner != null && spawner.m_enabled && Object.op_Implicit((Object)(object)spawner.m_prefab) && !(((Object)spawner.m_prefab).name != name) && string.IsNullOrEmpty(spawner.m_requiredPersistentEvent)) { num |= spawner.m_biome; info.Rate = Math.Max(info.Rate, DropRules.SpawnRate(spawner.m_spawnInterval, spawner.m_spawnChance, spawner.m_groupSizeMin, spawner.m_groupSizeMax)); } } } info.SpawnTier = DropRules.BiomeTier(num); return info; } private static int Count(Array items) { return items?.Length ?? 0; } private static void Write(string name, string entry, Info info, string source) { string path = Path.Combine(Paths.ConfigPath, "onononoki.IdolDrops.auto.txt"); if (Written.Count == 0) { LoadWritten(path); } if (!Written.Add(name)) { return; } string text = $"{entry,-40} health {info.Health:0}, spawn rate {info.Rate:0.#} per hour, tier from {source}"; Plugin.Log.LogInfo((object)("auto-added " + text)); try { if (!File.Exists(path)) { File.WriteAllText(path, "# creatures IdolDrops added on its own. copy an entry into a list in onononoki.IdolDrops.cfg to change it\n"); } File.AppendAllText(path, text + "\n"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("could not write the auto list: " + ex.Message)); } } private static void LoadWritten(string path) { try { if (!File.Exists(path)) { return; } string[] array = File.ReadAllLines(path); foreach (string text in array) { string[] array2 = text.Split(new char[2] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length >= 2 && !text.StartsWith("#")) { Written.Add(array2[1]); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("could not read the auto list: " + ex.Message)); } } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] internal static class AddIdol { private static readonly FieldRef<CharacterDrop, Character> CharacterOf = AccessTools.FieldRefAccess<CharacterDrop, Character>("m_character"); private static readonly FieldRef<Character, HitData> LastHitOf = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit"); [HarmonyPriority(0)] private static void Postfix(CharacterDrop __instance, List<KeyValuePair<GameObject, int>> __result) { if (!Plugin.Enabled.Value || __result == null) { return; } Character val = CharacterOf.Invoke(__instance); if (!Object.op_Implicit((Object)(object)val)) { return; } string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject); string source = null; if (!Plugin.Rules.TryGetValue(prefabName, out var value) && !AutoRules.TryGet(val, prefabName, out value, out source)) { return; } string text = SkipReason(val, value.Boss); if (text == null && __result.Any((KeyValuePair<GameObject, int> d) => Object.op_Implicit((Object)(object)d.Key) && ((Object)d.Key).name.StartsWith("Upgrader"))) { text = "it already drops an idol"; } if (text != null) { if (Plugin.LogDrops.Value) { Plugin.Log.LogInfo((object)(prefabName + ": no idol, " + text)); } return; } bool weapon = Random.value * 100f < Plugin.WeaponShare.Value; string text2 = ((source == null) ? "" : (" (auto, tier from " + source + ")")); if (value.Boss) { Add(__result, prefabName, value.Tier, weapon, "boss" + text2); return; } int num = Mathf.Clamp(val.GetLevel() - 1, 0, Plugin.MaxStars.Value); bool flag = FromEvent(val); float num2 = DropRules.Chance(value.Percent, num, Plugin.ChancePerStar.Value, flag, Plugin.EventMultiplier.Value, Plugin.GlobalMultiplier.Value); float value2 = Random.value; string text3 = string.Format("{0} star, tier {1}, chance {2:0.###}%{3}, roll {4:0.###}{5}", num, value.Tier, num2 * 100f, flag ? " (event)" : "", value2 * 100f, text2); if (value2 < num2) { Add(__result, prefabName, value.Tier, weapon, text3); } else if (Plugin.LogDrops.Value) { Plugin.Log.LogInfo((object)(prefabName + ": no idol, " + text3)); } } private static string SkipReason(Character character, bool boss) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 if (character.IsTamed()) { return "tamed"; } if ((int)character.m_faction == 11) { return "player summon"; } if (!Plugin.SummonsDrop.Value && SummonMark.IsMarked(character)) { return "spawned by another creature"; } if (boss || !Plugin.RequirePlayerKill.Value) { return null; } HitData obj = LastHitOf.Invoke(character); Character val = ((obj != null) ? obj.GetAttacker() : null); if (!Object.op_Implicit((Object)(object)val) || !val.IsPlayer()) { return "last hit not from a player"; } return null; } private static bool FromEvent(Character character) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) BaseAI baseAI = character.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val != null && val.IsEventCreature()) { return true; } PersistentEventSystem instance = PersistentEventSystem.instance; if (Object.op_Implicit((Object)(object)instance)) { return instance.GetActiveEvent(((Component)character).transform.position) != null; } return false; } private static void Add(List<KeyValuePair<GameObject, int>> drops, string creature, int tier, bool weapon, string detail) { string text = DropRules.IdolName(tier, weapon); GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(text) : null); if (!Object.op_Implicit((Object)(object)val)) { Plugin.Log.LogWarning((object)(creature + ": idol " + text + " not found in the item database")); return; } drops.Add(new KeyValuePair<GameObject, int>(val, 1)); if (Plugin.LogDrops.Value) { Plugin.Log.LogInfo((object)(creature + ": drops " + text + ", " + detail)); } } } internal static class SummonMark { internal const string Key = "IdolDrops_summoned"; internal static readonly MethodInfo Original = typeof(Object).GetMethods(BindingFlags.Static | BindingFlags.Public).First((MethodInfo m) => m.Name == "Instantiate" && m.IsGenericMethodDefinition && m.GetParameters().Length == 3 && m.GetParameters()[1].ParameterType == typeof(Vector3) && m.GetParameters()[2].ParameterType == typeof(Quaternion)).MakeGenericMethod(typeof(GameObject)); internal static readonly MethodInfo Wrapper = AccessTools.Method(typeof(SummonMark), "Instantiate", (Type[])null, (Type[])null); internal static GameObject Instantiate(GameObject prefab, Vector3 position, Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(prefab, position, rotation); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.GetComponent<Character>())) { ZNetView component = val.GetComponent<ZNetView>(); ZDO val2 = (Object.op_Implicit((Object)(object)component) ? component.GetZDO() : null); if (val2 != null) { val2.Set("IdolDrops_summoned", true); } } return val; } internal static bool IsMarked(Character character) { ZNetView component = ((Component)character).GetComponent<ZNetView>(); ZDO val = (Object.op_Implicit((Object)(object)component) ? component.GetZDO() : null); if (val != null) { return val.GetBool("IdolDrops_summoned", false); } return false; } internal static IEnumerable<CodeInstruction> Swap(IEnumerable<CodeInstruction> code) { foreach (CodeInstruction item in code) { if (CodeInstructionExtensions.Calls(item, Original)) { item.opcode = OpCodes.Call; item.operand = Wrapper; } yield return item; } } } [HarmonyPatch] internal static class MarkAbilitySpawns { private static MethodBase TargetMethod() { return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(SpawnAbility), "Spawn", (Type[])null, (Type[])null)); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> code) { return SummonMark.Swap(code); } } [HarmonyPatch(typeof(TriggerSpawner), "Spawn")] internal static class MarkTriggerSpawns { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> code) { return SummonMark.Swap(code); } } [HarmonyPatch(typeof(CharacterDrop), "DropItems")] internal static class MarkDeathSpawns { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> code) { return SummonMark.Swap(code); } } internal struct Rule { public int Tier; public float Percent; public bool Boss; } internal static class DropRules { internal const int MaxTier = 7; internal static readonly string[] Classes = new string[5] { "Swarm", "Common", "Uncommon", "Strong", "Rare" }; internal static readonly float[] DefaultPercents = new float[5] { 0.5f, 0.67f, 1f, 2.5f, 5f }; internal static readonly string[] DefaultLists = new string[5] { "Greyling:0, Neck:0, Leech:2, Bat_Swamp:2, Bat:3, Leech_cave:3, Tick:5, SeekerBrood:5, Charred_Twitcher:6, BlobMorkMini:7", "Boar:0, Skeleton_Meadows:0, Skeleton_Meadows_noarcher:0, Greydwarf:1, Skeleton:1, Skeleton_NoArcher:1, Draugr:2, Draugr_sleeping:2, Draugr_Ranged:2, Draugr_Ranged_sleeping:2, Skeleton_Swamps:2, Skeleton_Swamps_noarcher:2, Wolf:3, Skeleton_Mountains:3, Skeleton_Mountains_noarcher:3, Goblin:4, GoblinArcher:4, Seeker:5, Charred_Melee:6, Charred_Archer:6, Asksvin_hatchling:6, Elaking:7, Greydwarf_Frozen:7, Skeleton_DeepNorth:7, GoblinDeepNorth:7", "Greydwarf_Shaman:1, Ghost:1, Ghost_sleeping:1, Blob:2, Surtling:2, Hatchling:3, Ulv:3, BlobFrost:3, Deathsquito:4, GoblinShaman:4, BlobTar:4, Dverger:5, DvergerMage:5, DvergerMageFire:5, DvergerMageIce:5, DvergerMageSupport:5, Asksvin:6, Volture:6, BlobLava:6, DvergerAshlands:6, ElakingLantern:7, Greydwarf_Shaman_Frozen:7, ShadowPerson:7, BlobMork:7, DvergerDeepNorth:7", "Greydwarf_Elite:1, Troll:1, Troll_sleeping:1, Bjorn:1, Bjorn_sleeping:1, Draugr_Elite:2, Draugr_Elite_sleeping:2, BlobElite:2, Wraith:2, Serpent:2, Fenring:3, Fenring_Cultist:3, GoblinBrute:4, Lox:4, Unbjorn:4, Charred_Mage:6, BonemawSerpent:6, Barka:7, TrollFrost:7, ElakingMole:7, FallenWarrior:7", "Skeleton_Poison:1, Skeleton_Hildir_nochest:1, Abomination:2, Writhan:2, BogWitchKvastur:2, StoneGolem:3, Fenring_Cultist_Hildir_nochest:3, GoblinBruteBros_nochest:4, GoblinShaman_Hildir_nochest:4, SeekerBrute:5, Gjall:5, Morgen:6, Morgen_NonSleeping:6, FallenValkyrie:6, Charred_Melee_Dyrnwyn:6, JotunWarrior:7, JotunWarriorDualWield:7, JotunWitch:7" }; internal const string DefaultOverrides = "Tick:0.2, SeekerBrood:0.2"; internal const string DefaultBosses = "Eikthyr:0, gd_king:1, Bonemass:2, Dragon:3, GoblinKing:4, SeekerQueen:5, Fader:6, FrozenKing_p3:7, Skeleton_Hildir:1, Fenring_Cultist_Hildir:3, GoblinBrute_Hildir:4, GoblinBruteBros:4, GoblinShaman_Hildir:4"; internal const string DefaultExcluded = "Deer, Deer_White, Chicken, Hen, Boar_piggy, Wolf_cub, Lox_Calf, Moose, Moose_calf, Seal, Seal_Pup, Hare, Troll_Summoned, Charred_Twitcher_Summoned, Charred_Melee_Fader, Charred_Archer_Fader, Skeleton_aspect, BlobAspect, Skeleton_Friendly, Mistile, TentaRoot, Tendril, Tendril_back, DvergerTest, TrainingDummy, Hive, TheHive, Goblin_Gem, Ghost_Void, Ghost_old, TentaRoot_wild, FrozenKing_0, FrozenKing_p2, Aspect_Bonemass, Aspect_Eikthyr, Aspect_Elder, Aspect_Fader, Aspect_Moder, Aspect_SeekerQueen, Aspect_TentaRoot, Aspect_Yagluth"; internal static readonly float[] BaselineHealth = new float[8] { 30f, 40f, 100f, 75f, 175f, 200f, 400f, 175f }; internal static readonly float[] ClassRatios = new float[4] { 0.4f, 1.1f, 2.5f, 20f }; internal const float CommonRate = 50f; internal const float SwarmRate = 300f; internal static readonly float[] BossHealth = new float[8] { 500f, 2500f, 5000f, 7500f, 10000f, 12500f, 25000f, 30000f }; internal static Dictionary<string, Rule> Build(string[] lists, float[] percents, string overrides, string bosses, List<string> warnings) { Dictionary<string, Rule> dictionary = new Dictionary<string, Rule>(StringComparer.Ordinal); for (int i = 0; i < lists.Length; i++) { foreach (KeyValuePair<string, string> item in ParsePairs(lists[i], warnings)) { if (!TryTier(item.Value, out var tier)) { warnings.Add($"{Classes[i]}: {item.Key} has no tier from 0 to {7}, skipped"); continue; } if (dictionary.ContainsKey(item.Key)) { warnings.Add(Classes[i] + ": " + item.Key + " is already in another list, skipped"); continue; } dictionary[item.Key] = new Rule { Tier = tier, Percent = percents[i] }; } } foreach (KeyValuePair<string, string> item2 in ParsePairs(overrides, warnings)) { if (!TryPercent(item2.Value, out var percent)) { warnings.Add("Overrides: " + item2.Key + " has no valid percent, skipped"); continue; } if (!dictionary.TryGetValue(item2.Key, out var value)) { warnings.Add("Overrides: " + item2.Key + " is in no class list, skipped"); continue; } value.Percent = percent; dictionary[item2.Key] = value; } foreach (KeyValuePair<string, string> item3 in ParsePairs(bosses, warnings)) { if (!TryTier(item3.Value, out var tier2)) { warnings.Add($"Bosses: {item3.Key} has no tier from 0 to {7}, skipped"); continue; } dictionary[item3.Key] = new Rule { Tier = tier2, Percent = 100f, Boss = true }; } return dictionary; } internal static List<KeyValuePair<string, string>> ParsePairs(string value, List<string> warnings) { List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); string[] array = (value ?? "").Split(new char[1] { ',' }); foreach (string text in array) { if (text.Trim().Length != 0) { string[] array2 = text.Split(new char[1] { ':' }); if (array2.Length == 2 && array2[0].Trim().Length > 0 && array2[1].Trim().Length > 0) { list.Add(new KeyValuePair<string, string>(array2[0].Trim(), array2[1].Trim())); } else { warnings.Add("\"" + text.Trim() + "\" is not Name:value, skipped"); } } } return list; } private static bool TryTier(string text, out int tier) { if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out tier) && tier >= 0) { return tier <= 7; } return false; } private static bool TryPercent(string text, out float percent) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out percent) && percent >= 0f) { return percent <= 100f; } return false; } internal static float Chance(float percent, int stars, float perStar, bool fromEvent, float eventMultiplier, float globalMultiplier) { double num = (double)percent / 100.0 * Math.Pow(perStar, Math.Max(0, stars)); if (fromEvent) { num *= (double)eventMultiplier; } num *= (double)globalMultiplier; return (float)Math.Max(0.0, Math.Min(1.0, num)); } internal static string IdolName(int tier, bool weapon) { return string.Format("Upgrader{0}{1}", tier, weapon ? "Weapon" : "Armor"); } internal static HashSet<string> ParseNames(string value) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = (value ?? "").Split(new char[1] { ',' }); foreach (string text in array) { if (text.Trim().Length > 0) { hashSet.Add(text.Trim()); } } return hashSet; } internal static int BiomeTier(int biomes) { int[] array = new int[9] { 1, 8, 2, 256, 4, 16, 512, 32, 64 }; int[] array2 = new int[9] { 0, 1, 2, 2, 3, 4, 5, 6, 7 }; for (int i = 0; i < array.Length; i++) { if ((biomes & array[i]) != 0) { return array2[i]; } } return -1; } internal static int BossHealthTier(float health) { int result = 0; for (int i = 0; i < BossHealth.Length; i++) { if (health >= BossHealth[i]) { result = i; } } return result; } internal static float SpawnRate(float interval, float chance, int groupMin, int groupMax) { if (!(interval <= 0f)) { return 3600f / interval * chance / 100f * (float)(groupMin + groupMax) / 2f; } return 0f; } internal static int AutoClass(float health, int tier, float rate) { float num = health / BaselineHealth[Math.Max(0, Math.Min(7, tier))]; int num2 = 0; float[] classRatios = ClassRatios; foreach (float num3 in classRatios) { if (num >= num3) { num2++; } } if (rate >= 300f) { num2 = 0; } else if (rate >= 50f) { num2 = Math.Min(num2, 1); } return num2; } } [BepInPlugin("onononoki.IdolDrops", "IdolDrops", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "onononoki.IdolDrops"; public const string ModName = "IdolDrops"; public const string ModVersion = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> GlobalMultiplier; internal static ConfigEntry<float> ChancePerStar; internal static ConfigEntry<float> EventMultiplier; internal static ConfigEntry<float> WeaponShare; internal static ConfigEntry<bool> RequirePlayerKill; internal static ConfigEntry<bool> SummonsDrop; internal static ConfigEntry<int> MaxStars; internal static ConfigEntry<bool> AutoAddCreatures; internal static ConfigEntry<bool> LogDrops; private static ConfigEntry<string> _excluded; private static readonly ConfigEntry<float>[] ClassPercents = new ConfigEntry<float>[DropRules.Classes.Length]; private static readonly ConfigEntry<string>[] ClassLists = new ConfigEntry<string>[DropRules.Classes.Length]; private static ConfigEntry<string> _overrides; private static ConfigEntry<string> _bosses; internal static Dictionary<string, Rule> Rules = new Dictionary<string, Rule>(); private void Awake() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Expected O, but got Unknown //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Expected O, but got Unknown //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Expected O, but got Unknown //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Expected O, but got Unknown //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Expected O, but got Unknown //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Expected O, but got Unknown //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Expected O, but got Unknown //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Expected O, but got Unknown //IL_04ab: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, new ConfigDescription("Enemies drop Forge of Potential idols of their biome's tier.", (AcceptableValueBase)null, new object[1] { val })); GlobalMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "GlobalMultiplier", 1f, new ConfigDescription("Multiplies every drop chance. Bosses always drop one idol.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { val })); ChancePerStar = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "ChancePerStar", 2f, new ConfigDescription("Chance factor per star. 2 doubles the chance at 1 star and quadruples it at 2 stars, like the game's own star loot.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), new object[1] { val })); EventMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "EventMultiplier", 0.5f, new ConfigDescription("Chance factor for raid creatures and creatures killed during a world event such as the Jotun invasion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { val })); WeaponShare = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "WeaponShare", 50f, new ConfigDescription("Percent of drops that are Battle idols. The rest are Protection idols.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { val })); RequirePlayerKill = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "RequirePlayerKill", true, new ConfigDescription("Only creatures whose last hit came from a player drop idols. Stops farms and fights between creatures from making idols. Bosses always drop.", (AcceptableValueBase)null, new object[1] { val })); SummonsDrop = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "SummonsDrop", false, new ConfigDescription("Creatures spawned by other creatures and bosses (Bonemass's skeletons, Gjall's ticks, the Queen's brood, an Oozer's blobs) can drop idols too.", (AcceptableValueBase)null, new object[1] { val })); MaxStars = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "MaxStars", 2, new ConfigDescription("Stars above this count no more for the chance, for mods that add more than 2 stars.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), new object[1] { val })); AutoAddCreatures = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "AutoAddCreatures", true, new ConfigDescription("Enemies in no list (for example from other mods) drop idols too. Tier from the biome they spawn in, class from their health and spawn rate, bosses always drop. Each one is written to onononoki.IdolDrops.auto.txt next to this file.", (AcceptableValueBase)null, new object[1] { val })); for (int i = 0; i < DropRules.Classes.Length; i++) { string text = DropRules.Classes[i]; ClassPercents[i] = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Chances", text, DropRules.DefaultPercents[i], new ConfigDescription("Drop chance in percent for the creatures in the " + text + " list.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { val })); ClassLists[i] = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Creatures", text, DropRules.DefaultLists[i], new ConfigDescription("Creatures that drop at the " + text + " chance, as PrefabName:tier separated by commas. Tier 0 Meadows, 1 Black Forest, 2 Swamp, 3 Mountains, 4 Plains, 5 Mistlands, 6 Ashlands, 7 Deep North.", (AcceptableValueBase)null, new object[1] { val })); } _overrides = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Creatures", "Overrides", "Tick:0.2, SeekerBrood:0.2", new ConfigDescription("Own chance for single creatures, as PrefabName:percent separated by commas. The creature must also be in a class list.", (AcceptableValueBase)null, new object[1] { val })); _bosses = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Creatures", "Bosses", "Eikthyr:0, gd_king:1, Bonemass:2, Dragon:3, GoblinKing:4, SeekerQueen:5, Fader:6, FrozenKing_p3:7, Skeleton_Hildir:1, Fenring_Cultist_Hildir:3, GoblinBrute_Hildir:4, GoblinBruteBros:4, GoblinShaman_Hildir:4", new ConfigDescription("Bosses that always drop one idol, as PrefabName:tier separated by commas.", (AcceptableValueBase)null, new object[1] { val })); _excluded = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Creatures", "Excluded", "Deer, Deer_White, Chicken, Hen, Boar_piggy, Wolf_cub, Lox_Calf, Moose, Moose_calf, Seal, Seal_Pup, Hare, Troll_Summoned, Charred_Twitcher_Summoned, Charred_Melee_Fader, Charred_Archer_Fader, Skeleton_aspect, BlobAspect, Skeleton_Friendly, Mistile, TentaRoot, Tendril, Tendril_back, DvergerTest, TrainingDummy, Hive, TheHive, Goblin_Gem, Ghost_Void, Ghost_old, TentaRoot_wild, FrozenKing_0, FrozenKing_p2, Aspect_Bonemass, Aspect_Eikthyr, Aspect_Elder, Aspect_Fader, Aspect_Moder, Aspect_SeekerQueen, Aspect_TentaRoot, Aspect_Yagluth", new ConfigDescription("Creatures that are never auto-added, as prefab names separated by commas. Listed creatures still drop.", (AcceptableValueBase)null, new object[1] { val })); LogDrops = ((BaseUnityPlugin)this).Config.Bind<bool>("9 - Debug", "LogDrops", false, "Write one line to the log for every idol roll. Only affects your own game."); ConfigEntry<float>[] classPercents = ClassPercents; for (int j = 0; j < classPercents.Length; j++) { classPercents[j].SettingChanged += delegate { BuildRules(); }; } ConfigEntry<string>[] classLists = ClassLists; for (int j = 0; j < classLists.Length; j++) { classLists[j].SettingChanged += delegate { BuildRules(); }; } _overrides.SettingChanged += delegate { BuildRules(); }; _bosses.SettingChanged += delegate { BuildRules(); }; _excluded.SettingChanged += delegate { BuildRules(); }; SynchronizationManager.OnConfigurationSynchronized += delegate { BuildRules(); }; BuildRules(); new Harmony("onononoki.IdolDrops").PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"IdolDrops 1.0.0 loaded"); } private static void BuildRules() { string[] array = new string[ClassLists.Length]; float[] array2 = new float[ClassPercents.Length]; for (int i = 0; i < array.Length; i++) { array[i] = ClassLists[i].Value; array2[i] = ClassPercents[i].Value; } List<string> list = new List<string>(); Rules = DropRules.Build(array, array2, _overrides.Value, _bosses.Value, list); AutoRules.SetExcluded(_excluded.Value); foreach (string item in list) { Log.LogWarning((object)item); } Log.LogInfo((object)$"{Rules.Count} creatures drop idols"); } internal static float ClassPercent(int cls) { return ClassPercents[cls].Value; } } }