Decompiled source of SkillFern v1.0.0

SkillFern.dll

Decompiled 9 hours ago
using 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);
		}
	}
}