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 SkillFern v1.0.0
SkillFern.dll
Decompiled 9 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using ExitGames.Client.Photon; using HarmonyLib; using Newtonsoft.Json; using Photon.Pun; using Photon.Realtime; using REPOLib; using SkillFern.Custom; using SkillFern.Networking; using SkillFern.UI; using SkillFern.Utilities; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("SkillFern")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SkillFern")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("2317c45d-9d42-4c50-aeed-5f05c5bf5865")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace SkillFern { [BepInPlugin("com.dajadeninja.repo.skillfern", "Skill Fern", "0.0.0")] public class Plugin : BaseUnityPlugin { public static Plugin instance; private Harmony harmony; private void Awake() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown instance = this; harmony = new Harmony("com.dajadeninja.repo.skillfern"); ((BaseUnityPlugin)instance).Logger.LogInfo((object)"Setting up configuration. . ."); ConfigHelper.Initialize(((BaseUnityPlugin)this).Config); LogInfo("Loaded successfully! Loading patches. . ."); harmony.PatchAll(); LogInfo("Patches loaded"); AssetHelper.LoadBundles(); LogInfo("Newest 9"); } public static void LogInfo(string msg) { if (ConfigHelper.EnableDebug()) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)("Skill Fern: " + msg)); } } public static void LogError(string msg) { if (ConfigHelper.EnableDebug()) { ((BaseUnityPlugin)instance).Logger.LogError((object)("Skill Fern: " + msg)); } } } } namespace SkillFern.Utilities { public class AssetHelper { public static AssetBundle bundle; public static bool loaded; public static void LoadBundles() { BundleLoader.LoadBundle(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "SkillFern.bundle"), (Func<AssetBundle, IEnumerator>)OnBundleLoaded, true); } private static IEnumerator OnBundleLoaded(AssetBundle bundle) { Plugin.LogInfo("Assets successfully loaded!"); AssetHelper.bundle = bundle; loaded = true; yield break; } public static IEnumerator RegisterMenus(MenuManager menuManager) { yield return (object)new WaitUntil((Func<bool>)(() => loaded)); Plugin.LogInfo("Registering menu pages..."); menuManager.menuPages.Add(new MenuPages { menuPageIndex = (MenuPageIndex)MenuPageSkills.CUSTOM_PAGE_INDEX, menuPage = GetPrefab(MenuPageSkills.PREFAB_NAME) }); Plugin.LogInfo("Menu pages registered!"); } public static GameObject GetPrefab(string prefab) { return bundle.LoadAsset<GameObject>(prefab); } } public class ConfigHelper { private static ConfigEntry<bool> disableShopUpgrades; private static ConfigEntry<bool> fairDistribution; private static ConfigEntry<int> startingSkillPoints; private static ConfigEntry<int> baseSkillPointsEarned; private static ConfigEntry<int> moonSkillPoints; private static ConfigEntry<bool> enableDebug; public static bool ShopUpgradesDisabled() { return disableShopUpgrades.Value; } public static bool FairDistribution() { return false; } public static int StartingSkillPoints() { return startingSkillPoints.Value; } public static int BaseSkillPointsEarned() { return baseSkillPointsEarned.Value; } public static int MoonSkillPoints() { return moonSkillPoints.Value; } public static bool EnableDebug() { return enableDebug == null || enableDebug.Value; } public static void Initialize(ConfigFile config) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown startingSkillPoints = config.Bind<int>("General", "StartingSkillPoints", 0, new ConfigDescription("The number of skill points to start with (default 0)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); baseSkillPointsEarned = config.Bind<int>("General", "BasePointsEarned", 2, new ConfigDescription("The base number of skill points gained each level (default 2)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); moonSkillPoints = config.Bind<int>("General", "PointsPerMoon", 1, new ConfigDescription("How many additional skill points to earn per moon phase (default 1)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); disableShopUpgrades = config.Bind<bool>("Miscellaneous", "DisableUpgrades", true, "Whether to disable the vanilla shop upgrades (on by default)"); enableDebug = config.Bind<bool>("Debug", "EnableDebug", false, "Whether to spam the log with my debug info"); } } public class DebugHelper { public static void TestShop() { RunManager.instance.ChangeLevel(true, false, (ChangeLevelType)5); } public static void TestAllUpgrades() { Plugin.LogInfo("Testing all upgrades:"); string[] sKILL_NAMES = SkillData.SKILL_NAMES; foreach (string text in sKILL_NAMES) { Plugin.LogInfo("-- Trying " + text); SkillDataManager.instance.UpdateSkill(PlayerHelper.GetLocalSteamID(), text, 2); } } public static void AwardPoints(int points) { SkillNetworkSync.UpdateSkillPoints(PlayerHelper.GetLocalSteamID(), points); } } public static class PlayerHelper { private const int STARTING_ENERGY = 40; private const int ENERGY_INCREMENT = 10; private const int STARTING_HEALTH = 100; public const int HEALTH_INCREMENT = 20; private const float STARTING_SPEED = 5f; private const float STARTING_STRENGTH = 1f; private const float STRENGTH_INCREMENT = 0.2f; private const float STARTING_RANGE = 2.5f; private const float THROW_INCREMENT = 0.3f; public static bool IsLocalSteamID(string steamID) { return (Object)(object)SemiFunc.PlayerAvatarGetFromSteamID(steamID) == (Object)(object)SemiFunc.PlayerAvatarLocal(); } public static bool IsHost() { return PhotonNetwork.IsMasterClient; } public static List<string> GetAllPlayerSteamIDs() { List<string> list = new List<string>(); foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { list.Add((string)AccessTools.Field(typeof(PlayerAvatar), "steamID").GetValue(item)); } return list; } public static PlayerController GetLocalPlayerController() { return PlayerController.instance; } public static PlayerAvatar GetLocalPlayerAvatar() { return PlayerAvatar.instance; } public static string GetLocalSteamID() { //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) return SteamClient.SteamId.Value.ToString(); } public static void UpdatePlayerEnergy(int newLevel) { PlayerController localPlayerController = GetLocalPlayerController(); AccessTools.Field(typeof(PlayerController), "EnergyStart").SetValue(localPlayerController, (float)(40 + newLevel * 10)); AccessTools.Field(typeof(PlayerController), "EnergyCurrent").SetValue(localPlayerController, localPlayerController.EnergyStart); } public static void UpdatePlayerHealth(int newLevel, int oldValue) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); int num = (StatsManager.instance.playerUpgradeHealth.ContainsKey(GetLocalSteamID()) ? (newLevel - oldValue) : newLevel); StatsManager.instance.playerUpgradeHealth[GetLocalSteamID()] = newLevel; AccessTools.Field(typeof(PlayerHealth), "maxHealth").SetValue(localPlayerAvatar.playerHealth, 100 + newLevel * 20); int num2 = (int)AccessTools.Field(typeof(PlayerHealth), "maxHealth").GetValue(localPlayerAvatar.playerHealth); int num3 = (int)AccessTools.Field(typeof(PlayerHealth), "health").GetValue(localPlayerAvatar.playerHealth); if (num != 0) { CameraGlitch.Instance.PlayShortHeal(); AccessTools.Field(typeof(PlayerHealth), "health").SetValue(localPlayerAvatar.playerHealth, num3 + num * 20); if (GameManager.Multiplayer()) { localPlayerAvatar.photonView.RPC("UpdateHealthRPC", (RpcTarget)1, new object[4] { num2, num3 + num * 20, true, false }); } } } public static void UpdatePlayerExtraJump(int newLevel) { PlayerController localPlayerController = GetLocalPlayerController(); AccessTools.Field(typeof(PlayerController), "JumpExtra").SetValue(localPlayerController, newLevel); } public static void UpdatePlayerLaunch(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); object value = AccessTools.Field(typeof(PlayerAvatar), "tumble").GetValue(localPlayerAvatar); if (value != null) { AccessTools.Field(typeof(PlayerTumble), "tumbleLaunch").SetValue(value, newLevel); } } public static void UpdatePlayerTumbleClimb(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PlayerAvatar), "upgradeTumbleClimb").SetValue(localPlayerAvatar, (float)newLevel); } public static void UpdatePlayerDeathHeadBattery(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PlayerAvatar), "upgradeDeathHeadBattery").SetValue(localPlayerAvatar, (float)newLevel); } public static void UpdatePlayerMapPlayerCount(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PlayerAvatar), "upgradeMapPlayerCount").SetValue(localPlayerAvatar, newLevel); } public static void UpdatePlayerSpeed(int newLevel) { PlayerController localPlayerController = GetLocalPlayerController(); AccessTools.Field(typeof(PlayerController), "SprintSpeed").SetValue(localPlayerController, 5f + (float)newLevel); AccessTools.Field(typeof(PlayerController), "SprintSpeedUpgrades").SetValue(localPlayerController, (float)newLevel); AccessTools.Field(typeof(PlayerController), "playerOriginalSprintSpeed").SetValue(localPlayerController, 5f + (float)newLevel); } public static void UpdatePlayerStrength(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PhysGrabber), "grabStrength").SetValue(AccessTools.Field(typeof(PlayerAvatar), "physGrabber").GetValue(localPlayerAvatar), 1f + (float)newLevel * 0.2f); } public static void UpdatePlayerRange(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PhysGrabber), "grabRange").SetValue(AccessTools.Field(typeof(PlayerAvatar), "physGrabber").GetValue(localPlayerAvatar), 2.5f + (float)newLevel); } public static void UpdatePlayerThrow(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PhysGrabber), "throwStrength").SetValue(AccessTools.Field(typeof(PlayerAvatar), "physGrabber").GetValue(localPlayerAvatar), (float)newLevel * 0.3f); } public static void UpdatePlayerCrouchRest(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PlayerAvatar), "upgradeCrouchRest").SetValue(localPlayerAvatar, (float)newLevel); } public static void UpdatePlayerTumbleWings(int newLevel) { PlayerAvatar localPlayerAvatar = GetLocalPlayerAvatar(); AccessTools.Field(typeof(PlayerAvatar), "upgradeTumbleWings").SetValue(localPlayerAvatar, (float)newLevel); } } } namespace SkillFern.Networking { public static class SkillNetworkSync { private enum EVENT_TYPE { SKILL_UPDATE, NODE_PURCHASE, POINTS_UPDATE, SKILL_SYNC, POINTS_SYNC, CAREER_SYNC, CONFIG_SYNC } private const byte SKILL_DATA_CHANNEL = 187; private static bool isInitialized = false; public static int saveCountdown = -1; public static void Initialize() { if (!isInitialized && PhotonNetwork.NetworkingClient != null) { isInitialized = true; saveCountdown = -1; PhotonNetwork.NetworkingClient.EventReceived += OnEventReceived; Plugin.LogInfo("Network initialized"); } } public static void OnEventReceived(EventData data) { if (data.Code != 187) { return; } object[] array = (object[])data.CustomData; Plugin.LogInfo("Payload: " + array.ToString()); string text = (string)array[1]; switch ((EVENT_TYPE)array[0]) { case EVENT_TYPE.CONFIG_SYNC: { int moonSkillPoints = (int)array[2]; int baseSkillPoints = (int)array[3]; SkillDataManager.instance.moonSkillPoints = moonSkillPoints; SkillDataManager.instance.baseSkillPoints = baseSkillPoints; break; } case EVENT_TYPE.CAREER_SYNC: { int correctPoints = (int)array[2]; int ownedPoints = (int)array[3]; if (text == PlayerHelper.GetLocalSteamID()) { SkillDataManager.instance.ReconcileCareer(correctPoints, ownedPoints); } break; } case EVENT_TYPE.NODE_PURCHASE: { string nodeID = (string)array[2]; SkillDataManager.instance.PurchaseNode(text, nodeID); break; } case EVENT_TYPE.SKILL_UPDATE: case EVENT_TYPE.SKILL_SYNC: { string skillName = (string)array[2]; int newLevel = (int)array[3]; SkillDataManager.instance.UpdateSkill(text, skillName, newLevel, (EVENT_TYPE)array[0] == EVENT_TYPE.SKILL_SYNC); break; } case EVENT_TYPE.POINTS_UPDATE: case EVENT_TYPE.POINTS_SYNC: { int amount = (int)array[2]; SkillDataManager.instance.UpdateSkillPoints(text, amount, (EVENT_TYPE)array[0] == EVENT_TYPE.POINTS_SYNC); if (saveCountdown > 0 && PlayerHelper.IsHost()) { saveCountdown--; if (saveCountdown == 0) { saveCountdown = -1; Plugin.LogInfo("Forcing save after skill point updates. . ."); SaveManager.Save(SaveManager.lastFile); } } break; } } } public static void UpdateSkillPoints(string steamID, int amount, bool sync = false) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) Plugin.LogInfo(" Granting " + amount + " points to " + steamID); if (!GameManager.Multiplayer()) { SkillDataManager.instance.UpdateSkillPoints(steamID, amount); return; } object[] array = new object[3] { sync ? EVENT_TYPE.POINTS_SYNC : EVENT_TYPE.POINTS_UPDATE, steamID, amount }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)1 }; PhotonNetwork.RaiseEvent((byte)187, (object)array, val, SendOptions.SendReliable); } public static void UpdateSkill(string steamID, string skillName, int newLevel, bool sync = false) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!GameManager.Multiplayer()) { SkillDataManager.instance.UpdateSkill(steamID, skillName, newLevel); return; } object[] array = new object[4] { sync ? EVENT_TYPE.SKILL_SYNC : EVENT_TYPE.SKILL_UPDATE, steamID, skillName, newLevel }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)1 }; PhotonNetwork.RaiseEvent((byte)187, (object)array, val, SendOptions.SendReliable); } public static void UpdateSkill(string steamID, SkillData.SKILL_TYPE skillType, int newLevel) { UpdateSkill(steamID, SkillData.SKILL_NAMES[(int)skillType], newLevel); } public static void PurchaseNode(string steamID, string nodeID) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) if (!GameManager.Multiplayer()) { SkillDataManager.instance.PurchaseNode(steamID, nodeID); return; } Plugin.LogInfo("Purchasing node " + nodeID + " for " + steamID + " on network."); object[] array = new object[3] { EVENT_TYPE.NODE_PURCHASE, steamID, nodeID }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)1 }; PhotonNetwork.RaiseEvent((byte)187, (object)array, val, SendOptions.SendReliable); } public static void AwardSkillPointsForLevel() { saveCountdown = 0; foreach (string allPlayerSteamID in PlayerHelper.GetAllPlayerSteamIDs()) { saveCountdown++; int amount = SkillDataManager.CalculatePointsEarned(); UpdateSkillPoints(allPlayerSteamID, amount); } } public static void SyncAll() { //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Expected O, but got Unknown //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) Plugin.LogInfo("Syncing all skill data. . ."); List<string> list = new List<string>(); List<string> allPlayerSteamIDs = PlayerHelper.GetAllPlayerSteamIDs(); foreach (SkillData skillData in SkillDataManager.instance.skillDatas) { if (!allPlayerSteamIDs.Contains(skillData.steamID)) { Plugin.LogInfo("Skipping " + skillData.steamID + " who is not in this game."); continue; } Plugin.LogInfo("Syncing data for " + skillData.steamID); Plugin.LogInfo("Cycling skill datas"); string[] sKILL_NAMES = SkillData.SKILL_NAMES; foreach (string text in sKILL_NAMES) { UpdateSkill(skillData.steamID, text, (int)typeof(SkillData).GetField(text).GetValue(skillData), sync: true); } Plugin.LogInfo("Cycling owned nodes"); foreach (string ownedNode in skillData.ownedNodes) { PurchaseNode(skillData.steamID, ownedNode); } Plugin.LogInfo("Syncing skill points"); UpdateSkillPoints(skillData.steamID, skillData.skillPoints, sync: true); if (ConfigHelper.FairDistribution()) { Plugin.LogInfo("Reconciling career"); object[] array = new object[4] { EVENT_TYPE.CAREER_SYNC, skillData.steamID, SkillDataManager.instance.careerPoints, skillData.pointsGained }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)1 }; PhotonNetwork.RaiseEvent((byte)187, (object)array, val, SendOptions.SendReliable); } list.Add(skillData.steamID); } int num = allPlayerSteamIDs.Count - list.Count; if (num > 0) { Plugin.LogInfo("Creating new skill data for " + num + " new players."); } foreach (string item in allPlayerSteamIDs) { if (!list.Contains(item)) { if (ConfigHelper.FairDistribution()) { Plugin.LogInfo("Starting " + item + " with " + SkillDataManager.instance.careerPoints + " points."); UpdateSkillPoints(item, SkillDataManager.instance.careerPoints, sync: true); } else { Plugin.LogInfo("Starting " + item + " with " + ConfigHelper.StartingSkillPoints() + " points."); UpdateSkillPoints(item, ConfigHelper.StartingSkillPoints(), sync: true); } } } Plugin.LogInfo("Skill data synced!"); Plugin.LogInfo("Syncing moon phase data. . ."); object[] array2 = new object[4] { EVENT_TYPE.CONFIG_SYNC, PlayerHelper.GetLocalSteamID(), ConfigHelper.MoonSkillPoints(), ConfigHelper.BaseSkillPointsEarned() }; RaiseEventOptions val2 = new RaiseEventOptions { Receivers = (ReceiverGroup)1 }; PhotonNetwork.RaiseEvent((byte)187, (object)array2, val2, SendOptions.SendReliable); } } } namespace SkillFern.Custom { public static class SaveManager { public static string lastFile; public static void Save(string fileName) { lastFile = fileName; string text = Application.persistentDataPath + "/saves/" + fileName; string text2 = text + "/" + fileName + "_SkillFern.json"; string contents = JsonConvert.SerializeObject((object)SkillDataManager.instance, (Formatting)1); File.WriteAllText(text2, contents); Plugin.LogInfo("Saved data to " + text2); } public static void Load(string fileName) { lastFile = fileName; string text = Application.persistentDataPath + "/saves/" + fileName; string text2 = text + "/" + fileName + "_SkillFern.json"; if (File.Exists(text2)) { string text3 = File.ReadAllText(text2); SkillDataManager.instance = JsonConvert.DeserializeObject<SkillDataManager>(text3); Plugin.LogInfo("Loaded save data from " + text2); } else { Plugin.LogInfo("SAVE DOESN T EXIST"); SkillDataManager.instance = new SkillDataManager(); Plugin.LogInfo("Created new save data at " + text2); } } } [Serializable] public class SkillDataManager { public static SkillDataManager instance; public int careerPoints; public int moonSkillPoints; public int baseSkillPoints; [JsonProperty("skillDatas")] public List<SkillData> skillDatas { get; set; } [JsonConstructor] public SkillDataManager() { Plugin.LogInfo("NEW SKILLDATAMANAGER CREATED ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); skillDatas = new List<SkillData>(); careerPoints = ConfigHelper.StartingSkillPoints(); moonSkillPoints = ConfigHelper.MoonSkillPoints(); baseSkillPoints = ConfigHelper.BaseSkillPointsEarned(); } public static void Initialize() { if (instance == null) { instance = new SkillDataManager(); } } public static int CalculatePointsEarned(int offset = 0) { return instance.baseSkillPoints + RunManager.instance.CalculateMoonLevel(SemiFunc.RunGetLevelsCompleted() - 1 + offset) * instance.moonSkillPoints; } public int GetLocalSkillPoints() { return GetPlayerData(PlayerHelper.GetLocalSteamID()).skillPoints; } public SkillData GetLocalPlayerData() { return GetPlayerData(PlayerHelper.GetLocalSteamID()); } public SkillData GetPlayerData(string steamID) { SkillData skillData = skillDatas.Find((SkillData data) => data.steamID == steamID); if (skillData == null) { skillData = new SkillData(steamID); skillDatas.Add(skillData); } return skillData; } public void UpdateSkillPoints(string steamID, int amount, bool syncing = false) { if (skillDatas == null) { Plugin.LogInfo("Skill Data does not exist! Creating. . ."); skillDatas = new List<SkillData>(); } SkillData skillData = skillDatas.Find((SkillData data) => data.steamID == steamID); if (skillData == null) { skillData = new SkillData(steamID); skillDatas.Add(skillData); } if (syncing) { if (skillData.pointsGained == 0) { skillData.pointsGained = amount; } skillData.skillPoints = amount; } else { if (amount > 0) { if (steamID == PlayerHelper.GetLocalSteamID()) { careerPoints += amount; } skillData.pointsGained += amount; } skillData.skillPoints += amount; } Plugin.LogInfo(steamID + " now has " + skillData.skillPoints + " points"); } public void UpdateSkill(string steamID, string skillName, int newLevel, bool syncing = false) { if (skillDatas == null) { Plugin.LogInfo("Skill Data does not exist! Creating. . ."); skillDatas = new List<SkillData>(); } SkillData skillData = skillDatas.Find((SkillData data) => data.steamID == steamID); if (skillData == null) { skillData = new SkillData(steamID); skillDatas.Add(skillData); } int num = ((!syncing) ? ((int)typeof(SkillData).GetField(skillName).GetValue(skillData) + newLevel) : newLevel); typeof(SkillData).GetField(skillName).SetValue(skillData, num); if (!syncing) { int oldValue = UpdateDictionarySkillByName(steamID, skillName, num); if (!GameManager.Multiplayer() || PlayerHelper.IsLocalSteamID(steamID)) { UpdateLocalSkillByName(skillName, num, oldValue); } } } public void PurchaseNode(string steamID, string nodeID) { if (skillDatas == null) { Plugin.LogInfo("Skill Data does not exist! Creating. . ."); skillDatas = new List<SkillData>(); } SkillData skillData = skillDatas.Find((SkillData data) => data.steamID == steamID); if (skillData == null) { Plugin.LogInfo("Creating skill data for player " + steamID); skillData = new SkillData(steamID); skillDatas.Add(skillData); } if (!skillData.ownedNodes.Contains(nodeID)) { skillData.ownedNodes.Add(nodeID); } } public void UpdateLocalSkillByName(string skillName, int newLevel, int oldValue = 0) { switch (skillName) { case "healthLevels": PlayerHelper.UpdatePlayerHealth(newLevel, oldValue); break; case "staminaLevels": PlayerHelper.UpdatePlayerEnergy(newLevel); break; case "extraJumpLevels": PlayerHelper.UpdatePlayerExtraJump(newLevel); break; case "launchLevels": PlayerHelper.UpdatePlayerLaunch(newLevel); break; case "tumbleClimbLevels": PlayerHelper.UpdatePlayerTumbleClimb(newLevel); break; case "deathHeadBatteryLevels": PlayerHelper.UpdatePlayerDeathHeadBattery(newLevel); break; case "mapPlayerCountLevels": PlayerHelper.UpdatePlayerMapPlayerCount(newLevel); break; case "speedLevels": PlayerHelper.UpdatePlayerSpeed(newLevel); break; case "strengthLevels": PlayerHelper.UpdatePlayerStrength(newLevel); break; case "rangeLevels": PlayerHelper.UpdatePlayerRange(newLevel); break; case "throwLevels": PlayerHelper.UpdatePlayerThrow(newLevel); break; case "crouchRestLevels": PlayerHelper.UpdatePlayerCrouchRest(newLevel); break; case "tumbleWingsLevels": PlayerHelper.UpdatePlayerTumbleWings(newLevel); break; } } public int UpdateDictionarySkillByName(string steamID, string skillName, int newLevel) { int num = 0; switch (skillName) { case "healthLevels": { num = (StatsManager.instance.playerUpgradeHealth.ContainsKey(steamID) ? StatsManager.instance.playerUpgradeHealth[steamID] : 0); PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(steamID); int num2 = (int)AccessTools.Field(typeof(PlayerHealth), "health").GetValue(val.playerHealth); StatsManager.instance.SetPlayerHealth(steamID, num2 + (newLevel - num) * 20, true); StatsManager.instance.playerUpgradeHealth[steamID] = newLevel; break; } case "staminaLevels": StatsManager.instance.playerUpgradeStamina[steamID] = newLevel; break; case "extraJumpLevels": StatsManager.instance.playerUpgradeExtraJump[steamID] = newLevel; break; case "launchLevels": StatsManager.instance.playerUpgradeLaunch[steamID] = newLevel; break; case "tumbleClimbLevels": StatsManager.instance.playerUpgradeTumbleClimb[steamID] = newLevel; break; case "deathHeadBatteryLevels": StatsManager.instance.playerUpgradeDeathHeadBattery[steamID] = newLevel; break; case "mapPlayerCountLevels": StatsManager.instance.playerUpgradeMapPlayerCount[steamID] = newLevel; break; case "speedLevels": StatsManager.instance.playerUpgradeSpeed[steamID] = newLevel; break; case "strengthLevels": StatsManager.instance.playerUpgradeStrength[steamID] = newLevel; break; case "rangeLevels": StatsManager.instance.playerUpgradeRange[steamID] = newLevel; break; case "throwLevels": StatsManager.instance.playerUpgradeThrow[steamID] = newLevel; break; case "crouchRestLevels": StatsManager.instance.playerUpgradeCrouchRest[steamID] = newLevel; break; case "tumbleWingsLevels": StatsManager.instance.playerUpgradeTumbleWings[steamID] = newLevel; break; } return num; } public void ReconcileCareer(int correctPoints, int ownedPoints) { if (GetLocalPlayerData() != null) { GetLocalPlayerData().pointsGained = ownedPoints; } careerPoints = ownedPoints; int num = correctPoints - ownedPoints; if (num > 0) { SkillNetworkSync.UpdateSkillPoints(PlayerHelper.GetLocalSteamID(), num); } } } [Serializable] public class SkillData { public enum SKILL_TYPE { HEALTH, STAMINA, EXTRA_JUMP, LAUNCH, TUMBLE_CLIMB, DEATH_HEAD_BATTERY, MAP_PLAYER_COUNT, SPEED, STRENGTH, RANGE, THROW, CROUCH_REST, TUMBLE_WINGS } public static string[] SKILL_DISPLAY_NAMES = new string[13] { "Health", "Stamina", "Extra Jump", "Tumble Launch", "Tumble Climb", "Death Battery", "Player Count", "Speed", "Strength", "Range", "Throw", "Crouch Rest", "Tumble Wings" }; public static string[] SKILL_NAMES = new string[13] { "healthLevels", "staminaLevels", "extraJumpLevels", "launchLevels", "tumbleClimbLevels", "deathHeadBatteryLevels", "mapPlayerCountLevels", "speedLevels", "strengthLevels", "rangeLevels", "throwLevels", "crouchRestLevels", "tumbleWingsLevels" }; public string steamID; public int healthLevels; public int staminaLevels; public int extraJumpLevels; public int launchLevels; public int tumbleClimbLevels; public int deathHeadBatteryLevels; public int mapPlayerCountLevels; public int speedLevels; public int strengthLevels; public int rangeLevels; public int throwLevels; public int crouchRestLevels; public int tumbleWingsLevels; public int skillPoints; public int pointsGained; [JsonProperty("ownedNodes")] public List<string> ownedNodes { get; set; } public SkillData(string steamID) { this.steamID = steamID; healthLevels = 0; staminaLevels = 0; extraJumpLevels = 0; launchLevels = 0; tumbleClimbLevels = 0; deathHeadBatteryLevels = 0; mapPlayerCountLevels = 0; speedLevels = 0; strengthLevels = 0; rangeLevels = 0; throwLevels = 0; crouchRestLevels = 0; tumbleWingsLevels = 0; skillPoints = ConfigHelper.StartingSkillPoints(); ownedNodes = new List<string>(); pointsGained = ConfigHelper.StartingSkillPoints(); } [JsonConstructor] public SkillData() { ownedNodes = new List<string>(); } } } namespace SkillFern.UI { public class HoverText : MonoBehaviour { public float yOffset = 10f; public float flyInTime = 0.1f; public float fadeInTime = 0.05f; public float fadeOutTime = 0.2f; private TextMeshProUGUI textMesh; private bool shown; public void Start() { textMesh = ((Component)this).GetComponentInChildren<TextMeshProUGUI>(); ((TMP_Text)textMesh).alpha = 0f; ((TMP_Text)textMesh).SetText("", true); shown = false; } public void Update() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (shown) { if (((TMP_Text)textMesh).alpha < 1f) { TextMeshProUGUI obj = textMesh; ((TMP_Text)obj).alpha = ((TMP_Text)obj).alpha + Time.deltaTime / fadeInTime; } Vector2 anchoredPosition = ((TMP_Text)textMesh).rectTransform.anchoredPosition; float num = yOffset / flyInTime; if (anchoredPosition.y < 0f) { anchoredPosition.y = Mathf.MoveTowards(anchoredPosition.y, 0f, num * Time.deltaTime); } ((TMP_Text)textMesh).rectTransform.anchoredPosition = anchoredPosition; } else if (((TMP_Text)textMesh).alpha > 0f) { TextMeshProUGUI obj2 = textMesh; ((TMP_Text)obj2).alpha = ((TMP_Text)obj2).alpha - Time.deltaTime / fadeOutTime; } } public void SetText(string text) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (!shown) { ((TMP_Text)textMesh).alpha = 0f; ((TMP_Text)textMesh).SetText(text, true); shown = true; ((TMP_Text)textMesh).rectTransform.anchoredPosition = new Vector2(((TMP_Text)textMesh).rectTransform.anchoredPosition.x, 0f - yOffset); } } public void Clear() { shown = false; } } public class UpgradeNode : FernNode { public SkillData.SKILL_TYPE upgradeType; public int upgradeAmount; public new void Reset() { base.Reset(); upgradeAmount = 1; } public override void Activate() { SkillNetworkSync.UpdateSkill(PlayerHelper.GetLocalSteamID(), upgradeType, upgradeAmount); } public override string GetDisplayString() { return SkillData.SKILL_DISPLAY_NAMES[(int)upgradeType]; } } public class FernNode : MonoBehaviour { private class LineContainer { public GameObject lineObject; public FernNode dependency; } private List<LineContainer> lines = new List<LineContainer>(); public string nodeID; public Image targetImage; public TextMeshProUGUI costText; public FernNode[] dependencies; public int cost; protected bool owned; protected MenuButton button; protected bool wasHovering; private MenuPageSkills skillsPage; public bool isOwned() { return owned; } public void setOwned(bool newOwned) { owned = newOwned; } public void DrawLines(GameObject lineContainer, int lineThickness) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) lines = new List<LineContainer>(); for (int i = 0; i < dependencies.Length; i++) { LineContainer lineContainer2 = new LineContainer(); lineContainer2.lineObject = new GameObject("Fern Line"); lineContainer2.dependency = dependencies[i]; lineContainer2.lineObject.transform.SetParent(lineContainer.transform, false); Image val = lineContainer2.lineObject.AddComponent<Image>(); ((Graphic)val).color = Color.white; RectTransform rectTransform = ((Graphic)val).rectTransform; rectTransform.pivot = new Vector2(0f, 0.5f); RectTransform component = ((Component)this).GetComponent<RectTransform>(); RectTransform component2 = ((Component)dependencies[i]).GetComponent<RectTransform>(); Rect rect = component.rect; Vector3 val2 = ((Transform)component).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center)); rect = component2.rect; Vector3 val3 = ((Transform)component2).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center)); ((Transform)rectTransform).position = val2; float num = Vector3.Distance(val2, val3); rectTransform.sizeDelta = new Vector2(num, (float)lineThickness); Vector3 val4 = val3 - val2; float num2 = Mathf.Atan2(val4.y, val4.x) * 57.29578f; ((Transform)rectTransform).rotation = Quaternion.Euler(0f, 0f, num2); lines.Add(lineContainer2); } } public void OnValidate() { ((TMP_Text)costText).SetText(cost.ToString() ?? "", true); } public void Reset() { cost = 1; nodeID = Guid.NewGuid().ToString(); targetImage = ((Component)((Component)this).transform.GetChild(0)).GetComponent<Image>(); dependencies = new FernNode[1]; costText = ((Component)((Component)this).transform.GetChild(2)).GetComponent<TextMeshProUGUI>(); ((TMP_Text)costText).SetText(cost.ToString() ?? "", true); } public void Awake() { button = ((Component)this).GetComponent<MenuButton>(); skillsPage = ((Component)this).GetComponentInParent<MenuPageSkills>(); } public void Update() { if ((int)AccessTools.Field(typeof(MenuButton), "buttonState").GetValue(button) <= 1 && !owned) { wasHovering = true; skillsPage.SetHoverText(GetDisplayString()); } else if (wasHovering) { wasHovering = false; skillsPage.ClearHoverText(); } } public void Initialize() { UpdateStatus(); } public bool CanPurchase() { return !owned && (dependencies.Length == 0 || dependencies.Any((FernNode node) => node.isOwned())); } public void Purchase() { int localSkillPoints = SkillDataManager.instance.GetLocalSkillPoints(); if (localSkillPoints >= cost) { Plugin.LogInfo("Purchased node " + nodeID); owned = true; SkillNetworkSync.UpdateSkillPoints(PlayerHelper.GetLocalSteamID(), -cost); skillsPage.UpdateAllNodes(localSkillPoints - cost); Activate(); SkillNetworkSync.PurchaseNode(PlayerHelper.GetLocalSteamID(), nodeID); } } public virtual void Activate() { Plugin.LogInfo("Node " + nodeID + " has no effects"); } public void UpdateStatus() { //IL_007c: 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) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) if (owned) { ((Behaviour)costText).enabled = false; } else { ((TMP_Text)costText).SetText(cost.ToString() ?? "", true); } if (!owned && CanPurchase()) { ((Behaviour)button).enabled = true; } else { ((Behaviour)button).enabled = false; } if (owned) { ((Graphic)targetImage).color = Color.green; } else if (CanPurchase()) { ((Graphic)targetImage).color = Color.white; } else { ((Graphic)targetImage).color = Color.gray; } foreach (LineContainer line in lines) { if (owned && line.dependency.isOwned()) { ((Graphic)line.lineObject.GetComponent<Image>()).color = Color.green; } else if (owned || line.dependency.isOwned()) { ((Graphic)line.lineObject.GetComponent<Image>()).color = Color.white; } else { ((Graphic)line.lineObject.GetComponent<Image>()).color = Color.gray; } } } public virtual string GetDisplayString() { return "[BLANK]"; } } public class CustomMenuPage : MonoBehaviour { [HideInInspector] public static CustomMenuPage instance; [HideInInspector] public int customPageIndex; [HideInInspector] public MenuPage menuPage; protected virtual void Start() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) instance = this; menuPage = ((Component)this).GetComponent<MenuPage>(); menuPage.menuPageIndex = (MenuPageIndex)customPageIndex; } } public class MenuPageSkills : CustomMenuPage { public int lineThickness = 5; public GameObject lineContainer; public TextMeshProUGUI skillPointsText; [HideInInspector] public static string PREFAB_NAME = "Menu Page Skills"; [HideInInspector] public static int CUSTOM_PAGE_INDEX = 23; private List<FernNode> nodes; private HoverText hoverText; [ContextMenu("Draw Lines")] public void DrawLines() { for (int num = lineContainer.transform.childCount - 1; num >= 0; num--) { Object.DestroyImmediate((Object)(object)((Component)lineContainer.transform.GetChild(num)).gameObject); } List<string> list = new List<string>(); FernNode[] componentsInChildren = ((Component)this).GetComponentsInChildren<FernNode>(); foreach (FernNode fernNode in componentsInChildren) { fernNode.DrawLines(lineContainer, lineThickness); } } protected override void Start() { customPageIndex = CUSTOM_PAGE_INDEX; base.Start(); hoverText = ((Component)this).GetComponentInChildren<HoverText>(); ((TMP_Text)skillPointsText).SetText("Points: " + SkillDataManager.instance.GetLocalSkillPoints(), true); nodes = new List<FernNode>(((Component)this).GetComponentsInChildren<FernNode>()); DrawLines(); List<string> ownedNodes = SkillDataManager.instance.GetPlayerData(PlayerHelper.GetLocalSteamID()).ownedNodes; foreach (string id in ownedNodes) { FernNode fernNode = nodes.Find((FernNode n) => n.nodeID == id); if ((Object)(object)fernNode != (Object)null) { fernNode.setOwned(newOwned: true); } } foreach (FernNode node in nodes) { node.Initialize(); } } private void Update() { if (SemiFunc.InputDown((InputKey)18)) { ButtonEventBack(); } } public void UpdateAllNodes(int pointsCorrection = -1) { ((TMP_Text)skillPointsText).SetText("Points: " + ((pointsCorrection == -1) ? SkillDataManager.instance.GetLocalSkillPoints() : pointsCorrection), true); foreach (FernNode node in nodes) { node.UpdateStatus(); } } public void ButtonEventBack() { MenuManager.instance.PageCloseAll(); MenuManager.instance.PageOpen((MenuPageIndex)1, false); } public void ClearHoverText() { hoverText.Clear(); } public void SetHoverText(string text) { hoverText.SetText(text); } } public class SkillsButton : MonoBehaviour { public void ButtonEventOpenSkills() { Plugin.LogInfo("Opening skills menu"); MenuManager.instance.PageSwap((MenuPageIndex)MenuPageSkills.CUSTOM_PAGE_INDEX); } } } namespace SkillFern.Patches { [HarmonyPatch(typeof(RunManager))] public class RunManagerPatch { [HarmonyPatch("ResetProgress")] [HarmonyPrefix] private static void ResetSkillDataManager() { SkillDataManager.instance = new SkillDataManager(); } [HarmonyPatch("MoonGetAttributes")] [HarmonyPostfix] private static void SkillPointsMessage(ref List<MoonAttribute> __result) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown string text = "Earn " + SkillDataManager.CalculatePointsEarned(1) + " skill points per level."; foreach (MoonAttribute item in __result) { if ((Object)(object)item.LocalizedText == (Object)null) { return; } } __result.Add(new MoonAttribute { text = text, LocalizedText = null }); } } [HarmonyPatch(typeof(ResultScreenUI))] public class ResultScreenUIPatch { [HarmonyPatch("FinishResultScreen")] [HarmonyPostfix] private static void AwardSkillPoints() { if (PlayerHelper.IsHost()) { Plugin.LogInfo("Awarding points. . ."); SkillNetworkSync.AwardSkillPointsForLevel(); } } } [HarmonyPatch(typeof(MenuManager))] public class MenuManagerPatch { [HarmonyPatch("Start")] [HarmonyPostfix] public static void RegisterMenus(MenuManager __instance) { Plugin.LogInfo("Waiting to register menus..."); ((MonoBehaviour)__instance).StartCoroutine(AssetHelper.RegisterMenus(__instance)); } } [HarmonyPatch(typeof(MenuPageEsc))] public class MenuPageEscPatch { [HarmonyPatch("Start")] [HarmonyPostfix] public static void AddSkillsButton(MenuPageEsc __instance) { GameObject val = Object.Instantiate<GameObject>(AssetHelper.GetPrefab("Menu Button - Skills"), ((Component)__instance).transform); } } [HarmonyPatch(typeof(MenuPageLobby))] public class MenuPageLobbyPatch { [HarmonyPatch("Start")] [HarmonyPostfix] public static void Start() { Plugin.LogInfo("LOBBY PAGE START"); SkillNetworkSync.Initialize(); if (!PlayerHelper.IsHost()) { SkillDataManager.instance = new SkillDataManager(); } } [HarmonyPatch("ButtonStart")] [HarmonyPrefix] public static void OnGameStart(bool ___joiningPlayer) { if (!___joiningPlayer) { SkillNetworkSync.SyncAll(); } } } [HarmonyPatch(typeof(ShopManager))] public class ShopManagerPatch { [HarmonyPatch("GetAllItemsFromStatsManager")] [HarmonyPostfix] private static void RemoveItemUpgrades(ref List<Item> ___potentialItemUpgrades) { if (ConfigHelper.ShopUpgradesDisabled()) { ___potentialItemUpgrades.Clear(); } } } [HarmonyPatch(typeof(StatsManager))] public class StatsManagerPatch { [HarmonyPatch("SaveGame")] [HarmonyPostfix] private static void SaveSkills(string fileName) { SaveManager.Save(fileName); } [HarmonyPatch("LoadGame")] [HarmonyPostfix] private static void LoadSkills(string fileName) { SaveManager.Load(fileName); } } }