Decompiled source of InstantLvlUpSkills v1.0.0

BepInEx/plugins/InstantLvlUpSkills/InstantLvlUpSkills.dll

Decompiled 2 weeks ago
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace InstantLvlUpSkills;

[BepInPlugin("InstantLvlUpSkills.Valheim", "InstantLvlUpSkills", "1.0.0")]
public sealed class InstantLvlUpSkillsPlugin : BaseUnityPlugin
{
	private sealed class RaiseState
	{
		public object Skill;

		public float BeforeLevel;

		public bool Valid;
	}

	public const string PluginGuid = "InstantLvlUpSkills.Valheim";

	public const string PluginName = "InstantLvlUpSkills";

	public const string PluginVersion = "1.0.0";

	internal static ConfigEntry<bool> EnabledConfig;

	internal static ConfigEntry<bool> RemoveDeathSkillPenaltyConfig;

	internal static ManualLogSource ModLog;

	private static bool _handlingDeath = false;

	private static MethodInfo _getSkillMethod;

	private static MethodInfo _getNextLevelRequirementMethod;

	private static FieldInfo _skillLevelField;

	private static FieldInfo _skillAccumulatorField;

	private Harmony _harmony;

	private static bool _loggedWarning = false;

	private void Awake()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Expected O, but got Unknown
		//IL_0148: Expected O, but got Unknown
		ModLog = ((BaseUnityPlugin)this).Logger;
		EnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, new ConfigDescription("When enabled, every normal skill-gain event immediately grants exactly one full skill level, up to level 100.", (AcceptableValueBase)null, new object[0]));
		RemoveDeathSkillPenaltyConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Death Penalty", "Remove Skill Penalty", true, new ConfigDescription("When enabled, dying does not reduce skill levels or erase partial progress toward the next skill level.", (AcceptableValueBase)null, new object[0]));
		_getSkillMethod = AccessTools.Method(typeof(Skills), "GetSkill", new Type[1] { typeof(SkillType) }, (Type[])null);
		if (_getSkillMethod == null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Skills.GetSkill(SkillType) was not found. Runtime fallback discovery will be used.");
		}
		_harmony = new Harmony("InstantLvlUpSkills.Valheim");
		MethodInfo methodInfo = AccessTools.Method(typeof(InstantLvlUpSkillsPlugin), "RaiseSkillPrefix", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(InstantLvlUpSkillsPlugin), "RaiseSkillPostfix", (Type[])null, (Type[])null);
		MethodInfo[] methods = typeof(Skills).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		int num = 0;
		foreach (MethodInfo methodInfo3 in methods)
		{
			if (!(methodInfo3 == null) && !(methodInfo3.Name != "RaiseSkill"))
			{
				try
				{
					_harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo), new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not patch a Skills.RaiseSkill overload: " + ex.Message));
				}
			}
		}
		PatchDeathPenalty();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("InstantLvlUpSkills 1.0.0 loaded. Patched " + num + " Skills.RaiseSkill method(s)."));
	}

	private void PatchDeathPenalty()
	{
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Expected O, but got Unknown
		//IL_00cf: Expected O, but got Unknown
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Expected O, but got Unknown
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Skills), "OnDeath", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Skills), "LowerAllSkills", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Skills.OnDeath() was not found. Death penalty removal will be unavailable.");
				return;
			}
			if (methodInfo2 == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Skills.LowerAllSkills(...) was not found. Death penalty removal will be unavailable.");
				return;
			}
			MethodInfo methodInfo3 = AccessTools.Method(typeof(InstantLvlUpSkillsPlugin), "SkillsOnDeathPrefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.Method(typeof(InstantLvlUpSkillsPlugin), "SkillsOnDeathPostfix", (Type[])null, (Type[])null);
			MethodInfo methodInfo5 = AccessTools.Method(typeof(InstantLvlUpSkillsPlugin), "LowerAllSkillsPrefix", (Type[])null, (Type[])null);
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo3), new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo5), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Death skill penalty protection initialized.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not initialize death skill penalty protection: " + ex.Message));
		}
	}

	private static void SkillsOnDeathPrefix()
	{
		_handlingDeath = true;
	}

	private static void SkillsOnDeathPostfix()
	{
		_handlingDeath = false;
	}

	private static bool LowerAllSkillsPrefix()
	{
		if (!_handlingDeath)
		{
			return true;
		}
		if (EnabledConfig == null || !EnabledConfig.Value || RemoveDeathSkillPenaltyConfig == null || !RemoveDeathSkillPenaltyConfig.Value)
		{
			return true;
		}
		return false;
	}

	private void OnDestroy()
	{
		try
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
	}

	private static void RaiseSkillPrefix(Skills __instance, object[] __args, ref RaiseState __state)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		__state = new RaiseState();
		if (EnabledConfig == null || !EnabledConfig.Value || (Object)(object)__instance == (Object)null)
		{
			return;
		}
		try
		{
			if (!TryGetSkillType(__args, out var skillType))
			{
				return;
			}
			object skillObject = GetSkillObject(__instance, skillType);
			if (skillObject == null)
			{
				return;
			}
			CacheSkillMembers(skillObject);
			if (_skillLevelField == null)
			{
				return;
			}
			float num = Convert.ToSingle(_skillLevelField.GetValue(skillObject));
			__state.Skill = skillObject;
			__state.BeforeLevel = num;
			__state.Valid = true;
			if (num >= 100f)
			{
				_skillLevelField.SetValue(skillObject, 100f);
				if (_skillAccumulatorField != null)
				{
					_skillAccumulatorField.SetValue(skillObject, 0f);
				}
			}
			else if (_skillAccumulatorField != null)
			{
				float nextLevelRequirement = GetNextLevelRequirement(skillObject);
				if (nextLevelRequirement > 0f)
				{
					_skillAccumulatorField.SetValue(skillObject, nextLevelRequirement);
				}
			}
		}
		catch (Exception ex)
		{
			LogWarningOnce("RaiseSkill prefix failed: " + ex.Message);
		}
	}

	private static void RaiseSkillPostfix(ref RaiseState __state)
	{
		if (EnabledConfig == null || !EnabledConfig.Value || __state == null || !__state.Valid || __state.Skill == null || _skillLevelField == null)
		{
			return;
		}
		try
		{
			float beforeLevel = __state.BeforeLevel;
			if (beforeLevel >= 100f)
			{
				_skillLevelField.SetValue(__state.Skill, 100f);
				if (_skillAccumulatorField != null)
				{
					_skillAccumulatorField.SetValue(__state.Skill, 0f);
				}
				return;
			}
			float num = Mathf.Min(100f, Mathf.Floor(beforeLevel) + 1f);
			float num2 = Convert.ToSingle(_skillLevelField.GetValue(__state.Skill));
			if (Mathf.Abs(num2 - num) > 0.001f)
			{
				_skillLevelField.SetValue(__state.Skill, num);
			}
			if (_skillAccumulatorField != null)
			{
				_skillAccumulatorField.SetValue(__state.Skill, 0f);
			}
		}
		catch (Exception ex)
		{
			LogWarningOnce("RaiseSkill postfix failed: " + ex.Message);
		}
	}

	private static bool TryGetSkillType(object[] args, out SkillType skillType)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected I4, but got Unknown
		skillType = (SkillType)0;
		if (args == null)
		{
			return false;
		}
		foreach (object obj in args)
		{
			if (obj is SkillType)
			{
				skillType = (SkillType)(int)(SkillType)obj;
				return true;
			}
		}
		return false;
	}

	private static object GetSkillObject(Skills skills, SkillType skillType)
	{
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)skills == (Object)null)
		{
			return null;
		}
		try
		{
			if (_getSkillMethod == null)
			{
				MethodInfo[] methods = typeof(Skills).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!(methodInfo == null) && !(methodInfo.Name != "GetSkill"))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length == 1 && parameters[0].ParameterType == typeof(SkillType))
						{
							_getSkillMethod = methodInfo;
							break;
						}
					}
				}
			}
			if (_getSkillMethod == null)
			{
				return null;
			}
			return _getSkillMethod.Invoke(skills, new object[1] { skillType });
		}
		catch
		{
			return null;
		}
	}

	private static void CacheSkillMembers(object skill)
	{
		if (skill != null)
		{
			Type type = skill.GetType();
			if (_skillLevelField == null || _skillLevelField.DeclaringType != type)
			{
				_skillLevelField = AccessTools.Field(type, "m_level");
				_skillAccumulatorField = AccessTools.Field(type, "m_accumulator");
				_getNextLevelRequirementMethod = AccessTools.Method(type, "GetNextLevelRequirement", Type.EmptyTypes, (Type[])null);
			}
		}
	}

	private static float GetNextLevelRequirement(object skill)
	{
		if (skill == null || _getNextLevelRequirementMethod == null)
		{
			return 0f;
		}
		try
		{
			object obj = _getNextLevelRequirementMethod.Invoke(skill, null);
			if (obj == null)
			{
				return 0f;
			}
			return Convert.ToSingle(obj);
		}
		catch
		{
			return 0f;
		}
	}

	private static void LogWarningOnce(string message)
	{
		if (!_loggedWarning)
		{
			_loggedWarning = true;
			if (ModLog != null)
			{
				ModLog.LogWarning((object)message);
			}
		}
	}
}