Decompiled source of LevelingSystem SharingXP v2.5.5

DW.CharacterProgression.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.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CharacterProgressionMod.Commands;
using CharacterProgressionMod.Core;
using CharacterProgressionMod.Patches;
using CharacterProgressionMod.Skills;
using CharacterProgressionMod.UI;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using MonoMod.Utils;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LevelingSystem")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LevelingSystem")]
[assembly: AssemblyCopyright("Copyright ©  2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4d2a4a69-cbcf-4527-b504-7c1d1d3b3696")]
[assembly: AssemblyFileVersion("2.5.5")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.5.5.0")]
[module: UnverifiableCode]
namespace CharacterProgressionMod
{
	public sealed class RewardExpOnDeath : MonoBehaviour
	{
		private Character _character;

		private XpTable _creatureExperienceTable;

		private Player _lastPlayerAttacker;

		private float _lastPlayerAttackTime;

		public static XpTable CreatureExperienceTable { get; set; }

		public static float NearbyPlayerXpRadius { get; set; } = 50f;

		public static float LevelXpBonusPerStar { get; set; } = 0.5f;

		private void Start()
		{
			if (((Component)this).TryGetComponent<Character>(ref _character))
			{
				_creatureExperienceTable = CreatureExperienceTable;
				Character character = _character;
				character.m_onDeath = (Action)Delegate.Combine(character.m_onDeath, new Action(Character_OnDeath));
			}
		}

		private void Character_OnDeath()
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			Logger.LogDebug((object)$"Are we the owner of {((Object)_character).name}?: {_character.IsOwner()}");
			Character character = _character;
			character.m_onDeath = (Action)Delegate.Remove(character.m_onDeath, new Action(Character_OnDeath));
			if (!_character.IsOwner())
			{
				return;
			}
			string key = ((Object)_character).name.Replace("(Clone)", "").Trim();
			int num = (_creatureExperienceTable ?? CreatureExperienceTable)?.GetXp(key) ?? 0;
			if (num <= 0)
			{
				return;
			}
			int num2 = Mathf.Max(0, _character.GetLevel() - 1);
			float num3 = PluginRuntime.Config?.CreatureLevelXpBonusPerStar.Value ?? LevelXpBonusPerStar;
			float num4 = PluginRuntime.Config?.NearbyXpRadius.Value ?? NearbyPlayerXpRadius;
			num = Mathf.Max(1, Mathf.RoundToInt((float)num * (1f + (float)num2 * num3)));
			Vector3 position = ((Component)_character).transform.position;
			PlayerLevelProgression playerLevelProgression = default(PlayerLevelProgression);
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null) && !((Character)allPlayer).IsDead() && !(Vector3.Distance(((Component)allPlayer).transform.position, position) > num4) && ((Component)allPlayer).TryGetComponent<PlayerLevelProgression>(ref playerLevelProgression))
				{
					playerLevelProgression.AddExperience(num);
				}
			}
		}

		public void RegisterPlayerAttacker(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				_lastPlayerAttacker = player;
				_lastPlayerAttackTime = Time.time;
			}
		}

		public Player GetRecentPlayerAttacker(float maximumAgeSeconds = 30f)
		{
			if (!((Object)(object)_lastPlayerAttacker != (Object)null) || !(Time.time - _lastPlayerAttackTime <= maximumAgeSeconds))
			{
				return null;
			}
			return _lastPlayerAttacker;
		}
	}
	public class BiomeExperienceConfig
	{
		public class ExperienceCategory
		{
			public string Name { get; }

			public int BaseExp { get; set; }

			public float TierMultiplier { get; set; }

			public ExperienceCategory(string name, int baseExp, float tierMultiplier = 1f)
			{
				Name = name;
				BaseExp = baseExp;
				TierMultiplier = tierMultiplier;
			}
		}

		public ExperienceCategory Foraging { get; } = new ExperienceCategory("Foraging", 5);

		public ExperienceCategory Woodcutting { get; } = new ExperienceCategory("Woodcutting", 10, 1.2f);

		public ExperienceCategory Mining { get; } = new ExperienceCategory("Mining", 5, 1.2f);

		public ExperienceCategory CreatureKilling { get; } = new ExperienceCategory("CreatureKilling", 2, 2f);
	}
	public readonly struct LevelEvaluationResult
	{
		public int Level { get; }

		public int MaxExperience { get; }

		public int TotalExperience { get; }

		public int NextLevelTotalExperience { get; }

		public bool IsMaxLevel { get; }

		public LevelEvaluationResult(int level, int maxExperience, int totalExperience, int nextLevelTotalExperience, bool isMaxLevel)
		{
			Level = level;
			MaxExperience = maxExperience;
			TotalExperience = totalExperience;
			NextLevelTotalExperience = nextLevelTotalExperience;
			IsMaxLevel = isMaxLevel;
		}

		public float EvaluateProgressPercentage(int currentTotalExperience)
		{
			if (IsMaxLevel || MaxExperience <= 0)
			{
				return 100f;
			}
			return Mathf.Clamp((float)(currentTotalExperience - TotalExperience) / (float)MaxExperience * 100f, 0f, 100f);
		}

		public override string ToString()
		{
			return $"{Level} | {MaxExperience} | {TotalExperience} | {NextLevelTotalExperience} | {IsMaxLevel}";
		}
	}
	public class LevelTableGenerationSettings
	{
		public int MaxLevel { get; }

		public int InitialMaxExperience { get; }

		public MaxExperienceModifierFormula MaxExperienceModifierFormula { get; }

		public LevelTableGenerationSettings(int maxLevel, int initialMaxExperience, string maxExperienceModifierFormula)
		{
			MaxLevel = maxLevel;
			InitialMaxExperience = initialMaxExperience;
			MaxExperienceModifierFormula = new MaxExperienceModifierFormula(maxExperienceModifierFormula);
		}
	}
	public class MaxExperienceModifierFormula
	{
		private struct Modifier
		{
			public int Level { get; }

			public int Value { get; }

			public bool IsValuePercentage { get; }

			public Modifier(int level, int value, bool isValuePercentage)
			{
				Level = level;
				Value = value;
				IsValuePercentage = isValuePercentage;
			}
		}

		private readonly Modifier[] _modifiers;

		public MaxExperienceModifierFormula(string formula)
		{
			List<Modifier> list = new List<Modifier>(5);
			string[] array = new string(formula.Where((char c) => !char.IsWhiteSpace(c)).ToArray()).Split(new char[1] { ';' });
			for (int num = 0; num < array.Length; num++)
			{
				string[] array2 = array[num].Split(new char[1] { '=' });
				if (array2.Length != 2)
				{
					continue;
				}
				string text = array2[0];
				if (!IsInteger(text))
				{
					continue;
				}
				int level = int.Parse(text, NumberStyles.Integer);
				string text2 = array2[1];
				bool isValuePercentage = false;
				int value;
				if (IsPercentage(text2))
				{
					text2 = text2.TrimEnd(new char[1] { '%' });
					value = int.Parse(text2, NumberStyles.Integer);
					isValuePercentage = true;
				}
				else
				{
					if (!IsInteger(text2))
					{
						if (!IsArray(text2))
						{
							continue;
						}
						text2 = text2.Trim('[', ']');
						string[] array3 = text2.Split(new char[1] { ',' });
						foreach (string text3 in array3)
						{
							if (IsPercentage(text3) && int.TryParse(text3.TrimEnd(new char[1] { '%' }), out var result))
							{
								list.Add(new Modifier(level, result, isValuePercentage: true));
							}
							else if (IsInteger(text3))
							{
								value = int.Parse(text3, NumberStyles.Integer);
								list.Add(new Modifier(level, value, isValuePercentage: false));
							}
						}
						continue;
					}
					value = int.Parse(text2, NumberStyles.Integer);
				}
				list.Add(new Modifier(level, value, isValuePercentage));
				static bool IsArray(string text4)
				{
					return text4.StartsWith("[");
				}
				static bool IsInteger(string source)
				{
					return source.All(char.IsDigit);
				}
				static bool IsPercentage(string text4)
				{
					return text4.EndsWith("%");
				}
			}
			_modifiers = list.ToArray();
		}

		public int Evaluate(int level, int oldMaxExperience)
		{
			Modifier lastValidKey = _modifiers.LastOrDefault((Modifier key) => level >= key.Level);
			IEnumerable<Modifier> enumerable = _modifiers.Where((Modifier key) => key.Level == lastValidKey.Level);
			int num = oldMaxExperience;
			foreach (Modifier item in enumerable)
			{
				num = ((!item.IsValuePercentage) ? (num + item.Value) : (num + Mathf.CeilToInt((float)oldMaxExperience * ((float)item.Value / 100f))));
			}
			return num;
		}
	}
	public class LevelExperienceTable
	{
		private readonly int[] _entries;

		public int MaxLevel { get; }

		public LevelExperienceTable(int[] entries)
		{
			_entries = (int[])entries.Clone();
			MaxLevel = _entries.Length + 1;
			Logger.LogDebug((object)$"Player level table has been created! Max level is {MaxLevel}.");
		}

		public LevelExperienceTable(LevelTableGenerationSettings generationSettings)
		{
			int num = generationSettings.MaxLevel - 1;
			_entries = new int[num];
			int num2 = generationSettings.InitialMaxExperience;
			for (int i = 0; i < _entries.Length; i++)
			{
				_entries[i] = num2;
				int level = i + 2;
				num2 = generationSettings.MaxExperienceModifierFormula.Evaluate(level, num2);
			}
			MaxLevel = _entries.Length + 1;
			Logger.LogDebug((object)$"Player level table has been created! Max level is {MaxLevel}.");
		}

		internal int[] ExportEntries()
		{
			return (int[])_entries.Clone();
		}

		public int GetMaxExperience(int level)
		{
			int num = level - 1;
			if (num < 0 || num >= _entries.Length)
			{
				Logger.LogError((object)$"Level {level} is out of range. Max level is {_entries.Length}.");
				return 1;
			}
			return _entries[num];
		}

		public int GetTotalExperience(int level)
		{
			if (MaxLevel <= 1)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < _entries.Length && i + 1 < level; i++)
			{
				num += _entries[i];
			}
			return num;
		}

		public LevelEvaluationResult EvaluateLevel(int totalExperience)
		{
			totalExperience = Math.Max(0, totalExperience);
			int num = 0;
			for (int i = 0; i < _entries.Length; i++)
			{
				int num2 = _entries[i];
				num += num2;
				if (totalExperience < num)
				{
					int nextLevelTotalExperience = num;
					int totalExperience2 = num - num2;
					return new LevelEvaluationResult(i + 1, num2, totalExperience2, nextLevelTotalExperience, isMaxLevel: false);
				}
			}
			return new LevelEvaluationResult(MaxLevel, 0, num, num, isMaxLevel: true);
		}
	}
	public sealed class PlayerLevelProgression : MonoBehaviour
	{
		private const string TotalExpSaveKey = "Cozyheim!TotalExperience";

		private const string LevelSaveKey = "Cozyheim!Level";

		private const string LegacyExpSaveKey = "CozyXP";

		private const string LegacyLevelSaveKey = "CozyLevel";

		private static readonly int NetworkLevelKey = StringExtensionMethods.GetStableHashCode("LevelingSystem.Level");

		private Player _player;

		private string _addExperienceRpcId;

		private string _setLevelRpcId;

		private LevelExperienceTable _levelExperienceTable;

		private LevelEvaluationResult _currentLevelEvaluation;

		private bool _legacyMigrationChecked;

		private bool _playerDataLoading;

		private bool _playerDataReady;

		private bool _hasCurrentLevelEvaluation;

		private int _totalExperience;

		public PluginConfig Config { get; set; }

		public LevelExperienceTable LevelExperienceTable
		{
			get
			{
				return _levelExperienceTable;
			}
			set
			{
				if (_levelExperienceTable != value)
				{
					_levelExperienceTable = value;
					if ((Object)(object)_player != (Object)null && _playerDataReady)
					{
						UpdateLevel();
					}
				}
			}
		}

		public LevelEvaluationResult CurrentLevelEvaluation => _currentLevelEvaluation;

		public event Action<int, int> ExperienceChanged;

		public event Action<int, int> LevelChanged;

		private void Awake()
		{
			_addExperienceRpcId = RpcId.Generate("AddExperience");
			_setLevelRpcId = RpcId.Generate("SetLevel");
			_player = ((Component)this).GetComponent<Player>();
			ZNetView nview = ((Character)_player).m_nview;
			if (((nview != null) ? nview.GetZDO() : null) != null)
			{
				((Character)_player).m_nview.Register<int>(_addExperienceRpcId, (Action<long, int>)RPC_AddExperience);
				((Character)_player).m_nview.Register<int>(_setLevelRpcId, (Action<long, int>)RPC_SetLevel);
			}
		}

		private void Start()
		{
			if (!_playerDataReady && !_playerDataLoading)
			{
				LoadProgressionFromPlayerData();
			}
		}

		public void BeginPlayerDataLoad()
		{
			_playerDataLoading = true;
			_playerDataReady = false;
			_hasCurrentLevelEvaluation = false;
		}

		public void CompletePlayerDataLoad()
		{
			_playerDataLoading = false;
			_legacyMigrationChecked = false;
			LoadProgressionFromPlayerData();
		}

		public void PrepareForPlayerSave()
		{
			if (_playerDataReady && !_playerDataLoading && (Object)(object)_player != (Object)null && ((Character)_player).IsOwner())
			{
				PersistProgression();
			}
		}

		public void AddExperience(int expReward)
		{
			if (expReward <= 0 || (Object)(object)_player == (Object)null)
			{
				return;
			}
			ZNetView nview = ((Character)_player).m_nview;
			if (((nview != null) ? nview.GetZDO() : null) != null)
			{
				if (((Character)_player).IsOwner())
				{
					RPC_AddExperience(0L, expReward);
					return;
				}
				((Character)_player).m_nview.InvokeRPC(_addExperienceRpcId, new object[1] { expReward });
			}
		}

		public void SetLevel(int level)
		{
			if (((Character)_player).IsOwner() && _levelExperienceTable != null)
			{
				level = Mathf.Clamp(level, 1, _levelExperienceTable.MaxLevel);
				SetTotalExperience(_levelExperienceTable.GetTotalExperience(level), 0);
			}
		}

		public void RequestSetLevel(int level)
		{
			if ((Object)(object)_player == (Object)null)
			{
				return;
			}
			ZNetView nview = ((Character)_player).m_nview;
			if (((nview != null) ? nview.GetZDO() : null) != null)
			{
				if (((Character)_player).IsOwner())
				{
					SetLevel(level);
					return;
				}
				((Character)_player).m_nview.InvokeRPC(_setLevelRpcId, new object[1] { level });
			}
		}

		private void RPC_SetLevel(long sender, int level)
		{
			if (!((Character)_player).IsOwner())
			{
				return;
			}
			if (sender != 0L && (Object)(object)ZNet.instance != (Object)null)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer == null || serverPeer.m_uid != sender)
				{
					Logger.LogWarning((object)$"Rejected a progression level change from peer {sender}.");
					return;
				}
			}
			SetLevel(level);
		}

		private void RPC_AddExperience(long sender, int expReward)
		{
			if (((Character)_player).IsOwner() && expReward > 0)
			{
				float num = 1f;
				float num2 = Config?.XpRewardRandomSpread.Value ?? 0.05f;
				num *= Random.Range(1f - num2, 1f + num2);
				if (((Character)_player).GetSEMan().HaveStatusEffect(SEMan.s_statusEffectRested))
				{
					num *= Config?.RestedXpMultiplier.Value ?? 1.3f;
				}
				PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
				if (((Component)_player).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
				{
					num *= playerSkillProgression.GetMultiplier(SkillId.ExperienceGain);
				}
				float num3 = (float)expReward * num;
				int num4 = ((num3 >= 2.1474836E+09f) ? int.MaxValue : Mathf.Max(1, Mathf.RoundToInt(num3)));
				int totalExperience = (int)Math.Min(2147483647L, (long)GetTotalExperience() + (long)num4);
				Logger.LogDebug((object)$"Added {num4:N0} experience (x{num:F2})");
				SetTotalExperience(totalExperience, num4);
			}
		}

		private void SetTotalExperience(int totalExperience, int awardedExperience)
		{
			if (!_playerDataReady && !_playerDataLoading)
			{
				LoadProgressionFromPlayerData();
			}
			if (_playerDataReady)
			{
				_totalExperience = Mathf.Max(0, totalExperience);
				_player.m_customData["Cozyheim!TotalExperience"] = _totalExperience.ToString(CultureInfo.InvariantCulture);
				UpdateLevel();
				float num = _currentLevelEvaluation.EvaluateProgressPercentage(_totalExperience);
				Logger.LogDebug((object)($"Level progress: {_totalExperience - _currentLevelEvaluation.TotalExperience:N0} / " + $"{_currentLevelEvaluation.MaxExperience:N0} ({num:F0}%)"));
				this.ExperienceChanged?.Invoke(awardedExperience, _totalExperience);
			}
		}

		public int GetTotalExperience()
		{
			if (_playerDataReady)
			{
				return _totalExperience;
			}
			return ReadTotalExperienceFromPlayerData();
		}

		public int GetLevel()
		{
			if ((Object)(object)_player != (Object)null && !((Character)_player).IsOwner())
			{
				ZNetView nview = ((Character)_player).m_nview;
				int? obj;
				if (nview == null)
				{
					obj = null;
				}
				else
				{
					ZDO zDO = nview.GetZDO();
					obj = ((zDO != null) ? new int?(zDO.GetInt(NetworkLevelKey, 1)) : ((int?)null));
				}
				return Mathf.Max(1, obj ?? 1);
			}
			if (_playerDataReady && _hasCurrentLevelEvaluation)
			{
				return Mathf.Max(1, _currentLevelEvaluation.Level);
			}
			if (!_player.m_customData.TryGetValue("Cozyheim!Level", out var value) || !int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return 1;
			}
			return Mathf.Max(1, result);
		}

		public float GetProgress01()
		{
			if (_hasCurrentLevelEvaluation)
			{
				return _currentLevelEvaluation.EvaluateProgressPercentage(GetTotalExperience()) / 100f;
			}
			return 0f;
		}

		private void UpdateLevel()
		{
			if (_levelExperienceTable == null || (Object)(object)_player == (Object)null || !_playerDataReady)
			{
				return;
			}
			MigrateLegacyProgression();
			int level = GetLevel();
			_currentLevelEvaluation = _levelExperienceTable.EvaluateLevel(GetTotalExperience());
			_hasCurrentLevelEvaluation = true;
			int level2 = _currentLevelEvaluation.Level;
			_player.m_customData["Cozyheim!Level"] = level2.ToString(CultureInfo.InvariantCulture);
			if (((Character)_player).IsOwner())
			{
				ZNetView nview = ((Character)_player).m_nview;
				if (nview != null)
				{
					ZDO zDO = nview.GetZDO();
					if (zDO != null)
					{
						zDO.Set(NetworkLevelKey, level2, false);
					}
				}
			}
			if (level != level2)
			{
				this.LevelChanged?.Invoke(level, level2);
			}
			Logger.LogDebug((object)_currentLevelEvaluation.ToString());
		}

		private void LoadProgressionFromPlayerData()
		{
			if (!((Object)(object)_player == (Object)null) && _levelExperienceTable != null && !_playerDataLoading)
			{
				_playerDataReady = true;
				if (!((Character)_player).IsOwner())
				{
					_totalExperience = 0;
					_hasCurrentLevelEvaluation = false;
					return;
				}
				MigrateLegacyProgression();
				_totalExperience = ReadTotalExperienceFromPlayerData();
				_hasCurrentLevelEvaluation = false;
				UpdateLevel();
			}
		}

		private int ReadTotalExperienceFromPlayerData()
		{
			if ((Object)(object)_player == (Object)null)
			{
				return 0;
			}
			int result = 0;
			result = ((_player.m_customData.TryGetValue("Cozyheim!TotalExperience", out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) ? Mathf.Max(0, result) : 0);
			if (result == 0 && _player.m_customData.TryGetValue("Cozyheim!Level", out var value2) && int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) && result2 > 1)
			{
				int num = Mathf.Clamp(result2, 1, _levelExperienceTable.MaxLevel);
				int totalExperience = _levelExperienceTable.GetTotalExperience(num);
				Logger.LogWarning((object)($"Recovered missing total XP from saved level {num}: " + $"{totalExperience:N0} total XP."));
				return totalExperience;
			}
			return result;
		}

		private void PersistProgression()
		{
			_totalExperience = Mathf.Max(0, _totalExperience);
			if (!_hasCurrentLevelEvaluation)
			{
				_currentLevelEvaluation = _levelExperienceTable.EvaluateLevel(_totalExperience);
				_hasCurrentLevelEvaluation = true;
			}
			_player.m_customData["Cozyheim!TotalExperience"] = _totalExperience.ToString(CultureInfo.InvariantCulture);
			_player.m_customData["Cozyheim!Level"] = Mathf.Max(1, _currentLevelEvaluation.Level).ToString(CultureInfo.InvariantCulture);
			ZNetView nview = ((Character)_player).m_nview;
			if (nview != null)
			{
				ZDO zDO = nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set(NetworkLevelKey, Mathf.Max(1, _currentLevelEvaluation.Level), false);
				}
			}
		}

		private void MigrateLegacyProgression()
		{
			if (_legacyMigrationChecked || !((Character)_player).IsOwner() || _levelExperienceTable == null)
			{
				return;
			}
			_legacyMigrationChecked = true;
			if (_player.m_customData.ContainsKey("Cozyheim!TotalExperience"))
			{
				return;
			}
			int value;
			bool flag = TryReadCustomInt("CozyLevel", out value);
			int value2;
			bool flag2 = TryReadCustomInt("CozyXP", out value2);
			if (flag || flag2)
			{
				value = ((!flag) ? 1 : Mathf.Max(1, value));
				value2 = (flag2 ? Mathf.Max(0, value2) : 0);
				int num = Mathf.Clamp(value, 1, _levelExperienceTable.MaxLevel);
				int num2 = _levelExperienceTable.GetTotalExperience(num);
				if (num < _levelExperienceTable.MaxLevel && value2 > 0)
				{
					int maxExperience = _levelExperienceTable.GetMaxExperience(num);
					int num3 = TryGetLegacyLevelRequirement(value);
					float num4 = ((num3 > 0) ? Mathf.Clamp01((float)value2 / (float)num3) : Mathf.Clamp01((float)value2 / (float)maxExperience));
					int num5 = Mathf.Min(Mathf.RoundToInt((float)maxExperience * num4), Mathf.Max(0, maxExperience - 1));
					num2 += num5;
				}
				_player.m_customData["Cozyheim!TotalExperience"] = num2.ToString(CultureInfo.InvariantCulture);
				_player.m_customData["Cozyheim!Level"] = num.ToString(CultureInfo.InvariantCulture);
				Logger.LogInfo((object)($"Migrated legacy progression: level {value}, {value2} XP -> " + $"level {num}, {num2} total XP."));
			}
		}

		private bool TryReadCustomInt(string key, out int value)
		{
			value = 0;
			if (_player.m_customData.TryGetValue(key, out var value2))
			{
				return int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
			}
			return false;
		}

		private static int TryGetLegacyLevelRequirement(int level)
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "LevelingSystem", "PlayerXP", "Player XP Table.json");
				if (!File.Exists(path))
				{
					return 0;
				}
				Dictionary<string, int> dictionary = JsonConvert.DeserializeObject<Dictionary<string, int>>(File.ReadAllText(path));
				int value;
				return (dictionary != null && dictionary.TryGetValue("Lv" + level, out value)) ? Mathf.Max(0, value) : 0;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Could not read the legacy player XP table; raw progress will be preserved instead: " + ex.Message));
				return 0;
			}
		}
	}
	public sealed class XpTable
	{
		private const string EmbeddedConfigPath = "CharacterProgressionMod.Resources.default_configs";

		private readonly Dictionary<string, int> _entries = new Dictionary<string, int>();

		private readonly Dictionary<string, string> _groups = new Dictionary<string, string>();

		public string NameId { get; }

		public bool AllowGroups { get; }

		public string CustomConfigFolderPath { get; }

		public string CustomGroupsFolderPath { get; }

		public XpTable(Assembly resourceAssembly, string customFolderPath, bool allowGroups)
		{
			if (string.IsNullOrEmpty(customFolderPath))
			{
				Logger.LogError((object)"A critical error occurred during the initialization of the leveling system. Please report this to the mod author.");
				throw new ArgumentException("The customFolderPath parameter cannot be null or empty.");
			}
			NameId = Path.GetFileName(customFolderPath).ToLowerInvariant();
			AllowGroups = allowGroups;
			CustomConfigFolderPath = customFolderPath;
			CustomGroupsFolderPath = Path.Combine(customFolderPath, "groups");
			VerifyAndSetupConfigDirectory();
			if ((object)resourceAssembly == null)
			{
				Logger.LogError((object)"A critical error occurred during the initialization of the leveling system. Please report this to the mod author.");
				throw new ArgumentNullException("The resourceAssembly parameter cannot be null.");
			}
			LoadEmbeddedResources(resourceAssembly);
			LoadCustomResources();
			Logger.LogDebug((object)$"Loaded {NameId} xp table with {_entries.Count} entries.");
			if (AllowGroups)
			{
				Logger.LogDebug((object)$"Loaded {NameId} groups with {_groups.Count} entries.");
			}
		}

		internal Dictionary<string, int> ExportEntries()
		{
			return new Dictionary<string, int>(_entries);
		}

		internal Dictionary<string, string> ExportGroups()
		{
			return new Dictionary<string, string>(_groups);
		}

		internal void ReplaceEntries(Dictionary<string, int> entries, Dictionary<string, string> groups)
		{
			_entries.Clear();
			_groups.Clear();
			foreach (KeyValuePair<string, int> entry in entries)
			{
				_entries[entry.Key] = entry.Value;
			}
			foreach (KeyValuePair<string, string> group in groups)
			{
				_groups[group.Key] = group.Value;
			}
		}

		private void VerifyAndSetupConfigDirectory()
		{
			bool num = Directory.Exists(CustomConfigFolderPath);
			bool flag = Directory.Exists(CustomGroupsFolderPath);
			if (!(num && flag))
			{
				Logger.LogDebug((object)("Creating directories for custom " + NameId + " configs."));
				Directory.CreateDirectory(CustomConfigFolderPath);
				Directory.CreateDirectory(CustomGroupsFolderPath);
			}
		}

		private void LoadEmbeddedResources(Assembly resourceAssembly)
		{
			string[] manifestResourceNames = resourceAssembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				string pattern = "^CharacterProgressionMod.Resources.default_configs\\." + NameId + "\\.xp_tables\\.(?:[\\w-]+).json";
				if (Regex.IsMatch(text, pattern))
				{
					Dictionary<string, int> dictionary = JsonConvert.DeserializeObject<Dictionary<string, int>>(AssetUtils.LoadTextFromResources(text, resourceAssembly));
					if (dictionary.Count == 0)
					{
						Logger.LogError((object)("Skipped loading embedded " + NameId + " xp table file at '" + text + "' - no entries found. Please report this to the mod author."));
					}
					else
					{
						Extensions.AddRange<string, int>(_entries, dictionary);
					}
				}
				else
				{
					if (!AllowGroups)
					{
						continue;
					}
					string pattern2 = "^CharacterProgressionMod.Resources.default_configs\\." + NameId + "\\.groups\\.(?:[\\w-]+).json";
					if (!Regex.IsMatch(text, pattern2))
					{
						continue;
					}
					Dictionary<string, string[]> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(AssetUtils.LoadTextFromResources(text, resourceAssembly));
					if (dictionary2.Count == 0)
					{
						Logger.LogError((object)("Skipped loading embedded " + NameId + " group file at '" + text + "' - no entries found. Please report this to the mod author."));
						continue;
					}
					CollectionExtensions.Do<KeyValuePair<string, string[]>>((IEnumerable<KeyValuePair<string, string[]>>)dictionary2, (Action<KeyValuePair<string, string[]>>)delegate(KeyValuePair<string, string[]> pair)
					{
						CollectionExtensions.Do<string>((IEnumerable<string>)pair.Value, (Action<string>)delegate(string groupEntry)
						{
							_groups[groupEntry] = pair.Key;
						});
					});
				}
			}
		}

		private void LoadCustomResources()
		{
			if (!Directory.Exists(CustomConfigFolderPath))
			{
				VerifyAndSetupConfigDirectory();
				return;
			}
			string[] files = Directory.GetFiles(CustomConfigFolderPath, "*.json", SearchOption.TopDirectoryOnly);
			if (files.Length == 0)
			{
				Logger.LogDebug((object)("Skipping loading custom " + NameId + " configs - no files found in the custom config folder."));
				return;
			}
			string[] array = files;
			foreach (string text in array)
			{
				Dictionary<string, int> dictionary = JsonConvert.DeserializeObject<Dictionary<string, int>>(File.ReadAllText(text));
				if (dictionary.Count == 0)
				{
					Logger.LogWarning((object)("Skipped loading custom " + NameId + " xp table file at '" + text + "' - no entries found."));
				}
				else
				{
					CollectionExtensions.Do<KeyValuePair<string, int>>((IEnumerable<KeyValuePair<string, int>>)dictionary, (Action<KeyValuePair<string, int>>)delegate(KeyValuePair<string, int> pair)
					{
						_entries[pair.Key] = pair.Value;
					});
				}
			}
			if (!AllowGroups || !Directory.Exists(CustomGroupsFolderPath))
			{
				return;
			}
			files = Directory.GetFiles(CustomGroupsFolderPath, "*.json", SearchOption.TopDirectoryOnly);
			array = files;
			foreach (string text2 in array)
			{
				Dictionary<string, string[]> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(File.ReadAllText(text2));
				if (dictionary2.Count == 0)
				{
					Logger.LogWarning((object)("Skipped loading custom " + NameId + " group file at '" + text2 + "' - no entries found."));
					continue;
				}
				CollectionExtensions.Do<KeyValuePair<string, string[]>>((IEnumerable<KeyValuePair<string, string[]>>)dictionary2, (Action<KeyValuePair<string, string[]>>)delegate(KeyValuePair<string, string[]> pair)
				{
					CollectionExtensions.Do<string>((IEnumerable<string>)pair.Value, (Action<string>)delegate(string groupEntry)
					{
						_groups[groupEntry] = pair.Key;
					});
				});
			}
		}

		public void ReloadResources()
		{
			_entries.Clear();
			_groups.Clear();
			Assembly callingAssembly = ReflectionHelper.GetCallingAssembly();
			LoadEmbeddedResources(callingAssembly);
			LoadCustomResources();
		}

		public int GetXp(string key)
		{
			key = key.Replace("(Clone)", "");
			if (_entries.TryGetValue(key, out var value))
			{
				Logger.LogDebug((object)$"Found xp for '{key}': {value} xp");
				return value;
			}
			if (!AllowGroups || _groups.Count == 0)
			{
				Logger.LogDebug((object)("Skipping group check - groups are not allowed for the " + NameId + " xp table."));
				return 0;
			}
			if (!_groups.TryGetValue(key, out var value2))
			{
				Logger.LogDebug((object)("Failed to find a group for '" + key + "'."));
				return 0;
			}
			Logger.LogDebug((object)("Found group '" + value2 + "' for '" + key + "'."));
			if (!_entries.TryGetValue(value2, out value))
			{
				return 0;
			}
			return value;
		}
	}
}
namespace CharacterProgressionMod.UI
{
	internal sealed class FloatingTextEffect : MonoBehaviour
	{
		private string _value;

		private Color _color = Color.white;

		public void Configure(string value, Color color)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			_value = value;
			_color = color;
		}

		private IEnumerator Start()
		{
			Text componentInChildren = ((Component)this).GetComponentInChildren<Text>();
			CanvasGroup group = ((Component)this).GetComponentInChildren<CanvasGroup>() ?? ((Component)this).gameObject.AddComponent<CanvasGroup>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = _value;
				((Graphic)componentInChildren).color = _color;
			}
			Vector3 origin = ((Component)this).transform.position;
			Vector3 initialScale = ((Component)this).transform.localScale;
			for (float time = 0f; time < 2.2f; time += Time.deltaTime)
			{
				float num = time / 2.2f;
				((Component)this).transform.position = origin + Vector3.up * (0.85f * num);
				group.alpha = Mathf.Sin(num * (float)Math.PI);
				((Component)this).transform.localScale = initialScale * Mathf.Lerp(0.75f, 1.1f, Mathf.Sin(num * (float)Math.PI));
				yield return null;
			}
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		private void LateUpdate()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Camera.main != (Object)null)
			{
				((Component)this).transform.rotation = ((Component)Camera.main).transform.rotation;
			}
		}
	}
	internal sealed class LocalPlayerUiSpawner : MonoBehaviour
	{
		private IEnumerator Start()
		{
			Player player = ((Component)this).GetComponent<Player>();
			while ((Object)(object)player != (Object)null && (Object)(object)Player.m_localPlayer != (Object)(object)player)
			{
				yield return null;
			}
			while ((Object)(object)player != (Object)null && (Object)(object)ProgressionUiController.Instance == (Object)null)
			{
				GameObject val = PluginRuntime.Resources?.LevelingSystemUiPrefab;
				if ((Object)(object)val != (Object)null)
				{
					GameObject obj = Object.Instantiate<GameObject>(val);
					((Object)obj).name = "LevelingSystemUI_Modern";
					((Object)obj).hideFlags = (HideFlags)0;
					obj.SetActive(true);
					break;
				}
				yield return null;
			}
		}
	}
	internal sealed class ProgressionUiController : MonoBehaviour
	{
		private readonly List<Button> _categoryButtons = new List<Button>();

		private readonly List<RectTransform> _categoryContainers = new List<RectTransform>();

		private readonly List<SkillCardView> _cards = new List<SkillCardView>();

		private Player _player;

		private PlayerLevelProgression _progression;

		private PlayerSkillProgression _skills;

		private CanvasGroup _xpBarGroup;

		private CanvasGroup _skillsGroup;

		private Canvas _menuCanvas;

		private CanvasGroup _levelUpGroup;

		private RectTransform _xpBarRect;

		private RectTransform _xpBarContainer;

		private RectTransform _content;

		private Text _levelText;

		private Text _levelShadow;

		private Text _xpText;

		private Text _xpShadow;

		private Text _levelUpText;

		private Text _levelUpShadow;

		private Text _remainingPoints;

		private Image _xpFill;

		private ScrollRect _scrollRect;

		private Scrollbar _scrollbar;

		private Button _closeButton;

		private Button _resetButton;

		private GameObject _skillPrefab;

		private GameObject _skillDetailsPanel;

		private Text _skillDetailsTitle;

		private Text _skillDetailsBody;

		private GameObject _skillsShortcutHud;

		private bool _initialized;

		private bool _menuVisible;

		private int _currentCategory;

		private float _targetFill;

		private float _fillVelocity;

		private float _nextIntegrityCheck;

		public static ProgressionUiController Instance { get; private set; }

		private void Awake()
		{
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			_levelText = Find<Text>("XP Bar/LevelText");
			_levelShadow = Find<Text>("XP Bar/LevelText/Shadow");
			_xpText = Find<Text>("XP Bar/XP Bar/XPText");
			_xpShadow = Find<Text>("XP Bar/XP Bar/XPText/Shadow");
			_xpFill = Find<Image>("XP Bar/XP Bar/XPFill");
			_xpBarGroup = Find<CanvasGroup>("XP Bar");
			_xpBarContainer = ((Component)((Component)this).transform.Find("XP Bar")).GetComponent<RectTransform>();
			_xpBarRect = ((Component)((Component)this).transform.Find("XP Bar/XP Bar")).GetComponent<RectTransform>();
			_levelUpText = Find<Text>("LevelUp Pop-Up/LevelUpText");
			_levelUpShadow = Find<Text>("LevelUp Pop-Up/LevelUpText/Shadow");
			_levelUpGroup = Find<CanvasGroup>("LevelUp Pop-Up");
			_skillsGroup = Find<CanvasGroup>("Skills UI");
			_remainingPoints = Find<Text>("Skills UI/Remaining Points");
			_closeButton = Find<Button>("Skills UI/Close Menu");
			_resetButton = Find<Button>("Skills UI/Reset Skills Button");
			_content = ((Component)((Component)this).transform.Find("Skills UI/Scroll View/Viewport/Content")).GetComponent<RectTransform>();
			_scrollRect = Find<ScrollRect>("Skills UI");
			_scrollbar = Find<Scrollbar>("Skills UI/Scrollbar");
			Transform val = ((Component)this).transform.Find("Skills UI/Scroll View/Category Buttons");
			while (val.childCount < Enum.GetValues(typeof(SkillCategory)).Length)
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)val.GetChild(val.childCount - 1)).gameObject, val);
				((Object)obj).name = "Category Button (Rare)";
				Image component = obj.GetComponent<Image>();
				if ((Object)(object)component != (Object)null)
				{
					((Graphic)component).color = new Color(0.48f, 0.25f, 0.68f, 1f);
				}
			}
			for (int i = 0; i < val.childCount; i++)
			{
				_categoryButtons.Add(((Component)val.GetChild(i)).GetComponent<Button>());
			}
			while (((Transform)_content).childCount < Enum.GetValues(typeof(SkillCategory)).Length)
			{
				((Object)Object.Instantiate<GameObject>(((Component)((Transform)_content).GetChild(((Transform)_content).childCount - 1)).gameObject, (Transform)(object)_content)).name = "Rare Skills";
			}
			for (int j = 0; j < ((Transform)_content).childCount; j++)
			{
				_categoryContainers.Add(((Component)((Transform)_content).GetChild(j)).GetComponent<RectTransform>());
			}
		}

		private IEnumerator Start()
		{
			_levelUpGroup.alpha = 0f;
			ToggleMenu(visible: false);
			while ((Object)(object)Player.m_localPlayer == (Object)null || !((Component)Player.m_localPlayer).TryGetComponent<PlayerLevelProgression>(ref _progression) || !((Component)Player.m_localPlayer).TryGetComponent<PlayerSkillProgression>(ref _skills))
			{
				yield return null;
			}
			BindPlayer(Player.m_localPlayer, _progression, _skills);
			_skillPrefab = PluginRuntime.Resources?.SkillUiPrefab;
			if ((Object)(object)_skillPrefab == (Object)null)
			{
				Logger.LogError((object)"SkillUI prefab was not registered.");
				yield break;
			}
			ConfigureLayout();
			ConfigureButtons();
			CreateSkillDetailsPanel();
			CreateSkillsShortcutHud();
			OpenCategory(0);
			RefreshAll();
			_initialized = true;
			int num = SkillCatalog.Definitions.Count((SkillDefinition definition) => definition.Enabled);
			Logger.LogInfo((object)($"Modern progression UI initialized with {num} skills; " + "localization probe: '" + Localize("$ls_level") + "'."));
		}

		private void Update()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized)
			{
				return;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Player.m_localPlayer != (Object)(object)_player)
			{
				TryRebindLocalPlayer();
			}
			if (_menuVisible && Input.GetKeyDown((KeyCode)27))
			{
				ToggleMenu(visible: false);
			}
			else if (!IsTyping())
			{
				KeyboardShortcut value = PluginRuntime.Config.OpenSkillsMenu.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					ToggleMenu(!_menuVisible);
				}
			}
			_xpFill.fillAmount = Mathf.SmoothDamp(_xpFill.fillAmount, _targetFill, ref _fillVelocity, 0.18f, 8f);
			if (_menuVisible && Time.unscaledTime >= _nextIntegrityCheck)
			{
				_nextIntegrityCheck = Time.unscaledTime + 0.75f;
				EnsureCurrentCategoryIntegrity();
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			if (_menuVisible)
			{
				GUIManager.BlockInput(false);
			}
			UnbindPlayer();
		}

		private void LateUpdate()
		{
			if (_menuVisible)
			{
				ConfigureMenuSorting();
			}
		}

		private void ConfigureLayout()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			PluginConfig config = PluginRuntime.Config;
			ConfigureSkillsWindowLayout();
			ConfigureRaycastTargets();
			_xpBarGroup.alpha = ((config.ShowXp.Value || config.ShowLevel.Value) ? 1f : 0f);
			((Component)_levelText).gameObject.SetActive(config.ShowLevel.Value);
			((Component)_xpFill).gameObject.SetActive(config.ShowXp.Value);
			((Component)_xpText).gameObject.SetActive(config.ShowXp.Value);
			_xpBarContainer.anchoredPosition = config.XpBarPosition.Value;
			Vector2 sizeDelta = _xpBarRect.sizeDelta;
			sizeDelta.x *= config.XpBarScale.Value / 100f;
			_xpBarRect.sizeDelta = sizeDelta;
			_scrollRect.verticalScrollbar = (config.ShowScrollbar.Value ? _scrollbar : null);
			((Component)_scrollbar).gameObject.SetActive(config.ShowScrollbar.Value);
		}

		private void ConfigureRaycastTargets()
		{
			_menuCanvas = ((Component)_skillsGroup).gameObject.GetComponent<Canvas>() ?? ((Component)_skillsGroup).gameObject.AddComponent<Canvas>();
			if ((Object)(object)((Component)_skillsGroup).GetComponent<GraphicRaycaster>() == (Object)null)
			{
				((Component)_skillsGroup).gameObject.AddComponent<GraphicRaycaster>();
			}
			_xpBarGroup.blocksRaycasts = false;
			_levelUpGroup.blocksRaycasts = false;
			Graphic[] componentsInChildren = ((Component)_skillsGroup).GetComponentsInChildren<Graphic>(true);
			foreach (Graphic obj in componentsInChildren)
			{
				obj.raycastTarget = (Object)(object)((Component)obj).GetComponent<Selectable>() != (Object)null;
			}
			Graphic component = ((Component)_scrollRect.viewport).GetComponent<Graphic>();
			if ((Object)(object)component != (Object)null)
			{
				component.raycastTarget = true;
			}
			if ((Object)(object)((Selectable)_scrollbar).targetGraphic != (Object)null)
			{
				((Selectable)_scrollbar).targetGraphic.raycastTarget = true;
			}
		}

		private void ConfigureMenuSorting()
		{
			if (!((Object)(object)_menuCanvas == (Object)null))
			{
				Canvas val = (((Object)(object)Hud.instance != (Object)null) ? ((Component)Hud.instance).GetComponentInParent<Canvas>() : null);
				int num = (((Object)(object)val != (Object)null) ? val.sortingLayerID : ((Component)this).GetComponent<Canvas>().sortingLayerID);
				int num2 = (((Object)(object)val != (Object)null) ? Mathf.Max(500, val.sortingOrder + 1) : 500);
				if (!_menuCanvas.overrideSorting)
				{
					_menuCanvas.overrideSorting = true;
				}
				if (_menuCanvas.sortingLayerID != num)
				{
					_menuCanvas.sortingLayerID = num;
				}
				if (_menuCanvas.sortingOrder != num2)
				{
					_menuCanvas.sortingOrder = num2;
				}
			}
		}

		private void ConfigureSkillsWindowLayout()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = ((Component)_skillsGroup).GetComponent<RectTransform>();
			Vector2 sizeDelta = component.sizeDelta;
			sizeDelta.x = Mathf.Max(sizeDelta.x, 735f);
			sizeDelta.y = Mathf.Max(sizeDelta.y, 820f);
			component.sizeDelta = sizeDelta;
			Canvas.ForceUpdateCanvases();
			Transform val = ((Component)this).transform.Find("Skills UI/Scroll View/Category Buttons");
			RectTransform component2 = ((Component)val).GetComponent<RectTransform>();
			GridLayoutGroup component3 = ((Component)val).GetComponent<GridLayoutGroup>();
			int length = Enum.GetValues(typeof(SkillCategory)).Length;
			if ((Object)(object)component3 == (Object)null || length == 0)
			{
				return;
			}
			component3.constraint = (Constraint)1;
			component3.constraintCount = length;
			component3.startAxis = (Axis)0;
			((LayoutGroup)component3).childAlignment = (TextAnchor)1;
			float num = component3.spacing.x * (float)(length - 1);
			Rect rect = component2.rect;
			float num2 = Mathf.Floor((((Rect)(ref rect)).width - num) / (float)length);
			component3.cellSize = new Vector2(num2, 40f);
			for (int i = 0; i < val.childCount; i++)
			{
				Text componentInChildren = ((Component)val.GetChild(i)).GetComponentInChildren<Text>();
				if (!((Object)(object)componentInChildren == (Object)null))
				{
					RectTransform component4 = ((Component)componentInChildren).GetComponent<RectTransform>();
					((Transform)component4).localScale = Vector3.one;
					component4.anchorMin = Vector2.zero;
					component4.anchorMax = Vector2.one;
					component4.pivot = new Vector2(0.5f, 0.5f);
					component4.anchoredPosition = Vector2.zero;
					component4.sizeDelta = new Vector2(-8f, -6f);
					componentInChildren.resizeTextForBestFit = false;
					componentInChildren.fontSize = 18;
					componentInChildren.alignment = (TextAnchor)4;
					componentInChildren.horizontalOverflow = (HorizontalWrapMode)1;
					componentInChildren.verticalOverflow = (VerticalWrapMode)1;
				}
			}
			LayoutRebuilder.ForceRebuildLayoutImmediate(component2);
			RectTransform component5 = ((Component)((Component)this).transform.Find("Skills UI/Scroll View/Viewport")).GetComponent<RectTransform>();
			Canvas.ForceUpdateCanvases();
			foreach (RectTransform categoryContainer in _categoryContainers)
			{
				GridLayoutGroup component6 = ((Component)categoryContainer).GetComponent<GridLayoutGroup>();
				if (!((Object)(object)component6 == (Object)null))
				{
					component6.constraint = (Constraint)1;
					component6.constraintCount = 3;
					component6.startAxis = (Axis)0;
					((LayoutGroup)component6).childAlignment = (TextAnchor)1;
					rect = component5.rect;
					float num3 = ((Rect)(ref rect)).width - (float)((LayoutGroup)component6).padding.left - (float)((LayoutGroup)component6).padding.right - component6.spacing.x * 2f;
					component6.cellSize = new Vector2(Mathf.Floor(num3 / 3f), 200f);
					LayoutRebuilder.ForceRebuildLayoutImmediate(categoryContainer);
				}
			}
		}

		private void ConfigureButtons()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			for (int i = 0; i < _categoryButtons.Count; i++)
			{
				int categoryIndex = i;
				((UnityEventBase)_categoryButtons[i].onClick).RemoveAllListeners();
				((UnityEvent)_categoryButtons[i].onClick).AddListener((UnityAction)delegate
				{
					OpenCategory(categoryIndex);
				});
			}
			((UnityEventBase)_closeButton.onClick).RemoveAllListeners();
			((UnityEvent)_closeButton.onClick).AddListener((UnityAction)delegate
			{
				ToggleMenu(visible: false);
			});
			((UnityEventBase)_resetButton.onClick).RemoveAllListeners();
			((UnityEvent)_resetButton.onClick).AddListener((UnityAction)delegate
			{
				_skills.ResetAll();
			});
			Text componentInChildren = ((Component)_resetButton).GetComponentInChildren<Text>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = Localize("$ls_reset_skills");
			}
			Text componentInChildren2 = ((Component)_closeButton).GetComponentInChildren<Text>();
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				componentInChildren2.text = Localize("$ls_close_menu");
			}
		}

		public void ToggleMenu(bool visible)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			bool num = _menuVisible != visible;
			_menuVisible = visible;
			_skillsGroup.alpha = (visible ? 1f : 0f);
			_skillsGroup.interactable = visible;
			_skillsGroup.blocksRaycasts = visible;
			((Component)_skillsGroup).gameObject.SetActive(visible);
			if (visible)
			{
				ConfigureMenuSorting();
			}
			if ((Object)(object)_skillsShortcutHud != (Object)null)
			{
				_skillsShortcutHud.SetActive(!visible);
			}
			if ((Object)(object)_content != (Object)null)
			{
				_content.anchoredPosition = Vector2.zero;
			}
			if (num)
			{
				GUIManager.BlockInput(visible);
			}
			if (visible && _initialized)
			{
				RefreshAll();
			}
			if (!visible)
			{
				HideSkillDetails();
			}
		}

		public void ShowSkillDetails(SkillDefinition definition, Vector2 screenPosition)
		{
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_skillDetailsPanel == (Object)null) && definition != null && !((Object)(object)_skills == (Object)null))
			{
				string text = _skills.GetBonus(definition.Id).ToString("0.##", CultureInfo.InvariantCulture);
				string arg = definition.BonusPerRank.ToString("0.##", CultureInfo.InvariantCulture);
				_skillDetailsTitle.text = Localize(definition.NameToken);
				if (definition.Category == SkillCategory.Rare)
				{
					_skillDetailsBody.text = Localize(definition.DescriptionToken) + "\n" + string.Format(Localize("$ls_rare_cost"), definition.PointCost);
				}
				else
				{
					_skillDetailsBody.text = Localize(definition.DescriptionToken) + "\n" + string.Format(Localize("$ls_per_rank"), arg, definition.Unit) + "    " + Localize("$ls_total_bonus") + ": +" + text + definition.Unit;
				}
				_skillDetailsPanel.SetActive(true);
				ResizeSkillDetails();
				_skillDetailsPanel.transform.SetAsLastSibling();
				PositionSkillDetails(screenPosition);
			}
		}

		public void HideSkillDetails()
		{
			if ((Object)(object)_skillDetailsPanel != (Object)null)
			{
				_skillDetailsPanel.SetActive(false);
			}
		}

		private void CreateSkillDetailsPanel()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_00ab: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: 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)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Skill Details", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.layer = ((Component)_skillsGroup).gameObject.layer;
			val.transform.SetParent(((Component)_skillsGroup).transform, false);
			RectTransform component = val.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.anchoredPosition = Vector2.zero;
			component.sizeDelta = new Vector2(440f, 135f);
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = new Color(0.035f, 0.045f, 0.055f, 0.97f);
			((Graphic)component2).raycastTarget = false;
			_skillDetailsTitle = CreateDetailsText(val.transform, "Title", 21, (FontStyle)1, new Vector2(14f, -10f), new Vector2(-28f, 35f));
			((Graphic)_skillDetailsTitle).color = new Color(0.95f, 0.78f, 0.35f, 1f);
			_skillDetailsBody = CreateDetailsText(val.transform, "Description", 16, (FontStyle)0, new Vector2(14f, -47f), new Vector2(-28f, 80f));
			((Graphic)_skillDetailsBody).color = new Color(0.95f, 0.95f, 0.93f, 1f);
			_skillDetailsPanel = val;
			_skillDetailsPanel.SetActive(false);
		}

		private void CreateSkillsShortcutHud()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Expected O, but got Unknown
			GameObject val = new GameObject("SharingXP Skills Shortcut", new Type[4]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image),
				typeof(Button)
			});
			val.layer = ((Component)this).gameObject.layer;
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(1f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(1f, 1f);
			component.anchoredPosition = new Vector2(-275f, -18f);
			component.sizeDelta = new Vector2(190f, 58f);
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = new Color(0.025f, 0.035f, 0.045f, 0.88f);
			GameObject val2 = new GameObject("Icon", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			})
			{
				layer = ((Component)this).gameObject.layer
			};
			val2.transform.SetParent(val.transform, false);
			RectTransform component3 = val2.GetComponent<RectTransform>();
			component3.anchorMin = new Vector2(0f, 0.5f);
			component3.anchorMax = new Vector2(0f, 0.5f);
			component3.pivot = new Vector2(0f, 0.5f);
			component3.anchoredPosition = new Vector2(4f, 0f);
			component3.sizeDelta = new Vector2(50f, 50f);
			Image component4 = val2.GetComponent<Image>();
			component4.sprite = SkillIconProvider.Load("HUDSharingXP");
			component4.preserveAspect = true;
			((Graphic)component4).raycastTarget = false;
			GameObject val3 = new GameObject("Label", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Text)
			})
			{
				layer = ((Component)this).gameObject.layer
			};
			val3.transform.SetParent(val.transform, false);
			RectTransform component5 = val3.GetComponent<RectTransform>();
			component5.anchorMin = new Vector2(0f, 0f);
			component5.anchorMax = new Vector2(1f, 1f);
			component5.offsetMin = new Vector2(58f, 4f);
			component5.offsetMax = new Vector2(-5f, -4f);
			Text component6 = val3.GetComponent<Text>();
			component6.font = _remainingPoints.font;
			component6.fontSize = 17;
			component6.fontStyle = (FontStyle)1;
			component6.alignment = (TextAnchor)4;
			((Graphic)component6).color = new Color(0.94f, 0.8f, 0.42f, 1f);
			KeyboardShortcut value = PluginRuntime.Config.OpenSkillsMenu.Value;
			component6.text = "SharingXP\n[" + ((object)((KeyboardShortcut)(ref value)).MainKey/*cast due to .constrained prefix*/).ToString() + "]";
			((Graphic)component6).raycastTarget = false;
			Button component7 = val.GetComponent<Button>();
			((Selectable)component7).targetGraphic = (Graphic)(object)component2;
			((UnityEvent)component7.onClick).AddListener((UnityAction)delegate
			{
				ToggleMenu(!_menuVisible);
			});
			_skillsShortcutHud = val;
		}

		private void ResizeSkillDetails()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = _skillDetailsPanel.GetComponent<RectTransform>();
			RectTransform component2 = ((Component)_skillsGroup).GetComponent<RectTransform>();
			Rect rect = component2.rect;
			component.SetSizeWithCurrentAnchors((Axis)0, Mathf.Min(460f, ((Rect)(ref rect)).width - 24f));
			RectTransform rectTransform = ((Graphic)_skillDetailsTitle).rectTransform;
			RectTransform rectTransform2 = ((Graphic)_skillDetailsBody).rectTransform;
			float num = Mathf.Max(28f, _skillDetailsTitle.preferredHeight);
			rectTransform.SetSizeWithCurrentAnchors((Axis)1, num);
			rectTransform2.anchoredPosition = new Vector2(14f, -18f - num);
			float num2 = Mathf.Max(40f, _skillDetailsBody.preferredHeight);
			rectTransform2.SetSizeWithCurrentAnchors((Axis)1, num2);
			component.SetSizeWithCurrentAnchors((Axis)1, num + num2 + 32f);
		}

		private void PositionSkillDetails(Vector2 screenPosition)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_0050: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = ((Component)_skillsGroup).GetComponent<RectTransform>();
			RectTransform component2 = _skillDetailsPanel.GetComponent<RectTransform>();
			Vector2 val = default(Vector2);
			if (RectTransformUtility.ScreenPointToLocalPointInRectangle(component, screenPosition, (Camera)null, ref val))
			{
				Vector2 val2 = val + new Vector2(component2.sizeDelta.x * 0.52f, -75f);
				Rect rect = component.rect;
				float num = component2.sizeDelta.x * 0.5f;
				float num2 = component2.sizeDelta.y * 0.5f;
				val2.x = Mathf.Clamp(val2.x, ((Rect)(ref rect)).xMin + num, ((Rect)(ref rect)).xMax - num);
				val2.y = Mathf.Clamp(val2.y, ((Rect)(ref rect)).yMin + num2, ((Rect)(ref rect)).yMax - num2);
				component2.anchoredPosition = val2;
			}
		}

		private Text CreateDetailsText(Transform parent, string objectName, int fontSize, FontStyle fontStyle, Vector2 anchoredPosition, Vector2 sizeDelta)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(objectName, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Text)
			})
			{
				layer = ((Component)parent).gameObject.layer
			};
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0f, 1f);
			component.anchoredPosition = anchoredPosition;
			component.sizeDelta = sizeDelta;
			Text component2 = val.GetComponent<Text>();
			component2.font = _remainingPoints.font;
			component2.fontSize = fontSize;
			component2.fontStyle = fontStyle;
			component2.alignment = (TextAnchor)0;
			component2.horizontalOverflow = (HorizontalWrapMode)0;
			component2.verticalOverflow = (VerticalWrapMode)0;
			((Graphic)component2).raycastTarget = false;
			component2.supportRichText = true;
			return component2;
		}

		private void OpenCategory(int categoryIndex)
		{
			//IL_01da: 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_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			if (categoryIndex < 0 || categoryIndex >= _categoryContainers.Count)
			{
				return;
			}
			_currentCategory = categoryIndex;
			HideSkillDetails();
			_cards.Clear();
			for (int i = 0; i < _categoryContainers.Count; i++)
			{
				((Component)_categoryContainers[i]).gameObject.SetActive(false);
			}
			RectTransform val = _categoryContainers[categoryIndex];
			ClearContainer(val);
			SkillCategory category = (SkillCategory)categoryIndex;
			SkillDefinition[] array = SkillCatalog.Definitions.Where((SkillDefinition definition) => definition.Enabled && definition.Category == category).ToArray();
			Color color = ((Graphic)((Component)_categoryButtons[categoryIndex]).GetComponent<Image>()).color;
			SkillDefinition[] array2 = array;
			foreach (SkillDefinition skillDefinition in array2)
			{
				GameObject val2 = Object.Instantiate<GameObject>(_skillPrefab, (Transform)(object)val);
				val2.SetActive(false);
				((Object)val2).name = "SharingXP Skill Card - " + skillDefinition.Id;
				((Object)val2).hideFlags = (HideFlags)0;
				try
				{
					Image component = val2.GetComponent<Image>();
					if ((Object)(object)component != (Object)null)
					{
						((Graphic)component).color = color;
					}
					SkillCardView skillCardView = val2.AddComponent<SkillCardView>();
					skillCardView.Initialize(skillDefinition, _skills);
					_cards.Add(skillCardView);
					val2.SetActive(true);
				}
				catch (Exception arg)
				{
					val2.transform.SetParent((Transform)null, false);
					val2.SetActive(false);
					Object.Destroy((Object)(object)val2);
					Logger.LogError((object)$"Could not initialize skill card {skillDefinition.Id}: {arg}");
				}
			}
			((Component)val).gameObject.SetActive(true);
			float num2 = Mathf.Max(215f, Mathf.Ceil((float)array.Length / 3f) * 215f);
			_content.sizeDelta = new Vector2(_content.sizeDelta.x, num2);
			_content.anchoredPosition = Vector2.zero;
			UpdateCategoryLabels();
		}

		private void ClearContainer(RectTransform container)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			List<Transform> list = new List<Transform>();
			foreach (Transform item2 in (Transform)container)
			{
				Transform item = item2;
				list.Add(item);
			}
			foreach (Transform item3 in list)
			{
				item3.SetParent((Transform)null, false);
				((Component)item3).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)item3).gameObject);
			}
		}

		private void EnsureCurrentCategoryIntegrity()
		{
			if ((Object)(object)_skills == (Object)null || _currentCategory < 0 || _currentCategory >= _categoryContainers.Count)
			{
				return;
			}
			SkillCategory category = (SkillCategory)_currentCategory;
			SkillDefinition[] array = SkillCatalog.Definitions.Where((SkillDefinition definition) => definition.Enabled && definition.Category == category).ToArray();
			RectTransform val = _categoryContainers[_currentCategory];
			bool flag = _cards.Count == array.Length && ((Transform)val).childCount == array.Length;
			if (flag)
			{
				for (int num = 0; num < array.Length; num++)
				{
					SkillCardView skillCardView = _cards[num];
					if ((Object)(object)skillCardView == (Object)null || (Object)(object)((Component)skillCardView).transform.parent != (Object)(object)val || !skillCardView.Matches(array[num]))
					{
						flag = false;
						break;
					}
				}
			}
			if (!flag)
			{
				Logger.LogWarning((object)($"Repairing skill UI category {category}: expected {array.Length} cards, " + $"found {((Transform)val).childCount}."));
				OpenCategory(_currentCategory);
				return;
			}
			foreach (SkillCardView card in _cards)
			{
				card.Refresh();
			}
		}

		private bool TryRebindLocalPlayer()
		{
			Player localPlayer = Player.m_localPlayer;
			PlayerLevelProgression progression = default(PlayerLevelProgression);
			PlayerSkillProgression skills = default(PlayerSkillProgression);
			if ((Object)(object)localPlayer == (Object)null || !((Component)localPlayer).TryGetComponent<PlayerLevelProgression>(ref progression) || !((Component)localPlayer).TryGetComponent<PlayerSkillProgression>(ref skills))
			{
				return false;
			}
			BindPlayer(localPlayer, progression, skills);
			HideSkillDetails();
			OpenCategory(_currentCategory);
			RefreshAll();
			Logger.LogInfo((object)"Progression UI rebound to the new local player instance.");
			return true;
		}

		private void BindPlayer(Player player, PlayerLevelProgression progression, PlayerSkillProgression skills)
		{
			UnbindPlayer();
			_player = player;
			_progression = progression;
			_skills = skills;
			_progression.ExperienceChanged += OnExperienceChanged;
			_progression.LevelChanged += OnLevelChanged;
			_skills.RankChanged += OnRankChanged;
			_skills.SkillsReset += OnSkillsReset;
			_skills.CombatEffectRequested += SkillVisualEffects.Play;
		}

		private void UnbindPlayer()
		{
			if ((Object)(object)_progression != (Object)null)
			{
				_progression.ExperienceChanged -= OnExperienceChanged;
				_progression.LevelChanged -= OnLevelChanged;
			}
			if ((Object)(object)_skills != (Object)null)
			{
				_skills.RankChanged -= OnRankChanged;
				_skills.SkillsReset -= OnSkillsReset;
				_skills.CombatEffectRequested -= SkillVisualEffects.Play;
			}
			_player = null;
			_progression = null;
			_skills = null;
		}

		private void OnExperienceChanged(int awarded, int totalExperience)
		{
			RefreshExperience();
			if (awarded > 0)
			{
				SpawnExperienceText(awarded);
			}
		}

		private void OnLevelChanged(int oldLevel, int newLevel)
		{
			RefreshAll();
			if (newLevel > oldLevel)
			{
				((MonoBehaviour)this).StartCoroutine(ShowLevelUp(newLevel));
				SkillVisualEffects.PlayLevelUp(_player);
			}
		}

		private void OnRankChanged(SkillId id, int oldRank, int newRank)
		{
			RefreshSkills();
		}

		private void OnSkillsReset()
		{
			RefreshSkills();
		}

		private void RefreshAll()
		{
			RefreshExperience();
			RefreshSkills();
		}

		private void RefreshExperience()
		{
			LevelEvaluationResult currentLevelEvaluation = _progression.CurrentLevelEvaluation;
			string value = Localize("$ls_level") + " " + _progression.GetLevel();
			SetText(_levelText, _levelShadow, value);
			if (currentLevelEvaluation.IsMaxLevel)
			{
				SetText(_xpText, _xpShadow, Localize("$ls_max_level"));
				_targetFill = 1f;
			}
			else
			{
				string value2 = Mathf.Max(0, _progression.GetTotalExperience() - currentLevelEvaluation.TotalExperience).ToString("N0", CultureInfo.CurrentCulture) + " / " + currentLevelEvaluation.MaxExperience.ToString("N0", CultureInfo.CurrentCulture);
				SetText(_xpText, _xpShadow, value2);
				_targetFill = _progression.GetProgress01();
			}
		}

		private void RefreshSkills()
		{
			if ((Object)(object)_skills == (Object)null)
			{
				return;
			}
			_remainingPoints.text = string.Format(Localize("$ls_remaining_points"), _skills.GetAvailablePoints());
			UpdateCategoryLabels();
			foreach (SkillCardView card in _cards)
			{
				card?.Refresh();
			}
		}

		private void UpdateCategoryLabels()
		{
			if ((Object)(object)_skills == (Object)null)
			{
				return;
			}
			for (int i = 0; i < _categoryButtons.Count; i++)
			{
				SkillCategory category = (SkillCategory)i;
				int num = SkillCatalog.Definitions.Where((SkillDefinition definition) => definition.Category == category).Sum((SkillDefinition definition) => _skills.GetRank(definition.Id));
				Text componentInChildren = ((Component)_categoryButtons[i]).GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = Localize("$ls_category_" + category.ToString().ToLowerInvariant()) + " (" + num + ")";
				}
			}
		}

		private IEnumerator ShowLevelUp(int level)
		{
			SetText(_levelUpText, _levelUpShadow, Localize("$ls_level") + " " + level);
			yield return Fade(_levelUpGroup, 0f, 1f, 0.5f);
			yield return (object)new WaitForSeconds(2.2f);
			yield return Fade(_levelUpGroup, 1f, 0f, 1.2f);
		}

		private static IEnumerator Fade(CanvasGroup group, float from, float to, float duration)
		{
			for (float time = 0f; time < duration; time += Time.deltaTime)
			{
				group.alpha = Mathf.Lerp(from, to, time / duration);
				yield return null;
			}
			group.alpha = to;
		}

		private void SpawnExperienceText(int awarded)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = PluginRuntime.Resources?.ExperienceTextPrefab;
			if (!((Object)(object)val == (Object)null) && !((Object)(object)_player == (Object)null))
			{
				Vector3 val2 = Random.insideUnitSphere * 0.22f;
				val2.y = Mathf.Abs(val2.y);
				GameObject obj = Object.Instantiate<GameObject>(val, ((Character)_player).GetTopPoint() + val2, Quaternion.identity);
				((Object)obj).hideFlags = (HideFlags)0;
				obj.SetActive(true);
				obj.AddComponent<FloatingTextEffect>().Configure("+" + awarded + " XP", new Color(0.45f, 0.9f, 1f, 1f));
			}
		}

		private T Find<T>(string path) where T : Component
		{
			return ((Component)((Component)this).transform.Find(path)).GetComponent<T>();
		}

		private static void SetText(Text text, Text shadow, string value)
		{
			text.text = value;
			shadow.text = value;
		}

		private static string Localize(string token)
		{
			return LocalizationSetup.Translate(token);
		}

		private static bool IsTyping()
		{
			if (!Console.IsVisible() && !TextInput.IsVisible())
			{
				if ((Object)(object)Chat.instance != (Object)null)
				{
					return Chat.instance.HasFocus();
				}
				return false;
			}
			return true;
		}
	}
	internal sealed class SkillCardView : MonoBehaviour
	{
		private SkillDefinition _definition;

		private PlayerSkillProgression _progression;

		private Text _nameText;

		private Text _nameShadow;

		private Text _bonusText;

		private Text _bonusShadow;

		private Text _description;

		private Text _levelText;

		private Text _levelShadow;

		private Text _maxLevelText;

		private Text _maxLabelText;

		private Text _totalBonusText;

		private Text _totalBonusShadow;

		private Image _icon;

		private Image _levelFill;

		private Button _addButton;

		private Button _removeButton;

		private Button _resetButton;

		private CanvasGroup _addGroup;

		private CanvasGroup _removeGroup;

		private CanvasGroup _resetGroup;

		private GameObject _resetText;

		private static Sprite _detailsButtonSprite;

		public bool IsInitialized
		{
			get
			{
				if (_definition != null)
				{
					return (Object)(object)_progression != (Object)null;
				}
				return false;
			}
		}

		public bool Matches(SkillDefinition definition)
		{
			if (IsInitialized && definition != null)
			{
				return _definition.Id == definition.Id;
			}
			return false;
		}

		public void Initialize(SkillDefinition definition, PlayerSkillProgression progression)
		{
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: 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_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Expected O, but got Unknown
			_definition = definition;
			_progression = progression;
			_nameText = Find<Text>("Skill Name");
			_nameShadow = Find<Text>("Skill Name/Skill Name Shadow");
			_bonusText = Find<Text>("Bonus Text");
			_bonusShadow = Find<Text>("Bonus Text/Bonus Text Shadow");
			_description = Find<Text>("Description");
			_levelText = Find<Text>("Skill Level");
			_levelShadow = Find<Text>("Skill Level/Skill Level Shadow");
			_maxLevelText = Find<Text>("Skill Level Ring/Skill Level Max");
			_maxLabelText = Find<Text>("Skill Level Ring/Skill Level Max/Skill Level Max Text");
			_totalBonusText = Find<Text>("Total Bonus");
			_totalBonusShadow = Find<Text>("Total Bonus/Total Bonus Shadow");
			_icon = Find<Image>("Skill Icon");
			_levelFill = Find<Image>("Skill Level Ring/Skill Level Fill Slider");
			_addButton = Find<Button>("Add Skill Point (Mask)/Add Skill Point");
			_removeButton = Find<Button>("Remove Skill Point (Mask)/Remove Skill Point");
			_resetButton = Find<Button>("Reset Skill Point (Mask)/Reset Skill Point");
			_addGroup = ((Component)_addButton).GetComponent<CanvasGroup>();
			_removeGroup = ((Component)_removeButton).GetComponent<CanvasGroup>();
			_resetGroup = ((Component)_resetButton).GetComponent<CanvasGroup>();
			Transform obj = ((Component)this).transform.Find("Reset Skill Point (Mask)/Reset Text");
			_resetText = ((obj != null) ? ((Component)obj).gameObject : null);
			((UnityEventBase)_addButton.onClick).RemoveAllListeners();
			((UnityEventBase)_removeButton.onClick).RemoveAllListeners();
			((UnityEventBase)_resetButton.onClick).RemoveAllListeners();
			((UnityEvent)_addButton.onClick).AddListener((UnityAction)delegate
			{
				_progression.TryAddRanks(_definition.Id, (!IsShiftPressed()) ? 1 : 10);
			});
			((UnityEvent)_removeButton.onClick).AddListener((UnityAction)delegate
			{
				_progression.TryRemoveRanks(_definition.Id, (!IsShiftPressed()) ? 1 : 10);
			});
			((UnityEvent)_resetButton.onClick).AddListener((UnityAction)delegate
			{
				_progression.ResetRank(_definition.Id);
			});
			_icon.sprite = SkillIconProvider.Load(_definition.IconName);
			((Component)_description).gameObject.SetActive(false);
			SetText(_totalBonusText, _totalBonusShadow, Localize("$ls_total_bonus"));
			_maxLabelText.text = Localize("$ls_max");
			CreateDetailsButton();
			_progression.RankChanged += OnRankChanged;
			_progression.SkillsReset += Refresh;
			Refresh();
		}

		private void OnEnable()
		{
			if (IsInitialized)
			{
				Refresh();
			}
		}

		private void OnDestroy()
		{
			if (!((Object)(object)_progression == (Object)null))
			{
				_progression.RankChanged -= OnRankChanged;
				_progression.SkillsReset -= Refresh;
			}
		}

		private void OnRankChanged(SkillId id, int oldRank, int newRank)
		{
			Refresh();
		}

		public void Refresh()
		{
			if (!((Object)(object)_progression == (Object)null) && _definition != null)
			{
				int rank = _progression.GetRank(_definition.Id);
				string value = Localize(_definition.NameToken);
				string text = _progression.GetBonus(_definition.Id).ToString("0.##", CultureInfo.InvariantCulture);
				string value2 = ((_definition.Category != SkillCategory.Rare) ? ("+" + text + _definition.Unit) : ((rank > 0) ? Localize("$ls_unlocked") : string.Format(Localize("$ls_cost_points"), _definition.PointCost)));
				SetText(_nameText, _nameShadow, value);
				SetText(_bonusText, _bonusShadow, value2);
				SetText(_levelText, _levelShadow, rank.ToString(CultureInfo.InvariantCulture));
				_maxLevelText.text = _definition.MaxRank.ToString(CultureInfo.InvariantCulture);
				_levelFill.fillAmount = 0.1245f + (float)rank / (float)_definition.MaxRank * 0.75f;
				Toggle(_addGroup, rank < _definition.MaxRank && _progression.GetAvailablePoints() >= _definition.PointCost);
				Toggle(_removeGroup, rank > 0);
				Toggle(_resetGroup, rank > 0);
				if ((Object)(object)_resetText != (Object)null)
				{
					_resetText.SetActive(rank > 0);
				}
			}
		}

		private void CreateDetailsButton()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Skill Details Button", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			})
			{
				layer = ((Component)this).gameObject.layer
			};
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(1f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(1f, 1f);
			component.anchoredPosition = new Vector2(-5f, -5f);
			component.sizeDelta = new Vector2(24f, 24f);
			Image component2 = val.GetComponent<Image>();
			component2.sprite = GetDetailsButtonSprite();
			((Graphic)component2).color = Color.white;
			component2.preserveAspect = true;
			Color color = default(Color);
			((Color)(ref color))..ctor(0.96f, 0.84f, 0.57f, 1f);
			CreateInfoMarkPart(val.transform, "Info Dot", new Vector2(3.2f, 3.2f), new Vector2(0f, 5.2f), color);
			CreateInfoMarkPart(val.transform, "Info Stem", new Vector2(3.2f, 8.5f), new Vector2(0f, -2.1f), color);
			val.AddComponent<SkillDetailsHover>().Initialize(_definition);
		}

		private static void CreateInfoMarkPart(Transform parent, string name, Vector2 size, Vector2 position, Color color)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			})
			{
				layer = ((Component)parent).gameObject.layer
			};
			val.transform.SetParent(parent, false);
			RectTransform component = val.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.anchoredPosition = position;
			component.sizeDelta = size;
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = color;
			((Graphic)component2).raycastTarget = false;
		}

		private static Sprite GetDetailsButtonSprite()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_detailsButtonSprite != (Object)null)
			{
				return _detailsButtonSprite;
			}
			Texture2D val = new Texture2D(48, 48, (TextureFormat)4, false)
			{
				name = "SharingXP Details Button",
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)61
			};
			Color[] array = (Color[])(object)new Color[2304];
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(24f, 24f);
			float num = 23f;
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(0.045f, 0.055f, 0.06f, 0.96f);
			Color val4 = default(Color);
			((Color)(ref val4))..ctor(0.79f, 0.63f, 0.34f, 1f);
			for (int i = 0; i < 48; i++)
			{
				for (int j = 0; j < 48; j++)
				{
					float num2 = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), val2);
					float num3 = Mathf.Clamp01(num + 0.75f - num2);
					if (!(num3 <= 0f))
					{
						Color val5 = ((num2 >= num - 3f) ? val4 : val3);
						val5.a *= num3;
						array[i * 48 + j] = val5;
					}
				}
			}
			val.SetPixels(array);
			val.Apply(false, true);
			_detailsButtonSprite = Sprite.Create(val, new Rect(0f, 0f, 48f, 48f), new Vector2(0.5f, 0.5f), 48f);
			((Object)_detailsButtonSprite).name = "SharingXP Details Button Sprite";
			((Object)_detailsButtonSprite).hideFlags = (HideFlags)61;
			return _detailsButtonSprite;
		}

		private T Find<T>(string path) where T : Component
		{
			return ((Component)((Component)this).transform.Find(path)).GetComponent<T>();
		}

		private static void SetText(Text text, Text shadow, string value)
		{
			text.text = value;
			shadow.text = value;
		}

		private static void Toggle(CanvasGroup group, bool visible)
		{
			if (!((Object)(object)group == (Object)null))
			{
				group.alpha = (visible ? 1f : 0.2f);
				group.interactable = visible;
				group.blocksRaycasts = visible;
			}
		}

		private static string Localize(string token)
		{
			return LocalizationSetup.Translate(token);
		}

		private static bool IsShiftPressed()
		{
			if (!Input.GetKey((KeyCode)304))
			{
				return Input.GetKey((KeyCode)303);
			}
			return true;
		}
	}
	internal sealed class SkillDetailsHover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerClickHandler
	{
		private SkillDefinition _definition;

		private bool _hovered;

		public void Initialize(SkillDefinition definition)
		{
			_definition = definition;
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			_hovered = true;
			ProgressionUiController.Instance?.ShowSkillDetails(_definition, eventData.position);
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			_hovered = false;
			ProgressionUiController.Instance?.HideSkillDetails();
		}

		public void OnPointerClick(PointerEventData eventData)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			ProgressionUiController.Instance?.ShowSkillDetails(_definition, eventData.position);
		}

		private void OnDisable()
		{
			if (_hovered)
			{
				ProgressionUiController.Instance?.HideSkillDetails();
			}
			_hovered = false;
		}
	}
	internal static class SkillIconProvider
	{
		private const string BundleIconPath = "Assets/_Leveling System/Sprites/Skill Icons/";

		private static readonly Dictionary<string, Sprite> Cache = new Dictionary<string, Sprite>();

		public static Sprite Load(string iconName)
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			if (Cache.TryGetValue(iconName, out var value))
			{
				return value;
			}
			ModResources resources = PluginRuntime.Resources;
			Sprite val = ((resources != null) ? resources.AssetBundle.LoadAsset<Sprite>("Assets/_Leveling System/Sprites/Skill Icons/" + iconName + ".png") : null);
			if ((Object)(object)val != (Object)null)
			{
				Cache[iconName] = val;
				return val;
			}
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith("Resources.skill_icons." + iconName + ".png", StringComparison.OrdinalIgnoreCase));
			if (text == null)
			{
				Logger.LogWarning((object)("Skill icon '" + iconName + "' was not found."));
				return null;
			}
			using Stream stream = executingAssembly.GetManifestResourceStream(text);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val2 = new Texture2D(2, 2, (TextureFormat)5, false)
			{
				name = iconName,
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			MethodInfo methodInfo = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, null);
			if (!(methodInfo != null) || !(bool)methodInfo.Invoke(null, new object[3]
			{
				val2,
				memoryStream.ToArray(),
				false
			}))
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			Sprite val3 = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f), 100f);
			Cache[iconName] = val3;
			return val3;
		}
	}
	internal static class SkillVisualEffects
	{
		public static void Play(Vector3 position, CombatEffectType effectType)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			if (effectType == CombatEffectType.FireFistHit)
			{
				RareSkillEffects.PlayFireImpact(position);
				return;
			}
			GameObject val = PluginRuntime.Resources?.CriticalHitEffectPrefab;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, position, Quaternion.identity);
			((Object)val2).hideFlags = (HideFlags)0;
			val2.SetActive(true);
			switch (effectType)
			{
			case CombatEffectType.SummonHit:
				Tint(val2, new Color(0.2f, 0.75f, 1f, 1f));
				break;
			case CombatEffectType.WeakHitBlocked:
				Tint(val2, new Color(0.1f, 0.95f, 1f, 1f));
				if ((Object)(object)GameCamera.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					GameCamera.instance.AddShake(((Component)Player.m_localPlayer).transform.position, 4f, 0.12f, false);
				}
				break;
			default:
				if ((Object)(object)GameCamera.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					GameCamera.instance.AddShake(((Component)Player.m_localPlayer).transform.position, 10f, 0.25f, false);
				}
				break;
			}
			Object.Destroy((Object)(object)val2, 4f);
		}

		public static void PlayLevelUp(Player player)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			PluginConfig config = PluginRuntime.Config;
			if (config == null || config.LevelUpVFX.Value)
			{
				GameObject val = PluginRuntime.Resources?.LevelUpEffectPrefab;
				if ((Object)(object)val != (Object)null)
				{
					GameObject obj = Object.Instantiate<GameObject>(val, ((Character)player).GetCenterPoint(), Quaternion.identity, ((Component)player).transform);
					((Object)obj).hideFlags = (HideFlags)0;
					obj.SetActive(true);
					Object.Destroy((Object)(object)obj, 6f);
				}
			}
		}

		private static void Tint(GameObject effect, Color color)
		{
			//IL_000f: 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)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystem[] componentsInChildren = effect.GetComponentsInChildren<ParticleSystem>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				MainModule main = componentsInChildren[i].main;
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(color);
			}
		}
	}
}
namespace CharacterProgressionMod.Skills
{
	public enum CombatEffectType
	{
		CriticalHit,
		SummonHit,
		WeakHitBlocked,
		FireFistHit
	}
	public sealed class PlayerSkillProgression : MonoBehaviour
	{
		private const string SaveKeyPrefix = "Cozyheim!Skill!";

		private const string LegacySaveKeyPrefix = "LevelingSystem_";

		private const string SecondLifeReadyAtKey = "Cozyheim!SecondLifeReadyAt";

		private static readonly IReadOnlyDictionary<SkillId, string> LegacySkillNames = new Dictionary<SkillId, string>
		{
			[SkillId.Vitality] = "HP",
			[SkillId.HealthRegen] = "HPRegen",
			[SkillId.Endurance] = "Stamina",
			[SkillId.StaminaRegen] = "StaminaRegen",
			[SkillId.ArcaneCapacity] = "Eitr",
			[SkillId.EitrRegen] = "EitrRegen",
			[SkillId.CarryWeight] = "CarryWeight",
			[SkillId.MovementSpeed] = "MovementSpeed",
			[SkillId.Woodcutting] = "Woodcutting",
			[SkillId.Mining] = "Mining",
			[SkillId.PhysicalDamage] = "PhysicalDamage",
			[SkillId.ElementalDamage] = "ElementalDamage",
			[SkillId.PhysicalResistance] = "PhysicalResistance",
			[SkillId.ElementalResistance] = "ElementalResistance",
			[SkillId.SlashResistance] = "ResistanceSlash",
			[SkillId.BluntResistance] = "ResistanceBlunt",
			[SkillId.PierceResistance] = "ResistancePierce",
			[SkillId.FireResistance] = "ResistanceFire",
			[SkillId.FrostResistance] = "ResistanceFrost",
			[SkillId.LightningResistance] = "ResistanceLightning",
			[SkillId.PoisonResistance] = "ResistancePoison",
			[SkillId.SpiritResistance] = "ResistanceSpirit",
			[SkillId.CriticalChance] = "CriticalChance",
			[SkillId.CriticalDamage] = "CriticalDamage"
		};

		private readonly Dictionary<SkillId, int> _ranks = new Dictionary<SkillId, int>();

		private Player _player;

		private PluginConfig _config;

		private bool _loaded;

		private bool _playerDataLoading;

		private string _combatEffectRpcId;

		private float _secondLifeProtectionUntil;

		public Player Player => _player;

		public bool IsSecondLifeProtected => Time.time < _secondLifeProtectionUntil;

		public event Action<SkillId, int, int> RankChanged;

		public event Action SkillsReset;

		public event Action<Vector3, CombatEffectType> CombatEffectRequested;

		private void Awake()
		{
			_player = ((Component)this).GetComponent<Player>();
			_combatEffectRpcId = RpcId.Generate("CombatSkillEffect");
			ZNetView nview = ((Character)_player).m_nview;
			if (((nview != null) ? nview.GetZDO() : null) != null)
			{
				((Character)_player).m_nview.Register<Vector3, int>(_combatEffectRpcId, (Action<long, Vector3, int>)RPC_CombatEffect);
			}
		}

		private void Start()
		{
			Load();
		}

		public void Configure(PluginConfig config)
		{
			_config = config;
		}

		public int GetRank(SkillId id)
		{
			if ((Object)(object)_player != (Object)null && !((Character)_player).IsOwner())
			{
				ZNetView nview = ((Character)_player).m_nview;
				int? obj;
				if (nview == null)
				{
					obj = null;
				}
				else
				{
					ZDO zDO = nview.GetZDO();
					obj = ((zDO != null) ? new int?(zDO.GetInt(GetNetworkRankKey(id), 0)) : ((int?)null));
				}
				int? num = obj;
				return Mathf.Clamp(num.GetValueOrDefault(), 0, SkillCatalog.Get(id).MaxRank);
			}
			if (_playerDataLoading && !_loaded)
			{
				return 0;
			}
			EnsureLoaded();
			if (!_ranks.TryGetValue(id, out var value))
			{
				return 0;
			}
			return value;
		}

		public void BeginPlayerDataLoad()
		{
			_playerDataLoading = true;
			_loaded = false;
			_ranks.Clear();
		}

		public void CompletePlayerDataLoad()
		{
			_playerDataLoading = false;
			_loaded = false;
			_ranks.Clear();
			Load();
		}

		public void PrepareForPlayerSave()
		{
			if (_loaded && !_playerDataLoading)
			{
				Save();
			}
		}

		public float GetBonus(SkillId id)
		{
			SkillDefinition skillDefinition = SkillCatalog.Get(id);
			if (!skillDefinition.Enabled)
			{
				return 0f;
			}
			return skillDefinition.BaseBonus + (float)GetRank(id) * skillDefinition.BonusPerRank;
		}

		public float GetMultiplier(SkillId id)
		{
			return 1f + GetBonus(id) / 100f;
		}

		public int GetSpentPoints()
		{
			EnsureLoaded();
			return SkillCatalog.Definitions.Where((SkillDefinition definition) => definition.Enabled).Sum((SkillDefinition definition) => GetRank(definition.Id) * definition.PointCost);
		}

		public int GetAvailablePoints()
		{
			PlayerLevelProgression component = ((Component)this).GetComponent<PlayerLevelProgression>();
			int num = Mathf.FloorToInt((float)(((Object)(object)component == (Object)null) ? 1 : Mathf.Max(1, component.GetLevel())) * (_config?.SkillPointsPerLevel.Value ?? 1f));
			return Mathf.Max(0, num - GetSpentPoints());
		}

		public bool TryAddRank(SkillId id)
		{
			return TryAddRanks(id, 1) > 0;
		}

		public int TryAddRanks(SkillId id, int requestedRanks)
		{
			EnsureLoaded();
			SkillDefinition skillDefinition = SkillCatalog.Get(id);
			if (!((Character)_player).IsOwner() || !skillDefinition.Enabled || requestedRanks <= 0)
			{
				return 0;
			}
			int rank = GetRank(id);
			int num = GetAvailablePoints() / skillDefinition.PointCost;
			int num2 = Mathf.Min(requestedRanks, Mathf.Min(skillDefinition.MaxRank - rank, num));
			if (num2 <= 0)
			{
				return 0;
			}
			SetRank(id, rank + num2);
			return num2;
		}

		public bool TryRemoveRank(SkillId id)
		{
			return TryRemoveRanks(id, 1) > 0;
		}

		public int TryRemoveRanks(SkillId id, int requestedRanks)
		{
			EnsureLoaded();
			if (((Character)_player).IsOwner())
			{
				PluginConfig config = _config;
				if ((config == null || config.AllowSkillRefunds.Value) && requestedRanks > 0)
				{
					int rank = GetRank(id);
					if (rank <= 0)
					{
						return 0;
					}
					int num = Mathf.Min(requestedRanks, rank);
					SetRank(id, rank - num);
					return num;
				}
			}
			return 0;
		}

		public bool HasSkill(SkillId id)
		{
			if (SkillCatalog.Get(id).Enabled)
			{
				return GetRank(id) > 0;
			}
			return false;
		}

		public double GetSecondLifeRemainingSeconds()
		{
			if ((Object)(object)_player == (Object)null || !_player.m_customData.TryGetValue("Cozyheim!SecondLifeReadyAt", out var value) || !long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return 0.0;
			}
			return Math.Max(0.0, new DateTime(resu