Decompiled source of ValheimWeaponClasses v1.0.0

ValheimWeaponClasses.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.GUI;
using Jotunn.Managers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using ValheimWeaponClasses;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimWeaponClasses")]
[assembly: AssemblyDescription("Class Obelisk build piece for Valheim")]
[assembly: AssemblyProduct("ValheimWeaponClasses")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
public static class ArcherPerkManager
{
	private static Dictionary<long, float> arrowSlingerBuffs = new Dictionary<long, float>();

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

	public const float ARROW_SLINGER_DURATION = 10f;

	private const string Intro = "Expert marksmen with unparalleled bow and crossbow mastery.";

	private const string Outro = "Perfect for players who enjoy ranged combat and precision shooting.";

	public static readonly List<PerkInfo> Perks = new List<PerkInfo>
	{
		new PerkInfo
		{
			RequiredLevel = 10,
			Name = "Steady Draw",
			Description = "-15% stamina drain while drawing"
		},
		new PerkInfo
		{
			RequiredLevel = 20,
			Name = "Arrow Slinger",
			Description = "Arrows give a buff on hit that reduces draw time by 50% for 10 seconds"
		},
		new PerkInfo
		{
			RequiredLevel = 30,
			Name = "Wind Reader",
			Description = "+15% damage beyond 25m; -25% stamina while aiming"
		},
		new PerkInfo
		{
			RequiredLevel = 40,
			Name = "Magic Shot",
			Description = "50% chance to not consume an arrow on attack"
		},
		new PerkInfo
		{
			RequiredLevel = 50,
			Name = "Adrenaline Rush",
			Description = "Consecutive hits return 5% stamina"
		}
	};

	public static bool HasArcherPerk(Player player, int requiredLevel)
	{
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
		if (playerData == null || !playerData.IsClassActive(PlayerClass.Archer))
		{
			return false;
		}
		return playerData.GetClassLevel(PlayerClass.Archer) >= requiredLevel;
	}

	public static string GetClassDescription(Player player)
	{
		int currentLevel = PlayerClassManager.GetPlayerData(player)?.GetClassLevel(PlayerClass.Archer) ?? 0;
		return PerkDescriptionBuilder.Build("Expert marksmen with unparalleled bow and crossbow mastery.", Perks, "Perfect for players who enjoy ranged combat and precision shooting.", currentLevel);
	}

	public static float ApplyLv10_SteadyDrawStamina(Player player, float staminaCost)
	{
		if (!HasArcherPerk(player, 10))
		{
			return staminaCost;
		}
		if (((Character)player).IsDrawingBow())
		{
			return staminaCost * 0.85f;
		}
		return staminaCost;
	}

	public static void ApplyArrowSlingerDrawSpeed(Player player, ItemData weapon)
	{
		if (HasArcherPerk(player, 20) && weapon?.m_shared?.m_attack != null)
		{
			string name = weapon.m_shared.m_name;
			Attack attack = weapon.m_shared.m_attack;
			if (!originalDrawDurations.ContainsKey(name))
			{
				originalDrawDurations[name] = attack.m_drawDurationMin;
			}
			long playerID = player.GetPlayerID();
			if (arrowSlingerBuffs.ContainsKey(playerID) && Time.time < arrowSlingerBuffs[playerID])
			{
				attack.m_drawDurationMin = originalDrawDurations[name] * 0.5f;
			}
			else
			{
				attack.m_drawDurationMin = originalDrawDurations[name];
			}
		}
	}

	public static void RestoreDrawDuration(ItemData weapon)
	{
		if (weapon?.m_shared?.m_attack != null)
		{
			string name = weapon.m_shared.m_name;
			if (originalDrawDurations.ContainsKey(name))
			{
				weapon.m_shared.m_attack.m_drawDurationMin = originalDrawDurations[name];
			}
		}
	}

	public static void TriggerArrowSlingerBuff(Player archer)
	{
		if (HasArcherPerk(archer, 20))
		{
			long playerID = archer.GetPlayerID();
			float num = Time.time + 10f;
			if (!arrowSlingerBuffs.ContainsKey(playerID) || arrowSlingerBuffs[playerID] < num)
			{
				arrowSlingerBuffs[playerID] = num;
				AddArrowSlingerStatusEffect(archer);
				((Character)archer).Message((MessageType)1, "Arrow Slinger: 50% faster draw for 10s!", 0, (Sprite)null);
			}
		}
	}

	public static void AddArrowSlingerStatusEffect(Player player)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan != null)
			{
				RemoveArrowSlingerStatusEffect(player);
				ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
				Sprite val = ((currentWeapon != null) ? currentWeapon.GetIcon() : null);
				if ((Object)(object)val == (Object)null)
				{
					val = GetDefaultBowIcon();
				}
				SE_Stats val2 = ScriptableObject.CreateInstance<SE_Stats>();
				((Object)val2).name = "SE_ArrowSlinger";
				((StatusEffect)val2).m_name = "Arrow Slinger";
				((StatusEffect)val2).m_tooltip = "Draw speed increased by 50%";
				((StatusEffect)val2).m_icon = val;
				((StatusEffect)val2).m_ttl = 10f;
				((StatusEffect)val2).m_startMessage = "";
				((StatusEffect)val2).m_startMessageType = (MessageType)2;
				((StatusEffect)val2).m_stopMessage = "";
				((StatusEffect)val2).m_stopMessageType = (MessageType)2;
				sEMan.AddStatusEffect((StatusEffect)(object)val2, true, 0, 0f);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error adding Arrow Slinger status effect: " + ex.Message));
		}
	}

	private static void RemoveArrowSlingerStatusEffect(Player player)
	{
		try
		{
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan != null)
			{
				sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_ArrowSlinger"), true);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error removing Arrow Slinger status effect: " + ex.Message));
		}
	}

	private static Sprite GetDefaultBowIcon()
	{
		try
		{
			string[] array = new string[5] { "Bow", "BowFineWood", "BowHuntsman", "BowDraugrFang", "CrossbowArbalest" };
			foreach (string text in array)
			{
				ObjectDB instance = ObjectDB.instance;
				GameObject val = ((instance != null) ? instance.GetItemPrefab(text) : null);
				if ((Object)(object)val != (Object)null)
				{
					ItemDrop component = val.GetComponent<ItemDrop>();
					object obj;
					if (component == null)
					{
						obj = null;
					}
					else
					{
						ItemData itemData = component.m_itemData;
						obj = ((itemData != null) ? itemData.GetIcon() : null);
					}
					if ((Object)obj != (Object)null)
					{
						return component.m_itemData.GetIcon();
					}
				}
			}
			return null;
		}
		catch
		{
			return null;
		}
	}

	public static bool HasActiveArrowSlingerBuff(long playerID)
	{
		if (arrowSlingerBuffs.ContainsKey(playerID))
		{
			return Time.time < arrowSlingerBuffs[playerID];
		}
		return false;
	}

	public static float ApplyLv30_WindReaderDamage(Player archer, Vector3 shotOrigin, Vector3 hitPoint, float baseDamage)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		if (!HasArcherPerk(archer, 30))
		{
			return baseDamage;
		}
		float num = Vector3.Distance(shotOrigin, hitPoint);
		if (num > 25f)
		{
			float num2 = baseDamage * 0.15f;
			if (Random.Range(0f, 1f) < 0.2f)
			{
				((Character)archer).Message((MessageType)1, $"Long Shot! +15% damage ({num:F0}m)", 0, (Sprite)null);
			}
			return baseDamage + num2;
		}
		return baseDamage;
	}

	public static float ApplyLv30_WindReaderStamina(Player archer, float staminaDrain)
	{
		if (!HasArcherPerk(archer, 30))
		{
			return staminaDrain;
		}
		if (((Character)archer).IsDrawingBow())
		{
			return staminaDrain * 0.75f;
		}
		return staminaDrain;
	}

	public static bool ShouldConsumeArrow(Player archer)
	{
		if (!HasArcherPerk(archer, 40))
		{
			return true;
		}
		if (Random.Range(0f, 1f) < 0.5f)
		{
			((Character)archer).Message((MessageType)1, "Magic Shot! Arrow not consumed", 0, (Sprite)null);
			return false;
		}
		return true;
	}

	public static void TriggerAdrenalineRush(Player archer)
	{
		if (HasArcherPerk(archer, 50))
		{
			float num = ((Character)archer).GetMaxStamina() * 0.05f;
			((Character)archer).AddStamina(num);
			if (Random.Range(0f, 1f) < 0.3f)
			{
				((Character)archer).Message((MessageType)1, $"Adrenaline Rush! +{num:F0} stamina", 0, (Sprite)null);
			}
		}
	}

	public static void UpdateBuffs()
	{
		float currentTime = Time.time;
		foreach (long playerID in (from kvp in arrowSlingerBuffs
			where kvp.Value < currentTime
			select kvp.Key).ToList())
		{
			arrowSlingerBuffs.Remove(playerID);
			Player val = ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => p.GetPlayerID() == playerID));
			if ((Object)(object)val != (Object)null)
			{
				RemoveArrowSlingerStatusEffect(val);
			}
		}
	}
}
[HarmonyPatch]
public static class ArcherPerkPatches
{
	private static float lastCleanupTime;

	[HarmonyPatch(typeof(Player), "UpdateAttackBowDraw")]
	[HarmonyPrefix]
	public static void Player_UpdateAttackBowDraw_Prefix(Player __instance, ItemData weapon, float dt)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && weapon != null && ClassCombatManager.IsBowWeapon(weapon))
			{
				ArcherPerkManager.ApplyArrowSlingerDrawSpeed(__instance, weapon);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Player_UpdateAttackBowDraw_Prefix (Archer): " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Projectile), "OnHit")]
	[HarmonyPrefix]
	public static void Projectile_OnHit_Prefix(Projectile __instance, Collider collider, Vector3 hitPoint)
	{
		try
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)collider == (Object)null)
			{
				return;
			}
			Character component = ((Component)collider).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || component is Player)
			{
				return;
			}
			Player val = FindProjectileOwner(__instance);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(val);
			if (playerData != null && playerData.IsClassActive(PlayerClass.Archer))
			{
				ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon();
				if (ClassCombatManager.IsBowWeapon(currentWeapon))
				{
					ArcherPerkManager.RestoreDrawDuration(currentWeapon);
				}
				ArcherPerkManager.TriggerArrowSlingerBuff(val);
				ArcherPerkManager.TriggerAdrenalineRush(val);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Projectile_OnHit_Prefix (Archer): " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPrefix]
	public static void Character_Damage_Prefix_ArcherPerks(Character __instance, ref HitData hit)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_007d: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val == null || (Object)(object)__instance == (Object)null || __instance is Player || !IsProjectileDamage(hit) || !ClassCombatManager.IsBowWeapon(((Humanoid)val).GetCurrentWeapon()))
			{
				return;
			}
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(val);
			if (playerData != null && playerData.IsClassActive(PlayerClass.Archer))
			{
				float totalDamage = hit.GetTotalDamage();
				Vector3 position = ((Component)val).transform.position;
				Vector3 point = hit.m_point;
				float num = ArcherPerkManager.ApplyLv30_WindReaderDamage(val, position, point, totalDamage);
				if (num > totalDamage)
				{
					float num2 = num / totalDamage;
					hit.m_damage.m_damage *= num2;
					hit.m_damage.m_pierce *= num2;
					hit.m_damage.m_slash *= num2;
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Prefix_ArcherPerks: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Player), "UseStamina")]
	[HarmonyPrefix]
	public static void Player_UseStamina_Prefix(Player __instance, ref float v)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				v = ArcherPerkManager.ApplyLv10_SteadyDrawStamina(__instance, v);
				v = ArcherPerkManager.ApplyLv30_WindReaderStamina(__instance, v);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Player_UseStamina_Prefix (Archer): " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Game), "Update")]
	[HarmonyPostfix]
	public static void Game_Update_Postfix()
	{
		try
		{
			if (Time.time - lastCleanupTime >= 1f)
			{
				lastCleanupTime = Time.time;
				ArcherPerkManager.UpdateBuffs();
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Game_Update_Postfix (Archer cleanup): " + ex.Message));
		}
	}

	private static Player FindProjectileOwner(Projectile projectile)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ZNetView component = ((Component)projectile).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				long ownerID = component.GetZDO().GetLong("owner", 0L);
				if (ownerID != 0L)
				{
					return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => p.GetPlayerID() == ownerID));
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)projectile).transform.position) < 100f)
			{
				return localPlayer;
			}
			return null;
		}
		catch
		{
			return null;
		}
	}

	private static bool IsProjectileDamage(HitData hit)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Invalid comparison between Unknown and I4
		if ((int)hit.m_skill != 8 && (int)hit.m_skill != 14)
		{
			if (hit.m_damage.m_pierce > 0f && hit.m_damage.m_blunt == 0f)
			{
				return hit.m_damage.m_slash == 0f;
			}
			return false;
		}
		return true;
	}

	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	[HarmonyPrefix]
	public static void Inventory_RemoveItem_MagicShot_PreFix(Inventory __instance, ItemData item, int amount)
	{
		try
		{
			if (item == null || !IsArrowItem(item))
			{
				return;
			}
			Player val = FindPlayerWithInventory(__instance);
			if (!((Object)(object)val == (Object)null) && ClassCombatManager.IsBowWeapon(((Humanoid)val).GetCurrentWeapon()) && ArcherPerkManager.HasArcherPerk(val, 40) && Random.Range(0f, 1f) < 0.5f)
			{
				List<ItemData> list = (from i in __instance.GetAllItems()
					where i.m_shared.m_name == item.m_shared.m_name
					orderby i.m_stack
					select i).ToList();
				if (list.Count > 0)
				{
					ItemData obj = list.First();
					obj.m_stack += amount;
					((Character)val).Message((MessageType)1, "Magic Shot! Arrow not consumed", 0, (Sprite)null);
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Inventory_RemoveItem_MagicShot_PreFix: " + ex.Message));
		}
	}

	private static bool IsArrowItem(ItemData item)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Invalid comparison between Unknown and I4
		if (item?.m_shared == null)
		{
			return false;
		}
		if ((int)item.m_shared.m_itemType != 9 && !item.m_shared.m_name.ToLower().Contains("arrow"))
		{
			return item.m_shared.m_name.ToLower().Contains("bolt");
		}
		return true;
	}

	private static Player FindPlayerWithInventory(Inventory inventory)
	{
		return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => ((Humanoid)p).GetInventory() == inventory));
	}
}
[HarmonyPatch]
public static class AssassinPerkManager
{
	public class PoisonData
	{
		public int stacks;

		public float endTime;

		public float lastTickTime;

		public float damagePerTick;

		public float totalDamage;

		public Player source;

		public float originalSpeed;

		public float originalRunSpeed;
	}

	public static Dictionary<Character, PoisonData> poisonTracking = new Dictionary<Character, PoisonData>();

	private static Dictionary<Player, float> assassinationBuffs = new Dictionary<Player, float>();

	public const int MAX_POISON_STACKS = 3;

	public const float POISON_DURATION = 10f;

	public const float ASSASSINATION_SPEED_DURATION = 5f;

	public const float ASSASSINATION_SPEED_BONUS = 2f;

	public const float ENVENOMOUS_SLOW_PER_STACK = 0.2f;

	public const float TWIST_KNIFE_DAMAGE_BONUS = 0.25f;

	public const float TWIST_KNIFE_DAMAGE_REDUCTION = 0.15f;

	public const float ASSASSIN_BACKSTAB_BONUS = 0.3f;

	public const float ASSASSIN_CUT_THROAT_BONUS = 0.15f;

	public const float ASSASSIN_STEALTH_BONUS = 1f;

	public static bool DAMAGE_MODS_ON = true;

	private const string Intro = "Stealthy fighters who strike from the shadows with deadly precision.";

	private const string Outro = "Great for players who prefer tactical, stealthy gameplay and damage over time.";

	public static readonly List<PerkInfo> Perks = new List<PerkInfo>
	{
		new PerkInfo
		{
			RequiredLevel = 10,
			Name = "Cutthroat",
			Description = "+15% knife damage; +30% backstab multiplier"
		},
		new PerkInfo
		{
			RequiredLevel = 20,
			Name = "Venom Coating",
			Description = "Knife hits apply stacking Poison (up to 3 stacks) based on skill level"
		},
		new PerkInfo
		{
			RequiredLevel = 30,
			Name = "Envenomous",
			Description = "Poisons apply 15% movement speed slow per stack"
		},
		new PerkInfo
		{
			RequiredLevel = 40,
			Name = "Assassination",
			Description = "First knife hit from stealth deals +100% damage"
		},
		new PerkInfo
		{
			RequiredLevel = 50,
			Name = "Twist the Knife",
			Description = "+25% damage to poisoned targets; poisoned enemies deal -10% damage"
		}
	};

	public static bool HasAssassinPerk(Player player, int requiredLevel)
	{
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
		if (playerData == null || !playerData.IsClassActive(PlayerClass.Assassin))
		{
			return false;
		}
		return playerData.GetClassLevel(PlayerClass.Assassin) >= requiredLevel;
	}

	public static string GetClassDescription(Player player)
	{
		int currentLevel = PlayerClassManager.GetPlayerData(player)?.GetClassLevel(PlayerClass.Assassin) ?? 0;
		return PerkDescriptionBuilder.Build("Stealthy fighters who strike from the shadows with deadly precision.", Perks, "Great for players who prefer tactical, stealthy gameplay and damage over time.", currentLevel);
	}

	public static void ApplyLv20_VenomCoating(Player player, Character target, HitData hit)
	{
		if (HasAssassinPerk(player, 20) && !((Object)(object)target == (Object)null) && !target.IsDead())
		{
			float skillLevel = ((Character)player).GetSkillLevel((SkillType)2);
			float num = skillLevel / 10f;
			if (!poisonTracking.ContainsKey(target))
			{
				poisonTracking[target] = new PoisonData();
			}
			PoisonData poisonData = poisonTracking[target];
			if (poisonData.stacks < 3)
			{
				poisonData.stacks++;
				AddPoisonStatusEffect(target, poisonData.stacks);
				((Character)player).Message((MessageType)1, $"Venom Applied! {poisonData.stacks}/{3} stacks", 0, (Sprite)null);
			}
			poisonData.endTime = Time.time + 10f;
			poisonData.damagePerTick = num * (float)poisonData.stacks;
			poisonData.source = player;
			poisonData.totalDamage = skillLevel * (float)poisonData.stacks;
			target.GetSEMan().GetStatusEffect(StringExtensionMethods.GetStableHashCode("Poison"));
			float num2 = 1f - (float)poisonData.stacks * 0.2f;
			poisonData.originalSpeed = target.m_speed;
			target.m_speed *= num2;
			poisonData.originalRunSpeed = target.m_runSpeed;
			target.m_runSpeed *= num2;
		}
	}

	private static void AddPoisonStatusEffect(Character target, int stacks)
	{
		try
		{
			SEMan sEMan = target.GetSEMan();
			if (sEMan != null)
			{
				sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_AssassinPoison"), true);
				SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
				((Object)val).name = "SE_AssassinPoison";
				((StatusEffect)val).m_name = $"Venom ({stacks} stacks)";
				((StatusEffect)val).m_tooltip = $"Taking poison damage over time. {stacks}/{3} stacks";
				Sprite poisonIcon = GetPoisonIcon();
				if ((Object)(object)poisonIcon != (Object)null)
				{
					((StatusEffect)val).m_icon = poisonIcon;
				}
				((StatusEffect)val).m_ttl = 10f;
				sEMan.AddStatusEffect((StatusEffect)(object)val, true, 0, 0f);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error adding poison status effect: " + ex.Message));
		}
	}

	private static Sprite GetPoisonIcon()
	{
		try
		{
			ObjectDB instance = ObjectDB.instance;
			StatusEffect val = ((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Poison")) : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.m_icon;
			}
			return null;
		}
		catch
		{
			return null;
		}
	}

	public static float ApplyLv30_EnvenomousMovementSlow(Character target, float baseSpeed)
	{
		if ((Object)(object)target == (Object)null || !poisonTracking.ContainsKey(target))
		{
			return baseSpeed;
		}
		PoisonData poisonData = poisonTracking[target];
		if ((Object)(object)poisonData.source == (Object)null || !HasAssassinPerk(poisonData.source, 30))
		{
			return baseSpeed;
		}
		float num = 1f - (float)poisonData.stacks * 0.2f;
		num = Mathf.Max(num, 0.25f);
		return baseSpeed * num;
	}

	private static void ApplyAssassinationSpeedBuff(Character c, Player player)
	{
		player.GetPlayerID();
		float value = Time.time + 5f;
		assassinationBuffs[player] = value;
		AnimationSpeedManager.Set((Character)(object)player, "Assassin_Knife_AS", 2f);
		AddAssassinationStatusEffect(player);
	}

	private static void AddAssassinationStatusEffect(Player player)
	{
		try
		{
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan != null)
			{
				sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_AssassinationSpeed"), true);
				SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
				((Object)val).name = "SE_AssassinationSpeed";
				((StatusEffect)val).m_name = "Assassination Speed";
				((StatusEffect)val).m_tooltip = "+25% attack speed";
				ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
				((StatusEffect)val).m_icon = ((currentWeapon != null) ? currentWeapon.GetIcon() : null);
				((StatusEffect)val).m_ttl = 5f;
				sEMan.AddStatusEffect((StatusEffect)(object)val, true, 0, 0f);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error adding assassination status effect: " + ex.Message));
		}
	}

	public static void ProcessPoisonDamage()
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Expected O, but got Unknown
		//IL_00b8: 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_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		float time = Time.time;
		List<Character> list = new List<Character>();
		foreach (KeyValuePair<Character, PoisonData> item in poisonTracking)
		{
			Character key = item.Key;
			PoisonData value = item.Value;
			if ((Object)(object)key == (Object)null || key.IsDead() || time > value.endTime)
			{
				ClearSpeedDebuff(key, value.originalSpeed, value.originalRunSpeed);
				list.Add(key);
			}
			else if (time - value.lastTickTime >= 1f)
			{
				value.lastTickTime = time;
				HitData val = new HitData();
				val.m_damage.m_poison = value.damagePerTick;
				Player source = value.source;
				val.m_attacker = ((source != null) ? ((Character)source).GetZDOID() : ZDOID.None);
				val.m_point = ((Component)key).transform.position;
				key.Damage(val);
			}
		}
		foreach (Character item2 in list)
		{
			if ((Object)(object)item2 != (Object)null)
			{
				SEMan sEMan = item2.GetSEMan();
				if (sEMan != null)
				{
					sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_AssassinPoison"), true);
				}
			}
			poisonTracking.Remove(item2);
		}
	}

	public static void ClearSpeedDebuff(Character target, float originalSpeed, float originalRunSpeed)
	{
		if (!target.IsDead())
		{
			target.m_speed = originalSpeed;
			target.m_runSpeed = originalRunSpeed;
		}
	}

	public static bool IsStealthHit(Player attacker, Character target)
	{
		if ((Object)(object)attacker == (Object)null || (Object)(object)target == (Object)null)
		{
			return false;
		}
		BaseAI baseAI = target.GetBaseAI();
		if ((Object)(object)baseAI == (Object)null)
		{
			return false;
		}
		if (baseAI.IsAlerted())
		{
			return false;
		}
		if (baseAI.HaveTarget())
		{
			return false;
		}
		return true;
	}

	public static bool IsBackstab(Vector3 hitPoint, Character target)
	{
		//IL_000b: 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_0017: 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: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)target == (Object)null)
		{
			return false;
		}
		Vector3 val = hitPoint - ((Component)target).transform.position;
		Vector3 normalized = ((Vector3)(ref val)).normalized;
		return Vector3.Angle(((Component)target).transform.forward, normalized) > 120f;
	}

	public static HitData ModDamage(HitData hit, float mod)
	{
		if (hit == null)
		{
			return hit;
		}
		hit.m_damage.m_damage *= mod;
		hit.m_damage.m_slash *= mod;
		hit.m_damage.m_pierce *= mod;
		hit.m_damage.m_blunt *= mod;
		hit.m_damage.m_fire *= mod;
		hit.m_damage.m_frost *= mod;
		hit.m_damage.m_spirit *= mod;
		hit.m_damage.m_poison *= mod;
		return hit;
	}

	public static void CleanupBuffs()
	{
		float currentTime = Time.time;
		foreach (Player item in (from kvp in assassinationBuffs
			where kvp.Value < currentTime
			select kvp.Key).ToList())
		{
			assassinationBuffs.Remove(item);
			if ((Object)(object)item != (Object)null)
			{
				SEMan sEMan = ((Character)item).GetSEMan();
				if (sEMan != null)
				{
					sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_AssassinationSpeed"), true);
				}
				AnimationSpeedManager.Clear((Character)(object)item, "Assassin_Knife_AS");
			}
		}
	}

	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPrefix]
	public static void Character_Damage_Assassin_Prefix(Character __instance, ref HitData hit)
	{
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(Player.m_localPlayer);
			if (!playerData.IsClassActive("Assassin") || hit.m_damage.m_poison > 0f)
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val == null || (Object)(object)__instance == (Object)null || __instance is Player)
			{
				Character attacker2 = hit.GetAttacker();
				if (poisonTracking.ContainsKey(attacker2))
				{
					float mod = 0.15f;
					hit = ModDamage(hit, mod);
				}
			}
			else if (ClassCombatManager.IsKnifeWeapon(((Humanoid)val).GetCurrentWeapon()) && playerData != null && playerData.IsClassActive(PlayerClass.Assassin))
			{
				float num = 0f;
				if (HasAssassinPerk(val, 10))
				{
					num += 0.15f;
				}
				if (IsBackstab(hit.m_point, __instance) && HasAssassinPerk(val, 10))
				{
					num += 0.3f;
				}
				if (IsStealthHit(val, __instance) && HasAssassinPerk(val, 40))
				{
					num += 1f;
					ApplyAssassinationSpeedBuff(__instance, val);
				}
				if (HasAssassinPerk(val, 50))
				{
					num += 0.25f;
				}
				_ = hit.m_damage;
				_ = hit.m_damage;
				if (DAMAGE_MODS_ON)
				{
					hit = ModDamage(hit, num + 1f);
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Assassin_Prefix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPostfix]
	public static void Character_Damage_Assassin_Postfix(Character __instance, HitData hit)
	{
		try
		{
			if (hit.m_damage.m_poison > 0f)
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val != null && !((Object)(object)__instance == (Object)null) && !(__instance is Player) && !(hit.GetTotalDamage() <= 0f) && ClassCombatManager.IsKnifeWeapon(((Humanoid)val).GetCurrentWeapon()) && (hit.m_damage.m_blunt != 0f || hit.m_damage.m_slash != 0f || hit.m_damage.m_pierce != 0f))
			{
				PlayerClassData playerData = PlayerClassManager.GetPlayerData(val);
				if (playerData != null && playerData.IsClassActive(PlayerClass.Assassin))
				{
					ApplyLv20_VenomCoating(val, __instance, hit);
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Assassin_Postfix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Game), "Update")]
	[HarmonyPostfix]
	public static void Game_Update_Assassin_Postfix()
	{
		try
		{
			ProcessPoisonDamage();
			if (Time.time % 1f < Time.deltaTime)
			{
				CleanupBuffs();
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Game_Update_Assassin_Postfix: " + ex.Message));
		}
	}
}
public static class ClassCombatManager
{
	public static bool IsSwordWeapon(ItemData weapon)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Invalid comparison between Unknown and I4
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Invalid comparison between Unknown and I4
		if ((weapon == null || (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() != 3) && (weapon == null || (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() != 14))
		{
			return false;
		}
		string text = weapon.m_shared.m_name?.ToLower() ?? "";
		if (!text.Contains("sword") && !text.Contains("blade") && !text.Contains("saber"))
		{
			return text.Contains("katana");
		}
		return true;
	}

	public static bool IsBowWeapon(ItemData weapon)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Invalid comparison between Unknown and I4
		if (weapon == null)
		{
			return false;
		}
		return (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() == 4;
	}

	public static bool IsBluntWeapon(ItemData weapon)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Invalid comparison between Unknown and I4
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Invalid comparison between Unknown and I4
		if ((weapon == null || (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() != 3) && (weapon == null || (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() != 14))
		{
			return false;
		}
		string text = weapon.m_shared.m_name?.ToLower() ?? "";
		if (!text.Contains("mace") && !text.Contains("hammer") && !text.Contains("club"))
		{
			return text.Contains("sledge");
		}
		return true;
	}

	public static bool IsKnifeWeapon(ItemData weapon)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Invalid comparison between Unknown and I4
		if (weapon == null || (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() != 3)
		{
			return false;
		}
		string text = weapon.m_shared.m_name?.ToLower() ?? "";
		if (!text.Contains("knife") && !text.Contains("dagger") && !text.Contains("seax"))
		{
			return text.Contains("razor");
		}
		return true;
	}

	public static bool IsSpearWeapon(ItemData weapon)
	{
		string name = weapon.m_shared.m_name;
		if (name == null || !name.ToLower().Contains("spear"))
		{
			string name2 = weapon.m_shared.m_name;
			if (name2 == null || !name2.ToLower().Contains("atgeir"))
			{
				return weapon.m_shared.m_name?.ToLower().Contains("halberd") ?? false;
			}
		}
		return true;
	}

	public static bool IsUnarmedAttack(ItemData weapon)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		if (weapon != null && (int)weapon.m_shared.m_itemType != 0)
		{
			string name = weapon.m_shared.m_name;
			if (name == null || !name.ToLower().Contains("claws"))
			{
				return weapon.m_shared.m_name == "Unarmed";
			}
		}
		return true;
	}

	public static bool IsMagicWeapon(ItemData weapon)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Invalid comparison between Unknown and I4
		if (weapon != null && (int)(weapon.m_shared?.m_itemType).GetValueOrDefault() == 14)
		{
			string name = weapon.m_shared.m_name;
			if (name == null || !name.ToLower().Contains("staff"))
			{
				return (Object)(object)weapon.m_shared.m_attackStatusEffect != (Object)null;
			}
			return true;
		}
		return false;
	}

	public static float GetClassDamageMultiplier(Player player, ItemData weapon)
	{
		if ((Object)(object)player == (Object)null)
		{
			return 1f;
		}
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
		if (playerData == null || playerData.activeClasses.Count == 0)
		{
			return 1f;
		}
		float num = 1f;
		foreach (string activeClass in playerData.activeClasses)
		{
			float classSpecificDamageMultiplier = GetClassSpecificDamageMultiplier(activeClass, playerData, weapon);
			if (classSpecificDamageMultiplier > 1f)
			{
				num += classSpecificDamageMultiplier - 1f;
			}
		}
		return num;
	}

	private static float GetClassSpecificDamageMultiplier(string className, PlayerClassData playerData, ItemData weapon)
	{
		int classLevel = playerData.GetClassLevel(className);
		return className switch
		{
			"Sword Master" => GetSwordMasterDamageBonus(classLevel, weapon), 
			"Archer" => GetArcherDamageBonus(classLevel, weapon), 
			"Crusher" => GetCrusherDamageBonus(classLevel, weapon), 
			"Assassin" => GetAssassinDamageBonus(classLevel, weapon), 
			"Brawler" => GetBrawlerDamageBonus(classLevel, weapon), 
			"Wizard" => GetWizardDamageBonus(classLevel, weapon), 
			"Lancer" => GetLancerDamageBonus(classLevel, weapon), 
			"Bulwark" => GetBulwarkDamageBonus(classLevel, weapon), 
			_ => 1f, 
		};
	}

	private static float GetSwordMasterDamageBonus(int level, ItemData weapon)
	{
		if (!IsSwordWeapon(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 10)
		{
			num += 0.1f;
		}
		return 1f + num;
	}

	private static float GetArcherDamageBonus(int level, ItemData weapon)
	{
		if (!IsBowWeapon(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 50)
		{
			num += 0.2f;
		}
		return 1f + num;
	}

	private static float GetCrusherDamageBonus(int level, ItemData weapon)
	{
		if (!IsBluntWeapon(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 10)
		{
			num += 0.12f;
		}
		return 1f + num;
	}

	private static float GetAssassinDamageBonus(int level, ItemData weapon)
	{
		if (!IsKnifeWeapon(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 10)
		{
			num += 0.12f;
		}
		return 1f + num;
	}

	private static float GetBrawlerDamageBonus(int level, ItemData weapon)
	{
		if (!IsUnarmedAttack(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 10)
		{
			num += 0.15f;
		}
		return 1f + num;
	}

	private static float GetWizardDamageBonus(int level, ItemData weapon)
	{
		if (!IsMagicWeapon(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 10)
		{
			num += 0.08f;
		}
		return 1f + num;
	}

	private static float GetLancerDamageBonus(int level, ItemData weapon)
	{
		if (!IsSpearWeapon(weapon))
		{
			return 1f;
		}
		float num = 0f;
		num += (float)level * 0.005f;
		if (level >= 10)
		{
			num += 0.1f;
		}
		return 1f + num;
	}

	private static float GetBulwarkDamageBonus(int level, ItemData weapon)
	{
		return 1f;
	}

	public static void ShowDamageBonusMessage(Player player, float multiplier, string weaponType)
	{
		if (multiplier > 1f && (Object)(object)player == (Object)(object)Player.m_localPlayer)
		{
			float num = (multiplier - 1f) * 100f;
			if (Random.Range(0f, 1f) < 0.1f)
			{
				((Character)player).Message((MessageType)1, $"{weaponType} Mastery: +{num:F0}% damage!", 0, (Sprite)null);
			}
		}
	}

	public static float GetClassSpecificDamageMultiplierPublic(string className, PlayerClassData playerData, ItemData weapon)
	{
		int classLevel = playerData.GetClassLevel(className);
		return className switch
		{
			"Sword Master" => GetSwordMasterDamageBonus(classLevel, weapon), 
			"Archer" => GetArcherDamageBonus(classLevel, weapon), 
			"Crusher" => GetCrusherDamageBonus(classLevel, weapon), 
			"Assassin" => GetAssassinDamageBonus(classLevel, weapon), 
			"Brawler" => GetBrawlerDamageBonus(classLevel, weapon), 
			"Wizard" => GetWizardDamageBonus(classLevel, weapon), 
			"Lancer" => GetLancerDamageBonus(classLevel, weapon), 
			"Bulwark" => GetBulwarkDamageBonus(classLevel, weapon), 
			_ => 1f, 
		};
	}
}
[HarmonyPatch]
public static class CombatPatches
{
	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPrefix]
	public static void Character_Damage_Prefix(Character __instance, ref HitData hit)
	{
		try
		{
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val != null)
			{
				ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon();
				float classDamageMultiplier = ClassCombatManager.GetClassDamageMultiplier(val, currentWeapon);
				if (classDamageMultiplier > 1f)
				{
					hit.m_damage.m_slash *= classDamageMultiplier;
					hit.m_damage.m_pierce *= classDamageMultiplier;
					hit.m_damage.m_blunt *= classDamageMultiplier;
					hit.m_damage.m_chop *= classDamageMultiplier;
					string weaponTypeName = GetWeaponTypeName(currentWeapon);
					ClassCombatManager.ShowDamageBonusMessage(val, classDamageMultiplier, weaponTypeName);
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Prefix: " + ex.Message));
		}
	}

	private static string GetWeaponTypeName(ItemData weapon)
	{
		if (ClassCombatManager.IsSwordWeapon(weapon))
		{
			return "Sword";
		}
		if (ClassCombatManager.IsBowWeapon(weapon))
		{
			return "Bow";
		}
		if (ClassCombatManager.IsBluntWeapon(weapon))
		{
			return "Blunt";
		}
		if (ClassCombatManager.IsKnifeWeapon(weapon))
		{
			return "Knife";
		}
		if (ClassCombatManager.IsSpearWeapon(weapon))
		{
			return "Spear";
		}
		if (ClassCombatManager.IsUnarmedAttack(weapon))
		{
			return "Unarmed";
		}
		if (ClassCombatManager.IsMagicWeapon(weapon))
		{
			return "Magic";
		}
		return "Weapon";
	}
}
[HarmonyPatch]
public static class ProjectilePatches
{
	private class ProjectileInfo
	{
		public Player owner;

		public ItemData weapon;

		public float createdTime;

		public string projectileName;
	}

	private static Dictionary<int, ProjectileInfo> trackedProjectiles = new Dictionary<int, ProjectileInfo>();

	[HarmonyPatch(typeof(Projectile), "Setup")]
	[HarmonyPostfix]
	public static void Projectile_Setup_Postfix(Projectile __instance, Character owner, Vector3 velocity, float hitNoise, HitData hitData, ItemData item)
	{
		try
		{
			Player val = (Player)(object)((owner is Player) ? owner : null);
			if (val != null && (Object)(object)__instance != (Object)null)
			{
				ItemData weaponFromProjectileContext = GetWeaponFromProjectileContext(val, __instance, item);
				ProjectileInfo value = new ProjectileInfo
				{
					owner = val,
					weapon = weaponFromProjectileContext,
					createdTime = Time.time,
					projectileName = ((Object)__instance).name
				};
				trackedProjectiles[((Object)__instance).GetInstanceID()] = value;
				Debug.Log((object)("Registered projectile " + ((Object)__instance).name + " for " + val.GetPlayerName() + " with weapon type " + (weaponFromProjectileContext?.m_shared?.m_name ?? "unknown")));
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Projectile_Setup_Postfix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Projectile), "Awake")]
	[HarmonyPostfix]
	public static void Projectile_Awake_Postfix(Projectile __instance)
	{
		try
		{
			if ((Object)(object)__instance != (Object)null && !trackedProjectiles.ContainsKey(((Object)__instance).GetInstanceID()))
			{
				ProjectileInfo value = new ProjectileInfo
				{
					owner = null,
					weapon = null,
					createdTime = Time.time,
					projectileName = ((Object)__instance).name
				};
				trackedProjectiles[((Object)__instance).GetInstanceID()] = value;
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Projectile_Awake_Postfix: " + ex.Message));
		}
	}

	private static ItemData GetWeaponFromProjectileContext(Player player, Projectile projectile, ItemData item)
	{
		//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_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Expected O, but got Unknown
		if (item != null)
		{
			return item;
		}
		if ((Object)(object)player == (Object)null || (Object)(object)projectile == (Object)null)
		{
			return null;
		}
		string text = ((Object)projectile).name.ToLower();
		if (text.Contains("arrow"))
		{
			return ((IEnumerable<ItemData>)((Humanoid)player).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => (int)i.m_shared.m_itemType == 4));
		}
		if (text.Contains("spear") || text.Contains("harpoon"))
		{
			return new ItemData
			{
				m_shared = new SharedData(),
				m_shared = 
				{
					m_itemType = (ItemType)14
				},
				m_shared = 
				{
					m_name = "spear"
				}
			};
		}
		return ((Humanoid)player).GetCurrentWeapon();
	}

	private static Player ResolveProjectileOwner(Projectile projectile)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ZNetView component = ((Component)projectile).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				long ownerID = component.GetZDO().GetLong("owner", 0L);
				if (ownerID != 0L)
				{
					return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => p.GetPlayerID() == ownerID));
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)projectile).transform.position) < 50f)
			{
				return localPlayer;
			}
			return null;
		}
		catch
		{
			return null;
		}
	}

	[HarmonyPatch(typeof(Game), "Update")]
	[HarmonyPostfix]
	public static void Game_Update_Postfix()
	{
		try
		{
			if (Time.frameCount % 300 != 0)
			{
				return;
			}
			List<int> list = new List<int>();
			float time = Time.time;
			foreach (KeyValuePair<int, ProjectileInfo> trackedProjectile in trackedProjectiles)
			{
				if (time - trackedProjectile.Value.createdTime > 30f)
				{
					list.Add(trackedProjectile.Key);
				}
			}
			foreach (int item in list)
			{
				trackedProjectiles.Remove(item);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Game_Update_Postfix (projectile cleanup): " + ex.Message));
		}
	}
}
public static class ClassDamageBonusManager
{
	public static float DamageBonusPerLevel = 0.005f;

	public static float MaxDamageBonus = 0.25f;

	public static float CalculateDamageBonus(Player attacker, ItemData weapon)
	{
		if ((Object)(object)attacker == (Object)null || weapon == null)
		{
			return 1f;
		}
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(attacker);
		if (playerData == null || playerData.activeClasses.Count == 0)
		{
			return 1f;
		}
		float num = 1f;
		bool flag = false;
		foreach (string activeClass in playerData.activeClasses)
		{
			if (IsWeaponAppropriateForClass(weapon, activeClass))
			{
				float num2 = (float)playerData.GetClassLevel(activeClass) * DamageBonusPerLevel;
				num2 = Mathf.Min(num2, MaxDamageBonus);
				num += num2;
				flag = true;
			}
		}
		return num;
	}

	private static bool IsWeaponAppropriateForClass(ItemData weapon, string className)
	{
		return className switch
		{
			"Sword Master" => ClassCombatManager.IsSwordWeapon(weapon), 
			"Archer" => ClassCombatManager.IsBowWeapon(weapon), 
			"Crusher" => ClassCombatManager.IsBluntWeapon(weapon), 
			"Assassin" => ClassCombatManager.IsKnifeWeapon(weapon), 
			"Brawler" => ClassCombatManager.IsUnarmedAttack(weapon), 
			"Wizard" => ClassCombatManager.IsMagicWeapon(weapon), 
			"Lancer" => ClassCombatManager.IsSpearWeapon(weapon), 
			"Bulwark" => false, 
			_ => false, 
		};
	}

	public static string GetDamageBonusInfo(Player player, ItemData weapon)
	{
		if ((Object)(object)player == (Object)null || weapon == null)
		{
			return "No weapon equipped";
		}
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
		if (playerData == null || playerData.activeClasses.Count == 0)
		{
			return "No active classes";
		}
		StringBuilder stringBuilder = new StringBuilder();
		float num = 0f;
		bool flag = false;
		foreach (string activeClass in playerData.activeClasses)
		{
			if (IsWeaponAppropriateForClass(weapon, activeClass))
			{
				int classLevel = playerData.GetClassLevel(activeClass);
				float num2 = (float)classLevel * DamageBonusPerLevel;
				num2 = Mathf.Min(num2, MaxDamageBonus);
				stringBuilder.AppendLine($"{activeClass} Lv.{classLevel}: +{num2 * 100f:F1}%");
				num += num2;
				flag = true;
			}
		}
		if (!flag)
		{
			return "No active classes match " + weapon.m_shared.m_name;
		}
		stringBuilder.AppendLine($"Total Bonus: +{num * 100f:F1}%");
		stringBuilder.AppendLine($"Damage Multiplier: {1f + num:F3}x");
		return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
	}
}
[HarmonyPatch]
public static class DamageBonusPatches
{
	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPrefix]
	public static void Character_Damage_Prefix(Character __instance, ref HitData hit)
	{
		try
		{
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val == null || __instance is Player)
			{
				return;
			}
			ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon();
			if (currentWeapon != null)
			{
				float num = ClassDamageBonusManager.CalculateDamageBonus(val, currentWeapon);
				if (num > 1f)
				{
					hit.m_damage.m_damage *= num;
					hit.m_damage.m_blunt *= num;
					hit.m_damage.m_slash *= num;
					hit.m_damage.m_pierce *= num;
					hit.m_damage.m_chop *= num;
					hit.m_damage.m_pickaxe *= num;
					hit.m_damage.m_fire *= num;
					hit.m_damage.m_frost *= num;
					hit.m_damage.m_lightning *= num;
					hit.m_damage.m_poison *= num;
					hit.m_damage.m_spirit *= num;
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Prefix (Damage Bonus): " + ex.Message));
		}
	}
}
[BepInPlugin("com.bunzboi.valheimweaponclass", "ValheimWeaponClasses", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
internal class ClassObeliskMod : BaseUnityPlugin
{
	public const string PluginGUID = "com.bunzboi.valheimweaponclass";

	public const string PluginName = "ValheimWeaponClasses";

	public const string PluginVersion = "1.0.0";

	private GameObject TestPanel;

	private static ClassObeliskMod _instance;

	private Harmony _harmony;

	private ConfigEntry<bool> _enableMod;

	public static ClassObeliskMod Instance => _instance;

	private void Awake()
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		_instance = this;
		_enableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, "Enable the class obelisk mod");
		if (_enableMod.Value)
		{
			_harmony = new Harmony("com.bunzboi.valheimweaponclass");
			_harmony.PatchAll();
			PrefabManager.OnVanillaPrefabsAvailable += AddClassObelisk;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ValheimWeaponClasses loaded successfully!");
		}
	}

	private void AddClassObelisk()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Expected O, but got Unknown
		try
		{
			GameObject val = CreateClassObeliskPrefab();
			if ((Object)(object)val != (Object)null)
			{
				PieceConfig val2 = new PieceConfig();
				val2.Name = "$class_obelisk";
				val2.PieceTable = "Hammer";
				val2.Category = "Crafting";
				val2.AddRequirement("Wood", 2, true);
				CustomPiece val3 = new CustomPiece(val, true, val2);
				PieceManager.Instance.AddPiece(val3);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Class Obelisk piece added successfully!");
				PrefabManager.OnVanillaPrefabsAvailable -= AddClassObelisk;
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Error adding Class Obelisk: " + ex.Message));
		}
	}

	private GameObject CreateClassObeliskPrefab()
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab("piece_workbench");
			if ((Object)(object)prefab == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not find workbench prefab!");
				return null;
			}
			GameObject obj = PrefabManager.Instance.CreateClonedPrefab("ClassObelisk", prefab);
			new PieceConfig
			{
				Name = "$class_obelisk",
				PieceTable = "Hammer",
				Category = "Crafting"
			}.AddRequirement("Wood", 2, true);
			CraftingStation component = obj.GetComponent<CraftingStation>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			obj.AddComponent<ClassObeliskInteract>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Class Obelisk prefab created successfully!");
			return obj;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Error creating Class Obelisk prefab: " + ex.Message));
			return null;
		}
	}
}
public class ClassObeliskInteract : MonoBehaviour, Hoverable, Interactable
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static UnityAction <>9__20_0;

		internal void <CreateEnhancedCloseButton>b__20_0()
		{
			CloseClassSelectionGUI();
		}
	}

	public static GameObject classSelectionPanel;

	public static Text descriptionText;

	public static ScrollRect descriptionScrollRect;

	public static GameObject selectClassButton;

	public static string selectedClassName = "";

	public static readonly string[] ClassNames = new string[8] { "Sword Master", "Archer", "Crusher", "Assassin", "Brawler", "Wizard", "Lancer", "Bulwark" };

	private static readonly Dictionary<string, Func<Player, string>> DynamicClassDescriptionProviders = new Dictionary<string, Func<Player, string>>
	{
		{
			"Sword Master",
			SwordMasterPerkManager.GetClassDescription
		},
		{
			"Archer",
			ArcherPerkManager.GetClassDescription
		},
		{
			"Crusher",
			CrusherPerkManager.GetClassDescription
		},
		{
			"Assassin",
			AssassinPerkManager.GetClassDescription
		},
		{
			"Brawler",
			BrawlerPerkManager.GetClassDescription
		},
		{
			"Wizard",
			WizardPerkManager.GetClassDescription
		},
		{
			"Lancer",
			LancerPerkManager.GetClassDescription
		},
		{
			"Bulwark",
			BulwarkPerkManager.GetClassDescription
		}
	};

	private void Start()
	{
	}

	private void Update()
	{
		if ((Object)(object)classSelectionPanel != (Object)null && classSelectionPanel.activeSelf && ZInput.instance != null && (ZInput.GetButtonDown("Escape") || ZInput.GetButtonDown("JoyMenu")))
		{
			CloseClassSelectionGUI();
		}
	}

	public string GetHoverText()
	{
		return "Class Obelisk\n[<color=yellow>E</color>] Select your combat class";
	}

	public string GetHoverName()
	{
		return "Class Obelisk";
	}

	public bool Interact(Humanoid user, bool hold, bool alt)
	{
		if (hold || alt)
		{
			return false;
		}
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		OpenClassSelectionGUI(val);
		return true;
	}

	public bool UseItem(Humanoid user, ItemData item)
	{
		return false;
	}

	private void OpenClassSelectionGUI(Player player)
	{
		CloseClassSelectionGUI();
		CreateEnhancedClassSelectionGUI(player);
	}

	private void CreateEnhancedClassSelectionGUI(Player player)
	{
		try
		{
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
			if (playerData == null)
			{
				Debug.LogError((object)"Could not get player data for GUI creation");
				return;
			}
			CreateWoodenGUIPanel();
			CreateTwoColumnClassButtons(player);
			CreateDescriptionWindow(player);
			CreateSelectClassButton(player);
			CreateEnhancedCloseButton();
			string text = ((playerData.activeClasses.Count > 0) ? string.Join(", ", playerData.activeClasses) : "None");
			UpdateDescriptionText("Current Active Classes: " + text + "\n\nClick on a class above to see its description and benefits.");
			selectedClassName = "";
			UpdateSelectButton();
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("Error creating enhanced GUI: " + ex.Message));
		}
	}

	private void CreateWoodenGUIPanel()
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)classSelectionPanel))
		{
			if (GUIManager.Instance == null)
			{
				Logger.LogError((object)"GUIManager instance is null");
				return;
			}
			if (!Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront))
			{
				Logger.LogError((object)"GUIManager CustomGUI is null");
				return;
			}
			classSelectionPanel = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 1450f, 900f, false);
			classSelectionPanel.SetActive(false);
			classSelectionPanel.AddComponent<DragWindowCntrl>();
			GUIManager.Instance.CreateText("Choose a class!", classSelectionPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -50f), GUIManager.Instance.AveriaSerifBold, 30, GUIManager.Instance.ValheimOrange, true, Color.black, 400f, 50f, false).GetComponent<Text>().alignment = (TextAnchor)4;
		}
		bool flag = !classSelectionPanel.activeSelf;
		classSelectionPanel.SetActive(flag);
		GUIManager.BlockInput(flag);
	}

	private void CreateTwoColumnClassButtons(Player player)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("ButtonContainer");
		val.transform.SetParent(classSelectionPanel.transform, false);
		RectTransform obj = val.AddComponent<RectTransform>();
		obj.anchorMin = new Vector2(0.05f, 0.65f);
		obj.anchorMax = new Vector2(0.95f, 0.85f);
		obj.offsetMin = Vector2.zero;
		obj.offsetMax = Vector2.zero;
		float num = 180f;
		float num2 = 50f;
		float num3 = 25f;
		float num4 = 15f;
		float num5 = (0f - (num * 4f + num3 * 3f)) / 2f + num / 2f;
		float num6 = 25f;
		for (int i = 0; i < ClassNames.Length; i++)
		{
			int num7 = i % 4;
			int num8 = i / 4;
			float x = num5 + (float)num7 * (num + num3);
			float y = num6 - (float)num8 * (num2 + num4);
			CreateEnhancedClassButton(val, ClassNames[i], player, x, y, num, num2);
		}
	}

	private void CreateEnhancedClassButton(GameObject parent, string className, Player player, float x, float y, float width, float height)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Expected O, but got Unknown
		int classLevel = PlayerClassManager.GetPlayerData(player).GetClassLevel(className);
		string text = $"{className} ({classLevel})";
		GameObject obj = GUIManager.Instance.CreateButton(text, parent.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(x, y), (float)(int)width, (float)(int)height);
		((UnityEvent)obj.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
		{
			OnClassButtonClicked(className, player);
		});
		obj.AddComponent<ButtonHoverEffect>();
	}

	private void CreateDescriptionWindow(Player player)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Expected O, but got Unknown
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: 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)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("DescriptionContainer");
		val.transform.SetParent(classSelectionPanel.transform, false);
		RectTransform obj = val.AddComponent<RectTransform>();
		obj.anchorMin = new Vector2(0.15f, 0.2f);
		obj.anchorMax = new Vector2(0.85f, 0.6f);
		obj.offsetMin = Vector2.zero;
		obj.offsetMax = Vector2.zero;
		((Graphic)val.AddComponent<Image>()).color = new Color(0.1f, 0.1f, 0.1f, 0.8f);
		GameObject val2 = new GameObject("Viewport");
		val2.transform.SetParent(val.transform, false);
		RectTransform val3 = val2.AddComponent<RectTransform>();
		val3.anchorMin = Vector2.zero;
		val3.anchorMax = Vector2.one;
		val3.offsetMin = new Vector2(15f, 15f);
		val3.offsetMax = new Vector2(-15f, -15f);
		val2.AddComponent<RectMask2D>();
		GameObject obj2 = GUIManager.Instance.CreateText("Select a class to view its description...", val2.transform, Vector2.zero, Vector2.one, Vector2.zero, GUIManager.Instance.AveriaSerif, 18, new Color(0.9f, 0.9f, 0.9f, 1f), false, Color.black, 0f, 0f, false);
		descriptionText = obj2.GetComponent<Text>();
		descriptionText.alignment = (TextAnchor)0;
		descriptionText.horizontalOverflow = (HorizontalWrapMode)0;
		descriptionText.verticalOverflow = (VerticalWrapMode)1;
		RectTransform component = obj2.GetComponent<RectTransform>();
		component.anchorMin = new Vector2(0f, 1f);
		component.anchorMax = new Vector2(1f, 1f);
		component.pivot = new Vector2(0.5f, 1f);
		component.anchoredPosition = Vector2.zero;
		component.sizeDelta = Vector2.zero;
		ContentSizeFitter obj3 = obj2.AddComponent<ContentSizeFitter>();
		obj3.horizontalFit = (FitMode)0;
		obj3.verticalFit = (FitMode)2;
		descriptionScrollRect = val.AddComponent<ScrollRect>();
		descriptionScrollRect.viewport = val3;
		descriptionScrollRect.content = component;
		descriptionScrollRect.horizontal = false;
		descriptionScrollRect.vertical = true;
		descriptionScrollRect.movementType = (MovementType)2;
		descriptionScrollRect.scrollSensitivity = 20f;
	}

	private void CreateSelectClassButton(Player player)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Expected O, but got Unknown
		selectClassButton = GUIManager.Instance.CreateButton("Select Class", classSelectionPanel.transform, new Vector2(0.5f, 0.08f), new Vector2(0.5f, 0.08f), new Vector2(0f, 20f), 200f, 35f);
		((UnityEvent)selectClassButton.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
		{
			if (!string.IsNullOrEmpty(selectedClassName))
			{
				OnClassSelected(selectedClassName, player);
			}
		});
		UpdateSelectButton();
	}

	private void CreateEnhancedCloseButton()
	{
		//IL_001e: 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_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: 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_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Expected O, but got Unknown
		GameObject obj = GUIManager.Instance.CreateButton("X", classSelectionPanel.transform, new Vector2(0.92f, 0.92f), new Vector2(0.98f, 0.98f), Vector2.zero, 35f, 35f);
		Button component = obj.GetComponent<Button>();
		Image component2 = obj.GetComponent<Image>();
		if ((Object)(object)component2 != (Object)null)
		{
			((Graphic)component2).color = new Color(0.8f, 0.3f, 0.3f, 0.9f);
			ColorBlock colors = ((Selectable)component).colors;
			((ColorBlock)(ref colors)).normalColor = new Color(0.8f, 0.3f, 0.3f, 0.9f);
			((ColorBlock)(ref colors)).highlightedColor = new Color(0.9f, 0.4f, 0.4f, 1f);
			((ColorBlock)(ref colors)).pressedColor = new Color(0.7f, 0.2f, 0.2f, 1f);
			((Selectable)component).colors = colors;
		}
		Text componentInChildren = obj.GetComponentInChildren<Text>();
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren.fontSize = 18;
			componentInChildren.fontStyle = (FontStyle)1;
			((Graphic)componentInChildren).color = Color.white;
		}
		ButtonClickedEvent onClick = component.onClick;
		object obj2 = <>c.<>9__20_0;
		if (obj2 == null)
		{
			UnityAction val = delegate
			{
				CloseClassSelectionGUI();
			};
			<>c.<>9__20_0 = val;
			obj2 = (object)val;
		}
		((UnityEvent)onClick).AddListener((UnityAction)obj2);
	}

	private void OnClassButtonClicked(string className, Player player)
	{
		selectedClassName = className;
		Func<Player, string> value;
		string text = (DynamicClassDescriptionProviders.TryGetValue(className, out value) ? value(player) : ("Description for " + className + " coming soon..."));
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
		if (playerData != null && playerData.IsClassActive(className))
		{
			text += "\n\n\n<size=24><b>Active</b></size>";
		}
		UpdateDescriptionText(text);
		UpdateSelectButton();
	}

	private void UpdateDescriptionText(string description)
	{
		if ((Object)(object)descriptionText != (Object)null)
		{
			descriptionText.text = description;
			descriptionText.horizontalOverflow = (HorizontalWrapMode)0;
			descriptionText.verticalOverflow = (VerticalWrapMode)1;
			LayoutRebuilder.ForceRebuildLayoutImmediate(((Graphic)descriptionText).rectTransform);
			Canvas.ForceUpdateCanvases();
			if ((Object)(object)descriptionScrollRect != (Object)null)
			{
				descriptionScrollRect.verticalNormalizedPosition = 1f;
			}
		}
		else
		{
			Debug.LogError((object)"descriptionText is null when trying to update!");
		}
	}

	private void UpdateSelectButton()
	{
		if (!((Object)(object)selectClassButton != (Object)null))
		{
			return;
		}
		Button component = selectClassButton.GetComponent<Button>();
		Text componentInChildren = selectClassButton.GetComponentInChildren<Text>();
		if (string.IsNullOrEmpty(selectedClassName))
		{
			((Selectable)component).interactable = false;
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = "Select a Class";
			}
		}
		else
		{
			((Selectable)component).interactable = true;
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = "Select " + selectedClassName;
			}
		}
	}

	private void OnClassSelected(string className, Player player)
	{
		if (string.IsNullOrEmpty(className))
		{
			Debug.LogError((object)"Cannot select class: className is null or empty");
			if (player != null)
			{
				((Character)player).Message((MessageType)2, "Error: Invalid class selection", 0, (Sprite)null);
			}
			return;
		}
		if (PlayerClassManager.SetPlayerActiveClass(player, className))
		{
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
			string text = string.Join(", ", playerData.activeClasses);
			((Character)player).Message((MessageType)2, "Selected " + className + " Class! Active: " + text, 0, (Sprite)null);
		}
		else
		{
			((Character)player).Message((MessageType)2, "Failed to select " + className + " class", 0, (Sprite)null);
			Debug.LogError((object)("Failed to set active class " + className + " for " + player.GetPlayerName()));
		}
		CloseClassSelectionGUI();
	}

	private static void CloseClassSelectionGUI()
	{
		if ((Object)(object)classSelectionPanel != (Object)null)
		{
			Object.Destroy((Object)(object)classSelectionPanel);
			classSelectionPanel = null;
			descriptionText = null;
			descriptionScrollRect = null;
			selectClassButton = null;
			selectedClassName = "";
			if (GUIManager.Instance != null)
			{
				GUIManager.BlockInput(false);
			}
		}
	}
}
public class ButtonHoverEffect : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
{
	private Vector3 originalScale;

	private bool isHovering;

	private void Start()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		originalScale = ((Component)this).transform.localScale;
	}

	public void OnPointerEnter(PointerEventData eventData)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (!isHovering)
		{
			isHovering = true;
			((Component)this).transform.localScale = originalScale * 1.05f;
		}
	}

	public void OnPointerExit(PointerEventData eventData)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		if (isHovering)
		{
			isHovering = false;
			((Component)this).transform.localScale = originalScale;
		}
	}
}
public static class CrusherPerkManager
{
	public const float COLD_STEEL_FROST_MULTIPLIER = 0.2f;

	public const float THUNDERING_BLOWS_RADIUS = 5f;

	private const string Intro = "Powerful warriors who excel with heavy blunt weapons.";

	private const string Outro = "Best suited for players who like devastating area attacks and crowd control.";

	public static readonly List<PerkInfo> Perks = new List<PerkInfo>
	{
		new PerkInfo
		{
			RequiredLevel = 10,
			Name = "Bonebreaker",
			Description = "+15% blunt damage; +25% stagger power"
		},
		new PerkInfo
		{
			RequiredLevel = 20,
			Name = "Cold Steel",
			Description = "Melee attacks imbued with frost dealing +20% weapon damage as frost"
		},
		new PerkInfo
		{
			RequiredLevel = 30,
			Name = "Thundering Blows",
			Description = "Heavy melee attacks generate 2m shockwave of lightning damage"
		},
		new PerkInfo
		{
			RequiredLevel = 40,
			Name = "Might of the Earth",
			Description = "-30% stamina drain on attacks from wielding heavy weapons"
		},
		new PerkInfo
		{
			RequiredLevel = 50,
			Name = "Colossus",
			Description = "Ignore movement speed penalties from armor weight"
		}
	};

	public static bool HasCrusherPerk(Player player, int requiredLevel)
	{
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		PlayerClassData playerData = PlayerClassManager.GetPlayerData(player);
		if (playerData == null || !playerData.IsClassActive(PlayerClass.Crusher))
		{
			return false;
		}
		return playerData.GetClassLevel(PlayerClass.Crusher) >= requiredLevel;
	}

	public static string GetClassDescription(Player player)
	{
		int currentLevel = PlayerClassManager.GetPlayerData(player)?.GetClassLevel(PlayerClass.Crusher) ?? 0;
		return PerkDescriptionBuilder.Build("Powerful warriors who excel with heavy blunt weapons.", Perks, "Best suited for players who like devastating area attacks and crowd control.", currentLevel);
	}

	public static float ApplyLv10_BonebreakerDamage(Player player, float baseDamage)
	{
		if (!HasCrusherPerk(player, 10))
		{
			return baseDamage;
		}
		return baseDamage * 1.15f;
	}

	public static float ApplyLv10_BonebreakerStagger(Player player, float baseStagger)
	{
		if (!HasCrusherPerk(player, 10))
		{
			return baseStagger;
		}
		return baseStagger * 1.25f;
	}

	public static void ApplyLv20_ColdSteelFrost(Player player, ref HitData hit, float weaponDamage)
	{
		if (HasCrusherPerk(player, 20))
		{
			float num = weaponDamage * 0.2f;
			hit.m_damage.m_frost += num;
			if (Random.Range(0f, 1f) < 0.3f)
			{
				((Character)player).Message((MessageType)1, $"Cold Steel! +{num:F0} frost damage", 0, (Sprite)null);
			}
		}
	}

	public static void TriggerLv30_ThunderingBlows(Player player, Vector3 hitPoint, float baseDamage)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Expected O, but got Unknown
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
		if (!HasCrusherPerk(player, 30))
		{
			return;
		}
		List<Character> list = (from c in Physics.OverlapSphere(hitPoint, 5f)
			select ((Component)c).GetComponent<Character>() into c
			where (Object)(object)c != (Object)null && (Object)(object)c != (Object)(object)player && !c.IsDead() && c.IsMonsterFaction(0f)
			select c).ToList();
		if (list.Count == 0)
		{
			return;
		}
		float lightning = baseDamage * 0.5f;
		foreach (Character item in list)
		{
			HitData val = new HitData();
			val.m_attacker = ((Character)player).GetZDOID();
			val.m_damage.m_lightning = lightning;
			val.m_point = ((Component)item).transform.position;
			Vector3 val2 = ((Component)item).transform.position - hitPoint;
			val.m_dir = ((Vector3)(ref val2)).normalized;
			val.m_skill = (SkillType)3;
			item.Damage(val);
		}
		((Character)player).Message((MessageType)1, $"Thundering Blow! Hit {list.Count} enemies", 0, (Sprite)null);
	}

	private static void CreateThunderingBlowsEffect(Vector3 position)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: 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_0051: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.GetPrefab("lightningAOE") : null);
			if ((Object)(object)val != (Object)null)
			{
				Object.Instantiate<GameObject>(val, position, Quaternion.identity);
				return;
			}
			ZNetScene instance2 = ZNetScene.instance;
			GameObject val2 = ((instance2 != null) ? instance2.GetPrefab("vfx_HitSparks") : null);
			if ((Object)(object)val2 != (Object)null)
			{
				Object.Instantiate<GameObject>(val2, position, Quaternion.identity);
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("Could not create Thundering Blows effect: " + ex.Message));
		}
	}

	public static float ApplyLv40_MightOfTheEarthStamina(Player player, float staminaCost)
	{
		if (!HasCrusherPerk(player, 40))
		{
			return staminaCost;
		}
		ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
		if (currentWeapon != null && ClassCombatManager.IsBluntWeapon(currentWeapon))
		{
			return staminaCost * 0.7f;
		}
		return staminaCost;
	}

	public static bool IsHeavyAttack(AttackType attackType)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		return (int)attackType == 1;
	}
}
[HarmonyPatch]
public static class CrusherPerkPatches
{
	private static bool triggerThunderingBlowsDamage;

	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPrefix]
	public static void Character_Damage_Crusher_Prefix(Character __instance, ref HitData hit)
	{
		try
		{
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val == null || (Object)(object)__instance == (Object)null || __instance is Player || !ClassCombatManager.IsBluntWeapon(((Humanoid)val).GetCurrentWeapon()))
			{
				return;
			}
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(val);
			if (playerData != null && playerData.IsClassActive(PlayerClass.Crusher))
			{
				float totalDamage = hit.GetTotalDamage();
				float blunt = hit.m_damage.m_blunt;
				if (blunt > 0f)
				{
					hit.m_damage.m_blunt = CrusherPerkManager.ApplyLv10_BonebreakerDamage(val, blunt);
				}
				hit.m_staggerMultiplier = CrusherPerkManager.ApplyLv10_BonebreakerStagger(val, hit.m_staggerMultiplier);
				CrusherPerkManager.ApplyLv20_ColdSteelFrost(val, ref hit, totalDamage);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Crusher_Prefix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Humanoid), "StartAttack")]
	[HarmonyPrefix]
	public static void Humanoid_StartAttack_Prefix(Character target, bool secondaryAttack)
	{
		if (ClassCombatManager.IsBluntWeapon(((Humanoid)Player.m_localPlayer).GetCurrentWeapon()) && secondaryAttack)
		{
			triggerThunderingBlowsDamage = true;
		}
	}

	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPostfix]
	public static void Character_Damage_Crusher_Postfix(Character __instance, HitData hit)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val != null && !((Object)(object)__instance == (Object)null) && !(__instance is Player) && ClassCombatManager.IsBluntWeapon(((Humanoid)val).GetCurrentWeapon()))
			{
				PlayerClassData playerData = PlayerClassManager.GetPlayerData(val);
				if (playerData != null && playerData.IsClassActive(PlayerClass.Crusher) && hit.m_damage.m_blunt > 0f && triggerThunderingBlowsDamage)
				{
					CrusherPerkManager.TriggerLv30_ThunderingBlows(val, hit.m_point, hit.GetTotalDamage());
					triggerThunderingBlowsDamage = false;
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Character_Damage_Crusher_Postfix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Player), "UseStamina")]
	[HarmonyPrefix]
	public static void Player_UseStamina_Crusher_Prefix(Player __instance, ref float v)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				v = CrusherPerkManager.ApplyLv40_MightOfTheEarthStamina(__instance, v);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Player_UseStamina_Crusher_Prefix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Humanoid), "EquipItem")]
	[HarmonyPostfix]
	public static void Humanoid_EquipItem_Prefix(ItemData item, bool triggerEquipEffects = true)
	{
		try
		{
			if (item != null && CrusherPerkManager.HasCrusherPerk(Player.m_localPlayer, 50) && !(item.m_shared.m_movementModifier > 0f))
			{
				item.m_shared.m_movementModifier = 0f;
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in Player_GetRunSpeedFactor_Crusher_Postfix: " + ex.Message));
		}
	}
}
public enum PlayerClass
{
	SwordMaster,
	Archer,
	Crusher,
	Assassin,
	Brawler,
	Wizard,
	Lancer,
	Bulwark
}
public static class PlayerClassHelper
{
	private static readonly Dictionary<PlayerClass, string> DisplayNames = new Dictionary<PlayerClass, string>
	{
		{
			PlayerClass.SwordMaster,
			"Sword Master"
		},
		{
			PlayerClass.Archer,
			"Archer"
		},
		{
			PlayerClass.Crusher,
			"Crusher"
		},
		{
			PlayerClass.Assassin,
			"Assassin"
		},
		{
			PlayerClass.Brawler,
			"Brawler"
		},
		{
			PlayerClass.Wizard,
			"Wizard"
		},
		{
			PlayerClass.Lancer,
			"Lancer"
		},
		{
			PlayerClass.Bulwark,
			"Bulwark"
		}
	};

	private static readonly Dictionary<PlayerClass, string> InternalNames = new Dictionary<PlayerClass, string>
	{
		{
			PlayerClass.SwordMaster,
			"Sword Master"
		},
		{
			PlayerClass.Archer,
			"Archer"
		},
		{
			PlayerClass.Crusher,
			"Crusher"
		},
		{
			PlayerClass.Assassin,
			"Assassin"
		},
		{
			PlayerClass.Brawler,
			"Brawler"
		},
		{
			PlayerClass.Wizard,
			"Wizard"
		},
		{
			PlayerClass.Lancer,
			"Lancer"
		},
		{
			PlayerClass.Bulwark,
			"Bulwark"
		}
	};

	private static readonly Dictionary<PlayerClass, string> WeaponTypes = new Dictionary<PlayerClass, string>
	{
		{
			PlayerClass.SwordMaster,
			"Swords"
		},
		{
			PlayerClass.Archer,
			"Bows & Crossbows"
		},
		{
			PlayerClass.Crusher,
			"Maces & Hammers"
		},
		{
			PlayerClass.Assassin,
			"Knives"
		},
		{
			PlayerClass.Brawler,
			"Unarmed"
		},
		{
			PlayerClass.Wizard,
			"Staves"
		},
		{
			PlayerClass.Lancer,
			"Spears & Polearms"
		},
		{
			PlayerClass.Bulwark,
			"Shields"
		}
	};

	public static string GetDisplayName(PlayerClass playerClass)
	{
		return DisplayNames[playerClass];
	}

	public static string GetInternalName(PlayerClass playerClass)
	{
		return InternalNames[playerClass];
	}

	public static string GetWeaponType(PlayerClass playerClass)
	{
		return WeaponTypes[playerClass];
	}

	public static PlayerClass? ParseFromDisplayName(string displayName)
	{
		if (string.IsNullOrEmpty(displayName))
		{
			return null;
		}
		foreach (KeyValuePair<PlayerClass, string> displayName2 in DisplayNames)
		{
			if (string.Equals(displayName2.Value, displayName, StringComparison.OrdinalIgnoreCase))
			{
				return displayName2.Key;
			}
		}
		if (Enum.TryParse<PlayerClass>(displayName.Replace(" ", ""), ignoreCase: true, out var result))
		{
			return result;
		}
		return null;
	}

	public static PlayerClass? ParseFromInternalName(string internalName)
	{
		if (string.IsNullOrEmpty(internalName))
		{
			return null;
		}
		foreach (KeyValuePair<PlayerClass, string> internalName2 in InternalNames)
		{
			if (string.Equals(internalName2.Value, internalName, StringComparison.OrdinalIgnoreCase))
			{
				return internalName2.Key;
			}
		}
		return null;
	}

	public static List<PlayerClass> GetAllClasses()
	{
		return Enum.GetValues(typeof(PlayerClass)).Cast<PlayerClass>().ToList();
	}

	public static string[] GetAllDisplayNames()
	{
		return (from c in GetAllClasses()
			select GetDisplayName(c)).ToArray();
	}

	public static bool IsWeaponAppropriateForClass(ItemData weapon, PlayerClass playerClass)
	{
		return playerClass switch
		{
			PlayerClass.SwordMaster => ClassCombatManager.IsSwordWeapon(weapon), 
			PlayerClass.Archer => ClassCombatManager.IsBowWeapon(weapon), 
			PlayerClass.Crusher => ClassCombatManager.IsBluntWeapon(weapon), 
			PlayerClass.Assassin => ClassCombatManager.IsKnifeWeapon(weapon), 
			PlayerClass.Brawler => ClassCombatManager.IsUnarmedAttack(weapon), 
			PlayerClass.Wizard => ClassCombatManager.IsMagicWeapon(weapon), 
			PlayerClass.Lancer => ClassCombatManager.IsSpearWeapon(weapon), 
			PlayerClass.Bulwark => true, 
			_ => false, 
		};
	}

	public static List<string> ToInternalNames(List<PlayerClass> classes)
	{
		return classes.Select((PlayerClass c) => GetInternalName(c)).ToList();
	}

	public static List<PlayerClass> FromInternalNames(List<string> internalNames)
	{
		List<PlayerClass> list = new List<PlayerClass>();
		foreach (string internalName in internalNames)
		{
			PlayerClass? playerClass = ParseFromInternalName(internalName);
			if (playerClass.HasValue)
			{
				list.Add(playerClass.Value);
			}
		}
		return list;
	}
}
[Serializable]
public class PlayerClassData
{
	public Dictionary<string, int> classLevels = new Dictionary<string, int>();

	public Dictionary<string, float> classXP = new Dictionary<string, float>();

	public List<string> activeClasses = new List<string>();

	public Dictionary<string, bool> classUnlocked = new Dictionary<string, bool>();

	[SerializeField]
	private List<string> _classNames = new List<string>();

	[SerializeField]
	private List<int> _classLevels = new List<int>();

	[SerializeField]
	private List<float> _classXPValues = new List<float>();

	[SerializeField]
	private List<bool> _classUnlockedValues = new List<bool>();

	public PlayerClassData()
	{
		InitializeAllClasses();
	}

	private void InitializeAllClasses()
	{
		foreach (PlayerClass allClass in PlayerClassHelper.GetAllClasses())
		{
			string internalName = PlayerClassHelper.GetInternalName(allClass);
			if (!classLevels.ContainsKey(internalName))
			{
				classLevels[internalName] = 0;
			}
			if (!classXP.ContainsKey(internalName))
			{
				classXP[internalName] = 0f;
			}
			if (!classUnlocked.ContainsKey(internalName))
			{
				classUnlocked[internalName] = true;
			}
		}
	}

	public void PrepareForSerialization()
	{
		_classNames.Clear();
		_classLevels.Clear();
		_classXPValues.Clear();
		_classUnlockedValues.Clear();
		foreach (KeyValuePair<string, int> classLevel in classLevels)
		{
			_classNames.Add(classLevel.Key);
			_classLevels.Add(classLevel.Value);
			_classXPValues.Add(classXP.ContainsKey(classLevel.Key) ? classXP[classLevel.Key] : 0f);
			_classUnlockedValues.Add(!classUnlocked.ContainsKey(classLevel.Key) || classUnlocked[classLevel.Key]);
		}
	}

	public void RestoreFromSerialization()
	{
		classLevels.Clear();
		classXP.Clear();
		classUnlocked.Clear();
		for (int i = 0; i < _classNames.Count; i++)
		{
			if (i < _classLevels.Count)
			{
				classLevels[_classNames[i]] = _classLevels[i];
			}
			if (i < _classXPValues.Count)
			{
				classXP[_classNames[i]] = _classXPValues[i];
			}
			if (i < _classUnlockedValues.Count)
			{
				classUnlocked[_classNames[i]] = _classUnlockedValues[i];
			}
		}
		InitializeAllClasses();
	}

	public bool CanSelectSecondClass()
	{
		return classLevels.Values.Any((int level) => level >= 50);
	}

	public int GetMaxActiveClasses()
	{
		if (!CanSelectSecondClass())
		{
			return 1;
		}
		return 2;
	}

	public bool IsClassActive(PlayerClass playerClass)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		return activeClasses.Contains(internalName);
	}

	public bool IsClassActive(string className)
	{
		return activeClasses.Contains(className);
	}

	public bool CanActivateClass(PlayerClass playerClass)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (!classUnlocked.ContainsKey(internalName) || !classUnlocked[internalName])
		{
			return false;
		}
		if (IsClassActive(playerClass))
		{
			return false;
		}
		return activeClasses.Count < GetMaxActiveClasses();
	}

	public void SetActiveClass(PlayerClass playerClass)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (!classUnlocked.ContainsKey(internalName) || !classUnlocked[internalName])
		{
			Debug.LogWarning((object)("Cannot set active class " + internalName + ": class not unlocked"));
			return;
		}
		List<string> values = new List<string>(activeClasses);
		activeClasses.Clear();
		activeClasses.Add(internalName);
		Debug.Log((object)("Set active class: " + internalName + " (was: " + string.Join(", ", values) + ")"));
	}

	public void AddActiveClass(PlayerClass playerClass)
	{
		if (!CanActivateClass(playerClass))
		{
			Debug.LogWarning((object)$"Cannot activate class {playerClass}");
			return;
		}
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (!activeClasses.Contains(internalName))
		{
			activeClasses.Add(internalName);
			Debug.Log((object)("Added active class: " + internalName + ". Active classes: " + string.Join(", ", activeClasses)));
		}
	}

	public void RemoveActiveClass(PlayerClass playerClass)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (activeClasses.Remove(internalName))
		{
			Debug.Log((object)("Removed active class: " + internalName + ". Active classes: " + string.Join(", ", activeClasses)));
		}
	}

	public int GetClassLevel(PlayerClass playerClass)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (!classLevels.ContainsKey(internalName))
		{
			return 0;
		}
		return classLevels[internalName];
	}

	public int GetClassLevel(string className)
	{
		if (!classLevels.ContainsKey(className))
		{
			return 0;
		}
		return classLevels[className];
	}

	public float GetClassXP(PlayerClass playerClass)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (!classXP.ContainsKey(internalName))
		{
			return 0f;
		}
		return classXP[internalName];
	}

	public float GetClassXP(string className)
	{
		if (!classXP.ContainsKey(className))
		{
			return 0f;
		}
		return classXP[className];
	}

	public void AddClassXP(PlayerClass playerClass, float xpAmount)
	{
		string internalName = PlayerClassHelper.GetInternalName(playerClass);
		if (!classXP.ContainsKey(internalName))
		{
			classXP[internalName] = 0f;
		}
		classXP[internalName] += xpAmount;
		CheckForLevelUp(internalName);
	}

	public void AddClassXP(string className, float xpAmount)
	{
		if (!classXP.ContainsKey(className))
		{
			classXP[className] = 0f;
		}
		classXP[className] += xpAmount;
		CheckForLevelUp(className);
	}

	private void CheckForLevelUp(string className)
	{
		int classLevel = GetClassLevel(className);
		if (classLevel >= 50)
		{
			return;
		}
		float num = GetClassXP(className);
		float totalXPForLevel = XPCurveHelper.GetTotalXPForLevel(classLevel + 1);
		if (num >= totalXPForLevel)
		{
			classLevels[className] = classLevel + 1;
			Debug.Log((object)$"Class {className} leveled up to {classLevel + 1}!");
			if ((classLevel + 1) % 10 == 0)
			{
				Debug.Log((object)$"New perk unlocked for {className} at level {classLevel + 1}!");
			}
		}
	}

	public List<PlayerClass> GetActiveClassEnums()
	{
		return PlayerClassHelper.FromInternalNames(activeClasses);
	}
}
public static class PlayerClassManager
{
	private static Dictionary<long, PlayerClassData> playerData = new Dictionary<long, PlayerClassData>();

	private static readonly Dictionary<PlayerClass, Func<Player, string>> ClassDescriptionProviders = new Dictionary<PlayerClass, Func<Player, string>>
	{
		{
			PlayerClass.SwordMaster,
			SwordMasterPerkManager.GetClassDescription
		},
		{
			PlayerClass.Archer,
			ArcherPerkManager.GetClassDescription
		},
		{
			PlayerClass.Crusher,
			CrusherPerkManager.GetClassDescription
		},
		{
			PlayerClass.Assassin,
			AssassinPerkManager.GetClassDescription
		},
		{
			PlayerClass.Brawler,
			BrawlerPerkManager.GetClassDescription
		},
		{
			PlayerClass.Wizard,
			WizardPerkManager.GetClassDescription
		},
		{
			PlayerClass.Lancer,
			LancerPerkManager.GetClassDescription
		},
		{
			PlayerClass.Bulwark,
			BulwarkPerkManager.GetClassDescription
		}
	};

	public static PlayerClassData GetPlayerData(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			Debug.LogError((object)"GetPlayerData called with null player");
			return null;
		}
		long playerID = player.GetPlayerID();
		if (!playerData.ContainsKey(playerID))
		{
			playerData[playerID] = new PlayerClassData();
			Debug.Log((object)$"Created new class data for player {player.GetPlayerName()} (ID: {playerID})");
		}
		return playerData[playerID];
	}

	public static void SetPlayerDataDirectly(long playerId, PlayerClassData data)
	{
		playerData[playerId] = data;
		Debug.Log((object)$"[MANAGER] Directly set class data for player ID: {playerId}");
	}

	public static bool SetPlayerActiveClass(Player player, PlayerClass playerClass)
	{
		PlayerClassData playerClassData = GetPlayerData(player);
		if (playerClassData == null)
		{
			Debug.LogError((object)("Could not get player data for " + (((player != null) ? player.GetPlayerName() : null) ?? "null")));
			return false;
		}
		string text = string.Join(", ", playerClassData.activeClasses);
		playerClassData.SetActiveClass(playerClass);
		string text2 = string.Join(", ", playerClassData.activeClasses);
		Debug.Log((object)("Class change for " + player.GetPlayerName() + ": '" + text + "' -> '" + text2 + "'"));
		return playerClassData.IsClassActive(playerClass);
	}

	public static bool SetPlayerActiveClass(Player player, string className)
	{
		PlayerClass? playerClass = PlayerClassHelper.ParseFromInternalName(className);
		if (!playerClass.HasValue)
		{
			Debug.LogError((object)("Invalid class name: " + className));
			return false;
		}
		return SetPlayerActiveClass(player, playerClass.Value);
	}

	public static string[] GetAllClassNames()
	{
		return PlayerClassHelper.GetAllDisplayNames();
	}

	public static string GetWeaponTypeForClass(PlayerClass playerClass)
	{
		return PlayerClassHelper.GetWeaponType(playerClass);
	}

	public static string GetWeaponTypeForClass(string className)
	{
		PlayerClass? playerClass = PlayerClassHelper.ParseFromInternalName(className);
		if (!playerClass.HasValue)
		{
			return "Unknown";
		}
		return PlayerClassHelper.GetWeaponType(playerClass.Value);
	}

	public static bool HasActiveClasses(Player player)
	{
		PlayerClassData playerClassData = GetPlayerData(player);
		if (playerClassData != null)
		{
			return playerClassData.activeClasses.Count > 0;
		}
		return false;
	}

	public static string GetActiveClassesString(Player player)
	{
		PlayerClassData playerClassData = GetPlayerData(player);
		if (playerClassData == null || playerClassData.activeClasses.Count == 0)
		{
			return "None";
		}
		return string.Join(", ", playerClassData.activeClasses);
	}

	public static string BuildActiveClassesCompendiumText(Player player)
	{
		PlayerClassData playerClassData = GetPlayerData(player);
		if (playerClassData == null || playerClassData.activeClasses.Count == 0)
		{
			return "No active class selected. Visit a Class Obelisk to choose one.";
		}
		StringBuilder stringBuilder = new StringBuilder();
		foreach (PlayerClass activeClassEnum in playerClassData.GetActiveClassEnums())
		{
			int classLevel = playerClassData.GetClassLevel(activeClassEnum);
			stringBuilder.Append($"<color=yellow>{PlayerClassHelper.GetDisplayName(activeClassEnum)} — Level {classLevel}</color>\n");
			if (classLevel >= 50)
			{
				stringBuilder.Append("<color=#00FFFF>Max Level Reached</color>\n\n");
			}
			else
			{
				(float current, float required) xPProgress = XPCurveHelper.GetXPProgress(playerClassData.GetClassXP(activeClassEnum), classLevel);
				float item = xPProgress.current;
				float item2 = xPProgress.required;
				float num = ((item2 > 0f) ? (item / item2 * 100f) : 0f);
				stringBuilder.Append($"<color=#00FFFF>XP: {item:N0} / {item2:N0} ({num:F0}%) to next level</color>\n\n");
			}
			stringBuilder.Append(ClassDescriptionProviders[activeClassEnum](player));
			stringBuilder.Append("\n\n");
		}
		if (playerClassData.activeClasses.Count < playerClassData.GetMaxActiveClasses())
		{
			stringBuilder.Append("<color=#AAAAAA>A second class slot is available — visit a Class Obelisk to choose one.</color>");
		}
		return stringBuilder.ToString();
	}

	public static void ClearAllPlayerData()
	{
		playerData.Clear();
		Debug.Log((object)"Cleared all player class data from memory");
	}

	public static void ReloadPlayerData(Player player)
	{
		if (!((Object)(object)player == (Object)null))
		{
			long playerID = player.GetPlayerID();
			if (playerData.ContainsKey(playerID))
			{
				playerData.Remove(playerID);
			}
			GetPlayerData(player);
			Debug.Log((object)("Reloaded data for player " + player.GetPlayerName()));
		}
	}

	public static bool ResetActiveClassProgress(Player player, string activeClassInternalName)
	{
		PlayerClassData playerClassData = GetPlayerData(player);
		if ((Object)(object)player == (Object)null || playerClassData == null || string.IsNullOrWhiteSpace(activeClassInternalName))
		{
			return false;
		}
		if (!playerClassData.activeClasses.Contains(activeClassInternalName))
		{
			return false;
		}
		if (!playerClassData.classLevels.ContainsKey(activeClassInternalName))
		{
			playerClassData.classLevels[activeClassInternalName] = 0;
		}
		if (!playerClassData.classXP.ContainsKey(activeClassInternalName))
		{
			playerClassData.classXP[activeClassInternalName] = 0f;
		}
		playerClassData.classLevels[activeClassInternalName] = 0;
		playerClassData.classXP[activeClassInternalName] = 0f;
		if (!playerClassData.activeClasses.Contains(activeClassInternalName))
		{
			playerClassData.activeClasses.Add(activeClassInternalName);
		}
		return true;
	}
}
[HarmonyPatch(typeof(Player), "Save")]
public static class Player_Save_Patch
{
	private static void Postfix(Player __instance, ZPackage pkg)
	{
		try
		{
			PlayerClassData playerData = PlayerClassManager.GetPlayerData(__instance);
			if (playerData != null)
			{
				playerData.PrepareForSerialization();
				string text = JsonUtility.ToJson((object)playerData, false);
				pkg.Write(text);
			}
			else
			{
				pkg.Write("");
				Debug.LogWarning((object)("[PATCH:SAVE] No class data to save for " + __instance.GetPlayerName()));
			}
		}
		catch (Exception arg)
		{
			Debug.LogError((object)$"[PATCH:SAVE] Error saving class data: {arg}");
		}
	}
}
[HarmonyPatch(typeof(Player), "Load")]
public static class Player_Load_Patch
{
	private static void Postfix(Player __instance, ZPackage pkg)
	{
		try
		{
			if (pkg.GetPos() < pkg.Size())
			{
				string text = pkg.ReadString();
				if (!string.IsNullOrEmpty(text))
				{
					PlayerClassData playerClassData = JsonUtility.FromJson<PlayerClassData>(text);
					playerClassData.RestoreFromSerialization();
					PlayerClassManager.SetPlayerDataDirectly(__instance.GetPlayerID(), playerClassData);
				}
			}
		}
		catch (Exception arg)
		{
			Debug.LogError((object)$"[PATCH:LOAD] Error loading class data: {arg}");
		}
	}
}
public class PerkInfo
{
	public int RequiredLevel;

	public string Name;

	public string Description;
}
public static class PerkDescriptionBuilder
{
	public static string Build(string intro, List<PerkInfo> perks, string outro, int currentLevel)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.Append(intro).Append("\n\n");
		stringBuilder.Append("Passive Perks by Level:\n");
		foreach (PerkInfo perk in perks)
		{
			stringBuilder.Append((currentLevel >= perk.RequiredLevel) ? $"• Lv{perk.RequiredLevel} – {perk.Name}: {perk.Description}\n" : $"• Lv{perk.RequiredLevel} – ???\n");
		}
		stringBuilder.Append("\n").Append(outro);
		return stringBuilder.ToString();
	}
}
public static class SEUtils
{
	public static class SEReflection
	{
		private static readonly FieldInfo _fiTime = typeof(StatusEffect).GetField("m_time", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo _fiTTL = typeof(StatusEffect).GetField("m_ttl", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo _fiStacks = typeof(StatusEffect).GetField("m_stacks", BindingFlags.Instance | BindingFlags.NonPublic);

		public static float GetElapsedTime(StatusEffect se)
		{
			return (float)(_fiTime?.GetValue(se) ?? ((object)0f));
		}

		public static float GetDuration(StatusEffect se)
		{
			return (float)(_fiTTL?.GetValue(se) ?? ((object)0f));
		}

		public static int GetStacks(StatusEffect se)
		{
			return (int)(_fiStacks?.GetValue(se) ?? ((object)0));
		}
	}

	public static bool HasEffect<T>(Character c) where T : StatusEff