Decompiled source of ArcaneAttunement v1.0.3

plugins/ArcaneAttunement.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
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.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Snook")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright Snook 2026. All rights reserved.")]
[assembly: AssemblyDescription("Client-side baseline Eitr from Elemental Magic and Blood Magic.")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+3f6a3f63ea607c7cb6be11e9bd9c32777ef814af")]
[assembly: AssemblyProduct("Arcane Attunement")]
[assembly: AssemblyTitle("ArcaneAttunement")]
[assembly: AssemblyVersion("1.0.3.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 ArcaneAttunement
{
	public enum Preset
	{
		Attuned,
		Balanced,
		PowerOverwhelming
	}
	public readonly struct PresetRates
	{
		public float EitrPerLevel { get; }

		public float RegenPercentPer5Levels { get; }

		public PresetRates(float eitrPerLevel, float regenPercentPer5Levels)
		{
			EitrPerLevel = eitrPerLevel;
			RegenPercentPer5Levels = regenPercentPer5Levels;
		}
	}
	public static class AttunementMath
	{
		public static PresetRates RatesFor(Preset preset)
		{
			return preset switch
			{
				Preset.Attuned => new PresetRates(0.5f, 1.5f), 
				Preset.Balanced => new PresetRates(1f, 2f), 
				Preset.PowerOverwhelming => new PresetRates(1.5f, 5f), 
				_ => throw new ArgumentOutOfRangeException("preset", preset, "Unknown preset."), 
			};
		}

		public static float MaxEitrBonus(float elementalLevel, float bloodLevel, float eitrPerLevel)
		{
			return (elementalLevel + bloodLevel) * eitrPerLevel;
		}

		public static int RegenSteps(float elementalLevel, float bloodLevel)
		{
			return StepsPerFive(elementalLevel) + StepsPerFive(bloodLevel);
		}

		public static float RegenFraction(float elementalLevel, float bloodLevel, float regenPercentPer5Levels)
		{
			return (float)RegenSteps(elementalLevel, bloodLevel) * (regenPercentPer5Levels / 100f);
		}

		public static string ToConfigString(Preset preset)
		{
			if ((uint)preset <= 2u)
			{
				return preset.ToString();
			}
			throw new ArgumentOutOfRangeException("preset", preset, "Unknown preset.");
		}

		public static bool TryParsePreset(string text, out Preset preset)
		{
			preset = Preset.Attuned;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = text.Trim();
			if (text2.Equals("Attuned", StringComparison.OrdinalIgnoreCase))
			{
				preset = Preset.Attuned;
				return true;
			}
			if (text2.Equals("Balanced", StringComparison.OrdinalIgnoreCase))
			{
				preset = Preset.Balanced;
				return true;
			}
			if (text2.Equals("PowerOverwhelming", StringComparison.OrdinalIgnoreCase))
			{
				preset = Preset.PowerOverwhelming;
				return true;
			}
			return false;
		}

		private static int StepsPerFive(float level)
		{
			return (int)Math.Floor((double)level / 5.0);
		}
	}
	[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
	internal static class GetTotalFoodValuePatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(Player __instance, ref float eitr, Skills ___m_skills)
		{
			if (!Plugin.Enabled)
			{
				return;
			}
			try
			{
				if (Plugin.IsLocal(__instance))
				{
					eitr += SkillEitr.MaxBonus(___m_skills);
				}
			}
			catch (Exception exception)
			{
				Plugin.Fail(exception);
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyEitrRegen")]
	internal static class ModifyEitrRegenPatch
	{
		private static void Postfix(ref float eitrMultiplier, Character ___m_character)
		{
			if (!Plugin.Enabled)
			{
				return;
			}
			try
			{
				Player player = (Player)(object)((___m_character is Player) ? ___m_character : null);
				if (Plugin.IsLocal(player))
				{
					eitrMultiplier += SkillEitr.RegenBonus(GameAccess.SkillsOf(player));
				}
			}
			catch (Exception exception)
			{
				Plugin.Fail(exception);
			}
		}
	}
	internal static class GameAccess
	{
		private static FieldRef<Player> _localPlayer;

		private static FieldRef<Player, Skills> _skills;

		private static FieldRef<Skills, Dictionary<SkillType, Skill>> _skillData;

		private static FieldRef<Skill, float> _skillLevel;

		internal static void Init()
		{
			_localPlayer = AccessTools.StaticFieldRefAccess<Player>(RequireField(typeof(Player), "m_localPlayer"));
			_skills = AccessTools.FieldRefAccess<Player, Skills>("m_skills");
			_skillData = AccessTools.FieldRefAccess<Skills, Dictionary<SkillType, Skill>>("m_skillData");
			_skillLevel = AccessTools.FieldRefAccess<Skill, float>("m_level");
		}

		internal static Player LocalPlayer()
		{
			return _localPlayer.Invoke();
		}

		internal static Skills SkillsOf(Player player)
		{
			return _skills.Invoke(player);
		}

		internal static Dictionary<SkillType, Skill> SkillData(Skills skills)
		{
			return _skillData.Invoke(skills);
		}

		internal static float SkillLevel(Skill skill)
		{
			return _skillLevel.Invoke(skill);
		}

		private static FieldInfo RequireField(Type type, string name)
		{
			FieldInfo fieldInfo = AccessTools.Field(type, name);
			if (fieldInfo == null)
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return fieldInfo;
		}
	}
	[BepInPlugin("snook.arcaneattunement", "Arcane Attunement", "1.0.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "snook.arcaneattunement";

		public const string Name = "Arcane Attunement";

		public const string Version = "1.0.3";

		private static ManualLogSource Log;

		private static int _failureLogged;

		private static string _warnedPreset;

		internal static bool Enabled { get; private set; }

		internal static float EitrPerLevel { get; private set; }

		internal static float RegenPercentPer5Levels { get; private set; }

		private void Awake()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigEntry<bool> obj = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Apply the skill Eitr bonus. Read when the game starts. Changing this requires a restart.");
			ConfigEntry<string> preset = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Preset", "Balanced", "Accepted values: Attuned, Balanced, PowerOverwhelming.\nCase does not matter, and surrounding whitespace is ignored.\nAttuned: 0.5 Eitr per skill level, +1.5% Eitr regen per 5 levels.\nBalanced: 1.0 Eitr per skill level, +2% Eitr regen per 5 levels.\nPowerOverwhelming: 1.5 Eitr per skill level, +5% Eitr regen per 5 levels.\nA config manager applies this immediately. A hand edit of the file applies the next time the game starts.");
			Enabled = obj.Value;
			Preset preset2 = ApplyPreset(preset.Value);
			preset.SettingChanged += delegate
			{
				Preset preset3;
				bool num = AttunementMath.TryParsePreset(preset.Value, out preset3);
				ApplyPreset(preset.Value);
				if (num && Log != null)
				{
					Log.LogInfo((object)("Preset is now " + AttunementMath.ToConfigString(preset3) + ": " + EitrPerLevel.ToString("0.0") + " Eitr/level, +" + RegenPercentPer5Levels.ToString("0.0") + "% Eitr regen per 5 levels."));
				}
			};
			if (!Enabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Arcane Attunement is disabled in config.");
				return;
			}
			try
			{
				GameAccess.Init();
				new Harmony("snook.arcaneattunement").PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Arcane Attunement 1.0.3 (" + AttunementMath.ToConfigString(preset2) + "): " + EitrPerLevel.ToString("0.0") + " Eitr/level, +" + RegenPercentPer5Levels.ToString("0.0") + "% Eitr regen per 5 levels."));
			}
			catch (Exception exception)
			{
				Fail(exception);
			}
		}

		private static Preset ApplyPreset(string text)
		{
			if (!AttunementMath.TryParsePreset(text, out var preset))
			{
				string text2 = text ?? string.Empty;
				if (!string.Equals(text2, _warnedPreset, StringComparison.Ordinal))
				{
					_warnedPreset = text2;
					if (Log != null)
					{
						Log.LogWarning((object)("Preset '" + text2 + "' is not valid. Use Attuned, Balanced, or PowerOverwhelming. Balanced is used."));
					}
				}
				preset = Preset.Balanced;
			}
			else
			{
				_warnedPreset = null;
			}
			PresetRates presetRates = AttunementMath.RatesFor(preset);
			EitrPerLevel = presetRates.EitrPerLevel;
			RegenPercentPer5Levels = presetRates.RegenPercentPer5Levels;
			return preset;
		}

		internal static bool IsLocal(Player player)
		{
			if (Enabled && (Object)(object)player != (Object)null)
			{
				return (Object)(object)player == (Object)(object)GameAccess.LocalPlayer();
			}
			return false;
		}

		internal static void Fail(Exception exception)
		{
			Enabled = false;
			if (Interlocked.Exchange(ref _failureLogged, 1) == 0)
			{
				string text = "Arcane Attunement disabled itself after an error. Player loading and updates are left to run without the bonus.\n" + exception;
				if (Log != null)
				{
					Log.LogError((object)text);
				}
			}
		}
	}
	internal static class SkillEitr
	{
		internal static float Level(Skills skills, SkillType skillType)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)skills == (Object)null)
			{
				return 0f;
			}
			Dictionary<SkillType, Skill> dictionary = GameAccess.SkillData(skills);
			if (dictionary == null)
			{
				return 0f;
			}
			if (!dictionary.TryGetValue(skillType, out var value) || value == null)
			{
				return 0f;
			}
			return GameAccess.SkillLevel(value);
		}

		internal static float MaxBonus(Skills skills)
		{
			return AttunementMath.MaxEitrBonus(Level(skills, (SkillType)9), Level(skills, (SkillType)10), Plugin.EitrPerLevel);
		}

		internal static float RegenBonus(Skills skills)
		{
			return AttunementMath.RegenFraction(Level(skills, (SkillType)9), Level(skills, (SkillType)10), Plugin.RegenPercentPer5Levels);
		}
	}
}