Decompiled source of Magical v1.0.2

Magical.dll

Decompiled a year ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Magical")]
[assembly: AssemblyDescription("Magical")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Crystal")]
[assembly: AssemblyProduct("Magical")]
[assembly: AssemblyCopyright("Copyright © 2023 Crystal Ferrai")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("1414a5c1-1a42-448f-8f27-000b965fcb46")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.2.0")]
namespace Magical;

[BepInPlugin("dev.crystal.magical", "Magical", "1.0.2.0")]
[BepInProcess("valheim.exe")]
[BepInProcess("valheim_server.exe")]
public class MagicalPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Player))]
	private static class Player_Tracking_Patches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void Awake_Prefix(Player __instance)
		{
			SetPlayerValues(__instance);
			sPlayers.Add(__instance);
		}

		[HarmonyPatch("OnDestroy")]
		[HarmonyPrefix]
		private static void OnDestroy_Prefix(Player __instance)
		{
			sPlayers.Remove(__instance);
		}
	}

	[HarmonyPatch(typeof(Player))]
	private static class Player_Patches
	{
		private enum TranspilerState
		{
			Searching,
			Checking,
			Replacing,
			Searching2,
			Checking2,
			Replacing2,
			Finishing
		}

		[HarmonyPatch("UpdateFood")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> UpdateFood_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			TranspilerState state = TranspilerState.Searching;
			CodeInstruction previousInstruction = null;
			foreach (CodeInstruction instruction in instructions)
			{
				switch (state)
				{
				case TranspilerState.Searching:
					if (instruction.opcode == OpCodes.Ldfld && ((FieldInfo)instruction.operand).Name.Equals("m_foodRegenTimer"))
					{
						state = TranspilerState.Checking;
					}
					yield return instruction;
					break;
				case TranspilerState.Checking:
					if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 10f)
					{
						state = TranspilerState.Replacing;
						break;
					}
					yield return instruction;
					state = TranspilerState.Searching;
					break;
				case TranspilerState.Replacing:
					yield return new CodeInstruction(OpCodes.Ldc_R4, (object)HealthRegenTickRate.Value);
					yield return instruction;
					state = TranspilerState.Searching2;
					break;
				case TranspilerState.Searching2:
					if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 0f)
					{
						previousInstruction = instruction;
						state = TranspilerState.Checking2;
					}
					else
					{
						yield return instruction;
					}
					break;
				case TranspilerState.Checking2:
					if (instruction.opcode == OpCodes.Stloc_S)
					{
						previousInstruction = instruction;
						state = TranspilerState.Replacing2;
					}
					else
					{
						yield return previousInstruction;
						yield return instruction;
						state = TranspilerState.Searching2;
					}
					break;
				case TranspilerState.Replacing2:
					yield return new CodeInstruction(OpCodes.Ldc_R4, (object)BaseHealthRegen.Value);
					yield return previousInstruction;
					yield return instruction;
					state = TranspilerState.Finishing;
					break;
				case TranspilerState.Finishing:
					yield return instruction;
					break;
				}
			}
		}

		[HarmonyPatch("GetTotalFoodValue")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> GetTotalFoodValue_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			TranspilerState state = TranspilerState.Searching;
			foreach (CodeInstruction instruction in instructions)
			{
				switch (state)
				{
				case TranspilerState.Searching:
					if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 0f)
					{
						state = TranspilerState.Replacing;
					}
					else
					{
						yield return instruction;
					}
					break;
				case TranspilerState.Replacing:
					yield return new CodeInstruction(OpCodes.Ldc_R4, (object)BaseEitr.Value);
					yield return instruction;
					state = TranspilerState.Finishing;
					break;
				case TranspilerState.Finishing:
					yield return instruction;
					break;
				}
			}
		}
	}

	[HarmonyPatch(typeof(Attack))]
	private static class Attack_Patches
	{
		private enum TranspilerState
		{
			Searching,
			Replacing,
			Finishing
		}

		[HarmonyPatch("GetAttackStamina")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> GetAttackStamina_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceSkillModifier(instructions, SkillStaminaReduction.Value);
		}

		[HarmonyPatch("GetAttackEitr")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> GetAttackEitr_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceSkillModifier(instructions, SkillEitrReduction.Value);
		}

		[HarmonyPatch("GetAttackHealth")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> GetAttackHealth_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceSkillModifier(instructions, SkillHealthReduction.Value);
		}

		private static IEnumerable<CodeInstruction> ReplaceSkillModifier(IEnumerable<CodeInstruction> instructions, float newModifier)
		{
			TranspilerState state = TranspilerState.Searching;
			foreach (CodeInstruction instruction in instructions)
			{
				switch (state)
				{
				case TranspilerState.Searching:
					if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 0.33f)
					{
						state = TranspilerState.Replacing;
					}
					else
					{
						yield return instruction;
					}
					break;
				case TranspilerState.Replacing:
					yield return new CodeInstruction(OpCodes.Ldc_R4, (object)newModifier);
					yield return instruction;
					state = TranspilerState.Finishing;
					break;
				case TranspilerState.Finishing:
					yield return instruction;
					break;
				}
			}
		}
	}

	public const string ModId = "dev.crystal.magical";

	public static ConfigEntry<float> BaseStamina;

	public static ConfigEntry<float> BaseEitr;

	public static ConfigEntry<float> BaseHealth;

	public static ConfigEntry<float> BaseStaminaRegen;

	public static ConfigEntry<float> BaseEitrRegen;

	public static ConfigEntry<float> BaseHealthRegen;

	public static ConfigEntry<float> StaminaRegenDelay;

	public static ConfigEntry<float> EitrRegenDelay;

	public static ConfigEntry<float> HealthRegenTickRate;

	public static ConfigEntry<float> SkillStaminaReduction;

	public static ConfigEntry<float> SkillEitrReduction;

	public static ConfigEntry<float> SkillHealthReduction;

	private static Harmony sPlayerTrackingHarmony;

	private static Harmony sPlayerHarmony;

	private static Harmony sAttackHarmony;

	private static List<Player> sPlayers;

	static MagicalPlugin()
	{
		sPlayers = new List<Player>();
	}

	private void Awake()
	{
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Expected O, but got Unknown
		//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Expected O, but got Unknown
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e5: Expected O, but got Unknown
		BaseStamina = ((BaseUnityPlugin)this).Config.Bind<float>("Base", "BaseStamina", 50f, "Maximum stamina before any food modifiers are applied. Game default 50.");
		BaseStamina.SettingChanged += PlayerVariable_SettingChanged;
		BaseEitr = ((BaseUnityPlugin)this).Config.Bind<float>("Base", "BaseEitr", 0f, "Maximum eitr before any food modifiers are applied. Game default 0.");
		BaseEitr.SettingChanged += PlayerConstant_SettingChanged;
		BaseHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Base", "BaseHealth", 25f, "Maximum health before any food modifiers are applied. Game default 25.");
		BaseHealth.SettingChanged += PlayerVariable_SettingChanged;
		BaseStaminaRegen = ((BaseUnityPlugin)this).Config.Bind<float>("Regen", "BaseStaminaRegen", 6f, "The base rate of stamina regen per second, before any modifiers are applied. Game default 6.");
		BaseStaminaRegen.SettingChanged += PlayerVariable_SettingChanged;
		BaseEitrRegen = ((BaseUnityPlugin)this).Config.Bind<float>("Regen", "BaseEitrRegen", 2f, "The base rate of eitr regen per second, before any modifiers are applied. Game default 2.");
		BaseEitrRegen.SettingChanged += PlayerVariable_SettingChanged;
		BaseHealthRegen = ((BaseUnityPlugin)this).Config.Bind<float>("Regen", "BaseHealthRegen", 0f, "The base rate of health regen per health regen tick, before any modifiers are applied. Game default 0.");
		BaseHealthRegen.SettingChanged += PlayerConstant_SettingChanged;
		StaminaRegenDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Regen", "StaminaRegenDelay", 1f, "The number of seconds after using stamina before it starts to regenerate. Game default 1.");
		StaminaRegenDelay.SettingChanged += PlayerVariable_SettingChanged;
		EitrRegenDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Regen", "EitrRegenDelay", 1f, "The number of seconds after using eitr before it starts to regenerate. Game default 1.");
		EitrRegenDelay.SettingChanged += PlayerVariable_SettingChanged;
		HealthRegenTickRate = ((BaseUnityPlugin)this).Config.Bind<float>("Regen", "HealthRegenTickRate", 10f, "The number of seconds between ticks of health regeneration. Game default 10.");
		HealthRegenTickRate.SettingChanged += PlayerConstant_SettingChanged;
		SkillStaminaReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Skill", "SkillStaminaReduction", 0.33f, "Stamina cost reduction multiplier for actions based on player skill. Value represents reduction with 100 skill and will scale down at lower skill levels. Game default 0.33.");
		SkillStaminaReduction.SettingChanged += Attack_SettingChanged;
		SkillEitrReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Skill", "SkillEitrReduction", 0.33f, "Eitr cost reduction multiplier for actions based on player skill. Value represents reduction with 100 skill and will scale down at lower skill levels. Game default 0.33.");
		SkillEitrReduction.SettingChanged += Attack_SettingChanged;
		SkillHealthReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Skill", "SkillHealthReduction", 0.33f, "Health cost reduction multiplier for actions based on player skill. Value represents reduction with 100 skill and will scale down at lower skill levels. Game default 0.33.");
		SkillHealthReduction.SettingChanged += Attack_SettingChanged;
		sPlayerTrackingHarmony = new Harmony("dev.crystal.magical_Player_Tracking");
		sPlayerHarmony = new Harmony("dev.crystal.magical_Player");
		sAttackHarmony = new Harmony("dev.crystal.magical_Attack");
		ClampConfig();
		sPlayerTrackingHarmony.PatchAll(typeof(Player_Tracking_Patches));
		sPlayerHarmony.PatchAll(typeof(Player_Patches));
		sAttackHarmony.PatchAll(typeof(Attack_Patches));
	}

	private void OnDestroy()
	{
		sPlayerTrackingHarmony.UnpatchSelf();
		sPlayerHarmony.UnpatchSelf();
		sAttackHarmony.UnpatchSelf();
		sPlayers.Clear();
	}

	private static void ClampConfig()
	{
		if (BaseStamina.Value < 0f)
		{
			BaseStamina.Value = 0f;
		}
		if (BaseStamina.Value > 1000f)
		{
			BaseStamina.Value = 1000f;
		}
		if (BaseEitr.Value < 0f)
		{
			BaseEitr.Value = 0f;
		}
		if (BaseEitr.Value > 1000f)
		{
			BaseEitr.Value = 1000f;
		}
		if (BaseHealth.Value < 0f)
		{
			BaseHealth.Value = 0f;
		}
		if (BaseHealth.Value > 1000f)
		{
			BaseHealth.Value = 1000f;
		}
		if (BaseStaminaRegen.Value < 0.1f)
		{
			BaseStaminaRegen.Value = 0.1f;
		}
		if (BaseStaminaRegen.Value > 1000f)
		{
			BaseStaminaRegen.Value = 1000f;
		}
		if (BaseEitrRegen.Value < 0.1f)
		{
			BaseEitrRegen.Value = 0.1f;
		}
		if (BaseEitrRegen.Value > 1000f)
		{
			BaseEitrRegen.Value = 1000f;
		}
		if (BaseHealthRegen.Value < 0f)
		{
			BaseHealthRegen.Value = 0f;
		}
		if (BaseHealthRegen.Value > 1000f)
		{
			BaseHealthRegen.Value = 1000f;
		}
		if (StaminaRegenDelay.Value < 0.1f)
		{
			StaminaRegenDelay.Value = 0.1f;
		}
		if (StaminaRegenDelay.Value > 3600f)
		{
			StaminaRegenDelay.Value = 3600f;
		}
		if (EitrRegenDelay.Value < 0.1f)
		{
			EitrRegenDelay.Value = 0.1f;
		}
		if (EitrRegenDelay.Value > 3600f)
		{
			EitrRegenDelay.Value = 3600f;
		}
		if (HealthRegenTickRate.Value < 0.1f)
		{
			HealthRegenTickRate.Value = 0.1f;
		}
		if (HealthRegenTickRate.Value > 3600f)
		{
			HealthRegenTickRate.Value = 3600f;
		}
		if (SkillStaminaReduction.Value < 0f)
		{
			SkillStaminaReduction.Value = 0f;
		}
		if (SkillStaminaReduction.Value > 1f)
		{
			SkillStaminaReduction.Value = 1f;
		}
		if (SkillEitrReduction.Value < 0f)
		{
			SkillEitrReduction.Value = 0f;
		}
		if (SkillEitrReduction.Value > 1f)
		{
			SkillEitrReduction.Value = 1f;
		}
		if (SkillHealthReduction.Value < 0f)
		{
			SkillHealthReduction.Value = 0f;
		}
		if (SkillHealthReduction.Value > 1f)
		{
			SkillHealthReduction.Value = 1f;
		}
	}

	private void PlayerVariable_SettingChanged(object sender, EventArgs e)
	{
		ClampConfig();
		foreach (Player sPlayer in sPlayers)
		{
			SetPlayerValues(sPlayer);
		}
	}

	private void Attack_SettingChanged(object sender, EventArgs e)
	{
		ClampConfig();
		sAttackHarmony.UnpatchSelf();
		sAttackHarmony.PatchAll(typeof(Attack_Patches));
	}

	private void PlayerConstant_SettingChanged(object sender, EventArgs e)
	{
		ClampConfig();
		sPlayerHarmony.UnpatchSelf();
		sPlayerHarmony.PatchAll(typeof(Player_Patches));
	}

	private static void SetPlayerValues(Player player)
	{
		player.m_baseStamina = BaseStamina.Value;
		player.m_baseHP = BaseHealth.Value;
		player.m_staminaRegen = BaseStaminaRegen.Value;
		player.m_eiterRegen = BaseEitrRegen.Value;
		player.m_staminaRegenDelay = StaminaRegenDelay.Value;
		player.m_eitrRegenDelay = EitrRegenDelay.Value;
	}
}