Decompiled source of EquipmentExperience v1.0.1

plugins/EquipmentExperience.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EquipmentExperience")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Replaces the random Forge of Potential gamble with experience. Weapons, tools, shields and armor level up as you use them, a full bar guarantees the upgrade, and every refinement awakens guardians to fight.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+9f15a1302004ed43e207732883b5a72e2a054d72")]
[assembly: AssemblyProduct("EquipmentExperience")]
[assembly: AssemblyTitle("EquipmentExperience")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EquipmentExperience
{
	public static class Experience
	{
		public const string ExperienceKey = "EquipmentExperience.XP";

		private static readonly Dictionary<string, Recipe> ForgeRecipes = new Dictionary<string, Recipe>();

		private static ObjectDB _cachedDb;

		private static int _cachedRecipeCount;

		public static bool GainBeforeMaxLevel;

		public static PatchGroup RequireCore(PatchGroup group)
		{
			return group.RequireField(typeof(ItemData), "m_customData").RequireField(typeof(SharedData), "m_maxQuality").RequireField(typeof(ObjectDB), "m_recipes")
				.RequireField(typeof(Requirement), "m_upgraderResource");
		}

		public static Recipe GetForgeRecipe(ItemData item)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || item?.m_shared == null)
			{
				return null;
			}
			if ((Object)(object)instance != (Object)(object)_cachedDb || instance.m_recipes.Count != _cachedRecipeCount)
			{
				ForgeRecipes.Clear();
				_cachedDb = instance;
				_cachedRecipeCount = instance.m_recipes.Count;
			}
			string name = item.m_shared.m_name;
			if (!ForgeRecipes.TryGetValue(name, out var value))
			{
				foreach (Recipe recipe in instance.m_recipes)
				{
					if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && recipe.m_item.m_itemData.m_shared.m_name == name && GetIdol(recipe) != null)
					{
						value = recipe;
						break;
					}
				}
				ForgeRecipes[name] = value;
			}
			return value;
		}

		public static ItemData GetIdol(Recipe recipe)
		{
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val.m_upgraderResource && (Object)(object)val.m_resItem != (Object)null)
				{
					return val.m_resItem.m_itemData;
				}
			}
			return null;
		}

		public static bool IsAtMaxCraftingLevel(ItemData item)
		{
			if (item.m_shared.m_maxQuality > 1)
			{
				return item.m_quality >= item.m_shared.m_maxQuality;
			}
			return false;
		}

		public static bool CanGainExperience(ItemData item)
		{
			if (item?.m_shared != null && item.m_shared.m_maxQuality > 1 && (GainBeforeMaxLevel || IsAtMaxCraftingLevel(item)))
			{
				return (Object)(object)GetForgeRecipe(item) != (Object)null;
			}
			return false;
		}

		public static float GetRequired(ItemData item)
		{
			int num = Math.Max(0, item.m_quality - item.m_shared.m_maxQuality);
			return Plugin.BaseExperience.Value * Mathf.Pow(Plugin.ExperienceGrowth.Value, (float)num);
		}

		public static float GetExperience(ItemData item)
		{
			if (item.m_customData != null && item.m_customData.TryGetValue("EquipmentExperience.XP", out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 0f;
		}

		public static void SetExperience(ItemData item, float xp)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			item.m_customData["EquipmentExperience.XP"] = xp.ToString("R", CultureInfo.InvariantCulture);
		}

		public static float GetFill(ItemData item)
		{
			return Mathf.Clamp01(GetExperience(item) / GetRequired(item));
		}

		public static void AddExperience(ItemData item, float amount, int enemyTier)
		{
			if (amount <= 0f || !CanGainExperience(item))
			{
				return;
			}
			amount *= Plugin.ExperienceRate.Value * Tiers.Multiplier(item, enemyTier);
			float required = GetRequired(item);
			float experience = GetExperience(item);
			if (!(experience >= required))
			{
				float num = Mathf.Min(experience + amount, required);
				SetExperience(item, num);
				if (num >= required && Plugin.NotifyWhenReady.Value && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					string text = Localization.instance.Localize(item.m_shared.m_name);
					string text2 = (IsAtMaxCraftingLevel(item) ? (text + " is ready to be upgraded at the Forge of Potential") : (text + " has a full experience bar. Upgrade it to max level to refine it at the Forge of Potential"));
					((Character)Player.m_localPlayer).Message((MessageType)1, text2, 0, item.GetIcon(), false);
				}
			}
		}

		public static string FormatProgress(ItemData item)
		{
			float required = GetRequired(item);
			float num = Mathf.Min(GetExperience(item), required);
			return $"{Mathf.FloorToInt(num)} / {Mathf.CeilToInt(required)} ({Mathf.FloorToInt(num / required * 100f)}%)";
		}
	}
	public static class WeaponExperiencePatch
	{
		public static PatchGroup Group()
		{
			return new PatchGroup("Weapon and shield experience").With(Experience.RequireCore).RequireMethod(typeof(Humanoid), "GetCurrentWeapon").RequireMethod(typeof(Humanoid), "GetCurrentBlocker")
				.With(ArmorExperiencePatch.RequireArmorSlots)
				.Patch(typeof(WeaponExperiencePatch), typeof(Player), "RaiseSkill", new Type[2]
				{
					typeof(SkillType),
					typeof(float)
				}, (HarmonyPatchType)1, "Prefix");
		}

		private static void Prefix(Player __instance, SkillType skill, float value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Award(__instance, skill, value);
			}
			catch (Exception e)
			{
				Guard.Report("Weapon and shield experience", e);
			}
		}

		private static void Award(Player player, SkillType skill, float value)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer || (int)skill == 0)
			{
				return;
			}
			if ((int)skill == 6)
			{
				int enemyTier = Tiers.TakeTargetTier(((Component)player).transform.position);
				bool flag = value > 1f;
				float value2 = Plugin.ShieldExperienceRate.Value;
				Experience.AddExperience(((Humanoid)player).GetCurrentBlocker(), flag ? (value2 * Plugin.ParryExperienceMultiplier.Value) : value2, enemyTier);
				ArmorExperiencePatch.AddToWornArmor(player, value2 * Plugin.ArmorShareOfBlockExperience.Value, enemyTier);
			}
			else
			{
				ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
				if (currentWeapon != null && currentWeapon.m_shared.m_skillType == skill)
				{
					int enemyTier2 = Tiers.TakeTargetTier(((Component)player).transform.position);
					Experience.AddExperience(currentWeapon, value * Plugin.WeaponExperienceRate.Value, enemyTier2);
				}
			}
		}
	}
	public static class ArmorExperiencePatch
	{
		private static HitData _armorHit;

		private static float _damageBeforeArmor;

		public static PatchGroup Group()
		{
			return new PatchGroup("Armor experience").With(Experience.RequireCore).With(RequireArmorSlots).RequireMethod(typeof(Character), "GetMaxHealth")
				.Patch(typeof(ArmorExperiencePatch), typeof(HitData), "ApplyArmor", new Type[1] { typeof(float) }, (HarmonyPatchType)1, "ApplyArmorPrefix")
				.Patch(typeof(ArmorExperiencePatch), typeof(Player), "DamageArmorDurability", new Type[1] { typeof(HitData) }, (HarmonyPatchType)1, "DamageArmorDurabilityPrefix");
		}

		public static PatchGroup RequireArmorSlots(PatchGroup group)
		{
			return group.RequireField(typeof(Humanoid), "m_helmetItem").RequireField(typeof(Humanoid), "m_chestItem").RequireField(typeof(Humanoid), "m_legItem")
				.RequireField(typeof(Humanoid), "m_shoulderItem");
		}

		public static void AddToWornArmor(Player player, float amount, int enemyTier)
		{
			Experience.AddExperience(((Humanoid)player).m_helmetItem, amount, enemyTier);
			Experience.AddExperience(((Humanoid)player).m_chestItem, amount, enemyTier);
			Experience.AddExperience(((Humanoid)player).m_legItem, amount, enemyTier);
			Experience.AddExperience(((Humanoid)player).m_shoulderItem, amount, enemyTier);
		}

		private static void ApplyArmorPrefix(HitData __instance)
		{
			try
			{
				_armorHit = __instance;
				_damageBeforeArmor = ArmorDamage(__instance);
			}
			catch (Exception e)
			{
				Guard.Report("Armor experience", e);
			}
		}

		private static void DamageArmorDurabilityPrefix(Player __instance, HitData hit)
		{
			try
			{
				Award(__instance, hit);
			}
			catch (Exception e)
			{
				Guard.Report("Armor experience", e);
			}
			finally
			{
				_armorHit = null;
			}
		}

		private static void Award(Player player, HitData hit)
		{
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			if (!((Object)(object)attacker == (Object)null) && !((Object)(object)attacker == (Object)(object)player))
			{
				float num = ((hit == _armorHit) ? _damageBeforeArmor : ArmorDamage(hit));
				float maxHealth = ((Character)player).GetMaxHealth();
				if (!(maxHealth <= 0f))
				{
					float num2 = Mathf.Min(num / maxHealth, 1f);
					AddToWornArmor(player, num2 * Plugin.ArmorExperiencePerMaxHealth.Value, Tiers.EnemyTier(attacker));
				}
			}
		}

		private static float ArmorDamage(HitData hit)
		{
			return hit.GetTotalPhysicalDamage() + hit.GetTotalElementalDamage();
		}
	}
	public static class CraftingUpgradeCarryOverPatch
	{
		private struct Carried
		{
			public float Experience;

			public Vector2i Position;

			public string Name;

			public int Quality;
		}

		private static Carried? _carried;

		public static PatchGroup Group()
		{
			return new PatchGroup("Experience carry-over on crafting upgrades").With(Experience.RequireCore).RequireField(typeof(InventoryGui), "m_craftUpgradeItem").RequireField(typeof(CraftingStation), "m_upgrader")
				.RequireMethod(typeof(Inventory), "GetItemAt", typeof(int), typeof(int))
				.Patch(typeof(CraftingUpgradeCarryOverPatch), typeof(InventoryGui), "DoCrafting", new Type[1] { typeof(Player) }, (HarmonyPatchType)1, "Prefix")
				.Patch(typeof(CraftingUpgradeCarryOverPatch), typeof(InventoryGui), "DoCrafting", new Type[1] { typeof(Player) }, (HarmonyPatchType)2, "Postfix");
		}

		private static void Prefix(InventoryGui __instance, Player player)
		{
			_carried = null;
			try
			{
				Capture(__instance, player);
			}
			catch (Exception e)
			{
				Guard.Report("Experience carry-over on crafting upgrades", e);
			}
		}

		private static void Capture(InventoryGui gui, Player player)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			ItemData craftUpgradeItem = gui.m_craftUpgradeItem;
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if (craftUpgradeItem != null && (!((Object)(object)currentCraftingStation != (Object)null) || !currentCraftingStation.m_upgrader))
			{
				float experience = Experience.GetExperience(craftUpgradeItem);
				if (experience > 0f)
				{
					_carried = new Carried
					{
						Experience = experience,
						Position = craftUpgradeItem.m_gridPos,
						Name = craftUpgradeItem.m_shared.m_name,
						Quality = craftUpgradeItem.m_quality + 1
					};
				}
			}
		}

		private static void Postfix(Player player)
		{
			Carried? carried = _carried;
			_carried = null;
			if (!carried.HasValue)
			{
				return;
			}
			try
			{
				Restore(player, carried.Value);
			}
			catch (Exception e)
			{
				Guard.Report("Experience carry-over on crafting upgrades", e);
			}
		}

		private static void Restore(Player player, Carried carried)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			ItemData itemAt = ((Humanoid)player).GetInventory().GetItemAt(carried.Position.x, carried.Position.y);
			if (itemAt != null && itemAt.m_shared.m_name == carried.Name && itemAt.m_quality == carried.Quality)
			{
				Experience.SetExperience(itemAt, Mathf.Min(carried.Experience, Experience.GetRequired(itemAt)));
			}
		}
	}
	public static class TooltipPatch
	{
		public static PatchGroup Group()
		{
			return new PatchGroup("Tooltip experience bar").With(Experience.RequireCore).Patch(typeof(TooltipPatch), typeof(ItemData), "GetTooltip", new Type[6]
			{
				typeof(ItemData),
				typeof(int),
				typeof(bool),
				typeof(float),
				typeof(int),
				typeof(bool)
			}, (HarmonyPatchType)2, "Postfix");
		}

		private static void Postfix(ItemData item, bool crafting, ref string __result)
		{
			try
			{
				__result += Describe(item, crafting);
			}
			catch (Exception e)
			{
				Guard.Report("Tooltip experience bar", e);
			}
		}

		private static string Describe(ItemData item, bool crafting)
		{
			if (crafting || !Experience.CanGainExperience(item))
			{
				return "";
			}
			string text = ((Experience.GetFill(item) >= 1f) ? "#7FFF7F" : "orange");
			return "\nExperience: <color=" + text + ">" + Experience.FormatProgress(item) + "</color>";
		}
	}
	public readonly struct ForgeVerdict
	{
		public readonly bool Allowed;

		public readonly string Reason;

		private ForgeVerdict(bool allowed, string reason)
		{
			Allowed = allowed;
			Reason = reason;
		}

		public static ForgeVerdict Evaluate(ItemData item)
		{
			if (!Experience.IsAtMaxCraftingLevel(item))
			{
				return new ForgeVerdict(allowed: false, "Reach the maximum crafting level before using the Forge of Potential");
			}
			if (Experience.GetFill(item) < 1f)
			{
				return new ForgeVerdict(allowed: false, "Not enough experience to upgrade");
			}
			return new ForgeVerdict(allowed: true, null);
		}
	}
	public static class ForgeUpgradePatch
	{
		private struct PendingUpgrade
		{
			public int IdolTier;

			public int ForgeLevel;

			public Vector3 ForgePosition;

			public bool Performed;
		}

		private const float SuccessRoll = 0f;

		private static PendingUpgrade? _pending;

		public static PatchGroup Group()
		{
			return new PatchGroup("Forge of Potential upgrades").With(Experience.RequireCore).RequireField(typeof(InventoryGui), "m_craftRecipe").RequireField(typeof(InventoryGui), "m_craftUpgradeItem")
				.RequireField(typeof(CraftingStation), "m_upgrader")
				.RequireField(typeof(SharedData), "m_upgradeChance")
				.Patch(typeof(ForgeUpgradePatch), typeof(InventoryGui), "DoCrafting", new Type[1] { typeof(Player) }, (HarmonyPatchType)1, "Prefix")
				.Patch(typeof(ForgeUpgradePatch), typeof(InventoryGui), "DoCrafting", new Type[1] { typeof(Player) }, (HarmonyPatchType)4, "Finalizer")
				.Patch(typeof(ForgeUpgradePatch), typeof(Player), "GetItemUpgradeChance", new Type[1] { typeof(int) }, (HarmonyPatchType)1, "UpgradeChancePrefix");
		}

		private static bool Prefix(InventoryGui __instance, Player player)
		{
			_pending = null;
			try
			{
				return Decide(__instance, player);
			}
			catch (Exception e)
			{
				Guard.Report("Forge upgrade", e);
				((Character)player).Message((MessageType)2, "Equipment Experience error, upgrade blocked (see BepInEx log)", 0, (Sprite)null, false);
				return false;
			}
		}

		private static bool Decide(InventoryGui gui, Player player)
		{
			//IL_009f: 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)
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			ItemData craftUpgradeItem = gui.m_craftUpgradeItem;
			if ((Object)(object)gui.m_craftRecipe == (Object)null || craftUpgradeItem == null || (Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader)
			{
				return true;
			}
			if (Experience.GetIdol(gui.m_craftRecipe) == null)
			{
				return true;
			}
			ForgeVerdict forgeVerdict = ForgeVerdict.Evaluate(craftUpgradeItem);
			if (!forgeVerdict.Allowed)
			{
				((Character)player).Message((MessageType)2, forgeVerdict.Reason, 0, (Sprite)null, false);
				return false;
			}
			_pending = new PendingUpgrade
			{
				IdolTier = Tiers.IdolTier(gui.m_craftRecipe),
				ForgeLevel = craftUpgradeItem.m_quality + 1 - craftUpgradeItem.m_shared.m_maxQuality,
				ForgePosition = ((Component)currentCraftingStation).transform.position
			};
			return true;
		}

		private static void Finalizer(Exception __exception)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			PendingUpgrade? pending = _pending;
			_pending = null;
			if (__exception == null && pending.HasValue)
			{
				PendingUpgrade valueOrDefault = pending.GetValueOrDefault();
				if (valueOrDefault.Performed)
				{
					Guardians.Summon(valueOrDefault.IdolTier, valueOrDefault.ForgeLevel, valueOrDefault.ForgePosition);
				}
			}
		}

		private static bool UpgradeChancePrefix(ref float __result)
		{
			PendingUpgrade? pending = _pending;
			if (pending.HasValue)
			{
				PendingUpgrade valueOrDefault = pending.GetValueOrDefault();
				if (!valueOrDefault.Performed)
				{
					valueOrDefault.Performed = true;
					_pending = valueOrDefault;
					__result = 0f;
					return false;
				}
			}
			return true;
		}
	}
	public static class ForgePanelPatch
	{
		public static PatchGroup Group()
		{
			return new PatchGroup("Forge of Potential panel").With(Experience.RequireCore).RequireField(typeof(InventoryGui), "m_selectedRecipe").RequireField(typeof(InventoryGui), "m_itemCraftType")
				.RequireField(typeof(InventoryGui), "m_craftButton")
				.Patch(typeof(ForgePanelPatch), typeof(InventoryGui), "UpdateRecipe", new Type[2]
				{
					typeof(Player),
					typeof(float)
				}, (HarmonyPatchType)2, "Postfix");
		}

		private static void Postfix(InventoryGui __instance, Player player)
		{
			try
			{
				Update(__instance, player);
			}
			catch (Exception e)
			{
				Guard.Report("Forge of Potential panel", e);
			}
		}

		private static void Update(InventoryGui gui, Player player)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			ItemData itemData = ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData;
			Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe;
			if (!((Object)(object)currentCraftingStation == (Object)null) && currentCraftingStation.m_upgrader && itemData != null && !((Object)(object)recipe == (Object)null) && Experience.GetIdol(recipe) != null)
			{
				ForgeVerdict forgeVerdict = ForgeVerdict.Evaluate(itemData);
				string text;
				if (!Experience.IsAtMaxCraftingLevel(itemData))
				{
					text = "<color=orange>" + forgeVerdict.Reason + "</color>";
				}
				else
				{
					text = "Experience: " + Experience.FormatProgress(itemData) + "\n";
					text += ((!forgeVerdict.Allowed) ? ("<color=orange>" + forgeVerdict.Reason + "</color>") : (Guardians.WillSpawn ? "Ready. <color=orange>Refining will awaken the forge's guardians.</color>" : "Ready."));
				}
				((Component)gui.m_itemCraftType).gameObject.SetActive(true);
				gui.m_itemCraftType.text = text;
				if (!forgeVerdict.Allowed)
				{
					((Selectable)gui.m_craftButton).interactable = false;
					((Component)gui.m_craftButton).GetComponent<UITooltip>().m_text = forgeVerdict.Reason;
				}
			}
		}
	}
	public static class Guardians
	{
		private static readonly string[] TierNames = new string[8] { "Wooden", "Bronze", "Iron", "Silver", "BlackMetal", "BlackMarble", "Flametal", "Bloodgold" };

		private static readonly string[] DefaultGuardians = new string[8] { "Greydwarf_Elite:2", "Troll:1", "Abomination:1, Draugr_Elite:1", "StoneGolem:1", "GoblinBrute:1, Goblin:2", "SeekerBrute:1, Seeker:2", "FallenValkyrie:1, Charred_Melee:1", "JotunWarrior:1, JotunWitch:1" };

		private const int MaxStars = 2;

		private const float SpawnDelaySeconds = 2f;

		private const float MinSpawnDistance = 6f;

		private const float MaxSpawnDistance = 10f;

		private const float MaxSpawnHeightDifference = 4f;

		public static ConfigEntry<bool> Enabled;

		private static ConfigEntry<int> _forgeLevelsPerStar;

		private static ConfigEntry<int> _forgeLevelsPerExtraGuardian;

		private static ConfigEntry<int> _maxExtraGuardians;

		private static ConfigEntry<string>[] _tierGuardians;

		private static bool _available;

		public static bool WillSpawn
		{
			get
			{
				if (_available)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		public static void BindConfig(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Guardians", "SpawnGuardians", true, "Summon guardians at the Forge of Potential whenever an item is refined.");
			_forgeLevelsPerStar = config.Bind<int>("Guardians", "ForgeLevelsPerStar", 1, new ConfigDescription($"Guardians gain a star every this many forge levels past +1, up to {2} stars.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			_forgeLevelsPerExtraGuardian = config.Bind<int>("Guardians", "ForgeLevelsPerExtraGuardian", 2, new ConfigDescription("Once guardians have the maximum stars, every this many further forge levels adds one more copy of each guardian entry (starting with the first level after the stars max out).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			_maxExtraGuardians = config.Bind<int>("Guardians", "MaxExtraGuardians", 3, new ConfigDescription("Most extra copies added to each guardian entry by forge level.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>()));
			_tierGuardians = new ConfigEntry<string>[TierNames.Length];
			for (int i = 0; i < TierNames.Length; i++)
			{
				_tierGuardians[i] = config.Bind<string>("Guardians", $"Tier{i}{TierNames[i]}", DefaultGuardians[i], "Guardians summoned when refining with a " + TierNames[i] + " idol. Comma-separated Prefab:count or Prefab:count:stars entries; stars and count are the base values at forge level +1. Creatures from different factions fight each other, so keep each tier to one faction.");
			}
		}

		public static PatchGroup Requirements()
		{
			return new PatchGroup("Forge guardians").RequireMethod(typeof(ZNetScene), "GetPrefab", typeof(string)).RequireMethod(typeof(Character), "SetLevel", typeof(int)).RequireMethod(typeof(BaseAI), "SetHuntPlayer", typeof(bool))
				.RequireMethod(typeof(BaseAI), "Alert")
				.RequireMethod(typeof(ZoneSystem), "GetSolidHeight", typeof(Vector3), typeof(float).MakeByRefType(), typeof(int))
				.RequireField(typeof(ZoneSystem), "m_waterLevel");
		}

		public static void SetAvailable(bool available)
		{
			_available = available;
		}

		public static void Summon(int tier, int forgeLevel, Vector3 forgePosition)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (WillSpawn && tier >= 0 && tier < _tierGuardians.Length)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(SummonAfterDelay(tier, forgeLevel, forgePosition));
			}
		}

		private static IEnumerator SummonAfterDelay(int tier, int forgeLevel, Vector3 forgePosition)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			yield return (object)new WaitForSeconds(2f);
			try
			{
				Spawn(tier, forgeLevel, forgePosition);
			}
			catch (Exception e)
			{
				Guard.Report("Forge guardians", e);
			}
		}

		private static void Spawn(int tier, int forgeLevel, Vector3 forgePosition)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			(int BonusStars, int ExtraCopies) tuple = Scaling(forgeLevel);
			int item = tuple.BonusStars;
			int item2 = tuple.ExtraCopies;
			int num = 0;
			foreach (var item6 in Parse(_tierGuardians[tier].Value))
			{
				string item3 = item6.Prefab;
				int item4 = item6.Count;
				int item5 = item6.Stars;
				GameObject prefab = ZNetScene.instance.GetPrefab(item3);
				if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<Character>() == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Forge guardians: '" + item3 + "' is not a creature prefab, skipping."));
					continue;
				}
				for (int i = 0; i < item4 + item2; i++)
				{
					Vector3 val = FindSpawnPosition(forgePosition, ((Component)localPlayer).transform.position);
					Vector3 val2 = Vector3.ProjectOnPlane(((Component)localPlayer).transform.position - val, Vector3.up);
					Quaternion val3 = ((((Vector3)(ref val2)).sqrMagnitude > 0.01f) ? Quaternion.LookRotation(val2) : Quaternion.identity);
					GameObject obj = Object.Instantiate<GameObject>(prefab, val, val3);
					obj.GetComponent<Character>().SetLevel(1 + Math.Min(2, item5 + item));
					BaseAI component = obj.GetComponent<BaseAI>();
					if ((Object)(object)component != (Object)null)
					{
						component.SetHuntPlayer(true);
						component.Alert();
					}
					num++;
				}
			}
			if (num > 0)
			{
				((Character)localPlayer).Message((MessageType)2, "The forge's guardians awaken!", 0, (Sprite)null, false);
			}
		}

		public static (int BonusStars, int ExtraCopies) Scaling(int forgeLevel)
		{
			int num = Math.Max(1, _forgeLevelsPerStar.Value);
			int num2 = Math.Max(0, forgeLevel - 1);
			int item = Math.Min(2, num2 / num);
			int num3 = num2 - 2 * num;
			if (num3 <= 0)
			{
				return (BonusStars: item, ExtraCopies: 0);
			}
			int num4 = Math.Max(1, _forgeLevelsPerExtraGuardian.Value);
			int val = (num3 + num4 - 1) / num4;
			return (BonusStars: item, ExtraCopies: Math.Min(_maxExtraGuardians.Value, val));
		}

		private static IEnumerable<(string Prefab, int Count, int Stars)> Parse(string spec)
		{
			string[] array = spec.Split(',');
			foreach (string text in array)
			{
				string[] array2 = text.Trim().Split(':');
				if (array2[0].Length != 0)
				{
					int result = 1;
					int result2 = 0;
					if ((array2.Length > 1 && !int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) || (array2.Length > 2 && !int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2)))
					{
						Plugin.Log.LogWarning((object)("Forge guardians: can't parse '" + text.Trim() + "', expected Prefab:count[:stars]."));
					}
					else
					{
						yield return (Prefab: array2[0], Count: Mathf.Clamp(result, 0, 10), Stars: Mathf.Clamp(result2, 0, 2));
					}
				}
			}
		}

		private static Vector3 FindSpawnPosition(Vector3 forge, Vector3 fallback)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			Vector2 insideUnitCircle;
			float num2 = default(float);
			for (int i = 0; i < 30; i++)
			{
				insideUnitCircle = Random.insideUnitCircle;
				Vector2 normalized = ((Vector2)(ref insideUnitCircle)).normalized;
				float num = Random.Range(6f, 10f);
				Vector3 val = forge + new Vector3(normalized.x, 0f, normalized.y) * num;
				if (instance.GetSolidHeight(val, ref num2, 10) && num2 > instance.m_waterLevel && Mathf.Abs(num2 - forge.y) <= 4f)
				{
					val.y = num2 + 0.5f;
					return val;
				}
			}
			insideUnitCircle = Random.insideUnitCircle;
			Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * 6f;
			return fallback + new Vector3(val2.x, 1f, val2.y);
		}
	}
	public sealed class PatchGroup
	{
		private readonly string _name;

		private readonly List<string> _missing = new List<string>();

		private readonly List<(MethodBase Original, MethodInfo Patch, HarmonyPatchType Kind)> _patches = new List<(MethodBase, MethodInfo, HarmonyPatchType)>();

		public string Name => _name;

		public IReadOnlyList<string> Missing => _missing;

		public PatchGroup(string name)
		{
			_name = name;
		}

		public PatchGroup With(Func<PatchGroup, PatchGroup> requirements)
		{
			return requirements(this);
		}

		public PatchGroup RequireField(Type type, string name)
		{
			if (AccessTools.Field(type, name) == null)
			{
				_missing.Add("field " + type.Name + "." + name);
			}
			return this;
		}

		public PatchGroup RequireMethod(Type type, string name, params Type[] args)
		{
			if (AccessTools.Method(type, name, (args.Length == 0) ? null : args, (Type[])null) == null)
			{
				_missing.Add("method " + type.Name + "." + name);
			}
			return this;
		}

		public PatchGroup Patch(Type patchClass, Type type, string method, Type[] args, HarmonyPatchType kind, string patchMethod)
		{
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			MethodBase methodBase = AccessTools.Method(type, method, args, (Type[])null);
			if (methodBase == null)
			{
				_missing.Add("patch target " + type.Name + "." + method + "(" + string.Join(", ", Array.ConvertAll(args, (Type a) => a.Name)) + ")");
				return this;
			}
			MethodInfo methodInfo = AccessTools.Method(patchClass, patchMethod, (Type[])null, (Type[])null) ?? throw new MissingMethodException(patchClass.Name, patchMethod);
			ParameterInfo[] parameters = methodBase.GetParameters();
			ParameterInfo[] parameters2 = methodInfo.GetParameters();
			foreach (ParameterInfo parameter in parameters2)
			{
				if (!parameter.Name.StartsWith("__", StringComparison.Ordinal) && Array.TrueForAll(parameters, (ParameterInfo p) => p.Name != parameter.Name))
				{
					_missing.Add("parameter '" + parameter.Name + "' of " + type.Name + "." + method);
				}
			}
			_patches.Add((methodBase, methodInfo, kind));
			return this;
		}

		public bool Apply(Harmony harmony)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Invalid comparison between Unknown and I4
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Invalid comparison between Unknown and I4
			if (_missing.Count > 0)
			{
				Plugin.Log.LogError((object)(_name + ": disabled, game version " + Version.GetVersionString(false) + " is missing: " + string.Join("; ", _missing)));
				return false;
			}
			List<(MethodBase, MethodInfo)> list = new List<(MethodBase, MethodInfo)>();
			try
			{
				foreach (var patch in _patches)
				{
					MethodBase item = patch.Original;
					MethodInfo item2 = patch.Patch;
					HarmonyPatchType item3 = patch.Kind;
					HarmonyMethod val = new HarmonyMethod(item2);
					harmony.Patch(item, ((int)item3 == 1) ? val : null, ((int)item3 == 2) ? val : null, (HarmonyMethod)null, ((int)item3 == 4) ? val : null, (HarmonyMethod)null);
					list.Add((item, item2));
				}
			}
			catch (Exception arg)
			{
				foreach (var (methodBase, methodInfo) in list)
				{
					harmony.Unpatch(methodBase, methodInfo);
				}
				Plugin.Log.LogError((object)$"{_name}: disabled, patching failed on game version {Version.GetVersionString(false)}.\n{arg}");
				return false;
			}
			return true;
		}
	}
	public static class Guard
	{
		private static readonly HashSet<string> Reported = new HashSet<string>();

		public static void Report(string where, Exception e)
		{
			if (Reported.Add(where))
			{
				Plugin.Log.LogError((object)(where + " failed (further errors from it are suppressed), " + $"game version {Version.GetVersionString(false)}.\n{e}"));
			}
		}
	}
	[BepInPlugin("nerdtastic.equipmentexperience", "Equipment Experience", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "nerdtastic.equipmentexperience";

		public const string PluginName = "Equipment Experience";

		public const string PluginVersion = "1.0.1";

		public static ConfigEntry<float> ExperienceRate;

		public static ConfigEntry<float> WeaponExperienceRate;

		public static ConfigEntry<float> ShieldExperienceRate;

		public static ConfigEntry<float> ParryExperienceMultiplier;

		public static ConfigEntry<float> ArmorExperiencePerMaxHealth;

		public static ConfigEntry<float> ArmorShareOfBlockExperience;

		public static ConfigEntry<float> BaseExperience;

		public static ConfigEntry<float> ExperienceGrowth;

		public static ConfigEntry<bool> NotifyWhenReady;

		public static ManualLogSource Log;

		public static Plugin Instance;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Expected O, but got Unknown
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			ExperienceRate = ((BaseUnityPlugin)this).Config.Bind<float>("Experience", "ExperienceRate", 1f, new ConfigDescription("Global multiplier applied to all experience gained by items.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			WeaponExperienceRate = ((BaseUnityPlugin)this).Config.Bind<float>("Experience", "WeaponExperienceRate", 1f, new ConfigDescription("Experience a weapon or tool gains per point of skill experience it earns you (i.e. every hit that would raise your weapon/tool skill).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ShieldExperienceRate = ((BaseUnityPlugin)this).Config.Bind<float>("Experience", "ShieldExperienceRate", 2f, new ConfigDescription("Experience the blocking item (shield, or weapon if no shield) gains per block.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ParryExperienceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Experience", "ParryExperienceMultiplier", 2f, new ConfigDescription("Multiplier on the blocking item's experience for a parry (perfect block).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			ArmorExperiencePerMaxHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Experience", "ArmorExperiencePerMaxHealth", 12f, new ConfigDescription("Experience each equipped armor piece gains from a hit by an attacker that deals your full max health in damage; smaller hits give a proportional share (a hit for 25% of your health gives a quarter). Damage is measured after any block but before armor, and capped at your max health. Scaling by max health keeps armor leveling at a similar pace in every biome.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			ArmorShareOfBlockExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Experience", "ArmorShareOfBlockExperience", 0.5f, new ConfigDescription("Fraction of a block's experience (before the parry bonus) that each equipped armor piece also gains.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			BaseExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Leveling", "BaseExperience", 500f, new ConfigDescription("Experience needed to fill the bar for the first Forge of Potential upgrade (an item at its maximum crafting level).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1000000f), Array.Empty<object>()));
			ExperienceGrowth = ((BaseUnityPlugin)this).Config.Bind<float>("Leveling", "ExperienceGrowth", 1.5f, new ConfigDescription("Multiplier applied to the required experience for each further forge level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			NotifyWhenReady = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "NotifyWhenReady", true, "Show a message when an item's experience bar becomes full.");
			Guardians.BindConfig(((BaseUnityPlugin)this).Config);
			Tiers.BindConfig(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("nerdtastic.equipmentexperience");
			Tiers.SetAvailable(Tiers.Group().Apply(_harmony));
			Experience.GainBeforeMaxLevel = CraftingUpgradeCarryOverPatch.Group().Apply(_harmony);
			WeaponExperiencePatch.Group().Apply(_harmony);
			ArmorExperiencePatch.Group().Apply(_harmony);
			TooltipPatch.Group().Apply(_harmony);
			if (ForgeUpgradePatch.Group().Apply(_harmony))
			{
				ForgePanelPatch.Group().Apply(_harmony);
				Guardians.SetAvailable(Guardians.Requirements().Apply(_harmony));
			}
			else
			{
				Log.LogError((object)"The Forge of Potential is using the vanilla random upgrade mechanic.");
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public static class PatchGroups
	{
		public static PatchGroup[] All()
		{
			return new PatchGroup[8]
			{
				Tiers.Group(),
				CraftingUpgradeCarryOverPatch.Group(),
				WeaponExperiencePatch.Group(),
				ArmorExperiencePatch.Group(),
				TooltipPatch.Group(),
				ForgeUpgradePatch.Group(),
				ForgePanelPatch.Group(),
				Guardians.Requirements()
			};
		}
	}
	public static class Tiers
	{
		public const int Unknown = -1;

		private static readonly Regex IdolPrefab = new Regex("^Upgrader(\\d+)");

		private static readonly Dictionary<string, int> FactionTiers = new Dictionary<string, int>
		{
			["MountainMonsters"] = 3,
			["PlainsMonsters"] = 4,
			["MistlandsMonsters"] = 5,
			["Demon"] = 6,
			["DeepNorth"] = 7
		};

		private static ConfigEntry<float> _lowerTierMultiplier;

		private static ConfigEntry<float> _higherTierMultiplier;

		private static ConfigEntry<string> _enemyTierOverrides;

		private static string _parsedOverridesSource;

		private static Dictionary<string, int> _parsedOverrides = new Dictionary<string, int>();

		private static bool _available;

		private static int _targetTier = -1;

		private static int _targetFrame = -1;

		public static void BindConfig(ConfigFile config)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			_lowerTierMultiplier = config.Bind<float>("Tiers", "LowerTierMultiplier", 0.8f, new ConfigDescription("Experience multiplier for each biome the enemy is below the item's tier (0.8 = a Plains weapon earns 0.8^4 = 41% against Meadows enemies).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_higherTierMultiplier = config.Bind<float>("Tiers", "HigherTierMultiplier", 1.2f, new ConfigDescription("Experience multiplier for each biome the enemy is above the item's tier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
			_enemyTierOverrides = config.Bind<string>("Tiers", "EnemyTierOverrides", "Surtling:2, GoblinDeepNorth:7", "Comma-separated Prefab:tier entries for creatures whose tier isn't the biome they're in or their faction's home. Tiers: 0 Meadows, 1 Black Forest, 2 Swamp, 3 Mountains, 4 Plains, 5 Mistlands, 6 Ashlands, 7 Deep North.");
		}

		public static PatchGroup Group()
		{
			return new PatchGroup("Enemy tier tracking").RequireField(typeof(Character), "m_faction").RequireMethod(typeof(WorldGenerator), "GetBiome", typeof(Vector3)).Patch(typeof(Tiers), typeof(Character), "Damage", new Type[1] { typeof(HitData) }, (HarmonyPatchType)1, "DamagePrefix")
				.Patch(typeof(Tiers), typeof(Humanoid), "BlockAttack", new Type[2]
				{
					typeof(HitData),
					typeof(Character)
				}, (HarmonyPatchType)1, "BlockAttackPrefix");
		}

		public static void SetAvailable(bool available)
		{
			_available = available;
		}

		public static float Multiplier(ItemData item, int enemyTier)
		{
			int num = (_available ? ItemTier(item) : (-1));
			if (num == -1 || enemyTier == -1)
			{
				return 1f;
			}
			int num2 = enemyTier - num;
			if (num2 >= 0)
			{
				return Mathf.Pow(_higherTierMultiplier.Value, (float)num2);
			}
			return Mathf.Pow(_lowerTierMultiplier.Value, (float)(-num2));
		}

		public static int IdolTier(Recipe recipe)
		{
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val.m_upgraderResource && (Object)(object)val.m_resItem != (Object)null)
				{
					Match match = IdolPrefab.Match(((Object)((Component)val.m_resItem).gameObject).name);
					if (!match.Success)
					{
						return -1;
					}
					return int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
				}
			}
			return -1;
		}

		public static int ItemTier(ItemData item)
		{
			Recipe forgeRecipe = Experience.GetForgeRecipe(item);
			if (!((Object)(object)forgeRecipe != (Object)null))
			{
				return -1;
			}
			return IdolTier(forgeRecipe);
		}

		public static int EnemyTier(Character enemy)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (!_available || (Object)(object)enemy == (Object)null)
			{
				return -1;
			}
			if (Overrides().TryGetValue(PrefabName(((Component)enemy).gameObject), out var value))
			{
				return value;
			}
			if (FactionTiers.TryGetValue(((object)Unsafe.As<Faction, Faction>(ref enemy.m_faction)/*cast due to .constrained prefix*/).ToString(), out var value2))
			{
				return value2;
			}
			return LocationTier(((Component)enemy).transform.position);
		}

		public static int LocationTier(Vector3 position)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected I4, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			if (!_available || WorldGenerator.instance == null)
			{
				return -1;
			}
			Biome biome = WorldGenerator.instance.GetBiome(position);
			if ((int)biome <= 16)
			{
				switch (biome - 1)
				{
				default:
					if ((int)biome != 8)
					{
						if ((int)biome != 16)
						{
							break;
						}
						return 4;
					}
					return 1;
				case 0:
					return 0;
				case 1:
					return 2;
				case 3:
					return 3;
				case 2:
					break;
				}
			}
			else
			{
				if ((int)biome == 32)
				{
					return 6;
				}
				if ((int)biome == 64)
				{
					return 7;
				}
				if ((int)biome == 512)
				{
					return 5;
				}
			}
			return -1;
		}

		public static int TakeTargetTier(Vector3 playerPosition)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			int result = ((_targetFrame == Time.frameCount && _targetTier != -1) ? _targetTier : LocationTier(playerPosition));
			_targetTier = -1;
			_targetFrame = -1;
			return result;
		}

		private static void DamagePrefix(Character __instance, HitData hit)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && (Object)(object)__instance != (Object)(object)localPlayer && (Object)(object)hit.GetAttacker() == (Object)(object)localPlayer)
				{
					NoteTarget(EnemyTier(__instance));
				}
			}
			catch (Exception e)
			{
				Guard.Report("Enemy tier tracking", e);
			}
		}

		private static void BlockAttackPrefix(Humanoid __instance, Character attacker)
		{
			try
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && (Object)(object)attacker != (Object)null)
				{
					NoteTarget(EnemyTier(attacker));
				}
			}
			catch (Exception e)
			{
				Guard.Report("Enemy tier tracking", e);
			}
		}

		private static void NoteTarget(int tier)
		{
			if (_targetFrame != Time.frameCount)
			{
				_targetFrame = Time.frameCount;
				_targetTier = -1;
			}
			_targetTier = Math.Max(_targetTier, tier);
		}

		private static Dictionary<string, int> Overrides()
		{
			string value = _enemyTierOverrides.Value;
			if (value == _parsedOverridesSource)
			{
				return _parsedOverrides;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] array = value.Split(',');
			foreach (string text in array)
			{
				string[] array2 = text.Trim().Split(':');
				if (array2.Length == 2 && array2[0].Length > 0 && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					dictionary[array2[0]] = result;
				}
				else if (text.Trim().Length > 0)
				{
					Plugin.Log.LogWarning((object)("EnemyTierOverrides: can't parse '" + text.Trim() + "', expected Prefab:tier."));
				}
			}
			_parsedOverridesSource = value;
			_parsedOverrides = dictionary;
			return dictionary;
		}

		private static string PrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num < 0)
			{
				return name;
			}
			return name.Substring(0, num).TrimEnd();
		}
	}
	internal static class PluginInfo
	{
		public const string Version = "1.0.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}