Decompiled source of LevelingSystem SharingXP v2.1.1

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.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
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.Events;
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.1.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.1.1.0")]
[module: UnverifiableCode]
namespace CharacterProgressionMod
{
	public sealed class RewardExpOnDeath : MonoBehaviour
	{
		private Character _character;

		private XpTable _creatureExperienceTable;

		public static XpTable CreatureExperienceTable { get; set; }

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

		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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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;
			}
			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) > NearbyPlayerXpRadius) && ((Component)allPlayer).TryGetComponent<PlayerLevelProgression>(ref playerLevelProgression))
				{
					playerLevelProgression.AddExperience(num);
				}
			}
		}
	}
	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)
		{
			if (entries.Length == 0)
			{
				Logger.LogWarning((object)"No entries was given.");
				return;
			}
			_entries = entries;
			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}.");
		}

		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 static readonly int NetworkLevelKey = StringExtensionMethods.GetStableHashCode("LevelingSystem.Level");

		private Player _player;

		private string _addExperienceRpcId;

		private string _setLevelRpcId;

		private LevelExperienceTable _levelExperienceTable;

		private LevelEvaluationResult _currentLevelEvaluation;

		public PluginConfig Config { get; set; }

		public LevelExperienceTable LevelExperienceTable
		{
			get
			{
				return _levelExperienceTable;
			}
			set
			{
				if (_levelExperienceTable != value)
				{
					_levelExperienceTable = value;
					if ((Object)(object)_player != (Object)null)
					{
						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()
		{
			UpdateLevel();
		}

		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;
				if (((Character)_player).GetSEMan().HaveStatusEffect(SEMan.s_statusEffectRested))
				{
					num *= Config?.RestedXpMultiplier.Value ?? 1.2f;
				}
				PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
				if (((Component)_player).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
				{
					num *= playerSkillProgression.GetMultiplier(SkillId.ExperienceGain);
				}
				int num2 = Mathf.Max(1, Mathf.RoundToInt((float)expReward * num));
				int totalExperience = GetTotalExperience() + num2;
				Logger.LogDebug((object)$"Added {num2:N0} experience (x{num:F2})");
				SetTotalExperience(totalExperience, num2);
			}
		}

		private void SetTotalExperience(int totalExperience, int awardedExperience)
		{
			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 (!_player.m_customData.TryGetValue("Cozyheim!TotalExperience", out var value) || !int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return 0;
			}
			return Mathf.Max(0, result);
		}

		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 (!_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()
		{
			return _currentLevelEvaluation.EvaluateProgressPercentage(GetTotalExperience()) / 100f;
		}

		private void UpdateLevel()
		{
			if (_levelExperienceTable == null || (Object)(object)_player == (Object)null)
			{
				return;
			}
			int level = GetLevel();
			_currentLevelEvaluation = _levelExperienceTable.EvaluateLevel(GetTotalExperience());
			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());
		}
	}
	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.");
			}
		}

		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();
			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)
				{
					Object.Instantiate<GameObject>(val);
					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 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 bool _initialized;

		private bool _menuVisible;

		private int _currentCategory;

		private float _targetFill;

		private float _fillVelocity;

		public static ProgressionUiController Instance { get; private set; }

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			_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");
			for (int i = 0; i < val.childCount; i++)
			{
				_categoryButtons.Add(((Component)val.GetChild(i)).GetComponent<Button>());
			}
			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;
			}
			_player = Player.m_localPlayer;
			_skillPrefab = PluginRuntime.Resources?.SkillUiPrefab;
			if ((Object)(object)_skillPrefab == (Object)null)
			{
				Logger.LogError((object)"SkillUI prefab was not registered.");
				yield break;
			}
			_progression.ExperienceChanged += OnExperienceChanged;
			_progression.LevelChanged += OnLevelChanged;
			_skills.RankChanged += OnRankChanged;
			_skills.SkillsReset += OnSkillsReset;
			_skills.CombatEffectRequested += SkillVisualEffects.Play;
			ConfigureLayout();
			ConfigureButtons();
			OpenCategory(0);
			RefreshAll();
			_initialized = true;
		}

		private void Update()
		{
			//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)
			if (!_initialized)
			{
				return;
			}
			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);
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			if (_menuVisible)
			{
				GUIManager.BlockInput(false);
			}
			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;
			}
		}

		private void ConfigureLayout()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			PluginConfig config = PluginRuntime.Config;
			_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 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");
			}
		}

		public void ToggleMenu(bool visible)
		{
			//IL_006a: 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 ((Object)(object)_content != (Object)null)
			{
				_content.anchoredPosition = Vector2.zero;
			}
			if (num)
			{
				GUIManager.BlockInput(visible);
			}
			if (visible && _initialized)
			{
				RefreshAll();
			}
		}

		private void OpenCategory(int categoryIndex)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			if (categoryIndex < 0 || categoryIndex >= _categoryContainers.Count)
			{
				return;
			}
			_currentCategory = categoryIndex;
			_cards.Clear();
			for (int i = 0; i < _categoryContainers.Count; i++)
			{
				((Component)_categoryContainers[i]).gameObject.SetActive(i == categoryIndex);
			}
			RectTransform val = _categoryContainers[categoryIndex];
			foreach (Transform item in (Transform)val)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			SkillCategory category = (SkillCategory)categoryIndex;
			SkillDefinition[] array = SkillCatalog.Definitions.Where((SkillDefinition skillDefinition) => skillDefinition.Enabled && skillDefinition.Category == category).ToArray();
			Color color = ((Graphic)((Component)_categoryButtons[categoryIndex]).GetComponent<Image>()).color;
			SkillDefinition[] array2 = array;
			foreach (SkillDefinition definition in array2)
			{
				GameObject obj = Object.Instantiate<GameObject>(_skillPrefab, (Transform)(object)val);
				Image component = obj.GetComponent<Image>();
				if ((Object)(object)component != (Object)null)
				{
					((Graphic)component).color = color;
				}
				SkillCardView skillCardView = obj.AddComponent<SkillCardView>();
				skillCardView.Initialize(definition, _skills);
				_cards.Add(skillCardView);
			}
			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 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_0097: 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);
				Object.Instantiate<GameObject>(val, ((Character)_player).GetTopPoint() + val2, Quaternion.identity).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)
		{
			if (Localization.instance != null)
			{
				return Localization.instance.Localize(token);
			}
			return token.TrimStart(new char[1] { '$' });
		}

		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 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;

		public void Initialize(SkillDefinition definition, PlayerSkillProgression progression)
		{
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: 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");
			_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.TryAddRank(_definition.Id);
			});
			((UnityEvent)_removeButton.onClick).AddListener((UnityAction)delegate
			{
				_progression.TryRemoveRank(_definition.Id);
			});
			((UnityEvent)_resetButton.onClick).AddListener((UnityAction)delegate
			{
				_progression.ResetRank(_definition.Id);
			});
			_icon.sprite = SkillIconProvider.Load(_definition.IconName);
			_progression.RankChanged += OnRankChanged;
			_progression.SkillsReset += Refresh;
			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 = "+" + text + _definition.Unit;
				SetText(_nameText, _nameShadow, value);
				SetText(_bonusText, _bonusShadow, value2);
				SetText(_levelText, _levelShadow, rank.ToString(CultureInfo.InvariantCulture));
				_maxLevelText.text = _definition.MaxRank.ToString(CultureInfo.InvariantCulture);
				_description.text = Localize(_definition.DescriptionToken) + "\n" + string.Format(Localize("$ls_per_rank"), _definition.BonusPerRank.ToString("0.##", CultureInfo.InvariantCulture), _definition.Unit);
				_levelFill.fillAmount = 0.1245f + (float)rank / (float)_definition.MaxRank * 0.75f;
				Toggle(_addGroup, rank < _definition.MaxRank && _progression.GetAvailablePoints() > 0);
				Toggle(_removeGroup, rank > 0);
				Toggle(_resetGroup, rank > 0);
				if ((Object)(object)_resetText != (Object)null)
				{
					_resetText.SetActive(rank > 0);
				}
			}
		}

		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)
		{
			if (Localization.instance != null)
			{
				return Localization.instance.Localize(token);
			}
			return token.TrimStart(new char[1] { '$' });
		}
	}
	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_001d: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: 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)
			GameObject val = PluginRuntime.Resources?.CriticalHitEffectPrefab;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, position, Quaternion.identity);
			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)
				{
					Object.Destroy((Object)(object)Object.Instantiate<GameObject>(val, ((Character)player).GetCenterPoint(), Quaternion.identity, ((Component)player).transform), 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
	}
	public sealed class PlayerSkillProgression : MonoBehaviour
	{
		private const string SaveKeyPrefix = "Cozyheim!Skill!";

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

		private Player _player;

		private PluginConfig _config;

		private bool _loaded;

		private string _combatEffectRpcId;

		public Player Player => _player;

		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);
			}
			EnsureLoaded();
			if (!_ranks.TryGetValue(id, out var value))
			{
				return 0;
			}
			return value;
		}

		public float GetBonus(SkillId id)
		{
			SkillDefinition skillDefinition = SkillCatalog.Get(id);
			if (!skillDefinition.Enabled)
			{
				return 0f;
			}
			return (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));
		}

		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)
		{
			EnsureLoaded();
			if (!((Character)_player).IsOwner() || !SkillCatalog.Get(id).Enabled || GetAvailablePoints() <= 0)
			{
				return false;
			}
			int rank = GetRank(id);
			if (rank >= SkillCatalog.Get(id).MaxRank)
			{
				return false;
			}
			SetRank(id, rank + 1);
			return true;
		}

		public bool TryRemoveRank(SkillId id)
		{
			EnsureLoaded();
			if (((Character)_player).IsOwner())
			{
				PluginConfig config = _config;
				if (config == null || config.AllowSkillRefunds.Value)
				{
					int rank = GetRank(id);
					if (rank <= 0)
					{
						return false;
					}
					SetRank(id, rank - 1);
					return true;
				}
			}
			return false;
		}

		public void ResetRank(SkillId id)
		{
			EnsureLoaded();
			if (((Character)_player).IsOwner())
			{
				PluginConfig config = _config;
				if (config == null || config.AllowSkillRefunds.Value)
				{
					SetRank(id, 0);
				}
			}
		}

		public void RequestCombatEffect(Vector3 position, CombatEffectType effectType)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_player == (Object)null)
			{
				return;
			}
			ZNetView nview = ((Character)_player).m_nview;
			if (((nview != null) ? nview.GetZDO() : null) != null)
			{
				if (((Character)_player).IsOwner())
				{
					RPC_CombatEffect(0L, position, (int)effectType);
					return;
				}
				((Character)_player).m_nview.InvokeRPC(_combatEffectRpcId, new object[2]
				{
					position,
					(int)effectType
				});
			}
		}

		public void ResetAll()
		{
			EnsureLoaded();
			if (!((Character)_player).IsOwner())
			{
				return;
			}
			PluginConfig config = _config;
			if (config != null && !config.AllowSkillRefunds.Value)
			{
				return;
			}
			foreach (SkillDefinition definition in SkillCatalog.Definitions)
			{
				if (GetRank(definition.Id) > 0)
				{
					SetRank(definition.Id, 0, save: false);
				}
			}
			Save();
			this.SkillsReset?.Invoke();
		}

		private void SetRank(SkillId id, int rank, bool save = true)
		{
			SkillDefinition skillDefinition = SkillCatalog.Get(id);
			int rank2 = GetRank(id);
			rank = Mathf.Clamp(rank, 0, skillDefinition.MaxRank);
			if (rank2 != rank)
			{
				_ranks[id] = rank;
				if (save)
				{
					Save();
				}
				this.RankChanged?.Invoke(id, rank2, rank);
				if (id == SkillId.SummonHealth)
				{
					SummonBonusService.RefreshOwnedSummons(_player);
				}
			}
		}

		private void RPC_CombatEffect(long sender, Vector3 position, int effectType)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (((Character)_player).IsOwner() && Enum.IsDefined(typeof(CombatEffectType), effectType))
			{
				this.CombatEffectRequested?.Invoke(position, (CombatEffectType)effectType);
			}
		}

		private void EnsureLoaded()
		{
			if (!_loaded)
			{
				Load();
			}
		}

		private void Load()
		{
			if (_loaded || (Object)(object)_player == (Object)null || _config == null)
			{
				return;
			}
			foreach (SkillDefinition definition in SkillCatalog.Definitions)
			{
				int result = 0;
				if (_player.m_customData.TryGetValue("Cozyheim!Skill!" + definition.Id, out var value))
				{
					int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
				}
				_ranks[definition.Id] = Mathf.Clamp(result, 0, definition.MaxRank);
			}
			_loaded = true;
			PublishRanks();
		}

		private void Save()
		{
			if (!((Character)_player).IsOwner())
			{
				return;
			}
			foreach (KeyValuePair<SkillId, int> rank in _ranks)
			{
				_player.m_customData["Cozyheim!Skill!" + rank.Key] = rank.Value.ToString(CultureInfo.InvariantCulture);
				ZNetView nview = ((Character)_player).m_nview;
				if (nview != null)
				{
					ZDO zDO = nview.GetZDO();
					if (zDO != null)
					{
						zDO.Set(GetNetworkRankKey(rank.Key), rank.Value, false);
					}
				}
			}
		}

		private void PublishRanks()
		{
			if (!((Character)_player).IsOwner())
			{
				return;
			}
			foreach (KeyValuePair<SkillId, int> rank in _ranks)
			{
				ZNetView nview = ((Character)_player).m_nview;
				if (nview != null)
				{
					ZDO zDO = nview.GetZDO();
					if (zDO != null)
					{
						zDO.Set(GetNetworkRankKey(rank.Key), rank.Value, false);
					}
				}
			}
		}

		private static int GetNetworkRankKey(SkillId id)
		{
			return StringExtensionMethods.GetStableHashCode("LevelingSystem.Skill." + id);
		}
	}
	public static class SkillCatalog
	{
		private static IReadOnlyList<SkillDefinition> _definitions = Array.Empty<SkillDefinition>();

		private static IReadOnlyDictionary<SkillId, SkillDefinition> _byId = new Dictionary<SkillId, SkillDefinition>();

		public static IReadOnlyList<SkillDefinition> Definitions => _definitions;

		public static void Initialize(PluginConfig config)
		{
			_definitions = new SkillDefinition[10]
			{
				Create(config, SkillId.SummonDamage, SkillCategory.Offensive, "SummonDamage", "%"),
				Create(config, SkillId.CriticalChance, SkillCategory.Offensive, "CriticalHitChance", "%"),
				Create(config, SkillId.CriticalDamage, SkillCategory.Offensive, "CriticalHitDamage", "%"),
				Create(config, SkillId.SummonHealth, SkillCategory.Defensive, "SummonHealth", "%"),
				Create(config, SkillId.Vitality, SkillCategory.Defensive, "HP", string.Empty),
				Create(config, SkillId.WeakHitImmunity, SkillCategory.Defensive, "WeakHitImmunity", string.Empty),
				Create(config, SkillId.Endurance, SkillCategory.Core, "Stamina", string.Empty),
				Create(config, SkillId.ArcaneCapacity, SkillCategory.Core, "Eitr", string.Empty),
				Create(config, SkillId.ExperienceGain, SkillCategory.Utility, "ExperienceGain", "%"),
				Create(config, SkillId.CarryWeight, SkillCategory.Utility, "CarryWeight", string.Empty)
			};
			_byId = _definitions.ToDictionary((SkillDefinition definition) => definition.Id);
		}

		public static SkillDefinition Get(SkillId id)
		{
			return _byId[id];
		}

		private static SkillDefinition Create(PluginConfig config, SkillId id, SkillCategory category, string iconName, string unit)
		{
			string text = ToSnakeCase(id.ToString());
			return new SkillDefinition(id, category, "$ls_skill_" + text, "$ls_skill_" + text + "_description", iconName, unit, config.SkillTunings[id]);
		}

		private static string ToSnakeCase(string value)
		{
			List<char> list = new List<char>(value.Length + 4);
			for (int i = 0; i < value.Length; i++)
			{
				char c = value[i];
				if (i > 0 && char.IsUpper(c))
				{
					list.Add('_');
				}
				list.Add(char.ToLowerInvariant(c));
			}
			return new string(list.ToArray());
		}
	}
	public enum SkillCategory
	{
		Offensive,
		Defensive,
		Core,
		Utility
	}
	public sealed class SkillDefinition
	{
		public SkillId Id { get; }

		public SkillCategory Category { get; }

		public string NameToken { get; }

		public string DescriptionToken { get; }

		public string IconName { get; }

		public string Unit { get; }

		public SkillTuning Tuning { get; }

		public bool Enabled => Tuning.Enabled.Value;

		public int MaxRank => Tuning.MaxRank.Value;

		public float BonusPerRank => Tuning.BonusPerRank.Value;

		public SkillDefinition(SkillId id, SkillCategory category, string nameToken, string descriptionToken, string iconName, string unit, SkillTuning tuning)
		{
			Id = id;
			Category = category;
			NameToken = nameToken;
			DescriptionToken = descriptionToken;
			IconName = iconName;
			Unit = unit;
			Tuning = tuning;
		}
	}
	public enum SkillId
	{
		SummonDamage,
		SummonHealth,
		ExperienceGain,
		Vitality,
		WeakHitImmunity,
		Endurance,
		ArcaneCapacity,
		CarryWeight,
		CriticalChance,
		CriticalDamage
	}
	public sealed class SkillTuning
	{
		public ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<int> MaxRank { get; }

		public ConfigEntry<float> BonusPerRank { get; }

		public SkillTuning(ConfigEntry<bool> enabled, ConfigEntry<int> maxRank, ConfigEntry<float> bonusPerRank)
		{
			Enabled = enabled;
			MaxRank = maxRank;
			BonusPerRank = bonusPerRank;
		}
	}
	internal static class SummonBonusService
	{
		private static readonly int BaseHealthKey = StringExtensionMethods.GetStableHashCode("LevelingSystem.SummonBaseHealth");

		private static readonly int HealthMultiplierKey = StringExtensionMethods.GetStableHashCode("LevelingSystem.SummonHealthMultiplier");

		public static void ApplyHealthBonus(Character summon, Player owner)
		{
			PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
			if ((Object)(object)summon == (Object)null || (Object)(object)owner == (Object)null || summon.IsPlayer() || !summon.IsOwner() || !((Component)owner).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
			{
				return;
			}
			ZNetView nview = summon.m_nview;
			ZDO val = ((nview != null) ? nview.GetZDO() : null);
			if (val != null)
			{
				float num = Mathf.Max(1f, val.GetFloat(HealthMultiplierKey, 1f));
				float num2 = Mathf.Max(1f, summon.GetMaxHealth());
				float num3 = val.GetFloat(BaseHealthKey, 0f);
				if (num3 <= 0f)
				{
					num3 = num2 / num;
					val.Set(BaseHealthKey, num3);
				}
				else if (!Mathf.Approximately(num2, num3 * num))
				{
					num3 = num2 / num;
					val.Set(BaseHealthKey, num3);
				}
				float multiplier = playerSkillProgression.GetMultiplier(SkillId.SummonHealth);
				float num4 = Mathf.Max(1f, num3 * multiplier);
				if (!Mathf.Approximately(num2, num4))
				{
					float num5 = Mathf.Clamp01(summon.GetHealth() / num2);
					summon.SetMaxHealth(num4);
					summon.SetHealth(Mathf.Max(1f, num4 * num5));
					val.Set(HealthMultiplierKey, multiplier);
				}
			}
		}

		public static void RefreshOwnedSummons(Player owner)
		{
			if ((Object)(object)owner == (Object)null || !((Character)owner).IsOwner())
			{
				return;
			}
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if ((Object)(object)allCharacter != (Object)null && (Object)(object)SummonOwnerResolver.Resolve(allCharacter) == (Object)(object)owner)
				{
					ApplyHealthBonus(allCharacter, owner);
				}
			}
		}
	}
	public static class SummonOwnerResolver
	{
		private static readonly int OwnerPlayerIdKey = StringExtensionMethods.GetStableHashCode("LevelingSystem.OwnerPlayerId");

		private static readonly List<Func<Character, Player>> ExternalResolvers = new List<Func<Character, Player>>();

		public static void RegisterResolver(Func<Character, Player> resolver)
		{
			if (resolver != null && !ExternalResolvers.Contains(resolver))
			{
				ExternalResolvers.Add(resolver);
			}
		}

		public static void UnregisterResolver(Func<Character, Player> resolver)
		{
			ExternalResolvers.Remove(resolver);
		}

		public static void RememberOwner(Character summon, Player owner)
		{
			if (!((Object)(object)summon == (Object)null) && !((Object)(object)owner == (Object)null) && !summon.IsPlayer())
			{
				ZNetView nview = summon.m_nview;
				ZDO val = ((nview != null) ? nview.GetZDO() : null);
				if (val != null && summon.IsOwner())
				{
					val.Set(OwnerPlayerIdKey, owner.GetPlayerID());
				}
				SummonBonusService.ApplyHealthBonus(summon, owner);
			}
		}

		public static Player Resolve(Character summon)
		{
			if ((Object)(object)summon == (Object)null || summon.IsPlayer())
			{
				return null;
			}
			Func<Character, Player>[] array = ExternalResolvers.ToArray();
			foreach (Func<Character, Player> func in array)
			{
				try
				{
					Player val = func(summon);
					if ((Object)(object)val != (Object)null)
					{
						RememberOwner(summon, val);
						return val;
					}
				}
				catch (Exception ex)
				{
					Logger.LogWarning((object)("A summon owner resolver failed: " + ex.Message));
				}
			}
			ZNetView nview = summon.m_nview;
			ZDO val2 = ((nview != null) ? nview.GetZDO() : null);
			long num = ((val2 != null) ? val2.GetLong(OwnerPlayerIdKey, 0L) : 0);
			if (num != 0L)
			{
				Player val3 = FindPlayer(num);
				if ((Object)(object)val3 != (Object)null)
				{
					return val3;
				}
			}
			BaseAI baseAI = summon.GetBaseAI();
			MonsterAI val4 = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
			if (val4 != null)
			{
				GameObject followTarget = val4.GetFollowTarget();
				Player val5 = (((Object)(object)followTarget == (Object)null) ? null : followTarget.GetComponent<Player>());
				if ((Object)(object)val5 != (Object)null)
				{
					RememberOwner(summon, val5);
					return val5;
				}
			}
			if (val2 != null)
			{
				string text = val2.GetString(ZDOVars.s_follow, string.Empty);
				if (!string.IsNullOrWhiteSpace(text))
				{
					foreach (Player allPlayer in Player.GetAllPlayers())
					{
						if ((Object)(object)allPlayer != (Object)null && allPlayer.GetPlayerName() == text)
						{
							RememberOwner(summon, allPlayer);
							return allPlayer;
						}
					}
				}
			}
			return null;
		}

		private static Player FindPlayer(long playerId)
		{
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null && allPlayer.GetPlayerID() == playerId)
				{
					return allPlayer;
				}
			}
			return null;
		}
	}
}
namespace CharacterProgressionMod.Patches
{
	[HarmonyPatch]
	internal static class Patcher
	{
		[HarmonyPatch(typeof(Character))]
		internal static class CharacterInitPatch
		{
			[HarmonyPostfix]
			[HarmonyPatch("Awake")]
			private static void CharacterAwake_Postfix(Character __instance)
			{
				if (!__instance.IsPlayer() && __instance.IsOwner())
				{
					((Component)__instance).gameObject.AddComponent<RewardExpOnDeath>();
				}
			}
		}

		[HarmonyPatch(typeof(Player))]
		internal static class PlayerInitPatch
		{
			[HarmonyPostfix]
			[HarmonyPatch("Awake")]
			private static void PlayerAwake_Postfix(Player __instance)
			{
				PlayerLevelProgression obj = ((Component)__instance).GetComponent<PlayerLevelProgression>() ?? ((Component)__instance).gameObject.AddComponent<PlayerLevelProgression>();
				obj.LevelExperienceTable = _resources.LevelExperienceTable;
				obj.Config = _config;
				(((Component)__instance).GetComponent<PlayerSkillProgression>() ?? ((Component)__instance).gameObject.AddComponent<PlayerSkillProgression>()).Configure(_config);
			}

			[HarmonyPostfix]
			[HarmonyPatch("Start")]
			private static void PlayerStart_Postfix(Player __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)ProgressionUiController.Instance != (Object)null))
				{
					((Component)__instance).gameObject.AddComponent<LocalPlayerUiSpawner>();
				}
			}
		}

		private static readonly Harmony Harmony = new Harmony("node.deepwolf.LevelingSystem");

		private static PluginConfig _config;

		private static ModResources _resources;

		public static void PatchAll(PluginConfig config, ModResources resources)
		{
			Harmony.PatchAll(Assembly.GetExecutingAssembly());
			_config = config;
			_resources = resources;
			RewardExpOnDeath.CreatureExperienceTable = resources.CreatureExperienceTable;
			RewardExpOnDeath.NearbyPlayerXpRadius = config.NearbyXpRadius.Value;
			ForagingExpSourcePatch.PickableExperienceTable = resources.PickableExperienceTable;
		}

		public static void Unpatch()
		{
			Harmony.UnpatchSelf();
		}
	}
	[HarmonyPatch]
	internal static class CreatureKillExpSourcePatch
	{
	}
	[HarmonyPatch]
	internal static class ForagingExpSourcePatch
	{
		public static XpTable PickableExperienceTable { get; set; }

		[HarmonyPatch(typeof(Pickable), "Interact")]
		[HarmonyPostfix]
		private static void Interact_Postfix(Pickable __instance, Humanoid character, bool __result)
		{
			if (!__result)
			{
				return;
			}
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val != null && __instance.m_picked)
			{
				int num = PickableExperienceTable?.GetXp(((Object)__instance).name) ?? 0;
				PlayerLevelProgression playerLevelProgression = default(PlayerLevelProgression);
				if (num > 0 && ((Component)val).TryGetComponent<PlayerLevelProgression>(ref playerLevelProgression))
				{
					playerLevelProgression.AddExperience(num);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class SkillBonusPatches
	{
		[HarmonyPatch(typeof(Character), "GetMaxHealth")]
		[HarmonyPostfix]
		private static void GetMaxHealth_Postfix(Character __instance, ref float __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
			if (val != null && ((Component)val).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
			{
				__result += playerSkillProgression.GetBonus(SkillId.Vitality);
			}
		}

		[HarmonyPatch(typeof(Player), "GetMaxStamina")]
		[HarmonyPostfix]
		private static void GetMaxStamina_Postfix(Player __instance, ref float __result)
		{
			PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
			if (((Component)__instance).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
			{
				__result += playerSkillProgression.GetBonus(SkillId.Endurance);
			}
		}

		[HarmonyPatch(typeof(Player), "GetMaxEitr")]
		[HarmonyPostfix]
		private static void GetMaxEitr_Postfix(Player __instance, ref float __result)
		{
			PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
			if (((Component)__instance).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
			{
				__result += playerSkillProgression.GetBonus(SkillId.ArcaneCapacity);
			}
		}

		[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
		[HarmonyPostfix]
		private static void GetMaxCarryWeight_Postfix(Player __instance, ref float __result)
		{
			PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
			if (((Component)__instance).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
			{
				__result += playerSkillProgression.GetBonus(SkillId.CarryWeight);
			}
		}

		[HarmonyPatch(typeof(Character), "ApplyDamage")]
		[HarmonyPrefix]
		[HarmonyPriority(600)]
		private static void ApplyDamage_Prefix(Character __instance, HitData hit)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (hit == null || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				ApplyWeakHitImmunity(val, hit);
			}
			else
			{
				if ((int)__instance.GetFaction() == 0)
				{
					return;
				}
				Character attacker = hit.GetAttacker();
				if ((Object)(object)attacker == (Object)null || (Object)(object)attacker == (Object)(object)__instance)
				{
					return;
				}
				Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
				if (val2 != null)
				{
					ApplyCriticalHit(val2, hit);
					return;
				}
				Player val3 = SummonOwnerResolver.Resolve(attacker);
				PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
				if ((Object)(object)val3 == (Object)null || !((Component)val3).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
				{
					return;
				}
				SummonBonusService.ApplyHealthBonus(attacker, val3);
				float multiplier = playerSkillProgression.GetMultiplier(SkillId.SummonDamage);
				if (!(multiplier <= 1f))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(multiplier);
					PluginConfig config = PluginRuntime.Config;
					if (config == null || config.SummonHitVfx.Value)
					{
						playerSkillProgression.RequestCombatEffect(__instance.GetCenterPoint(), CombatEffectType.SummonHit);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "DamageArmorDurability")]
		[HarmonyPrefix]
		[HarmonyPriority(600)]
		private static bool DamageArmorDurability_Prefix(Character __instance, HitData hit)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			PlayerSkillProgression skills;
			if (val != null)
			{
				return !ShouldBlockWeakHit(val, hit, out skills);
			}
			return true;
		}

		private static void ApplyWeakHitImmunity(Player victim, HitData hit)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldBlockWeakHit(victim, hit, out var skills))
			{
				((DamageTypes)(ref hit.m_damage)).Modify(0f);
				PluginConfig config = PluginRuntime.Config;
				if (config == null || config.WeakHitImmunityVfx.Value)
				{
					skills.RequestCombatEffect(((Character)victim).GetCenterPoint(), CombatEffectType.WeakHitBlocked);
				}
			}
		}

		private static bool ShouldBlockWeakHit(Player victim, HitData hit, out PlayerSkillProgression skills)
		{
			skills = null;
			if ((Object)(object)victim == (Object)null || hit == null || !((Component)victim).TryGetComponent<PlayerSkillProgression>(ref skills))
			{
				return false;
			}
			float bonus = skills.GetBonus(SkillId.WeakHitImmunity);
			float totalDamage = hit.GetTotalDamage();
			if (bonus <= 0f || totalDamage <= 0.1f || totalDamage > bonus)
			{
				return false;
			}
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker == (Object)null || (Object)(object)attacker == (Object)(object)victim || attacker.IsPlayer() || !BaseAI.IsEnemy(attacker, (Character)(object)victim))
			{
				return false;
			}
			if (attacker.IsBoss())
			{
				return PluginRuntime.Config?.WeakHitImmunityAffectsBosses.Value ?? false;
			}
			return true;
		}

		private static void ApplyCriticalHit(Player player, HitData hit)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			PlayerSkillProgression playerSkillProgression = default(PlayerSkillProgression);
			if (!((Component)player).TryGetComponent<PlayerSkillProgression>(ref playerSkillProgression))
			{
				return;
			}
			float num = playerSkillProgression.GetBonus(SkillId.CriticalChance) / 100f;
			if (!(num <= 0f) && !(Random.value >= num))
			{
				float num2 = 1.5f + playerSkillProgression.GetBonus(SkillId.CriticalDamage) / 100f;
				((DamageTypes)(ref hit.m_damage)).Modify(num2);
				PluginConfig config = PluginRuntime.Config;
				if (config == null || config.CriticalHitVfx.Value)
				{
					playerSkillProgression.RequestCombatEffect(hit.m_point, CombatEffectType.CriticalHit);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Tameable), "Command")]
	internal static class SummonOwnerPatch
	{
		[HarmonyPrefix]
		private static void Command_Prefix(Tameable __instance, Player __0)
		{
			SummonOwnerResolver.RememberOwner(((Object)(object)__instance == (Object)null) ? null : ((Component)__instance).GetComponent<Character>(), __0);
		}
	}
	[HarmonyPatch(typeof(Tameable), "RPC_Command")]
	internal static class SummonOwnerRpcPatch
	{
		[HarmonyPostfix]
		private static void Command_Postfix(Tameable __instance, ZDOID __1)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null))
			{
				GameObject val = ZNetScene.instance.FindInstance(__1);
				Player owner = (((Object)(object)val == (Object)null) ? null : val.GetComponent<Player>());
				SummonOwnerResolver.RememberOwner(((Component)__instance).GetComponent<Character>(), owner);
			}
		}
	}
}
namespace CharacterProgressionMod.Loaders
{
	public static class EmbeddedResourceLoader
	{
		public static string Load(string path)
		{
			return AssetUtils.LoadTextFromResources(path);
		}
	}
	public class FileResourceLoader
	{
		public static string Load(string path)
		{
			return string.Empty;
		}
	}
}
namespace CharacterProgressionMod.Core
{
	internal static class LocalizationSetup
	{
		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, English());
			text = "Russian";
			localization.AddTranslation(ref text, Russian());
		}

		private static Dictionary<string, string> English()
		{
			return new Dictionary<string, string>
			{
				["ls_level"] = "Level",
				["ls_max_level"] = "MAX LEVEL",
				["ls_remaining_points"] = "Skill points: {0}",
				["ls_reset_skills"] = "Reset skills",
				["ls_category_offensive"] = "Offensive",
				["ls_category_defensive"] = "Defensive",
				["ls_category_core"] = "Core",
				["ls_category_utility"] = "Utility",
				["ls_per_rank"] = "+{0}{1} per rank",
				["ls_skill_summon_damage"] = "Spirit Command",
				["ls_skill_summon_damage_description"] = "Increases damage dealt by creatures summoned or commanded by you, including compatible modded creatures.",
				["ls_skill_summon_health"] = "Spirit Ward",
				["ls_skill_summon_health_description"] = "Increases maximum health of creatures summoned or commanded by you.",
				["ls_skill_experience_gain"] = "Wisdom",
				["ls_skill_experience_gain_description"] = "Increases all experience earned by this progression system.",
				["ls_skill_vitality"] = "Vitality",
				["ls_skill_vitality_description"] = "Increases your maximum health.",
				["ls_skill_weak_hit_immunity"] = "Unyielding Ward",
				["ls_skill_weak_hit_immunity_description"] = "Completely negates hostile monster attacks whose direct damage, after armor and resistances, does not exceed the listed threshold. Players, environmental damage, damage over time and bosses are unaffected by default.",
				["ls_skill_endurance"] = "Endurance",
				["ls_skill_endurance_description"] = "Increases your maximum stamina.",
				["ls_skill_arcane_capacity"] = "Arcane Capacity",
				["ls_skill_arcane_capacity_description"] = "Increases your maximum Eitr.",
				["ls_skill_carry_weight"] = "Pack Bearer",
				["ls_skill_carry_weight_description"] = "Increases your maximum carry weight.",
				["ls_skill_critical_chance"] = "Keen Eye",
				["ls_skill_critical_chance_description"] = "Grants a chance for direct attacks to become critical hits.",
				["ls_skill_critical_damage"] = "Brutal Precision",
				["ls_skill_critical_damage_description"] = "Increases critical hit damage beyond its base 50% bonus."
			};
		}

		private static Dictionary<string, string> Russian()
		{
			return new Dictionary<string, string>
			{
				["ls_level"] = "Уровень",
				["ls_max_level"] = "МАКС. УРОВЕНЬ",
				["ls_remaining_points"] = "Очки навыков: {0}",
				["ls_reset_skills"] = "Сбросить навыки",
				["ls_category_offensive"] = "Атака",
				["ls_category_defensive"] = "Защита",
				["ls_category_core"] = "Основа",
				["ls_category_utility"] = "Полезное",
				["ls_per_rank"] = "+{0}{1} за ранг",
				["ls_skill_summon_damage"] = "Воля духов",
				["ls_skill_summon_damage_description"] = "Увеличивает урон призванных или следующих за вами существ, включая совместимых существ из других модов.",
				["ls_skill_summon_health"] = "Оберег духов",
				["ls_skill_summon_health_description"] = "Увеличивает максимальное здоровье призванных или следующих за вами существ.",
				["ls_skill_experience_gain"] = "Мудрость",
				["ls_skill_experience_gain_description"] = "Увеличивает весь опыт, получаемый в этой системе развития.",
				["ls_skill_vitality"] = "Живучесть",
				["ls_skill_vitality_description"] = "Увеличивает максимальное здоровье персонажа.",
				["ls_skill_weak_hit_immunity"] = "Несокрушимый оберег",
				["ls_skill_weak_hit_immunity_description"] = "Полностью отменяет прямой урон враждебных монстров, если после брони и сопротивлений он не превышает указанный порог. По умолчанию не действует на игроков, окружение, периодический урон и боссов.",
				["ls_skill_endurance"] = "Выносливость",
				["ls_skill_endurance_description"] = "Увеличивает максимальный запас выносливости.",
				["ls_skill_arcane_capacity"] = "Магический запас",
				["ls_skill_arcane_capacity_description"] = "Увеличивает максимальный запас эйтра.",
				["ls_skill_carry_weight"] = "Носильщик",
				["ls_skill_carry_weight_description"] = "Увеличивает максимальный переносимый вес.",
				["ls_skill_critical_chance"] = "Меткий глаз",
				["ls_skill_critical_chance_description"] = "Даёт прямым атакам шанс стать критическими.",
				["ls_skill_critical_damage"] = "Смертельная точность",
				["ls_skill_critical_damage_description"] = "Увеличивает урон критического удара сверх базового бонуса в 50%."
			};
		}
	}
	public class ModResources
	{
		private const string AssetsPath = "Assets/_Leveling System/";

		private readonly PluginConfig _config;

		public AssetBundle AssetBundle { get; }

		public LevelExperienceTable LevelExperienceTable { get; private set; }

		public XpTable CreatureExperienceTable { get; }

		public XpTable PickableExperienceTable { get; }

		public GameObject LevelingSystemUiPrefab { get; private set; }

		public GameObject SkillUiPrefab { get; private set; }

		public GameObject LevelUpEffectPrefab { get; private set; }

		public GameObject CriticalHitEffectPrefab { get; private set; }

		public GameObject ExperienceTextPrefab { get; private set; }

		public GameObject CriticalDamageTextPrefab { get; private set; }

		public ModResources(PluginConfig config)
		{
			_config = config;
			AssetBundle = AssetUtils.LoadAssetBundleFromResources("leveling_system");
			PrefabManager.OnVanillaPrefabsAvailable += LoadAssets;
			int value = _config.MaxLevel.Value;
			int value2 = _config.InitialMaxExperience.Value;
			string value3 = _config.MaxExperienceModifierFormula.Value;
			LevelExperienceTable = new LevelExperienceTable(new LevelTableGenerationSettings(value, value2, value3));
			CreatureExperienceTable = new XpTable(Assembly.GetExecutingAssembly(), PluginConfig.CustomCreaturesDirectory, allowGroups: false);
			PickableExperienceTable = new XpTable(Assembly.GetExecutingAssembly(), PluginConfig.CustomPickablesDirectory, allowGroups: false);
		}

		public void SetLevelExperienceTable(LevelExperienceTable experienceTable)
		{
			LevelExperienceTable = experienceTable;
		}

		private void LoadAssets()
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			LevelingSystemUiPrefab = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/LevelingSystemUI.prefab");
			LevelingSystemUiPrefab.AddComponent<ProgressionUiController>();
			LevelUpEffectPrefab = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/LevelUpEffectNew.prefab");
			CriticalHitEffectPrefab = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/CriticalHitEffect.prefab");
			ExperienceTextPrefab = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/XPText.prefab");
			CriticalDamageTextPrefab = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/CritDamageText.prefab");
			SkillUiPrefab = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/SkillUI.prefab");
			GameObject val = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/LevelingDummy.prefab");
			PieceManager.Instance.AddPiece(new CustomPiece(val, PieceTables.Hammer, false));
			GameObject val2 = AssetBundle.LoadAsset<GameObject>("Assets/_Leveling System/Prefabs/LevelingDummyStrawman.prefab");
			PieceManager.Instance.AddPiece(new CustomPiece(val2, PieceTables.Hammer, false));
			PrefabManager.OnVanillaPrefabsAvailable -= LoadAssets;
		}
	}
	public sealed class PluginConfig
	{
		private class ConfigEntryBuilder
		{
			private class EntrySettings
			{
				public string Section { get; set; }

				public string Key { get; set; }

				public ConfigDescription Description { get; set; }
			}

			private readonly ConfigFile _configFile;

			private readonly EntrySettings _entrySettings = new EntrySettings();

			private string _description = string.Empty;

			private AcceptableValueBase _acceptableValues;

			private ConfigurationManagerAttributes _attributes = new ConfigurationManagerAttributes();

			public ConfigEntryBuilder(ConfigFile configFile)
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Expected O, but got Unknown
				_configFile = configFile;
			}

			public ConfigEntryBuilder SetSection(string section)
			{
				_entrySettings.Section = section;
				return this;
			}

			public ConfigEntryBuilder SetKey(string key)
			{
				_entrySettings.Key = key;
				return this;
			}

			public ConfigEntryBuilder SetDescription(string description)
			{
				_description = description;
				return this;
			}

			public ConfigEntryBuilder SetAcceptableValues(AcceptableValueBase acceptableValues)
			{
				_acceptableValues = acceptableValues;
				return this;
			}

			public ConfigEntryBuilder RequireAdmin()
			{
				_attributes.IsAdminOnly = true;
				return this;
			}

			public ConfigEntry<TValue> Build<TValue>(TValue defaultValue = default(TValue))
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Expected O, but got Unknown
				_entrySettings.Description = new ConfigDescription(_description, _acceptableValues, new object[1] { _attributes });
				ConfigEntry<TValue> result = _configFile.Bind<TValue>(_entrySettings.Section, _entrySettings.Key, defaultValue, _entrySettings.Description);
				Reset();
				return result;
			}

			private void Reset()
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Expected O, but got Unknown
				_description = string.Empty;
				_acceptableValues = null;
				_attributes = new ConfigurationManagerAttributes();
			}
		}

		private const string CustomFolder = "custom";

		private const string CategoriesFolderName = "categories";

		private readonly ConfigFile _configFile;

		public static string CustomMiningDirectory => Path.Combine("custom", "mining");

		public static string CustomMiningCategoriesDirectory => Path.Combine("custom", "mining", "categories");

		public static string CustomWoodcuttingDirectory => Path.Combine("custom", "woodcutting");

		public static string CustomWoodcuttingCategoriesDirectory => Path.Combine("custom", "woodcutting", "categories");

		public static string CustomCreaturesDirectory => Path.Combine("custom", "creatures");

		public static string CustomPickablesDirectory => Path.Combine("custom", "pickables");

		public static string CustomPickablesCategoriesDirectory => Path.Combine("custom", "pickables", "categories");

		public static string CustomPlayerDirectory => Path.Combine("custom", "player");

		public ConfigEntry<bool> LevelUpVFX { get; }

		public ConfigEntry<bool> SummonHitVfx { get; }

		public ConfigEntry<bool> CriticalHitVfx { get; }

		public ConfigEntry<bool> WeakHitImmunityVfx { get; }

		public ConfigEntry<bool> ShowScrollbar { get; }

		public ConfigEntry<float> SkillPointsPerLevel { get; }

		public ConfigEntry<bool> AllowSkillRefunds { get; }

		public ConfigEntry<bool> WeakHitImmunityAffectsBosses { get; }

		public IReadOnlyDictionary<SkillId, SkillTuning> SkillTunings { get; }

		public ConfigEntry<int> MaxLevel { get; }

		public ConfigEntry<int> InitialMaxExperience { get; }

		public ConfigEntry<string> MaxExperienceModifierFormula { get; }

		public ConfigEntry<float> RestedXpMultiplier { get; }

		public ConfigEntry<float> NearbyXpRadius { get; }

		public ConfigEntry<KeyboardShortcut> OpenSkillsMenu { get; }

		public ConfigEntry<bool> ShowLevel { get; }

		public ConfigEntry<bool> ShowXp { get; }

		public ConfigEntry<float> XpBarScale { get; }

		public ConfigEntry<Vector2> XpBarPosition { get; }

		public PluginConfig(ConfigFile configFile)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			_configFile = configFile;
			_configFile.SaveOnConfigSet = true;
			string weakHitMaxRankKey = $"{SkillId.WeakHitImmunity}.MaxRank";
			bool flag = File.Exists(_configFile.ConfigFilePath) && File.ReadLines(_configFile.ConfigFilePath).Any((string line) => line.TrimStart(Array.Empty<char>()).StartsWith(weakHitMaxRankKey + " =", StringComparison.Ordinal));
			ConfigEntryBuilder configEntryBuilder = new ConfigEntryBuilder(_configFile);
			ShowLevel = configEntryBuilder.SetSection("HUD").SetKey("ShowLevel").Build(defaultValue: true);
			ShowXp = configEntryBuilder.SetKey("ShowXp").Build(defaultValue: true);
			XpBarScale = configEntryBuilder.SetKey("XpBarScale").SetAcceptableValues((AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f)).Build(100f);
			XpBarPosition = configEntryBuilder.SetKey("XpBarPosition").Build<Vector2>(Vector2.zero);
			ShowScrollbar = configEntryBuilder.SetSection("Menus").SetKey("ShowScrollbar").SetDescription("Unchecking this only disables the graphics, you will still be able to scroll.")
				.Build(defaultValue: true);
			MaxLevel = configEntryBuilder.SetSection("Progression").SetKey("MaxLevel").RequireAdmin()
				.SetAcceptableValues((AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 999))
				.Build(50);
			InitialMaxExperience = configEntryBuilder.SetKey("InitialMaxExperience").RequireAdmin().Build(75);
			MaxExperienceModifierFormula = configEntryBuilder.SetKey("MaxExperienceModifierFormula").RequireAdmin().Build("0=[10,15%]");
			SkillPointsPerLevel = configEntryBuilder.SetKey("SkillPointsPerLevel").RequireAdmin().Build(1f);
			RestedXpMultiplier = configEntryBuilder.SetKey("RestedXpMultiplier").RequireAdmin().SetAcceptableValues((AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f))
				.Build(1.2f);
			NearbyXpRadius = configEntryBuilder.SetKey("NearbyXpRadius").RequireAdmin().SetAcceptableValues((AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f))
				.Build(50f);
			AllowSkillRefunds = configEntryBuilder.SetSection("Skills").SetKey("AllowSkillRefunds").RequireAdmin()
				.Build(defaultValue: true);
			OpenSkillsMenu = configEntryBuilder.SetSection("Input").SetKey("OpenSkillsMenu").Build<KeyboardShortcut>(new KeyboardShortcut((KeyCode)105, Array.Empty<KeyCode>()));
			LevelUpVFX = configEntryBuilder.SetSection("VFX").SetKey("LevelUpVFX").Build(defaultValue: true);
			SummonHitVfx = configEntryBuilder.SetKey("SummonHitVfx").Build(defaultValue: true);
			CriticalHitVfx = configEntryBuilder.SetKey("CriticalHitVfx").Build(defaultValue: true);
			WeakHitImmunityVfx = configEntryBuilder.SetKey("WeakHitImmunityVfx").Build(defaultValue: true);
			WeakHitImmunityAffectsBosses = configEntryBuilder.SetSection("Skills").SetKey("WeakHitImmunityAffectsBosses").SetDescription("Allow Weak Hit Immunity to negate direct attacks from bosses.")
				.RequireAdmin()
				.Build(defaultValue: false);
			SkillTuning skillTuning = BindSkill(configEntryBuilder, SkillId.WeakHitImmunity, 20, 5f);
			SkillTunings = new Dictionary<SkillId, SkillTuning>
			{
				[SkillId.SummonDamage] = BindSkill(configEntryBuilder, SkillId.SummonDamage, 10, 4f),
				[SkillId.SummonHealth] = BindSkill(configEntryBuilder, SkillId.SummonHealth, 10, 5f),
				[SkillId.ExperienceGain] = BindSkill(configEntryBuilder, SkillId.ExperienceGain, 10, 2f),
				[SkillId.Vitality] = BindSkill(configEntryBuilder, SkillId.Vitality, 20, 5f),
				[SkillId.WeakHitImmunity] = skillTuning,
				[SkillId.Endurance] = BindSkill(configEntryBuilder, SkillId.Endurance, 20, 5f),
				[SkillId.ArcaneCapacity] = BindSkill(configEntryBuilder, SkillId.ArcaneCapacity, 20, 5f),
				[SkillId.CarryWeight] = BindSkill(configEntryBuilder, SkillId.CarryWeight, 20, 7.5f),
				[SkillId.CriticalChance] = BindSkill(configEntryBuilder, SkillId.CriticalChance, 10, 0.5f),
				[SkillId.CriticalDamage] = BindSkill(configEntryBuilder, SkillId.CriticalDamage, 10, 5f)
			};
			ConfigEntry<int> val = configEntryBuilder.SetSection("Internal").SetKey("ConfigRevision").Build(0);
			if (val.Value < 2)
			{
				if (flag && skillTuning.MaxRank.Value == 10)
				{
					skillTuning.MaxRank.Value = 20;
				}
				val.Value = 2;
			}
		}

		private static SkillTuning BindSkill(ConfigEntryBuilder builder, SkillId id, int defaultMaxRank, float defaultBonusPerRank)
		{
			ConfigEntry<bool> enabled = builder.SetSection("Skills").SetKey($"{id}.Enabled").RequireAdmin()
				.Build(defaultValue: true);
			ConfigEntry<int> maxRank = builder.SetKey($"{id}.MaxRank").RequireAdmin().SetAcceptableValues((AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100))
				.Build(defaultMaxRank);
			ConfigEntry<float> bonusPerRank = builder.SetKey($"{id}.BonusPerRank").RequireAdmin().SetAcceptableValues((AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f))
				.Build(defaultBonusPerRank);
			return new SkillTuning(enabled, maxRank, bonusPerRank);
		}
	}
	internal static class PluginDependencyFinder
	{
		public static class Guids
		{
			public const string SmoothbrainsJewelcrafting = "org.bepinex.plugins.jewelcrafting";
		}

		public static bool CanFind(string pluginGuid)
		{
			return Chainloader.PluginInfos.Values.Any((PluginInfo x) => x.Metadata.GUID == pluginGuid);
		}
	}
	public static class PluginInfo
	{
		public const string ModName = "LevelingSystem";

		public const string Version = "2.1.1";

		public const string Guid = "node.deepwolf.LevelingSystem";
	}
	[BepInPlugin("node.deepwolf.LevelingSystem", "LevelingSystem", "2.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal sealed class PluginInitializer : BaseUnityPlugin
	{
		private void Awake()
		{
			InitCommands();
			LocalizationSetup.Register();
			PluginConfig config = new PluginConfig(((BaseUnityPlugin)this).Config);
			SkillCatalog.Initialize(config);
			ModResources resources = new ModResources(config);
			PluginRuntime.Initialize(config, resources);
			Patcher.PatchAll(config, resources);
			static void InitCommands()
			{
				CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetLevelCommand());
				CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new LevelUpCommand());
			}
		}

		private void OnDestroy()
		{
			Patcher.Unpatch();
		}
	}
	internal static class PluginRuntime
	{
		public static PluginConfig Config { get; private set; }

		public static ModResources Resources { get; private set; }

		public static void Initialize(PluginConfig config, ModResources resources)
		{
			Config = config;
			Resources = resources;
		}
	}
	public static class RpcId
	{
		private const string Prefix = "node.deepwolf.LevelingSystem!";

		public static string Generate(string rpcName)
		{
			return "node.deepwolf.LevelingSystem!" + rpcName;
		}
	}
}
namespace CharacterProgressionMod.Commands
{
	public class LevelUpCommand : ConsoleCommand
	{
		public override string Name => "level_up";

		public override string Help => "Adds progression levels to a connected player. Usage: level_up <playerName|me> <amount>";

		public override bool IsCheat => true;

		public override bool IsNetwork => true;

		public override bool OnlyServer => true;

		public override void Run(string[] args)
		{
			if (args.Length < 2 || !int.TryParse(args[1], out var result) || result < 1)
			{
				SetLevelCommand.Print(((ConsoleCommand)this).Help);
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && !ZNet.instance.LocalPlayerIsAdminOrHost())
			{
				SetLevelCommand.Print("Only the server or host can change progression levels.");
				return;
			}
			Player val = SetLevelCommand.FindPlayer(args[0]);
			PlayerLevelProgression playerLevelProgression = default(PlayerLevelProgression);
			if ((Object)(object)val == (Object)null || !((Component)val).TryGetComponent<PlayerLevelProgression>(ref playerLevelProgression))
			{
				SetLevelCommand.Print("Player '" + args[0] + "' was not found on this peer.");
				return;
			}
			int num = playerLevelProgression.GetLevel() + result;
			playerLevelProgression.RequestSetLevel(num);
			SetLevelCommand.Print($"Requested level {num} for {val.GetPlayerName()}.");
		}
	}
	public class SetLevelCommand : ConsoleCommand
	{
		public override string Name => "set_level";

		public override string Help => "Sets a connected player's progression level. Usage: set_level <playerName|me> <level>";

		public override bool IsCheat => true;

		public override bool IsNetwork => true;

		public override bool OnlyServer => true;

		public override void Run(string[] args)
		{
			if (args.Length < 2 || !int.TryParse(args[1], out var result) || result < 1)
			{
				Print(((ConsoleCommand)this).Help);
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && !ZNet.instance.LocalPlayerIsAdminOrHost())
			{
				Print("Only the server or host can change progression levels.");
				return;
			}
			Player val = FindPlayer(args[0]);
			PlayerLevelProgression playerLevelProgression = default(PlayerLevelProgression);
			if ((Object)(object)val == (Object)null || !((Component)val).TryGetComponent<PlayerLevelProgression>(ref playerLevelProgression))
			{
				Print("Player '" + args[0] + "' was not found on this peer.");
				return;
			}
			playerLevelProgression.RequestSetLevel(result);
			Print($"Requested level {result} for {val.GetPlayerName()}.");
		}

		internal static Player FindPlayer(string name)
		{
			if (name.Equals("me", StringComparison.OrdinalIgnoreCase))
			{
				return Player.m_localPlayer;
			}
			return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player player) => (Object)(object)player != (Object)null && player.GetPlayerName().Equals(name, StringComparison.OrdinalIgnoreCase)));
		}

		internal static void Print(string message)
		{
			Console instance = Console.instance;
			if (instance != null)
			{
				instance.Print(message);
			}
		}
	}
}