Decompiled source of DeathPenalty v1.3.1

DeathPenalty.dll

Decompiled 4 days 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 ConditionalConfigSync;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("DeathPenalty")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Crystal")]
[assembly: AssemblyProduct("DeathPenalty")]
[assembly: AssemblyCopyright("Copyright © 2026 Crystal Ferrai")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("f35b4ef7-5ab1-40f7-864f-09be2f9fcc8c")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.3.1.0")]
namespace DeathPenalty;

[BepInPlugin("dev.crystal.deathpenalty", "Death Penalty", "1.3.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInProcess("valheim.exe")]
[BepInProcess("valheim_server.exe")]
public class DeathPenaltyPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Skills))]
	private static class Skills_Level_Patches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void Awake_Postfix(Skills __instance)
		{
			__instance.m_DeathLowerFactor = SkillLossPercent.Value * 0.01f;
		}
	}

	[HarmonyPatch(typeof(Skills))]
	private static class Skills_Accumulator_Patches
	{
		private enum TranspilerState
		{
			Searching,
			Updating,
			Finishing
		}

		[HarmonyPatch("LowerAllSkills")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> LowerAllSkills_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.Ldc_R4 && (float)instruction.operand == 0f)
					{
						previousInstruction = instruction;
						state = TranspilerState.Updating;
					}
					else
					{
						yield return instruction;
					}
					break;
				case TranspilerState.Updating:
					if (instruction.opcode == OpCodes.Stfld && ((FieldInfo)instruction.operand).Name == "m_accumulator")
					{
						state = TranspilerState.Finishing;
					}
					else
					{
						yield return previousInstruction;
						yield return instruction;
						state = TranspilerState.Searching;
					}
					previousInstruction = null;
					break;
				case TranspilerState.Finishing:
					yield return instruction;
					break;
				}
			}
			if (state != TranspilerState.Finishing)
			{
				throw new InvalidOperationException("[DeathPenalty] Failed to patch Skills.LowerAllSkills");
			}
		}
	}

	[HarmonyPatch(typeof(Player))]
	private static class Player_Patches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void Awake_Postfix(Player __instance)
		{
			__instance.m_hardDeathCooldown = MercyEffectDuration.Value;
			sPlayers.Add(__instance);
		}

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

		[HarmonyPatch("OnDeath")]
		[HarmonyPrefix]
		private static bool OnDeath_Prefix(Skills __instance)
		{
			Debug.LogWarning((object)$"m_DeathLowerFactor={__instance.m_DeathLowerFactor}, m_skillReductionRate={Game.m_skillReductionRate}");
			return true;
		}
	}

	[HarmonyPatch(typeof(TombStone))]
	private static class TombStone_Patches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void Awake_Postfix(TombStone __instance)
		{
			__instance.m_lootStatusEffect.m_ttl = SafetyEffectDuration.Value;
			sTombStones.Add(__instance);
		}

		[HarmonyPatch("UpdateDespawn")]
		[HarmonyPostfix]
		private static void UpdateDespawn_Postfix(TombStone __instance)
		{
			sTombStones.Remove(__instance);
		}
	}

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

	public const string ModName = "Death Penalty";

	public const string ModVersion = "1.3.1.0";

	internal static readonly ConfigSync ConfigSync;

	public static ConfigEntry<float> SkillLossPercent;

	public static ConfigEntry<bool> ResetLevelProgress;

	public static ConfigEntry<float> MercyEffectDuration;

	public static ConfigEntry<float> SafetyEffectDuration;

	private static Harmony sSkillsLevelHarmony;

	private static Harmony sSkillsAccumulatorHarmony;

	private static Harmony sPlayerHarmony;

	private static Harmony sTombStoneHarmony;

	private static readonly List<Player> sPlayers;

	private static readonly List<TombStone> sTombStones;

	static DeathPenaltyPlugin()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Expected O, but got Unknown
		ConfigSync = new ConfigSync("dev.crystal.deathpenalty")
		{
			DisplayName = "Death Penalty",
			CurrentVersion = "1.3.1.0",
			MinimumRequiredVersion = "1.3.1.0",
			ModRequired = true,
			ModRequirementMode = (ModRequirementMode)0
		};
		sPlayers = new List<Player>();
		sTombStones = new List<TombStone>();
	}

	private void Awake()
	{
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Expected O, but got Unknown
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Expected O, but got Unknown
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Expected O, but got Unknown
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Expected O, but got Unknown
		SkillLossPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "SkillLossPercent", 5f, "The percent loss suffered to the total level of all skills when the player dies. Range 0-100. 0 disables skill loss. 50 reduces all skills by half. 100 resets all skills to 0. Game default is 5. This value gets multiplied by the global key SkillReductionRate which is tied to the death penalty world modifier. [The value will be enforced on a server.]");
		SkillLossPercent.SettingChanged += SkillLossPercent_SettingChanged;
		((ConditionalConfigSync)ConfigSync).AddConfigEntry<float>(SkillLossPercent, (ConfigSyncMode)0);
		ResetLevelProgress = ((BaseUnityPlugin)this).Config.Bind<bool>("Death", "ResetLevelProgress", true, "Whether to reset progress towards the next level for all skills when the player dies. This penalty applies in addition to subtracting levels. Game default is true. [The value will be enforced on a server.]");
		ResetLevelProgress.SettingChanged += ResetLevelProgress_SettingChanged;
		((ConditionalConfigSync)ConfigSync).AddConfigEntry<bool>(ResetLevelProgress, (ConfigSyncMode)0);
		MercyEffectDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "MercyEffectDuration", 600f, "The duration, in seconds, of the \"No Skill Loss\" status effect that is granted on death which prevents further loss of skills via subsequent deaths. Game default is 600. [The value will be enforced on a server.]");
		MercyEffectDuration.SettingChanged += MercyEffectDuration_SettingChanged;
		((ConditionalConfigSync)ConfigSync).AddConfigEntry<float>(MercyEffectDuration, (ConfigSyncMode)0);
		SafetyEffectDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "SafetyEffectDuration", 50f, "The duration, in seconds, of the \"Corpse Run\" status effect that is granted upon looting a tombstone which boosts regen and other stats. Game default is 50. [The value will be enforced on a server.]");
		SafetyEffectDuration.SettingChanged += SafetyEffectDuration_SettingChanged;
		((ConditionalConfigSync)ConfigSync).AddConfigEntry<float>(SafetyEffectDuration, (ConfigSyncMode)0);
		ClampConfig();
		sSkillsLevelHarmony = new Harmony("dev.crystal.deathpenalty_Skills_Level");
		sSkillsAccumulatorHarmony = new Harmony("dev.crystal.deathpenalty_Skills_Accumulator");
		sPlayerHarmony = new Harmony("dev.crystal.deathpenalty_Player");
		sTombStoneHarmony = new Harmony("dev.crystal.deathpenalty_TombStone");
		sSkillsLevelHarmony.PatchAll(typeof(Skills_Level_Patches));
		sPlayerHarmony.PatchAll(typeof(Player_Patches));
		sTombStoneHarmony.PatchAll(typeof(TombStone_Patches));
		if (!ResetLevelProgress.Value)
		{
			sSkillsAccumulatorHarmony.PatchAll(typeof(Skills_Accumulator_Patches));
		}
	}

	private void OnDestroy()
	{
		sSkillsLevelHarmony.UnpatchSelf();
		sSkillsAccumulatorHarmony.UnpatchSelf();
		sPlayerHarmony.UnpatchSelf();
		sTombStoneHarmony.UnpatchSelf();
	}

	private void SkillLossPercent_SettingChanged(object sender, EventArgs e)
	{
		ClampConfig();
		foreach (Player sPlayer in sPlayers)
		{
			((Character)sPlayer).GetSkills().m_DeathLowerFactor = SkillLossPercent.Value * 0.01f;
		}
	}

	private void ResetLevelProgress_SettingChanged(object sender, EventArgs e)
	{
		if (ResetLevelProgress.Value)
		{
			sSkillsAccumulatorHarmony.UnpatchSelf();
		}
		else
		{
			sSkillsAccumulatorHarmony.PatchAll(typeof(Skills_Accumulator_Patches));
		}
	}

	private void MercyEffectDuration_SettingChanged(object sender, EventArgs e)
	{
		ClampConfig();
		foreach (Player sPlayer in sPlayers)
		{
			sPlayer.m_hardDeathCooldown = MercyEffectDuration.Value;
		}
	}

	private void SafetyEffectDuration_SettingChanged(object sender, EventArgs e)
	{
		ClampConfig();
		foreach (TombStone sTombStone in sTombStones)
		{
			sTombStone.m_lootStatusEffect.m_ttl = SafetyEffectDuration.Value;
		}
	}

	private static void ClampConfig()
	{
		if (SkillLossPercent.Value < 0f)
		{
			SkillLossPercent.Value = 0f;
		}
		if (SkillLossPercent.Value > 100f)
		{
			SkillLossPercent.Value = 100f;
		}
		if (MercyEffectDuration.Value < 0f)
		{
			MercyEffectDuration.Value = 0f;
		}
		if (float.IsPositiveInfinity(MercyEffectDuration.Value))
		{
			MercyEffectDuration.Value = float.MaxValue;
		}
		if (SafetyEffectDuration.Value < 0f)
		{
			SafetyEffectDuration.Value = 0f;
		}
		if (float.IsPositiveInfinity(SafetyEffectDuration.Value))
		{
			SafetyEffectDuration.Value = float.MaxValue;
		}
	}
}