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 BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("RPGSkillsMod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("RPGSkillsMod")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("25df85b1-0101-469c-b93b-aa6ed3e7dbb0")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.2.0.0")] [HarmonyPatch(typeof(StatsManager), "SaveFileCreate")] internal static class SaveFileCreatePatch { [HarmonyPostfix] private static void ResetClass() { Plugin.SelectedClass = PlayerClass.None; Plugin.Log.LogInfo((object)"New save created. Class reset."); } } [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class LoadGamePatch { [HarmonyPostfix] private static void LoadClass(string fileName, List backupList) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown ES3Settings val = new ES3Settings(Path.Combine(Application.persistentDataPath, "saves", fileName, fileName + ".es3"), (EncryptionType)1, "Why would you want to cheat?... :o It's no fun. :') :'D", (ES3Settings)null); if (!ES3.KeyExists("selectedClass", val)) { Plugin.SelectedClass = PlayerClass.None; Plugin.Log.LogInfo((object)"No saved class found."); return; } string text = ES3.Load("selectedClass", val); if (Enum.TryParse(text, out var result)) { Plugin.SelectedClass = result; Plugin.Log.LogInfo((object)$"Loaded class: {result}"); } else { Plugin.SelectedClass = PlayerClass.None; Plugin.Log.LogWarning((object)("Invalid saved class: " + text)); } } } [HarmonyPatch(typeof(StatsManager), "SaveGame")] internal static class SaveGamePatch { [HarmonyPostfix] private static void SaveClass(string fileName) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown if (!string.IsNullOrEmpty(fileName)) { ES3Settings val = new ES3Settings(Path.Combine(Application.persistentDataPath, "saves", fileName, fileName + ".es3"), (EncryptionType)1, "Why would you want to cheat?... :o It's no fun. :') :'D", (ES3Settings)null); ES3.Save("selectedClass", Plugin.SelectedClass.ToString(), val); Plugin.Log.LogInfo((object)$"Saved class: {Plugin.SelectedClass}"); } } } [HarmonyPatch(typeof(RunManager), "Update")] internal static class SkillPatch { private static void Postfix() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.EnableMod.Value || (Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { return; } if (SemiFunc.RunIsLevel()) { SkillManager.Update(); if (Input.GetKeyDown(Plugin.SkillKey.Value)) { SkillManager.TryUseSkill(); } } else { SkillManager.ResetCooldown(); } } } internal static class SkillManager { private static float remainingCooldown; public static bool IsReady => remainingCooldown <= 0f; public static float RemainingCooldown => Mathf.Max(0f, remainingCooldown); public static void Update() { if (remainingCooldown > 0f) { remainingCooldown -= Time.deltaTime; if (remainingCooldown < 0f) { remainingCooldown = 0f; } } } public static bool TryUseSkill() { if (!IsReady) { return false; } Skill skill = SkillDatabase.Get(Plugin.SelectedClass); Plugin.Log.LogInfo((object)("Using skill : " + skill.Name)); remainingCooldown = skill.Cooldown; return true; } public static void ResetCooldown() { remainingCooldown = 0f; } } [HarmonyPatch(typeof(RunManager), "Update")] internal static class SkillUI { private static GameObject skillUI; private static TextMeshProUGUI skillText; private static TextMeshProUGUI lobbyDescription; private static TextMeshProUGUI lobbyProperties; private static TextMeshProUGUI levelCooldown; private static TextMeshProUGUI levelKey; private static void Postfix() { if (Plugin.EnableMod.Value && !((Object)(object)LevelGenerator.Instance == (Object)null) && LevelGenerator.Instance.Generated) { if ((Object)(object)skillUI == (Object)null) { CreateUI(); } if (Plugin.SelectedClass == PlayerClass.None) { skillUI.SetActive(false); } else if (SemiFunc.RunIsLobby() || SemiFunc.RunIsShop()) { skillUI.SetActive(true); ((Component)lobbyDescription).gameObject.SetActive(true); ((Component)lobbyProperties).gameObject.SetActive(true); ((Component)levelCooldown).gameObject.SetActive(false); ((Component)levelKey).gameObject.SetActive(false); RefreshLobbyShop(); } else if (SemiFunc.RunIsLevel()) { skillUI.SetActive(true); ((Component)lobbyDescription).gameObject.SetActive(false); ((Component)lobbyProperties).gameObject.SetActive(false); ((Component)levelCooldown).gameObject.SetActive(true); RefreshLevel(); } else { skillUI.SetActive(false); } } } private static void CreateUI() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) skillUI = Object.Instantiate(((Component)EnergyUI.instance).gameObject, ((Component)EnergyUI.instance).transform.parent); ((Object)skillUI).name = "SkillUI"; Object.Destroy((Object)(object)skillUI.GetComponent()); DestroyChild("EnergyMax"); DestroyChild("Zap"); DestroyChild("Scanlines"); skillText = skillUI.GetComponent(); ((TMP_Text)skillText).fontSize = 32f; ((Graphic)skillText).color = Color.yellow; RectTransform component = skillUI.GetComponent(); component.anchorMin = new Vector2(0f, 0f); component.anchorMax = new Vector2(0f, 0f); component.pivot = new Vector2(0f, 0f); component.anchoredPosition = new Vector2(0f, 50f); component.sizeDelta = new Vector2(260f, 160f); lobbyDescription = CreateText("Description", "", new Vector2(0f, -85f), 18f, Color.white); lobbyProperties = CreateText("Properties", "", new Vector2(0f, -120f), 18f, Color.green); levelCooldown = CreateText("Cooldown", "", new Vector2(0f, -85f), 18f, Color.white); levelKey = CreateText("Key", ((object)Plugin.SkillKey.Value/*cast due to .constrained prefix*/).ToString() + " to use", new Vector2(0f, -105f), 18f, Color.grey); RefreshLevel(); } private static void RefreshLobbyShop() { Skill skill = SkillDatabase.Get(Plugin.SelectedClass); ((TMP_Text)skillText).text = skill.Name; ((TMP_Text)lobbyDescription).text = skill.Description; ((TMP_Text)lobbyProperties).text = string.Join("\n", skill.Properties.ConvertAll((string p) => "• " + p)); } private static void RefreshLevel() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) Skill skill = SkillDatabase.Get(Plugin.SelectedClass); ((TMP_Text)skillText).text = skill.Name; if (SkillManager.IsReady) { ((Component)levelKey).gameObject.SetActive(true); ((Graphic)levelCooldown).color = Color.green; ((TMP_Text)levelCooldown).text = "READY"; } else { ((Component)levelKey).gameObject.SetActive(false); ((Graphic)levelCooldown).color = Color.white; ((TMP_Text)levelCooldown).text = $"{SkillManager.RemainingCooldown:0}s remaining..."; } } private static TextMeshProUGUI CreateText(string name, string text, Vector2 position, float size, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(skillUI.transform, false); RectTransform obj = val.AddComponent(); obj.anchorMin = new Vector2(0f, 1f); obj.anchorMax = new Vector2(0f, 1f); obj.pivot = new Vector2(0f, 1f); obj.anchoredPosition = position; obj.sizeDelta = new Vector2(480f, 50f); TextMeshProUGUI obj2 = val.AddComponent(); ((TMP_Text)obj2).font = ((TMP_Text)skillText).font; ((TMP_Text)obj2).fontSharedMaterial = ((TMP_Text)skillText).fontSharedMaterial; ((TMP_Text)obj2).fontSize = size; ((Graphic)obj2).color = color; ((TMP_Text)obj2).text = text; ((TMP_Text)obj2).alignment = (TextAlignmentOptions)257; return obj2; } private static void DestroyChild(string child) { Transform val = skillUI.transform.Find(child); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } } public enum PlayerClass { None, Assassin, Alchemist, Druid, Guardian, Necromancer, Scout, Warrior } [HarmonyPatch(typeof(RunManager), "Update")] internal static class ClassUI { public static GameObject classUI; private static TextMeshProUGUI classText; private static TextMeshProUGUI leftArrow; private static TextMeshProUGUI rightArrow; private static TextMeshProUGUI leftKey; private static TextMeshProUGUI rightKey; private static void Postfix() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.EnableMod.Value || (Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { return; } if ((Object)(object)classUI == (Object)null) { CreateUI(); } if (SemiFunc.RunIsLobby() || SemiFunc.RunIsShop()) { classUI.SetActive(true); ((Component)leftArrow).gameObject.SetActive(true); ((Component)rightArrow).gameObject.SetActive(true); ((Component)leftKey).gameObject.SetActive(true); ((Component)rightKey).gameObject.SetActive(true); if (Input.GetKeyDown(Plugin.PreviousClassKey.Value)) { PreviousClass(); Refresh(); } if (Input.GetKeyDown(Plugin.NextClassKey.Value)) { NextClass(); Refresh(); } } else if (SemiFunc.RunIsLevel()) { classUI.SetActive(true); ((Component)leftArrow).gameObject.SetActive(false); ((Component)rightArrow).gameObject.SetActive(false); ((Component)leftKey).gameObject.SetActive(false); ((Component)rightKey).gameObject.SetActive(false); } else { classUI.SetActive(false); } } private static void CreateUI() { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) classUI = Object.Instantiate(((Component)EnergyUI.instance).gameObject, ((Component)EnergyUI.instance).transform.parent); ((Object)classUI).name = "ClassUI"; Object.Destroy((Object)(object)classUI.GetComponent()); DestroyIfExists(classUI.transform, "EnergyMax"); DestroyIfExists(classUI.transform, "Zap"); DestroyIfExists(classUI.transform, "Scanlines"); classText = classUI.GetComponent(); ((TMP_Text)classText).fontSize = 32f; ((Graphic)classText).color = Color.white; ((TMP_Text)classText).alignment = (TextAlignmentOptions)4097; RectTransform component = classUI.GetComponent(); component.sizeDelta = new Vector2(260f, 30f); component.anchoredPosition = new Vector2(0f, -62f); leftArrow = CreateText("LeftArrow", "◀", new Vector2(-120f, -25f), 24f, Color.yellow); rightArrow = CreateText("RightArrow", "▶", new Vector2(-90f, -25f), 24f, Color.yellow); leftKey = CreateText("LeftKey", ((object)Plugin.PreviousClassKey.Value/*cast due to .constrained prefix*/).ToString(), new Vector2(-120f, -50f), 18f, Color.grey); rightKey = CreateText("RightKey", ((object)Plugin.NextClassKey.Value/*cast due to .constrained prefix*/).ToString(), new Vector2(-90f, -50f), 18f, Color.grey); Refresh(); } private static TextMeshProUGUI CreateText(string name, string text, Vector2 position, float size, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(classUI.transform, false); val.AddComponent().anchoredPosition = position; TextMeshProUGUI obj = val.AddComponent(); ((TMP_Text)obj).font = ((TMP_Text)classText).font; ((TMP_Text)obj).fontSharedMaterial = ((TMP_Text)classText).fontSharedMaterial; ((TMP_Text)obj).alignment = (TextAlignmentOptions)514; ((TMP_Text)obj).fontSize = size; ((Graphic)obj).color = color; ((TMP_Text)obj).text = text; return obj; } private static void Refresh() { if (!((Object)(object)classText == (Object)null)) { ((TMP_Text)classText).text = Plugin.SelectedClass.ToString(); } } private static void PreviousClass() { int length = Enum.GetValues(typeof(PlayerClass)).Length; Plugin.SelectedClass = (PlayerClass)((int)(Plugin.SelectedClass - 1 + length) % length); } private static void NextClass() { int length = Enum.GetValues(typeof(PlayerClass)).Length; Plugin.SelectedClass = (PlayerClass)((int)(Plugin.SelectedClass + 1) % length); } private static void DestroyIfExists(Transform parent, string name) { Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } } [BepInPlugin("com.spefire.rpgskillsmod", "RPGSkillsMod", "0.0.1")] public class Plugin : BaseUnityPlugin { public const string ModGUID = "com.spefire.rpgskillsmod"; public const string ModName = "RPGSkillsMod"; public const string ModVersion = "0.0.1"; public const string totallyNormalString = "Why would you want to cheat?... :o It's no fun. :') :'D"; public static ConfigEntry EnableMod; public static ConfigEntry PreviousClassKey; public static ConfigEntry NextClassKey; public static ConfigEntry SkillKey; internal static ManualLogSource Log; public static PlayerClass SelectedClass; private void Awake() { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; EnableMod = ((BaseUnityPlugin)this).Config.Bind("General", "EnableMod", true, "Enable or disable the mod."); PreviousClassKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "PreviousClassKey", (KeyCode)287, "Key used to select the previous class."); NextClassKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "NextClassKey", (KeyCode)289, "Key used to select the next class."); SkillKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "SkillKey", (KeyCode)102, "Key used to activate the skill."); Log.LogInfo((object)"RPGSkillsMod loaded..."); new Harmony("com.spefire.rpgskillsmod").PatchAll(); Log.LogInfo((object)"...with Harmony patched !"); } } public class Skill { public string Name { get; } public string Description { get; } public List Properties { get; } public float Cooldown { get; } public Skill(string name, string description, List properties, float cooldown) { Name = name; Description = description; Properties = properties; Cooldown = cooldown; } } public static class SkillDatabase { private static readonly Dictionary Skills = new Dictionary { { PlayerClass.None, new Skill("No Skill", "", new List(), 0f) }, { PlayerClass.Necromancer, new Skill("Resurrection", "Revive a fallen ally by sacrificing part of your own health.", new List { "Health Cost: 50 HP", "Target: Dead Ally", "Cooldown: 60 s" }, 60f) }, { PlayerClass.Druid, new Skill("Nature's Blessing", "Heal nearby allies within a small radius.", new List { "Heal Radius: 8 m", "Affects nearby allies", "Cooldown: 25 s" }, 25f) }, { PlayerClass.Assassin, new Skill("Shadow Veil", "Become invisible and undetectable for a short duration.", new List { "Duration: 8 s", "Breaks on attack", "Cooldown: 45 s" }, 45f) }, { PlayerClass.Scout, new Skill("Adrenaline Rush", "Gain unlimited stamina for a short duration.", new List { "Infinite Stamina", "Duration: 12 s", "Cooldown: 30 s" }, 30f) }, { PlayerClass.Warrior, new Skill("Battle Rage", "Greatly increase your strength for a short duration.", new List { "Increased Strength", "Duration: 15 s", "Cooldown: 45 s" }, 45f) }, { PlayerClass.Alchemist, new Skill("Transmutation", "Duplicate the item currently held in your hands.", new List { "Duplicates held item", "Single use per activation", "Cooldown: 60 s" }, 60f) }, { PlayerClass.Guardian, new Skill("Taunt", "Force nearby enemies to focus their attention on you.", new List { "Taunt Radius: 15 m", "Duration: 8 s", "Cooldown: 35 s" }, 35f) } }; public static Skill Get(PlayerClass playerClass) { return Skills[playerClass]; } }