Decompiled source of SkillFern v1.4.0

SkillFern.dll

Decompiled a month 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 REPOLib.Modules;
using SkillFern.Custom;
using SkillFern.Networking;
using SkillFern.UI;
using SkillFern.Unity;
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 33");
		}

		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 IEnumerator RegisterSkillCubes()
		{
			yield return (object)new WaitUntil((Func<bool>)(() => loaded));
			Plugin.LogInfo("Waiting to register skill cubes...");
			Plugin.LogInfo("Registering skill cubes...");
			ShopHelper.skillCubes = bundle.LoadAllAssets<SkillCube>();
			SkillCube[] skillCubes = ShopHelper.skillCubes;
			foreach (SkillCube cube in skillCubes)
			{
				((Item)cube).maxAmountInShop = 0;
				((Item)cube).physicalItem = false;
				StatsManager.instance.itemDictionary.Add(((Object)cube).name, (Item)(object)cube);
				string targetName = ((PrefabRef<GameObject>)(object)cube.shopPrefab).PrefabName;
				GameObject prefab = GetPrefab(targetName);
				((PrefabRef<GameObject>)(object)((Item)cube).prefab).SetPrefab(prefab, ((Object)prefab).name);
				NetworkPrefabs.RegisterNetworkPrefab(prefab);
				Plugin.LogInfo("Registered network prefab for " + ((Object)prefab).name);
			}
			Plugin.LogInfo(ShopHelper.skillCubes.Length + " skill cubes 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;

		private static ConfigEntry<int> edgeNodeBase;

		private static ConfigEntry<int> edgeNodeIncrement;

		private static ConfigEntry<bool> skillPointsPurchaseable;

		private static ConfigEntry<int> skillCubeCost;

		private static ConfigEntry<int> minShopPointsPerInterval;

		private static ConfigEntry<int> maxShopPointsPerInterval;

		private static ConfigEntry<int> levelsPerInterval;

		private static ConfigEntry<int> shopPointsCap;

		private static ConfigEntry<float> discountPerPoint;

		public static bool ShopUpgradesDisabled()
		{
			return disableShopUpgrades.Value;
		}

		public static bool FairDistribution()
		{
			return fairDistribution.Value;
		}

		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 int EdgeNodeBase()
		{
			return edgeNodeBase.Value;
		}

		public static int EdgeNodeIncrement()
		{
			return edgeNodeIncrement.Value;
		}

		public static bool SkillPointsPurchaseable()
		{
			return skillPointsPurchaseable.Value;
		}

		public static float SkillCubeCost()
		{
			return skillCubeCost.Value;
		}

		public static int MinShopPointsPerInterval()
		{
			return minShopPointsPerInterval.Value;
		}

		public static int MaxShopPointsPerInterval()
		{
			return maxShopPointsPerInterval.Value;
		}

		public static int LevelsPerInterval()
		{
			return levelsPerInterval.Value;
		}

		public static int ShopPointsCap()
		{
			return shopPointsCap.Value;
		}

		public static float DiscountPerPoint()
		{
			return discountPerPoint.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
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Expected O, but got Unknown
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: 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>()));
			edgeNodeBase = config.Bind<int>("Edge Nodes", "EdgeNodeBaseCost", 5, new ConfigDescription("Starting cost for each edge node (default 5)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			edgeNodeIncrement = config.Bind<int>("Edge Nodes", "EdgeNodeIncrement", 1, new ConfigDescription("How much the price of an edge node goes up per purchase (default 1)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			skillPointsPurchaseable = config.Bind<bool>("Shop", "SkillPointsInShop", true, "Whether skill point cubes should appear in the shop (on by default)");
			skillCubeCost = config.Bind<int>("Shop", "CostPerSkillPoint", 25, new ConfigDescription("How much skill point cubes cost per point (in thousands) (default 25)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			minShopPointsPerInterval = config.Bind<int>("Shop - Advanced", "MinCubePointsPerInterval", 1, new ConfigDescription("Minimum number of skill points purchaseable in shop per interval (default 1)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			maxShopPointsPerInterval = config.Bind<int>("Shop - Advanced", "MaxCubePointsPerInterval", 2, new ConfigDescription("Maximum number of skill points purchaseable in shop per interval (default 2)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			levelsPerInterval = config.Bind<int>("Shop - Advanced", "LevelsPerInterval", 2, new ConfigDescription("Number of levels before interval advances (default 2)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			shopPointsCap = config.Bind<int>("Shop - Advanced", "ShopPointsCap", 15, new ConfigDescription("Max number of points ever available in the shop (default 14)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			discountPerPoint = config.Bind<float>("Shop - Advanced", "DiscountPerPoint", 0.02f, new ConfigDescription("Discount applied to bundles per additional point in bundle. (default 0.02)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			disableShopUpgrades = config.Bind<bool>("Miscellaneous", "DisableUpgrades", true, "Whether to disable the vanilla shop upgrades (on by default)");
			fairDistribution = config.Bind<bool>("Miscellaneous", "FairDistribution", true, "Whether to distribute equal skill points to players who joined late or missed levels to ensure they don't fall behind (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);
			Plugin.LogInfo("LOCAL Updating health starting at " + num3);
			if (num != 0)
			{
				CameraGlitch.Instance.PlayShortHeal();
				AccessTools.Field(typeof(PlayerHealth), "health").SetValue(localPlayerAvatar.playerHealth, num3 + num * 20);
			}
		}

		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);
		}
	}
	public class ShopHelper
	{
		private const int MIN_ONES = 2;

		public static SkillCube[] skillCubes;

		private static int[] cubeValues = new int[3] { 1, 3, 5 };

		public static void SpawnSkillCubes(List<Item> potentialItemUpgrades)
		{
			int num = SemiFunc.RunGetLevelsCompleted() / ConfigHelper.LevelsPerInterval() + 1;
			int num2 = Mathf.Min(Random.Range(ConfigHelper.MinShopPointsPerInterval() * num, Mathf.Min(ConfigHelper.MaxShopPointsPerInterval() * num + 1, ConfigHelper.ShopPointsCap())), ConfigHelper.ShopPointsCap());
			int num3 = num2;
			int num4 = 2;
			Plugin.LogInfo("Distributing " + num2 + " points into shop");
			while (num2 > 0)
			{
				Plugin.LogInfo(" - Starting Cycle | totalPoints: " + num2 + " | pointsLeft: " + num3);
				int num5 = 0;
				if (num3 > 10)
				{
					num5 = 2;
				}
				else if (num3 > 4 && num2 >= cubeValues[1])
				{
					num5 = 1;
				}
				int num6 = Random.Range(num5, 3);
				if (num4 > 0)
				{
					num4--;
					num6 = 0;
				}
				else if (cubeValues[num6] <= num2)
				{
					num3 -= cubeValues[num6];
				}
				if (cubeValues[num6] <= num2)
				{
					num2 -= cubeValues[num6];
					Plugin.LogInfo(" - - Adding a " + cubeValues[num6] + "SP cube");
					potentialItemUpgrades.Add((Item)(object)skillCubes[num6]);
				}
			}
		}
	}
}
namespace SkillFern.Unity
{
	[CreateAssetMenu(fileName = "SP Cube", menuName = "SkillFern/SkillCube")]
	public class SkillCube : Item
	{
		[HideInInspector]
		public static int SKILL_CUBE_INDEX = 25;

		public int skillPointValue = 1;

		public PrefabRef shopPrefab;

		private void Reset()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			base.disabled = false;
			base.itemName = "SP Cube ";
			base.itemNameLocalized = null;
			base.description = "";
			base.itemType = (itemType)SKILL_CUBE_INDEX;
			base.emojiIcon = (emojiIcon)5;
			base.itemVolume = (itemVolume)6;
			base.maxAmount = 6;
			base.maxAmountInShop = 6;
			base.maxPurchaseAmount = 999;
			base.maxPurchase = false;
			base.physicalItem = false;
		}
	}
}
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 int careerCountdown = -1;

		public static void Initialize()
		{
			if (!isInitialized && PhotonNetwork.NetworkingClient != null)
			{
				isInitialized = true;
				saveCountdown = -1;
				careerCountdown = -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 steamID = (string)array[1];
			switch ((EVENT_TYPE)array[0])
			{
			case EVENT_TYPE.CONFIG_SYNC:
			{
				int moonSkillPoints = (int)array[2];
				int baseSkillPoints = (int)array[3];
				int baseEdgeNode = (int)array[4];
				int edgeNodeIncrement = (int)array[5];
				SkillDataManager.instance.moonSkillPoints = moonSkillPoints;
				SkillDataManager.instance.baseSkillPoints = baseSkillPoints;
				SkillDataManager.instance.baseEdgeNode = baseEdgeNode;
				SkillDataManager.instance.edgeNodeIncrement = edgeNodeIncrement;
				break;
			}
			case EVENT_TYPE.CAREER_SYNC:
				break;
			case EVENT_TYPE.NODE_PURCHASE:
			{
				string nodeID = (string)array[2];
				bool sync = (bool)array[3];
				SkillDataManager.instance.PurchaseNode(steamID, nodeID, sync);
				break;
			}
			case EVENT_TYPE.SKILL_UPDATE:
			case EVENT_TYPE.SKILL_SYNC:
			{
				string skillName = (string)array[2];
				int newLevel = (int)array[3];
				SkillDataManager.instance.UpdateSkill(steamID, 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(steamID, 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);
					}
				}
				if (careerCountdown > 0 && PlayerHelper.IsHost())
				{
					careerCountdown--;
					if (careerCountdown == 0)
					{
						careerCountdown = -1;
						Plugin.LogInfo("Syncing careers. . .");
						SyncCareer();
					}
				}
				break;
			}
			}
		}

		public static void UpdateSkillPointsForAll(int amount)
		{
			foreach (string allPlayerSteamID in PlayerHelper.GetAllPlayerSteamIDs())
			{
				UpdateSkillPoints(allPlayerSteamID, amount);
			}
		}

		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, bool sync = false)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_0084: 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[4]
			{
				EVENT_TYPE.NODE_PURCHASE,
				steamID,
				nodeID,
				sync
			};
			RaiseEventOptions val = new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			};
			PhotonNetwork.RaiseEvent((byte)187, (object)array, val, SendOptions.SendReliable);
		}

		public static void AwardSkillPointsForLevel()
		{
			if (!GameManager.Multiplayer())
			{
				int amount = SkillDataManager.CalculatePointsEarned();
				SkillDataManager.instance.UpdateSkillPoints(PlayerHelper.GetLocalSteamID(), amount);
				SaveManager.Save(SaveManager.lastFile);
				return;
			}
			saveCountdown = 0;
			foreach (string allPlayerSteamID in PlayerHelper.GetAllPlayerSteamIDs())
			{
				saveCountdown++;
				int amount2 = SkillDataManager.CalculatePointsEarned();
				UpdateSkillPoints(allPlayerSteamID, amount2);
			}
		}

		public static void SyncAll()
		{
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_035f: Expected O, but got Unknown
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			if (!GameManager.Multiplayer() || !PlayerHelper.IsHost())
			{
				return;
			}
			Plugin.LogInfo("Syncing all skill data. . .");
			List<string> list = new List<string>();
			List<string> allPlayerSteamIDs = PlayerHelper.GetAllPlayerSteamIDs();
			careerCountdown = 0;
			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, sync: true);
				}
				Plugin.LogInfo("Syncing skill points");
				UpdateSkillPoints(skillData.steamID, skillData.skillPoints, sync: true);
				careerCountdown++;
				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))
				{
					careerCountdown++;
					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);
					}
					list.Add(item);
				}
			}
			Plugin.LogInfo("Skill data synced!");
			Plugin.LogInfo("Syncing moon phase data. . .");
			object[] array = new object[6]
			{
				EVENT_TYPE.CONFIG_SYNC,
				PlayerHelper.GetLocalSteamID(),
				ConfigHelper.MoonSkillPoints(),
				ConfigHelper.BaseSkillPointsEarned(),
				ConfigHelper.EdgeNodeBase(),
				ConfigHelper.EdgeNodeIncrement()
			};
			RaiseEventOptions val = new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			};
			PhotonNetwork.RaiseEvent((byte)187, (object)array, val, SendOptions.SendReliable);
		}

		public static void SyncCareer()
		{
			Plugin.LogInfo("Career points: " + SkillDataManager.instance.careerPoints);
			if (!ConfigHelper.FairDistribution())
			{
				return;
			}
			Plugin.LogInfo("Reconciling careers");
			foreach (string allPlayerSteamID in PlayerHelper.GetAllPlayerSteamIDs())
			{
				if (!(allPlayerSteamID == PlayerHelper.GetLocalSteamID()))
				{
					SkillData playerData = SkillDataManager.instance.GetPlayerData(allPlayerSteamID);
					Plugin.LogInfo("Reconciling " + allPlayerSteamID);
					Plugin.LogInfo("Points gained: " + playerData.pointsGained);
					int num = SkillDataManager.instance.careerPoints - playerData.pointsGained;
					Plugin.LogInfo("Difference: " + num);
					if (num > 0)
					{
						Plugin.LogInfo("Adjusting " + num + " career points to " + allPlayerSteamID);
						UpdateSkillPoints(allPlayerSteamID, num);
					}
				}
			}
		}
	}
}
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;

		public int baseEdgeNode;

		public int edgeNodeIncrement;

		[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();
			baseEdgeNode = ConfigHelper.EdgeNodeBase();
			edgeNodeIncrement = ConfigHelper.EdgeNodeIncrement();
		}

		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)
				{
					skillData.pointsGained = amount;
				}
				Plugin.LogInfo("!!! CREATED NEW SKILL DATA with " + skillData.pointsGained + " points");
			}
			if (syncing)
			{
				skillData.skillPoints = amount;
			}
			else
			{
				if (amount > 0)
				{
					if (steamID == PlayerHelper.GetLocalSteamID())
					{
						careerPoints += amount;
					}
					skillData.pointsGained += amount;
				}
				skillData.skillPoints += amount;
			}
			if (steamID == PlayerHelper.GetLocalSteamID())
			{
				CameraGlitch.Instance.PlayUpgrade();
				if ((Object)(object)MenuPageSkills.instance != (Object)null)
				{
					MenuPageSkills.instance.UpdateSkillPointsText();
				}
			}
			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, bool sync = 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)
			{
				Plugin.LogInfo("Creating skill data for player " + steamID);
				skillData = new SkillData(steamID);
				skillDatas.Add(skillData);
			}
			if (!sync || !PlayerHelper.IsHost())
			{
				skillData.ownedNodes.Add(nodeID);
			}
			else
			{
				Plugin.LogInfo("SKIPPING NODE ADDING");
			}
		}

		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);
				int playerHealth = StatsManager.instance.GetPlayerHealth(steamID);
				Plugin.LogInfo("Updating health starting at " + playerHealth);
				StatsManager.instance.SetPlayerHealth(steamID, playerHealth + (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;
		}
	}
	[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 = 0;
		}

		[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 EdgeNode : UpgradeNode
	{
		private int timesPurchased;

		public new void Awake()
		{
			base.Awake();
			cost = SkillDataManager.instance.baseEdgeNode;
			timesPurchased = 0;
		}

		public override void setOwned(bool newOwned)
		{
			if (newOwned)
			{
				timesPurchased++;
				cost = SkillDataManager.instance.baseEdgeNode + SkillDataManager.instance.edgeNodeIncrement * timesPurchased;
			}
		}

		public override void UpdateStatus()
		{
			base.UpdateStatus();
			if (!CanPurchase())
			{
				((Behaviour)costText).enabled = false;
			}
			else
			{
				((Behaviour)costText).enabled = true;
			}
		}
	}
	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[] targetImages;

		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 virtual void setOwned(bool newOwned)
		{
			owned = newOwned;
		}

		public void DrawLines(GameObject lineContainer, int lineThickness)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			if (Application.isEditor)
			{
				Debug.Log((object)((Object)((Component)this).gameObject).name);
				nodeID = Guid.NewGuid().ToString();
				List<Image> list = ((Component)((Component)this).transform).GetComponentsInChildren<Image>().ToList();
				list.RemoveAt(0);
				targetImages = list.ToArray();
				((TMP_Text)((Component)((Component)this).transform.GetChild(1)).GetComponent<TextMeshProUGUI>()).SetText("", true);
				costText = ((Component)((Component)this).transform.GetChild(2)).GetComponent<TextMeshProUGUI>();
				((TMP_Text)costText).SetText(cost.ToString() ?? "", true);
			}
			if (dependencies != null && dependencies.Length != 0)
			{
				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 virtual void Reset()
		{
			cost = 1;
			nodeID = Guid.NewGuid().ToString();
			List<Image> list = ((Component)((Component)this).transform).GetComponentsInChildren<Image>().ToList();
			list.RemoveAt(0);
			targetImages = list.ToArray();
			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 virtual 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);
				setOwned(newOwned: 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 virtual void UpdateStatus()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: 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;
			}
			Image[] array = targetImages;
			foreach (Image val in array)
			{
				if (owned)
				{
					((Graphic)val).color = Color.green;
				}
				else if (CanPurchase())
				{
					((Graphic)val).color = Color.white;
				}
				else
				{
					((Graphic)val).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 new static MenuPageSkills instance;

		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()
		{
			instance = this;
			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)
		{
			UpdateSkillPointsText(pointsCorrection);
			foreach (FernNode node in nodes)
			{
				node.UpdateStatus();
			}
		}

		public void UpdateSkillPointsText(int pointsCorrection = -1)
		{
			((TMP_Text)skillPointsText).SetText("Points: " + ((pointsCorrection == -1) ? SkillDataManager.instance.GetLocalSkillPoints() : pointsCorrection), true);
		}

		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(ExtractionPoint))]
	public class ExtractionPointPatch
	{
		[HarmonyPatch("DestroyAllPhysObjectsInShoppingList")]
		[HarmonyPrefix]
		public static void ProcessSkillCubes(ExtractionPoint __instance)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Invalid comparison between Unknown and I4
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			List<ItemAttributes> list = (List<ItemAttributes>)AccessTools.Field(typeof(ShopManager), "shoppingList").GetValue(ShopManager.instance);
			Plugin.LogInfo("Checking " + list.Count + " items");
			foreach (ItemAttributes item in list)
			{
				Plugin.LogInfo("Item is of type " + ((object)Unsafe.As<itemType, itemType>(ref item.item.itemType)/*cast due to .constrained prefix*/).ToString());
				if ((int)item.item.itemType == SkillCube.SKILL_CUBE_INDEX)
				{
					SkillNetworkSync.UpdateSkillPointsForAll(((SkillCube)(object)item.item).skillPointValue);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemAttributes))]
	public class ItemAttributesPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		public static void Initialize(ref float ___itemValueMin, ref float ___itemValueMax, Item ___item)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if ((int)___item.itemType == SkillCube.SKILL_CUBE_INDEX)
			{
				___itemValueMin = ConfigHelper.SkillCubeCost() * (float)((SkillCube)(object)___item).skillPointValue;
				___itemValueMax = ___itemValueMin;
			}
		}

		[HarmonyPatch("GetValue")]
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		public static bool SkillCubePrice(ref int ___value, PhotonView ___photonView, Item ___item)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			if (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient)
			{
				return true;
			}
			if ((int)___item.itemType != SkillCube.SKILL_CUBE_INDEX)
			{
				return true;
			}
			___value = (int)(ConfigHelper.SkillCubeCost() * (float)((SkillCube)(object)___item).skillPointValue);
			___value -= (int)((float)___value * ConfigHelper.DiscountPerPoint() * (float)(((SkillCube)(object)___item).skillPointValue - 1));
			if (GameManager.Multiplayer() && ___photonView.ViewID != 0)
			{
				___photonView.RPC("GetValueRPC", (RpcTarget)1, new object[1] { ___value });
			}
			return false;
		}
	}
	[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 (!GameManager.Multiplayer())
			{
				Plugin.LogInfo("Awarding points. . .");
				SkillNetworkSync.AwardSkillPointsForLevel();
			}
			else 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();
			}
			if (ConfigHelper.SkillPointsPurchaseable())
			{
				ShopHelper.SpawnSkillCubes(___potentialItemUpgrades);
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager))]
	public class StatsManagerPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		public static void RegisterSkillCubes(StatsManager __instance)
		{
			Plugin.LogInfo("Waiting to register skill cubes...");
			((MonoBehaviour)__instance).StartCoroutine(AssetHelper.RegisterSkillCubes());
		}

		[HarmonyPatch("ItemPurchase")]
		[HarmonyPrefix]
		public static bool ItemPurchase(string itemName)
		{
			Plugin.LogInfo("Purchasing item: " + itemName);
			Plugin.LogInfo("Physical: " + StatsManager.instance.itemDictionary[itemName].physicalItem);
			if (itemName.Substring(0, 7) == "SP Cube")
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch("SaveGame")]
		[HarmonyPostfix]
		private static void SaveSkills(string fileName)
		{
			SaveManager.Save(fileName);
		}

		[HarmonyPatch("LoadGame")]
		[HarmonyPostfix]
		private static void LoadSkills(string fileName)
		{
			SaveManager.Load(fileName);
		}
	}
}