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 AlwaysEitr v1.1.1
plugins/AlwaysEitr.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; 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: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("AlwaysEitr")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1+bd0a04ff84e51dd1ec98d4eeea7b5d25bee23013")] [assembly: AssemblyProduct("AlwaysEitr")] [assembly: AssemblyTitle("AlwaysEitr")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.0")] [module: UnverifiableCode] [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 Oddhatter.AlwaysEitr { [BepInPlugin("com.oddhatter.alwayseitr", "AlwaysEitr", "1.1.1")] [BepInProcess("valheim.exe")] public sealed class AlwaysEitrPlugin : BaseUnityPlugin { public const string Guid = "com.oddhatter.alwayseitr"; public const string Name = "AlwaysEitr"; public const string Version = "1.1.1"; internal static ManualLogSource Log; internal static ConfigFile Cfg; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> BaseEitr; internal static ConfigEntry<float> EitrRegenMultiplier; private const float ConfigReloadDelay = 0.5f; private Harmony _harmony; private FileSystemWatcher _configWatcher; private volatile bool _configFileChanged; private float _configReloadAt = -1f; private bool _configLockedLogged; private void Awake() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Cfg = ((BaseUnityPlugin)this).Config; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. When false the mod changes nothing: eitr comes only from food and summon limits are vanilla."); BaseEitr = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BaseEitr", 200f, new ConfigDescription("Always-on eitr pool, no food needed. Eitr from food is added on top (200 here + 100 from food = 300, back to 200 when the food wears off). 0 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>())); EitrRegenMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "EitrRegenMultiplier", 1f, new ConfigDescription("Eitr refill speed. 1 = vanilla, 2 = twice as fast. Gear and status effects that change eitr regen still apply on top.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); _harmony = new Harmony("com.oddhatter.alwayseitr"); _harmony.PatchAll(typeof(AlwaysEitrPlugin).Assembly); WatchConfigFile(); Log.LogInfo((object)"AlwaysEitr 1.1.1 loaded."); } private void Update() { if (_configFileChanged) { _configFileChanged = false; _configReloadAt = Time.realtimeSinceStartup + 0.5f; } if (_configReloadAt >= 0f && Time.realtimeSinceStartup >= _configReloadAt) { _configReloadAt = -1f; ReloadConfigFromDisk(); } } private void OnDestroy() { _configWatcher?.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void WatchConfigFile() { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; _configWatcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath)) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite), IncludeSubdirectories = false }; _configWatcher.Changed += OnConfigFileEvent; _configWatcher.Created += OnConfigFileEvent; _configWatcher.Renamed += OnConfigFileEvent; _configWatcher.EnableRaisingEvents = true; } private void OnConfigFileEvent(object sender, FileSystemEventArgs e) { _configFileChanged = true; } private void ReloadConfigFromDisk() { if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { return; } bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { ((BaseUnityPlugin)this).Config.Reload(); _configLockedLogged = false; Log.LogInfo((object)"Configuration reloaded from disk."); } catch (IOException ex) { if (!_configLockedLogged) { _configLockedLogged = true; Log.LogWarning((object)("Config file busy, will retry: " + ex.Message)); } _configReloadAt = Time.realtimeSinceStartup + 0.5f; } catch (Exception ex2) { Log.LogError((object)("Failed to reload configuration: " + ex2.Message)); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] internal static class Player_GetTotalFoodValue_Patch { private static void Postfix(ref float eitr) { if (AlwaysEitrPlugin.Enabled.Value) { eitr += AlwaysEitrPlugin.BaseEitr.Value; } } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class Player_UpdateStats_Patch { private static void Prefix(Player __instance, out float __state) { __state = __instance.m_eiterRegen; if (AlwaysEitrPlugin.Enabled.Value) { __instance.m_eiterRegen *= AlwaysEitrPlugin.EitrRegenMultiplier.Value; } } private static void Finalizer(Player __instance, float __state) { __instance.m_eiterRegen = __state; } } internal static class Summons { internal const string Section = "Summons"; private static readonly Dictionary<string, ConfigEntry<int>> s_byStaff = new Dictionary<string, ConfigEntry<int>>(); private static readonly Dictionary<string, List<ConfigEntry<int>>> s_byCreature = new Dictionary<string, List<ConfigEntry<int>>>(); private static readonly HashSet<string> s_keys = new HashSet<string>(); internal static void Scan(ObjectDB db) { //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Expected O, but got Unknown if ((Object)(object)db == (Object)null || db.m_items == null) { return; } HashSet<string> hashSet = new HashSet<string>(); foreach (Recipe recipe in db.m_recipes) { if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null) { hashSet.Add(((Object)((Component)recipe.m_item).gameObject).name); } } foreach (GameObject item in db.m_items) { ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null); if ((Object)(object)val == (Object)null || !hashSet.Contains(((Object)item).name) || s_byStaff.ContainsKey(((Object)item).name)) { continue; } SharedData shared = val.m_itemData.m_shared; List<SpawnAbility> list = new List<SpawnAbility>(); AddSpawnAbilities(shared.m_attack, list); AddSpawnAbilities(shared.m_secondaryAttack, list); SpawnAbility val2 = null; List<string> list2 = new List<string>(); foreach (SpawnAbility item2 in list) { GameObject[] array = (GameObject[])(((object)item2.m_spawnPrefab) ?? ((object)new GameObject[0])); foreach (GameObject val3 in array) { if ((Object)(object)val3 != (Object)null && (Object)(object)val3.GetComponent<Character>() != (Object)null && !list2.Contains(((Object)val3).name)) { list2.Add(((Object)val3).name); val2 = val2 ?? item2; } } } if ((Object)(object)val2 == (Object)null) { continue; } string text = Localize(shared.m_name); string text2 = Localize(val2.m_spawnPrefab[0].GetComponent<Character>().m_name); string text3 = SafeKey(text); if (text3.Length == 0 || !s_keys.Add(text3)) { text3 = SafeKey(text + " (" + ((Object)item).name + ")"); s_keys.Add(text3); } ConfigEntry<int> val4 = AlwaysEitrPlugin.Cfg.Bind<int>("Summons", text3, 0, new ConfigDescription("Most " + text2 + " summons from the " + text + " you can have out at once. 0 = the normal game limit (" + NormalLimit(val2) + ").", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>())); s_byStaff[((Object)item).name] = val4; foreach (string item3 in list2) { if (!s_byCreature.TryGetValue(item3, out var value)) { value = (s_byCreature[item3] = new List<ConfigEntry<int>>()); } value.Add(val4); } AlwaysEitrPlugin.Log.LogInfo((object)("Summoning staff: " + text + " (" + ((Object)item).name + ") summons " + string.Join(", ", list2) + "; normal limit " + NormalLimit(val2) + ".")); } } private static string NormalLimit(SpawnAbility spawn) { int maxSpawned = spawn.m_maxSpawned; if (spawn.m_setMaxInstancesFromWeaponLevel) { if (maxSpawned <= 0) { return "one per upgrade level of the staff"; } return $"one per upgrade level of the staff, at most {maxSpawned}"; } List<string> list = new List<string>(); if (spawn.m_levelUpSettings != null) { List<LevelUpSettings> list2 = new List<LevelUpSettings>(spawn.m_levelUpSettings); list2.Sort((LevelUpSettings a, LevelUpSettings b) => a.m_skillLevel.CompareTo(b.m_skillLevel)); foreach (LevelUpSettings item in list2) { if (item.m_maxSpawns > 0) { int num = ((maxSpawned > 0) ? Mathf.Min(item.m_maxSpawns, maxSpawned) : item.m_maxSpawns); list.Add((item.m_skillLevel > 0) ? $"{num} from skill level {item.m_skillLevel}" : num.ToString()); } } } if (list.Count > 0) { return string.Join(", ", list); } if (maxSpawned <= 0) { return "none"; } return maxSpawned.ToString(); } private static string Localize(string token) { if (Localization.instance == null) { return token; } return Localization.instance.Localize(token); } private static string SafeKey(string name) { string text = "=\n\t\\\"'[]"; for (int i = 0; i < text.Length; i++) { name = name.Replace(text[i].ToString(), ""); } return name.Trim(); } private static void AddSpawnAbilities(Attack attack, List<SpawnAbility> into) { GameObject val = attack?.m_attackProjectile; if (!((Object)(object)val == (Object)null)) { SpawnAbility component = val.GetComponent<SpawnAbility>(); if ((Object)(object)component != (Object)null) { into.Add(component); } Projectile component2 = val.GetComponent<Projectile>(); SpawnAbility val2 = (((Object)(object)component2 != (Object)null && (Object)(object)component2.m_spawnOnHit != (Object)null) ? component2.m_spawnOnHit.GetComponent<SpawnAbility>() : null); if ((Object)(object)val2 != (Object)null) { into.Add(val2); } } } internal static int MaxForStaff(ItemData item) { if (!AlwaysEitrPlugin.Enabled.Value || item == null || (Object)(object)item.m_dropPrefab == (Object)null) { return 0; } if (!s_byStaff.TryGetValue(((Object)item.m_dropPrefab).name, out var value)) { return 0; } return value.Value; } internal static int MaxForCreature(GameObject creature) { if (!AlwaysEitrPlugin.Enabled.Value || !s_byCreature.TryGetValue(Utils.GetPrefabName(creature), out var value)) { return 0; } int num = 0; foreach (ConfigEntry<int> item in value) { num = Mathf.Max(num, item.Value); } return num; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Awake_Patch { private static void Postfix(ObjectDB __instance) { Summons.Scan(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDB_CopyOtherDB_Patch { private static void Postfix(ObjectDB __instance) { Summons.Scan(__instance); } } [HarmonyPatch(typeof(SpawnAbility), "Setup")] internal static class SpawnAbility_Setup_Patch { private static void Prefix(SpawnAbility __instance, Character owner, ItemData item) { if (!((Object)(object)owner == (Object)null) && !((Object)(object)owner != (Object)(object)Player.m_localPlayer)) { int num = Summons.MaxForStaff(item); if (num > 0) { __instance.m_maxSpawned = num; } } } } [HarmonyPatch(typeof(Tameable), "UnsummonMaxInstances")] internal static class Tameable_UnsummonMaxInstances_Patch { private static void Prefix(Tameable __instance, ref int maxInstances) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance.m_monsterAI == (Object)null) && !((Object)(object)__instance.m_monsterAI.GetFollowTarget() != (Object)(object)((Component)localPlayer).gameObject)) { int num = Summons.MaxForCreature(((Component)__instance).gameObject); if (num > 0) { maxInstances = num; } } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }