Decompiled source of AlwaysEitr v1.1.1

plugins/AlwaysEitr.dll

Decompiled a week ago
using 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)
		{
		}
	}
}