Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SkillGainModifier v0.1.1
plugins/SkillGainModifier.dll
Decompiled a month agousing 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>>(); } }