Decompiled source of SkillGainModifier v0.1.1

plugins/SkillGainModifier.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SkillGainModifier")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SkillGainModifier")]
[assembly: AssemblyCopyright("Copyright © Juuso Piippo 2026-")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b56f4534-679e-43b6-b28f-3e68f83d562f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace SkillGainModifier;

internal class SkillData
{
	public float level;

	public float progress;

	public override string ToString()
	{
		return $"level: {level}, progress: {progress}";
	}
}
[BepInPlugin("jujuz1.mods.skillgainmodifier", "SkillGainModifier", "0.1.1")]
public class SkillGainModifier : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	public static class Patch_Skills_RaiseSkill
	{
		private static void Prefix(SkillType skillType, ref float factor)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (!skillGainModifiers.TryGetValue(skillType, out var value))
			{
				LogError($"Couldn't find value by key {skillType}. Raising skill by default (1.0f)");
				return;
			}
			LogDebug($"{skillType} gain before: {factor}");
			if (value.Value == 0f)
			{
				float value2 = skillGainModifiers[(SkillType)999].Value;
				LogDebug($"Using global value: {value2}");
				factor *= value2;
			}
			else
			{
				LogDebug($"Using overriden value: {value.Value}");
				if (value.Value < 0f)
				{
					LogWarning($"{skillType} has a negative modifier of {value.Value}. This will result in negative xp gain, which will not be shown in the UI beyond the progress bar. Are you sure this is correct? Applying negative xp gain...");
				}
				factor *= value.Value;
			}
			LogDebug($"After: {factor}\n");
		}
	}

	[HarmonyPatch(typeof(Skills), "LowerAllSkills")]
	public static class Patch_Skills_LowerAllSkills
	{
		private static void Prefix(Skills __instance, ref float factor)
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			LogDebug($"Game m_skillReductionRate: {Game.m_skillReductionRate}");
			LogDebug($"Skills m_skillReductionRate: {__instance.m_DeathLowerFactor}\n");
			if (skillReductionModifier.Value == 1f)
			{
				LogDebug("Default skill reduction!");
				return;
			}
			LogDebug($"Factor before modifying: {factor}");
			if (skillReductionModifier.Value == 0f)
			{
				LogDebug("No skill reduction!");
				LogInfo("Reduction modifier is 0. Saving skill progress...\n");
				{
					foreach (KeyValuePair<SkillType, Skill> skillDatum in __instance.m_skillData)
					{
						SkillData skillData = new SkillData
						{
							level = skillDatum.Value.m_level,
							progress = skillDatum.Value.m_accumulator
						};
						SkillGainModifier.skillData.Add(skillDatum.Key, skillData);
						LogDebug($"Added {skillDatum.Key}, {skillData}");
					}
					return;
				}
			}
			if (skillReductionModifier.Value < 0f)
			{
				LogWarning($"Any reduction modifier below zero results in the level increasing when dying! Current modifier: {skillReductionModifier.Value}. Recommended values are 0-0.2! Applying positive xp gain...");
			}
			factor = skillReductionModifier.Value;
			LogDebug($"Factor after modifying: {factor}\n");
		}

		private static void Postfix(Skills __instance)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			LogDebug("Skill progress before applying saved:");
			bool flag = false;
			foreach (KeyValuePair<SkillType, Skill> skillDatum in __instance.m_skillData)
			{
				float level = skillDatum.Value.m_level;
				string text = "";
				if (skillDatum.Value.m_level < 0f)
				{
					if (!flag)
					{
						WarnAboutReductionModifier();
						flag = true;
					}
					LogWarning($"Skill {skillDatum.Key} would have had a level of {level} if it wasn't corrected to 0!");
					skillDatum.Value.m_level = 0f;
					text = $"Corrected level: {skillDatum.Value.m_level}";
				}
				LogDebug($"{skillDatum.Key}: level: {level} progress: {skillDatum.Value.m_accumulator}. {text}");
			}
			if (skillData.Count <= 0)
			{
				return;
			}
			LogInfo("Applying saved skill progress...\n");
			foreach (KeyValuePair<SkillType, Skill> skillDatum2 in __instance.m_skillData)
			{
				SkillData value = new SkillData
				{
					level = 0f,
					progress = 0f
				};
				if (!skillData.TryGetValue(skillDatum2.Key, out value))
				{
					LogError($"Couldn't find value by key {skillDatum2.Key}. Setting value to default (0.0f)");
				}
				else
				{
					LogDebug($"Found {skillDatum2.Key}, setting data to {value}");
				}
				__instance.m_skillData[skillDatum2.Key].m_level = value.level;
				__instance.m_skillData[skillDatum2.Key].m_accumulator = value.progress;
			}
			skillData.Clear();
		}
	}

	[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
	public static class Patch_Player_UpdateStats
	{
		private static void Postfix(Player __instance)
		{
			WarnAboutReductionModifier();
			if (!noSkillDrainEnabled.Value && __instance.m_timeSinceDeath <= 10000f)
			{
				LogDebug("No skill drain disabled, timeSinceDeath set to 999999.0f");
				__instance.m_timeSinceDeath = 999999f;
			}
		}
	}

	[HarmonyPatch(typeof(TombStone), "Awake")]
	public static class Patch_TombStone_Awake
	{
		private static void Postfix(TombStone __instance)
		{
			StatusEffect lootStatusEffect = __instance.m_lootStatusEffect;
			lootStatusEffect.m_ttl = corpseRunDuration.Value;
			LogDebug($"Set {lootStatusEffect} ttl (duration) to: {lootStatusEffect.m_ttl}");
		}
	}

	public const string pluginGUID = "jujuz1.mods.skillgainmodifier";

	public const string pluginName = "SkillGainModifier";

	public const string pluginVersion = "0.1.1";

	private readonly Harmony harmonyInstance = new Harmony("jujuz1.mods.skillgainmodifier");

	private static readonly ManualLogSource logger = Logger.CreateLogSource("SkillGainModifier");

	private static Dictionary<SkillType, SkillData> skillData = new Dictionary<SkillType, SkillData>();

	private static string configFileName = "jujuz1.mods.skillgainmodifier.cfg";

	private static string configFileFullPath;

	private static ConfigEntry<bool> loggingEnabled;

	private static ConfigEntry<float> corpseRunDuration;

	private static ConfigEntry<bool> noSkillDrainEnabled;

	private static readonly Dictionary<SkillType, ConfigEntry<float>> skillGainModifiers;

	private static ConfigEntry<float> skillReductionModifier;

	private DateTime lastReloadTime;

	private const long ONE_SECOND_IN_TICKS = 10000000L;

	private const long RELOAD_DELAY = 10000000L;

	private static DateTime lastReductionWarningTime;

	private const long WARNING_INTERVAL = 100000000L;

	public void Awake()
	{
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Invalid comparison between Unknown and I4
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
		loggingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Logging Enabled", true, "Enable logging");
		noSkillDrainEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "No skill drain enabled", true, "Enable no skill drain. The default length for it is 10 minutes, and its too big of a hassle to modify to work correctly. So here is a feature to enable or disable it :)");
		corpseRunDuration = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Duration", 60f, "Corpse run duration. The default for it is 50 seconds, here we set a default of 60 seconds");
		skillGainModifiers[(SkillType)999] = ((BaseUnityPlugin)this).Config.Bind<float>("Skill Gain", "Global", 2.5f, "Multiplier for all XP gain. A factor of 2.5x is a solid default value. I mean who in the hell is reaching level 100 in any playthrough with the default of 1x?");
		foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
		{
			SkillType val = value;
			if ((int)val != 0 && (int)val != 999)
			{
				skillGainModifiers[val] = ((BaseUnityPlugin)this).Config.Bind<float>("Skill Gain", ((object)(SkillType)(ref val)).ToString(), 0f, "");
			}
		}
		skillReductionModifier = ((BaseUnityPlugin)this).Config.Bind<float>("Skill reduction", "Modifier", 0f, "A multiplier for skill reduction when dying. RECOMMENDED VALUES: 0-0.2, see later for explanation. The default value of 0 means the skill level AND progress is not affected at all. A value of 1 signals to use the game's default world modifiers. Any value other than 0 resets skill progress! The game does the following when calculating the new level: new level = modifier * current level. Using a value above one will always set the level 0!!! So be careful with too big modifiers. The code checks that the minimum level is capped to 0 for safety");
		((BaseUnityPlugin)this).Config.Save();
		((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
		SetupWatcher();
		WarnAboutReductionModifier(atStartup: true);
		Assembly executingAssembly = Assembly.GetExecutingAssembly();
		harmonyInstance.PatchAll(executingAssembly);
	}

	private void OnDestroy()
	{
		((BaseUnityPlugin)this).Config.Save();
	}

	private void SetupWatcher()
	{
		FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, configFileName);
		fileSystemWatcher.Changed += ReadConfigValues;
		fileSystemWatcher.Created += ReadConfigValues;
		fileSystemWatcher.Renamed += ReadConfigValues;
		fileSystemWatcher.IncludeSubdirectories = true;
		fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
		fileSystemWatcher.EnableRaisingEvents = true;
	}

	private void ReadConfigValues(object sender, FileSystemEventArgs e)
	{
		DateTime now = DateTime.Now;
		long num = now.Ticks - lastReloadTime.Ticks;
		if (!File.Exists(configFileFullPath))
		{
			LogError("Config doesn't exist, check " + configFileFullPath);
			return;
		}
		if (num < 10000000)
		{
			LogDebug($"Attempting new reload in {(float)(10000000 - num) / 10000000f}s");
			return;
		}
		lastReloadTime = now;
		try
		{
			LogInfo("Attempting to reload configuration...");
			((BaseUnityPlugin)this).Config.Reload();
		}
		catch
		{
			LogError("There was an issue loading " + configFileName);
		}
		LogInfo("Reloaded configuration!\n");
	}

	private static void LogInfo(string message)
	{
		if (loggingEnabled.Value)
		{
			logger.LogInfo((object)message);
		}
	}

	private static void LogDebug(string message)
	{
		if (loggingEnabled.Value)
		{
			logger.LogDebug((object)message);
		}
	}

	private static void LogWarning(string message)
	{
		if (loggingEnabled.Value)
		{
			logger.LogWarning((object)message);
		}
	}

	private static void LogError(string message)
	{
		if (loggingEnabled.Value)
		{
			logger.LogError((object)message);
		}
	}

	private static void WarnAboutReductionModifier(bool atStartup = false)
	{
		bool flag = skillReductionModifier.Value > 1f;
		bool flag2 = skillReductionModifier.Value < 0f;
		if (!(flag || flag2))
		{
			return;
		}
		if (atStartup)
		{
			if (flag)
			{
				LogWarning($"Any reduction modifier above one results in the level being 0 when dying! Current modifier: {skillReductionModifier.Value}. Recommended values are 0-0.2! The mod will warn you about this every {10L}s");
			}
			else if (flag2)
			{
				LogWarning($"Any reduction modifier below zero results in the level increasing when dying! Current modifier: {skillReductionModifier.Value}. Recommended values are 0-0.2! The mod will warn you about this every {10L}s");
			}
			return;
		}
		DateTime now = DateTime.Now;
		if (now.Ticks - lastReductionWarningTime.Ticks >= 100000000)
		{
			lastReductionWarningTime = now;
			if (flag)
			{
				LogWarning($"Any reduction modifier above one results in the level being 0 when dying! Current modifier: {skillReductionModifier.Value}. Recommended values are 0-0.2!");
			}
			else if (flag2)
			{
				LogWarning($"Any reduction modifier below zero results in the level increasing when dying! Current modifier: {skillReductionModifier.Value}. Recommended values are 0-0.2!");
			}
		}
	}

	static SkillGainModifier()
	{
		string configPath = Paths.ConfigPath;
		char directorySeparatorChar = Path.DirectorySeparatorChar;
		configFileFullPath = configPath + directorySeparatorChar + configFileName;
		skillGainModifiers = new Dictionary<SkillType, ConfigEntry<float>>();
	}
}