Decompiled source of Leveling v0.1.7

plugins/com.atomic.leveling.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Leveling.Misc;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
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: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.atomic.leveling")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.1.6.0")]
[assembly: AssemblyInformationalVersion("0.1.6+c766263ec5361d8fb5d5c0247cc71ea1130a9f58")]
[assembly: AssemblyProduct("com.atomic.leveling")]
[assembly: AssemblyTitle("Leveling")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.6.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Leveling
{
	public static class LevelingAPI
	{
		private static int _level = 1;

		private static float _experience = 0f;

		private static Dictionary<string, bool> _oneUseItems = new Dictionary<string, bool>();

		private static readonly Dictionary<Player, int> PlayerLevels = new Dictionary<Player, int>();

		private static float ExperienceToNextLevel => _level * 100;

		public static int Level
		{
			get
			{
				return _level;
			}
			private set
			{
				if (_level != value)
				{
					_level = value;
					Netcode.Instance?.SendLevelUpdateRPC(value);
					SaveManager.SaveData(_level, _experience, _oneUseItems);
					LevelingAPI.OnLocalPlayerLevelChanged?.Invoke(value);
				}
			}
		}

		public static float Experience
		{
			get
			{
				return _experience;
			}
			private set
			{
				_experience = value;
				CheckLevelUp();
				SaveManager.SaveData(_level, _experience, _oneUseItems);
			}
		}

		public static Dictionary<string, bool> OneUseItems
		{
			get
			{
				return _oneUseItems;
			}
			private set
			{
				_oneUseItems = value;
				SaveManager.SaveData(_level, _experience, _oneUseItems);
			}
		}

		public static event Action<int> OnLocalPlayerLevelChanged;

		public static event Action<float> OnLocalPlayerExperienceChanged;

		public static event Action<Player, int> OnRemotePlayerLevelChanged;

		private static void CheckLevelUp()
		{
			while (Experience >= ExperienceToNextLevel)
			{
				Experience -= ExperienceToNextLevel;
				Level++;
				Plugin.Log.LogInfo((object)$"Player Leveled Up! New Level: {Level}");
			}
		}

		private static float CalculateMultiplier()
		{
			int currentAscent = Ascents.currentAscent;
			float num = 1f;
			if (currentAscent < 0)
			{
				return num = 0.8f;
			}
			return num = 1f + (float)currentAscent * 0.1f;
		}

		public static void AddExperience(float amount, bool applyAscentMultiplier = true)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (amount > 0f && amount <= 2000f)
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).name.ToLower().Contains("level"))
				{
					float num = (applyAscentMultiplier ? CalculateMultiplier() : 1f);
					amount *= num;
					Experience += amount;
					LevelingAPI.OnLocalPlayerExperienceChanged?.Invoke(amount);
					Plugin.Log.LogInfo((object)$"Gained {amount} XP. Current XP: {Experience}/{ExperienceToNextLevel}");
				}
			}
		}

		public static void AddOneUseItem(string itemName)
		{
			if (!string.IsNullOrEmpty(itemName) && !_oneUseItems.ContainsKey(itemName))
			{
				_oneUseItems.Add(itemName, value: false);
				SaveManager.SaveData(_level, _experience, _oneUseItems);
			}
		}

		public static void SetOneUseItem(string itemName)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(itemName) && _oneUseItems.ContainsKey(itemName))
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).name.ToLower().Contains("level"))
				{
					_oneUseItems[itemName] = true;
					SaveManager.SaveData(_level, _experience, _oneUseItems);
					Plugin.Log.LogMessage((object)("Set " + itemName + " to true for being a one use item."));
				}
			}
		}

		public static void LoadLocalPlayerStats(PlayerSaveData data)
		{
			_level = data.Level;
			_experience = data.Experience;
			_oneUseItems = data.OneUseItems;
			Dictionary<string, bool> defaultOneUseItems = SaveManager.GetDefaultOneUseItems();
			foreach (KeyValuePair<string, bool> item in defaultOneUseItems)
			{
				if (!_oneUseItems.ContainsKey(item.Key))
				{
					_oneUseItems.Add(item.Key, item.Value);
					Plugin.Log.LogMessage((object)$"DefaultItems was missing {item}, this has now been added.");
				}
			}
			SaveManager.SaveData(_level, _experience, _oneUseItems);
			Plugin.Log.LogInfo((object)$"Local player stats initialized to Lvl: {_level}, Exp: {_experience}");
		}

		public static void SetRemotePlayerLevel(Player player, int level)
		{
			if (!PlayerLevels.TryGetValue(player, out var value) || value != level)
			{
				PlayerLevels[player] = level;
				Plugin.Log.LogInfo((object)$"Internal level set for {player.NickName}: Lvl {level}");
				LevelingAPI.OnRemotePlayerLevelChanged?.Invoke(player, level);
			}
		}

		public static int GetPlayerLevel(Player player)
		{
			if (player.IsLocal)
			{
				return Level;
			}
			if (PlayerLevels.TryGetValue(player, out var value))
			{
				return value;
			}
			return 1;
		}
	}
	[BepInPlugin("com.atomic.leveling", "Leveling", "0.1.6")]
	public class Plugin : BaseUnityPlugin
	{
		private Harmony harmony = null;

		public static ConfigEntry<bool> automaticBackups;

		public static ConfigEntry<int> backupsBeforeDeletion;

		public static ConfigEntry<string> backupToLoad;

		public static float XPGainedThisRun;

		public const string Id = "com.atomic.leveling";

		internal static ManualLogSource Log { get; private set; }

		public static string Name => "Leveling";

		public static string Version => "0.1.6";

		private void Awake()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)("Plugin " + Name + " is loaded!"));
			automaticBackups = ((BaseUnityPlugin)this).Config.Bind<bool>("Backups", "Automatic Backups", true, "Creates backups of your save file automatically on game startup");
			backupsBeforeDeletion = ((BaseUnityPlugin)this).Config.Bind<int>("Backups", "Backups Before Deletion", 6, "How many backups can be created before it starts deleting old backups.");
			string[] backupDataForConfig = SaveManager.GetBackupDataForConfig();
			List<string> list = new List<string> { "Current Save" };
			list.AddRange(backupDataForConfig);
			ConfigDescription val = new ConfigDescription("Select a backup to load. The selection will replace your main save file on game start. Leave as 'Current Save' to skip loading a backup. Game restart is required after selection.", (AcceptableValueBase)(object)new AcceptableValueList<string>(list.ToArray()), Array.Empty<object>());
			backupToLoad = ((BaseUnityPlugin)this).Config.Bind<string>("Backups", "Backup To Load", "Current Save", val);
			if (automaticBackups.Value)
			{
				SaveManager.CreateBackup(backupsBeforeDeletion.Value);
			}
			string value = backupToLoad.Value;
			if (!value.Equals("Current Save", StringComparison.OrdinalIgnoreCase))
			{
				string text = ParseLabelToFilename(value);
				Log.LogWarning((object)("Attempting to load selected backup (Label: " + value + ", File: " + text + ")"));
				if (SaveManager.LoadBackup(text))
				{
					backupToLoad.Value = "Current Save";
					((BaseUnityPlugin)this).Config.Save();
				}
			}
			Netcode.EnsureInitialized();
			PlayerSaveData data = SaveManager.LoadData();
			LevelingAPI.LoadLocalPlayerStats(data);
			LevelingAPI.OnRemotePlayerLevelChanged += HandleRemotePlayerLevelChange;
			LevelingAPI.OnLocalPlayerExperienceChanged += ExperienceGain;
			LevelingAPI.OnLocalPlayerLevelChanged += LevelGain;
			harmony = new Harmony("com.atomic.leveling");
			harmony.PatchAll();
		}

		private string ParseLabelToFilename(string label)
		{
			try
			{
				int num = label.IndexOf("Level: ") + "Level: ".Length;
				int num2 = label.IndexOf(" || Experience:");
				if (num2 == -1)
				{
					num2 = label.IndexOf(" Experience:");
				}
				if (num < "Level: ".Length || num2 == -1 || num2 <= num)
				{
					Log.LogError((object)("Failed to find Level component in label: " + label));
					return "Current Save";
				}
				string arg = label.Substring(num, num2 - num).Trim();
				int startIndex = label.IndexOf("Experience: ") + "Experience: ".Length;
				string text = label.Substring(startIndex).Trim();
				if (!float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
				{
					Log.LogError((object)("Failed to parse experience string: " + text + ". Defaulting to Current Save."));
					return "Current Save";
				}
				int num3 = (int)Math.Floor(result);
				int num4 = (int)((result - (float)num3) * 1000f);
				return $"L{arg}_E{num3}_{num4:D3}.backup";
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to parse backup label '" + label + "' into filename. Error: " + ex.Message));
				return "Current Save";
			}
		}

		private void HandleRemotePlayerLevelChange(Player player, int newLevel)
		{
			if (player != null && !player.IsLocal)
			{
				string arg = Patches.RemoveLevelTag(player.NickName);
				player.NickName = $"{arg} [{newLevel}]";
				Log.LogInfo((object)("Updated remote player name: " + player.NickName));
			}
		}

		private void ExperienceGain(float newExp)
		{
			Log.LogInfo((object)"Trying to create ui.");
			CreateExperienceGUI(newExp, out GameObject _, xpOrLevel: false);
		}

		private void LevelGain(int newLevel)
		{
			Log.LogInfo((object)"Trying to create ui.");
			CreateExperienceGUI(0f, out GameObject _, xpOrLevel: true);
		}

		private void CreateExperienceGUI(float xpGain, out GameObject expTextObj, bool xpOrLevel)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: 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_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI heroDayText = GUIManager.instance.heroDayText;
			if ((Object)(object)heroDayText == (Object)null)
			{
				Log.LogError((object)"Could not find HeroDayText! XP UI cannot be created.");
				expTextObj = null;
				return;
			}
			GameObject val = new GameObject("XPGainCanvas");
			val.transform.SetParent(((Component)GUIManager.instance).transform, false);
			Canvas val2 = val.AddComponent<Canvas>();
			val2.renderMode = (RenderMode)0;
			val.AddComponent<CanvasScaler>();
			val.AddComponent<GraphicRaycaster>();
			expTextObj = new GameObject("ExperienceText");
			expTextObj.transform.SetParent(val.transform, false);
			expTextObj.AddComponent<CanvasRenderer>();
			TextMeshProUGUI val3 = expTextObj.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val3).font = ((TMP_Text)heroDayText).font;
			((Graphic)val3).color = ((Graphic)heroDayText).color;
			((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val3).fontSize = ((TMP_Text)heroDayText).fontSize / 2.5f;
			((TMP_Text)val3).outlineWidth = 0.1f;
			((TMP_Text)val3).outlineColor = Color32.op_Implicit(((Graphic)heroDayText).color - new Color(0.5f, 0.5f, 0.5f, 0f));
			XPAnimator xPAnimator = expTextObj.AddComponent<XPAnimator>();
			xPAnimator.text = val3;
			if (xpOrLevel)
			{
				((TMP_Text)val3).text = $"LEVEL UP! LVL {LevelingAPI.Level}";
				xPAnimator.isLevelUp = true;
			}
			else
			{
				XPGainedThisRun += xpGain;
				((TMP_Text)val3).text = $"+{Math.Round(xpGain, 2)} XP";
			}
			RectTransform component = expTextObj.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.sizeDelta = new Vector2(200f, 80f);
			if (xpOrLevel)
			{
				component.anchoredPosition = new Vector2(100f, -10f);
			}
			else
			{
				component.anchoredPosition = new Vector2(100f, 5f);
			}
			Log.LogInfo((object)$"Created XP Gain GUI: +{xpGain} XP");
		}
	}
	[HarmonyPatch]
	public class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerHandler), "RegisterCharacter")]
		public static void Character_Reg_Postfix(Character character)
		{
			if (!Object.op_Implicit((Object)(object)character) || !Object.op_Implicit((Object)(object)((MonoBehaviourPun)character).photonView))
			{
				return;
			}
			Player owner = ((MonoBehaviourPun)character).photonView.Owner;
			if (owner == null)
			{
				return;
			}
			int playerLevel = LevelingAPI.GetPlayerLevel(owner);
			string text = RemoveLevelTag(owner.NickName);
			if (!owner.IsLocal)
			{
				return;
			}
			Netcode.Instance?.SendLevelUpdateRPC(playerLevel);
			Netcode.Instance?.RequestAllPlayerLevels();
			Player[] playerList = PhotonNetwork.PlayerList;
			foreach (Player val in playerList)
			{
				if (!val.IsLocal)
				{
					int playerLevel2 = LevelingAPI.GetPlayerLevel(val);
					string arg = RemoveLevelTag(val.NickName);
					val.NickName = $"{arg} [{playerLevel2}]";
				}
			}
			Plugin.Log.LogInfo((object)$"Character Registered (Local): {owner.NickName} is Lvl {playerLevel}. Broadcasting and Requesting Sync.");
		}

		public static string RemoveLevelTag(string nickname)
		{
			int num = nickname.LastIndexOf('[');
			if (num > 0 && nickname.EndsWith("]"))
			{
				string text = nickname.Substring(num);
				return nickname.Substring(0, num).Trim();
			}
			return nickname;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PauseMenuAccoladesPage), "Start")]
		public static void Accolades_OnPageEnter_Postfix(PauseMenuAccoladesPage __instance)
		{
			//IL_00c2: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = ((Component)__instance).gameObject;
			if (Object.op_Implicit((Object)(object)gameObject.transform.Find("Level")))
			{
				((TMP_Text)((Component)gameObject.transform.Find("Level")).GetComponent<TextMeshProUGUI>()).text = $"Level: {LevelingAPI.Level}  XP: {LevelingAPI.Experience}/{LevelingAPI.Level * 100}";
				return;
			}
			GameObject gameObject2 = ((Component)gameObject.transform.Find("Peaks")).gameObject;
			GameObject levelUI = Object.Instantiate<GameObject>(gameObject2, gameObject2.transform.parent);
			((Object)levelUI).name = "Level";
			levelUI.transform.localPosition = gameObject2.transform.localPosition + new Vector3(0f, -35f, 0f);
			((TMP_Text)levelUI.GetComponent<TextMeshProUGUI>()).text = $"Level: {LevelingAPI.Level}  XP: {Math.Round(LevelingAPI.Experience, 2)}/{LevelingAPI.Level * 100}";
			LevelingAPI.OnLocalPlayerExperienceChanged += delegate
			{
				((TMP_Text)levelUI.GetComponent<TextMeshProUGUI>()).text = $"Level: {LevelingAPI.Level}  XP: {Math.Round(LevelingAPI.Experience, 2)}/{LevelingAPI.Level * 100}";
			};
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UIPlayerNames), "Init")]
		public static void UIPlayerNames_Init_Postfix(UIPlayerNames __instance)
		{
			PlayerName[] playerNameText = __instance.playerNameText;
			foreach (PlayerName val in playerNameText)
			{
				TextMeshProUGUI text = val.text;
				if (!((Object)(object)text != (Object)null) || !((Object)(object)val.characterInteractable != (Object)null) || !((Object)(object)val.characterInteractable.character != (Object)null) || !((Object)(object)((MonoBehaviourPun)val.characterInteractable.character).photonView != (Object)null))
				{
					continue;
				}
				Player player = ((MonoBehaviourPun)val.characterInteractable.character).photonView.Owner;
				int playerLevel = LevelingAPI.GetPlayerLevel(player);
				string arg = RemoveLevelTag(player.NickName);
				((TMP_Text)text).text = $"{arg} [{playerLevel}]";
				LevelingAPI.OnRemotePlayerLevelChanged += delegate(Player changedPlayer, int newLevel)
				{
					if (!((Object)(object)text == (Object)null) && changedPlayer == player)
					{
						string arg2 = RemoveLevelTag(changedPlayer.NickName);
						((TMP_Text)text).text = $"{arg2} [{newLevel}]";
					}
				};
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(RunManager), "StartRun")]
		public static void RunManager_StartRun_Postfix()
		{
			Plugin.XPGainedThisRun = 0f;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EndScreen), "Start")]
		public static void EndScreen_Start_Postfix(EndScreen __instance)
		{
			Transform val = ((Component)__instance).transform.Find("Panel/Margin/Layout/Window_BADGES/Title (1)");
			TextMeshProUGUI component = ((Component)val).gameObject.GetComponent<TextMeshProUGUI>();
			if (!((TMP_Text)component).text.Contains("(XP GAINED:"))
			{
				((TMP_Text)component).text = $"{((TMP_Text)component).text} (XP GAINED: +{Plugin.XPGainedThisRun})";
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(BoardingPass), "UpdateAscent")]
		public static void BoardingPass_UpdateAscent_Postfix(BoardingPass __instance)
		{
			int ascentIndex = __instance._ascentIndex;
			float num = 1f;
			num = ((ascentIndex >= 0) ? (1f + (float)ascentIndex * 0.1f) : 0.8f);
			GameObject gameObject = ((Component)((Component)__instance).transform.Find("BoardingPass/Panel/Ascent/Title")).gameObject;
			TextMeshProUGUI component = gameObject.GetComponent<TextMeshProUGUI>();
			((TMP_Text)component).text = $"{((TMP_Text)component).text} (XP: {num}X)";
		}
	}
}
namespace Leveling.Misc
{
	public class Netcode : MonoBehaviourPun
	{
		private static Netcode _instance;

		private PhotonView _photonView = null;

		public static Netcode Instance
		{
			get
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Expected O, but got Unknown
				if ((Object)(object)_instance == (Object)null)
				{
					GameObject val = new GameObject("LevelingNetcode");
					_instance = val.AddComponent<Netcode>();
					Object.DontDestroyOnLoad((Object)(object)val);
				}
				return _instance;
			}
		}

		public static void EnsureInitialized()
		{
			_ = Instance;
		}

		private void Awake()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			_photonView = ((Component)this).GetComponent<PhotonView>();
			if ((Object)(object)_photonView == (Object)null)
			{
				_photonView = ((Component)this).gameObject.AddComponent<PhotonView>();
				_photonView.ViewID = 8437;
			}
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		public void SendLevelUpdateRPC(int newLevel)
		{
			if (PhotonNetwork.InRoom)
			{
				_photonView.RPC("RPC_ReceiveLevelUpdate", (RpcTarget)0, new object[1] { newLevel });
				Plugin.Log.LogInfo((object)$"Broadcasted level update: Lvl {newLevel}");
			}
		}

		public void RequestAllPlayerLevels()
		{
			if (PhotonNetwork.InRoom)
			{
				_photonView.RPC("RPC_RequestPlayerLevels", (RpcTarget)2, Array.Empty<object>());
				Plugin.Log.LogInfo((object)"Requested all existing player levels from Master Client.");
			}
		}

		[PunRPC]
		public void RPC_RequestPlayerLevels(PhotonMessageInfo info)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.IsMasterClient)
			{
				_photonView.RPC("RPC_RespondWithMyLevel", info.Sender, new object[1] { LevelingAPI.Level });
				Plugin.Log.LogInfo((object)$"Master Client responding to {info.Sender.NickName}'s level request with own level: Lvl {LevelingAPI.Level}");
			}
		}

		[PunRPC]
		public void RPC_RespondWithMyLevel(int level, PhotonMessageInfo info)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Player sender = info.Sender;
			if (sender != null && !sender.IsLocal)
			{
				LevelingAPI.SetRemotePlayerLevel(sender, level);
				Plugin.Log.LogInfo((object)$"Received level sync from {sender.NickName}: Lvl {level}");
			}
		}

		[PunRPC]
		public void RPC_ReceiveLevelUpdate(int level, PhotonMessageInfo info)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Player sender = info.Sender;
			if (sender != null && !sender.IsLocal)
			{
				LevelingAPI.SetRemotePlayerLevel(sender, level);
				Plugin.Log.LogInfo((object)$"Received level update from {sender.NickName}: Lvl {level}");
			}
		}
	}
	public class PlayerSaveData
	{
		public int Level { get; set; } = 1;


		public float Experience { get; set; } = 0f;


		public Dictionary<string, bool> OneUseItems { get; set; } = new Dictionary<string, bool> { { "BUGLEBBNO", false } };

	}
	public static class SaveManager
	{
		private const string SAVE_FILE_NAME = "player_stats.sav";

		private const string BACKUP_FILE_EXTENSION = ".backup";

		private static readonly byte[] MagicHeader = new byte[5] { 254, 202, 222, 175, 1 };

		private static readonly byte[] MagicFooter = new byte[5] { 2, 239, 205, 186, 253 };

		private static string BaseDirPath
		{
			get
			{
				string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
				string text = Path.Combine(folderPath, "LandCrab", "PEAK", "PEAKLeveling");
				if (!Directory.Exists(text))
				{
					Directory.CreateDirectory(text);
				}
				return text;
			}
		}

		private static string SaveFilePath => Path.Combine(BaseDirPath, "player_stats.sav");

		public static Dictionary<string, bool> GetDefaultOneUseItems()
		{
			return new Dictionary<string, bool>(new PlayerSaveData().OneUseItems);
		}

		private static string GetSaveFilePath(string fileName)
		{
			return Path.Combine(BaseDirPath, fileName);
		}

		public static void SaveData(int level, float experience, Dictionary<string, bool> oneUseItems)
		{
			try
			{
				PlayerSaveData playerSaveData = new PlayerSaveData
				{
					Level = level,
					Experience = experience,
					OneUseItems = oneUseItems
				};
				string s = JsonConvert.SerializeObject((object)playerSaveData);
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				byte[] array = new byte[MagicHeader.Length + bytes.Length + MagicFooter.Length];
				Buffer.BlockCopy(MagicHeader, 0, array, 0, MagicHeader.Length);
				Buffer.BlockCopy(bytes, 0, array, MagicHeader.Length, bytes.Length);
				Buffer.BlockCopy(MagicFooter, 0, array, MagicHeader.Length + bytes.Length, MagicFooter.Length);
				string contents = Convert.ToBase64String(array);
				File.WriteAllText(SaveFilePath, contents);
				Plugin.Log.LogInfo((object)("Saved data (obfuscated) to: " + SaveFilePath));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to save data: " + ex.Message));
			}
		}

		public static PlayerSaveData LoadData()
		{
			if (!File.Exists(SaveFilePath))
			{
				Plugin.Log.LogInfo((object)"Save file not found. Loading default new game data (Lvl 1, Exp 0).");
				return new PlayerSaveData();
			}
			try
			{
				string s = File.ReadAllText(SaveFilePath);
				byte[] array = Convert.FromBase64String(s);
				int num = MagicHeader.Length + MagicFooter.Length;
				if (array.Length < num)
				{
					throw new InvalidDataException("Save file too short after decoding. File is corrupt or invalid.");
				}
				for (int i = 0; i < MagicHeader.Length; i++)
				{
					if (array[i] != MagicHeader[i])
					{
						throw new InvalidDataException("Magic Header mismatch. File is corrupt or modified.");
					}
				}
				int num2 = array.Length - MagicFooter.Length;
				for (int j = 0; j < MagicFooter.Length; j++)
				{
					if (array[num2 + j] != MagicFooter[j])
					{
						throw new InvalidDataException("Magic Footer mismatch. File is corrupt or modified.");
					}
				}
				int num3 = array.Length - num;
				byte[] array2 = new byte[num3];
				Buffer.BlockCopy(array, MagicHeader.Length, array2, 0, num3);
				string @string = Encoding.UTF8.GetString(array2);
				PlayerSaveData playerSaveData = JsonConvert.DeserializeObject<PlayerSaveData>(@string);
				Plugin.Log.LogInfo((object)$"Loaded data (obfuscated) from: {SaveFilePath} (Lvl {playerSaveData.Level}, Exp {playerSaveData.Experience})");
				return playerSaveData;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Error loading or deserializing save file. Possible corruption/tampering. Loading default new game data. Error: " + ex.Message));
				return new PlayerSaveData();
			}
		}

		private static string GetBackupFileName(PlayerSaveData data)
		{
			int num = (int)Math.Floor(data.Experience);
			int num2 = (int)((data.Experience - (float)num) * 1000f);
			return string.Format("L{0}_E{1}_{2:D3}{3}", data.Level, num, num2, ".backup");
		}

		private static string GetBackupFileBasePattern(PlayerSaveData data)
		{
			int num = (int)Math.Floor(data.Experience);
			return $"L{data.Level}_E{num}_";
		}

		private static PlayerSaveData DecodeBackup(string filePath)
		{
			try
			{
				string s = File.ReadAllText(filePath);
				byte[] array = Convert.FromBase64String(s);
				int num = MagicHeader.Length + MagicFooter.Length;
				if (array.Length < num)
				{
					throw new InvalidDataException("Backup too short.");
				}
				for (int i = 0; i < MagicHeader.Length; i++)
				{
					if (array[i] != MagicHeader[i])
					{
						throw new InvalidDataException("Backup Header mismatch.");
					}
				}
				int num2 = array.Length - MagicFooter.Length;
				for (int j = 0; j < MagicFooter.Length; j++)
				{
					if (array[num2 + j] != MagicFooter[j])
					{
						throw new InvalidDataException("Backup Footer mismatch.");
					}
				}
				int num3 = array.Length - num;
				byte[] array2 = new byte[num3];
				Buffer.BlockCopy(array, MagicHeader.Length, array2, 0, num3);
				string @string = Encoding.UTF8.GetString(array2);
				return JsonConvert.DeserializeObject<PlayerSaveData>(@string);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not decode backup file: " + Path.GetFileName(filePath) + ". Error: " + ex.Message));
				return null;
			}
		}

		public static void CreateBackup(int maxBackups)
		{
			if (!File.Exists(SaveFilePath))
			{
				Plugin.Log.LogInfo((object)"Cannot create backup: No main save file exists yet.");
				return;
			}
			PlayerSaveData playerSaveData = LoadData();
			if (playerSaveData == null)
			{
				Plugin.Log.LogError((object)"Failed to load current save data for backup check.");
				return;
			}
			string backupFileName = GetBackupFileName(playerSaveData);
			string baseMatchPattern = GetBackupFileBasePattern(playerSaveData);
			List<FileInfo> source = GetBackupFilesInternal().ToList();
			List<FileInfo> list = source.Where((FileInfo f) => f.Name.StartsWith(baseMatchPattern, StringComparison.OrdinalIgnoreCase)).ToList();
			bool flag = false;
			foreach (FileInfo item in list)
			{
				PlayerSaveData existingData = DecodeBackup(item.FullName);
				if (existingData != null)
				{
					bool flag2 = playerSaveData.Level == existingData.Level;
					bool flag3 = playerSaveData.Experience == existingData.Experience;
					bool flag4 = playerSaveData.OneUseItems.Count == existingData.OneUseItems.Count && playerSaveData.OneUseItems.All<KeyValuePair<string, bool>>((KeyValuePair<string, bool> pair) => existingData.OneUseItems.ContainsKey(pair.Key) && existingData.OneUseItems[pair.Key] == pair.Value);
					if (flag2 && flag3 && flag4)
					{
						Plugin.Log.LogInfo((object)"Skipping backup: Current save data is identical to an existing backup.");
						flag = true;
						break;
					}
				}
			}
			if (flag)
			{
				return;
			}
			foreach (FileInfo item2 in list)
			{
				try
				{
					File.Delete(item2.FullName);
					Plugin.Log.LogInfo((object)("Deleted old backup for replacement: " + item2.Name));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Failed to delete old backup " + item2.Name + " for replacement: " + ex.Message));
				}
			}
			string destFileName = Path.Combine(BaseDirPath, backupFileName);
			try
			{
				File.Copy(SaveFilePath, destFileName, overwrite: true);
				Plugin.Log.LogInfo((object)("Created new backup: " + backupFileName));
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)("Failed to create backup: " + ex2.Message));
			}
			CleanupOldBackups(maxBackups);
		}

		private static void CleanupOldBackups(int maxBackups)
		{
			if (maxBackups <= 0)
			{
				return;
			}
			IEnumerable<FileInfo> backupFilesInternal = GetBackupFilesInternal();
			List<FileInfo> list = backupFilesInternal.OrderBy((FileInfo f) => f.CreationTime).Skip(maxBackups).ToList();
			foreach (FileInfo item in list)
			{
				try
				{
					File.Delete(item.FullName);
					Plugin.Log.LogInfo((object)("Deleted old backup: " + item.Name));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Failed to delete old backup " + item.Name + ": " + ex.Message));
				}
			}
		}

		private static IEnumerable<FileInfo> GetBackupFilesInternal()
		{
			DirectoryInfo directoryInfo = new DirectoryInfo(BaseDirPath);
			return from f in directoryInfo.GetFiles("*.backup")
				orderby f.CreationTime descending
				select f;
		}

		public static string[] GetBackupDataForConfig()
		{
			List<string> list = new List<string>();
			List<FileInfo> list2 = GetBackupFilesInternal().ToList();
			if (!list2.Any())
			{
				return list.ToArray();
			}
			int num = 1;
			foreach (FileInfo item2 in list2)
			{
				PlayerSaveData playerSaveData = DecodeBackup(item2.FullName);
				if (playerSaveData != null)
				{
					string arg = $"Level: {playerSaveData.Level} || Experience: {Math.Round(playerSaveData.Experience, 3)}";
					string item = string.Format("{0} backup{1} ago || {2}", num, (num > 1) ? "s" : "", arg);
					list.Add(item);
					num++;
				}
			}
			return list.ToArray();
		}

		public static bool LoadBackup(string backupFileName)
		{
			if (backupFileName.Equals("Current Save", StringComparison.OrdinalIgnoreCase))
			{
				Plugin.Log.LogInfo((object)"Attempted to load 'Current Save' backup. No action taken.");
				return true;
			}
			string saveFilePath = GetSaveFilePath(backupFileName);
			if (!File.Exists(saveFilePath))
			{
				Plugin.Log.LogError((object)("Backup file not found at path: " + saveFilePath));
				return false;
			}
			try
			{
				File.Copy(saveFilePath, SaveFilePath, overwrite: true);
				Plugin.Log.LogInfo((object)("Successfully loaded backup '" + backupFileName + "' over main save file."));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to copy backup file: " + ex.Message));
				return false;
			}
		}
	}
	public class XPAnimator : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <Animate>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public XPAnimator <>4__this;

			private Color <c>5__1;

			private RectTransform <rt>5__2;

			private Vector2 <startPos>5__3;

			private Vector2 <endPos>5__4;

			private float <t>5__5;

			private float <a>5__6;

			private float <a>5__7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <Animate>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<rt>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//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_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0094: 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_00a0: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
				//IL_0119: Unknown result type (might be due to invalid IL or missing references)
				//IL_0162: Unknown result type (might be due to invalid IL or missing references)
				//IL_016c: Expected O, but got Unknown
				//IL_01da: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_0205: Unknown result type (might be due to invalid IL or missing references)
				//IL_021c: Unknown result type (might be due to invalid IL or missing references)
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<c>5__1 = ((Graphic)<>4__this.text).color;
					<c>5__1.a = 0f;
					((Graphic)<>4__this.text).color = <c>5__1;
					<rt>5__2 = ((TMP_Text)<>4__this.text).rectTransform;
					<startPos>5__3 = <rt>5__2.anchoredPosition;
					<endPos>5__4 = <startPos>5__3 + new Vector2(0f, <>4__this.floatDistance);
					<t>5__5 = 0f;
					goto IL_013c;
				case 1:
					<>1__state = -1;
					goto IL_013c;
				case 2:
					<>1__state = -1;
					<t>5__5 = 0f;
					break;
				case 3:
					{
						<>1__state = -1;
						break;
					}
					IL_013c:
					if (<t>5__5 < <>4__this.fadeInTime)
					{
						<t>5__5 += Time.deltaTime;
						<a>5__6 = <t>5__5 / <>4__this.fadeInTime;
						<c>5__1.a = <a>5__6;
						((Graphic)<>4__this.text).color = <c>5__1;
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					<>2__current = (object)new WaitForSeconds(<>4__this.stayTime);
					<>1__state = 2;
					return true;
				}
				if (<t>5__5 < <>4__this.floatUpTime)
				{
					<t>5__5 += Time.deltaTime;
					<a>5__7 = 1f - <t>5__5 / <>4__this.floatUpTime;
					<c>5__1.a = <a>5__7;
					((Graphic)<>4__this.text).color = <c>5__1;
					if (!<>4__this.isLevelUp)
					{
						<rt>5__2.anchoredPosition = Vector2.Lerp(<startPos>5__3, <endPos>5__4, <t>5__5 / <>4__this.floatUpTime);
					}
					<>2__current = null;
					<>1__state = 3;
					return true;
				}
				Object.Destroy((Object)(object)((Component)<>4__this).gameObject);
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public TextMeshProUGUI text;

		public bool isLevelUp = false;

		public float fadeInTime = 0.3f;

		public float stayTime = 2f;

		public float floatUpTime = 1f;

		public float floatDistance = 50f;

		private void Start()
		{
			((MonoBehaviour)this).StartCoroutine(Animate());
		}

		[IteratorStateMachine(typeof(<Animate>d__7))]
		private IEnumerator Animate()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <Animate>d__7(0)
			{
				<>4__this = this
			};
		}
	}
}
namespace Leveling.Awarders
{
	[HarmonyPatch]
	internal class AchievementPatches
	{
		private const float BadgeExp = 10f;

		private static bool IsAscentAchievement(ACHIEVEMENTTYPE type)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Invalid comparison between Unknown and I4
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			return (int)type >= 33 && (int)type <= 39;
		}

		[HarmonyPatch(typeof(AchievementManager), "ThrowAchievement")]
		[HarmonyPrefix]
		private static void AddRepeatedAchievements(AchievementManager __instance, ACHIEVEMENTTYPE type)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (!__instance.runBasedValueData.steamAchievementsPreviouslyUnlocked.Contains(type) && !IsAscentAchievement(type))
			{
				LevelingAPI.AddExperience(10f);
			}
		}
	}
	[HarmonyPatch]
	public class CampfirePatches
	{
		private static float xpToAward = 10f;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Campfire), "Light_Rpc")]
		public static void Campfire_Light_Rpc_Patch(Campfire __instance)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			Segment advanceToSegment = __instance.advanceToSegment;
			if (1 == 0)
			{
			}
			float num = (advanceToSegment - 1) switch
			{
				0 => 0f, 
				1 => 5f, 
				2 => 10f, 
				3 => 15f, 
				_ => 0f, 
			};
			if (1 == 0)
			{
			}
			float num2 = num;
			float amount = xpToAward + num2;
			LevelingAPI.AddExperience(amount);
		}
	}
	[HarmonyPatch]
	internal class CharacterPatches
	{
		private static float moraleBoostCooldown = 60f;

		private static float lastMoraleBoostXPTime = float.NegativeInfinity;

		private static float climbSpamPreventionTime = float.NegativeInfinity;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "Zombify")]
		private static void Character_Zombify_Postfix(Character __instance)
		{
			if (__instance.IsLocal)
			{
				int num = 100;
				LevelingAPI.AddExperience(num);
				Plugin.Log.LogInfo((object)$"Awarded {num} XP for zombifying.");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "GetFedItemRPC")]
		private static void Character_GetFedItemRPC_Postfix(Character __instance)
		{
			if (__instance.IsLocal)
			{
				int num = 10;
				LevelingAPI.AddExperience(num);
				Plugin.Log.LogInfo((object)$"Awarded {num} XP for being fed.");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "RPCA_Die")]
		private static void Character_Die_Postfix(Character __instance)
		{
			if (__instance.IsLocal)
			{
				int num = 5;
				LevelingAPI.AddExperience(num);
				Plugin.Log.LogInfo((object)$"Awarded {num} XP for dying.");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "MoraleBoost")]
		private static void Character_MoraleBoost_Postfix(Character __instance)
		{
			if (__instance.IsLocal && !(Time.time - lastMoraleBoostXPTime < moraleBoostCooldown))
			{
				lastMoraleBoostXPTime = Time.time;
				int num = 10;
				LevelingAPI.AddExperience(num);
				Plugin.Log.LogInfo((object)$"Awarded {num} XP for morale boost.");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "OnStartClimb")]
		private static void Character_OnStartClimb_Postfix(Character __instance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!__instance.IsLocal)
			{
				return;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name != "Airport" && !(Time.time - climbSpamPreventionTime < 15f))
			{
				int num = Random.Range(0, 100);
				if (num <= 5)
				{
					climbSpamPreventionTime = Time.time;
					int num2 = 15;
					LevelingAPI.AddExperience(num2);
					Plugin.Log.LogInfo((object)$"Awarded {num2} XP for climbing.");
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "RPCA_Revive")]
		private static void Character_RPCA_Revive_Postfix(Character __instance)
		{
			if (__instance.IsLocal)
			{
				int num = 50;
				LevelingAPI.AddExperience(num);
				Plugin.Log.LogInfo((object)$"Awarded {num} XP for being revived.");
			}
			else
			{
				int num2 = 100;
				LevelingAPI.AddExperience(num2);
				Plugin.Log.LogInfo((object)$"Awarded {num2} XP for reviving someone.");
			}
		}
	}
	[HarmonyPatch]
	internal class GlobalEventsPatches
	{
		private static float CalculateEscapeExperience()
		{
			int currentAscent = Ascents.currentAscent;
			if (currentAscent < 0)
			{
				return 250f;
			}
			return 500f + (float)currentAscent * 50f;
		}

		[HarmonyPatch(typeof(GlobalEvents), "TriggerRunEnded")]
		[HarmonyPostfix]
		public static void GlobalEvents_TriggerRunEnded()
		{
			Character localCharacter = Character.localCharacter;
			if (localCharacter.refs.stats.won)
			{
				float num = CalculateEscapeExperience();
				LevelingAPI.AddExperience(num, applyAscentMultiplier: false);
				Plugin.Log.LogInfo((object)$"Awarded {num} XP for winning the game.");
			}
			else if (localCharacter.refs.stats.somebodyElseWon)
			{
				float num2 = 50f;
				LevelingAPI.AddExperience(num2, applyAscentMultiplier: false);
				Plugin.Log.LogInfo((object)$"Awarded {num2} XP for teamate winning");
			}
		}
	}
	[HarmonyPatch]
	internal class LuaggagePatches
	{
		private const float OpenLuggageExp = 15f;

		private const float MinimumDistanceFromLuggage = 7f;

		private static float LocalCharacterDistanceFrom(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(position, Character.localCharacter.Center) * CharacterStats.unitsToMeters;
		}

		[HarmonyPatch(typeof(GlobalEvents), "TriggerLuggageOpened")]
		[HarmonyPostfix]
		public static void IncrementOpenedLuggages(Luggage luggage, Character character)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (!(LocalCharacterDistanceFrom(((Component)luggage).transform.position) > 7f))
			{
				switch (luggage.displayName)
				{
				case "Ancient Luggage":
					LevelingAPI.AddExperience(35f);
					break;
				case "Explorer's Luggage":
					LevelingAPI.AddExperience(25f);
					break;
				case "Big Luggage":
					LevelingAPI.AddExperience(20f);
					break;
				default:
					LevelingAPI.AddExperience(15f);
					break;
				}
			}
		}
	}
	internal class TrackedItem
	{
		private Item item;

		private float lastTimeUsed;
	}
	[HarmonyPatch]
	internal class UseItemPatches
	{
		private const Rarity FallbackRarity = 0;

		private const float CooldownTime = 30f;

		private static List<string> blacklistedItems = new List<string> { "Passport", "Bing Bong", "Binoculars", "Torn Page", "Scroll", "Guidebook", "Parasol" };

		private static List<string> trackedItemNames = new List<string> { "Faerie Lantern", "Lantern", "Torch" };

		private static readonly Dictionary<string, float> itemCooldowns = new Dictionary<string, float>();

		private static float CalculateExperience(Rarity itemRarity)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			float num = 100f;
			if (LootData.RarityWeights.ContainsKey(itemRarity))
			{
				num = LootData.RarityWeights[itemRarity];
			}
			return 100f / num * Mathf.Log10(num);
		}

		private static bool TryGetItemRarity(GameObject itemObject, out Rarity itemRarity)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected I4, but got Unknown
			LootData component = itemObject.GetComponent<LootData>();
			if ((Object)(object)component == (Object)null)
			{
				itemRarity = (Rarity)0;
				return false;
			}
			itemRarity = (Rarity)(int)component.Rarity;
			return true;
		}

		[HarmonyPatch(typeof(Item), "FinishCastPrimary")]
		[HarmonyPostfix]
		public static void OnPrimaryUse(Item __instance)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			string itemName = __instance.UIData.itemName;
			Dictionary<string, bool> oneUseItems = LevelingAPI.OneUseItems;
			if (!__instance.lastHolderCharacter.IsLocal || __instance.OnPrimaryFinishedCast == null || blacklistedItems.Contains(itemName) || (oneUseItems.TryGetValue(itemName, out var value) && value))
			{
				return;
			}
			if (trackedItemNames.Contains(itemName))
			{
				float time = Time.time;
				if (itemCooldowns.TryGetValue(itemName, out var value2) && time < value2 + 30f)
				{
					return;
				}
				itemCooldowns[itemName] = time;
			}
			if (!TryGetItemRarity(((Component)__instance).gameObject, out var itemRarity))
			{
				itemRarity = (Rarity)0;
			}
			float num = 1f;
			if (__instance.totalUses > 0)
			{
				num = __instance.totalUses;
			}
			float amount = CalculateExperience(itemRarity) / num;
			LevelingAPI.AddExperience(amount);
			LevelingAPI.SetOneUseItem(itemName);
		}

		[HarmonyPatch(typeof(Item), "FinishCastSecondary")]
		[HarmonyPostfix]
		public static void OnSecondaryUse(Item __instance)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			string itemName = __instance.UIData.itemName;
			Dictionary<string, bool> oneUseItems = LevelingAPI.OneUseItems;
			if (!__instance.lastHolderCharacter.IsLocal || __instance.OnSecondaryFinishedCast == null || blacklistedItems.Contains(itemName) || (oneUseItems.TryGetValue(itemName, out var value) && value))
			{
				return;
			}
			if (trackedItemNames.Contains(itemName))
			{
				float time = Time.time;
				if (itemCooldowns.TryGetValue(itemName, out var value2) && time < value2 + 30f)
				{
					return;
				}
				itemCooldowns[itemName] = time;
			}
			Rarity itemRarity = (Rarity)0;
			TryGetItemRarity(((Component)__instance).gameObject, out itemRarity);
			float num = 1f;
			if (__instance.totalUses > 0)
			{
				num = __instance.totalUses;
			}
			float amount = CalculateExperience(itemRarity) / num;
			LevelingAPI.AddExperience(amount);
			LevelingAPI.SetOneUseItem(itemName);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}