Decompiled source of GlitnirRacesKingdom v0.0.6

GlitnirRaces.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GlitnirRaces")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GlitnirRaces")]
[assembly: AssemblyTitle("GlitnirRaces")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("glitnir.races", "Glitnir Races", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class GlitnirRacesPlugin : BaseUnityPlugin
{
	public class RaceConfig
	{
		public string RaceId;

		public string DisplayName;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<string> Scale;

		public ConfigEntry<string> SkinColor;

		public ConfigEntry<string> HairColor;

		public ConfigEntry<string> EarColor;

		public ConfigEntry<string> EyeColor;

		public ConfigEntry<string> SecretCode;

		public ConfigEntry<float> HealthBonus;

		public ConfigEntry<float> StaminaBonus;

		public ConfigEntry<float> EitrBonus;

		public ConfigEntry<float> CarryWeightBonus;

		public ConfigEntry<float> HealthRegenMultiplier;

		public ConfigEntry<float> StaminaRegenMultiplier;

		public ConfigEntry<float> EitrRegenMultiplier;

		public ConfigEntry<float> DamageDoneMultiplier;

		public ConfigEntry<float> BackstabMultiplier;

		public ConfigEntry<float> DamageTakenMultiplier;

		public ConfigEntry<float> LifestealPercent;

		public ConfigEntry<float> StealthMultiplier;

		public ConfigEntry<float> SpeedMultiplier;

		public ConfigEntry<string> Resistances;

		public ConfigEntry<float> HeatResistanceBonus;

		public ConfigEntry<float> LowHealthThreshold;

		public ConfigEntry<float> LowHealthDamageBonus;

		public ConfigEntry<bool> StillHealAura;

		public ConfigEntry<float> StillHealAmount;

		public ConfigEntry<float> StillHealInterval;

		public ConfigEntry<float> StillHealRequiredStillTime;

		public ConfigEntry<bool> StillHealDayOnly;

		public ConfigEntry<bool> IronStanceEnabled;

		public ConfigEntry<float> IronStanceExtraDamageReduction;

		public ConfigEntry<bool> SecondWindEnabled;

		public ConfigEntry<float> SecondWindThreshold;

		public ConfigEntry<float> SecondWindHealAmount;

		public ConfigEntry<float> SecondWindStaminaAmount;

		public ConfigEntry<float> SecondWindCooldown;

		public ConfigEntry<string> SkillDamageMultipliers;

		public ConfigEntry<string> AttackDamageTypeMultipliers;

		public ConfigEntry<bool> NoResourceCost;

		public ConfigEntry<float> XpGainMultiplier;

		public ConfigEntry<float> KnockbackMultiplier;

		public ConfigEntry<float> EitrCostMultiplier;

		public ConfigEntry<string> StaminaCostModifiers;

		public ConfigEntry<bool> NightOnlyBonuses;

		public ConfigEntry<float> DayNightHealthShift;

		public ConfigEntry<string> BiomeHealthShifts;
	}

	private enum RaceAbility
	{
		None,
		Orc,
		Elf,
		Dwarf,
		Human
	}

	[HarmonyPatch(typeof(FejdStartup), "SetupCharacterPreview")]
	private static class FejdCharacterPreviewPatch
	{
		private static void Postfix(FejdStartup __instance, PlayerProfile profile)
		{
			GlitnirRacesPlugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)("[GlitnirRaces/Preview] SetupCharacterPreview disparou pro perfil '" + ((profile != null) ? profile.GetName() : null) + "'."));
			}
			try
			{
				ApplyPreviewRace(__instance, profile);
			}
			catch (Exception arg)
			{
				GlitnirRacesPlugin instance2 = Instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogWarning((object)$"Falha ao aplicar raça no preview do menu: {arg}");
				}
			}
		}
	}

	[HarmonyPatch(typeof(Player), "OnSpawned")]
	private static class PlayerSpawnedPatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)Instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Instance.RestoreLocalSavedRaceIfNeeded();
				SendVersionCheckToServer();
			}
			if (IsPlayerLocked(__instance))
			{
				SetZdoLocked(__instance, locked: true);
			}
			Instance.ApplyRaceWithDelays(__instance);
			if (!IsHeadless())
			{
				string race = GetRace(__instance);
				if (!string.IsNullOrWhiteSpace(race))
				{
					UpdateRaceStatusEffect(__instance, race);
				}
			}
		}
	}

	[HarmonyPatch(typeof(Player), "OnDeath")]
	private static class PlayerDeathPatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Instance == (Object)null) && !IsHeadless())
			{
				Instance.ApplyRaceWithDelays(__instance);
				string race = GetRace(__instance);
				if (!string.IsNullOrWhiteSpace(race))
				{
					UpdateRaceStatusEffect(__instance, race);
				}
			}
		}
	}

	[HarmonyPatch(typeof(Player), "CreateDeathEffects")]
	private static class PlayerRagdollRacePatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)Instance == (Object)null || IsHeadless() || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			Ragdoll ragdoll = __instance.GetRagdoll();
			if ((Object)(object)ragdoll == (Object)null)
			{
				return;
			}
			string race = GetRace(__instance);
			if (string.IsNullOrWhiteSpace(race) || !IsRaceEnabled(race))
			{
				return;
			}
			try
			{
				ZNetView component = ((Component)ragdoll).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.GetZDO() != null)
				{
					component.GetZDO().Set("GlitnirRagdollRace", race);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)("[GlitnirRaces] Falha ao gravar raça no ZDO do ragdoll: " + ex.Message));
			}
			ApplyRaceVisualToRagdoll(ragdoll, race);
		}
	}

	[HarmonyPatch(typeof(Ragdoll), "Awake")]
	private static class RagdollAwakePatch
	{
		private static void Postfix(Ragdoll __instance)
		{
			if ((Object)(object)Instance == (Object)null || IsHeadless() || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			try
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.GetZDO() != null)
				{
					string text = component.GetZDO().GetString("GlitnirRagdollRace", "");
					if (!string.IsNullOrWhiteSpace(text) && IsRaceEnabled(text))
					{
						ApplyRaceVisualToRagdoll(__instance, text);
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)("[GlitnirRaces] Falha ao ler raça do ZDO do ragdoll: " + ex.Message));
			}
		}
	}

	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	private static class ZNetDisconnectPatch
	{
		private static void Prefix(ZNetPeer peer)
		{
			if (peer == null)
			{
				return;
			}
			try
			{
				OnPeerDisconnected(peer.m_uid);
			}
			catch
			{
			}
		}
	}

	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	private static class ZNetShutdownPatch
	{
		private static void Prefix()
		{
			NetworkRaces.Clear();
			LastAppliedRace.Clear();
			OriginalSkinColors.Clear();
			LastSkinRefreshRequest.Clear();
			KnownAdmins.Clear();
			HeadCache.Clear();
			HeadCacheRace.Clear();
			ApplyRaceCoroutineGeneration.Clear();
			ZNetViewCache.Clear();
			PendingAltarSwitch.Clear();
			_cachedSavePrefix = null;
			_cachedZNetInstance = null;
			_cachedCharacterId = 0L;
			_cachedServerKey = null;
			RpcRegistered = false;
			RegisteredRpcInstance = null;
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.lastBroadcastRace = null;
				Instance.isRestoringLocalRace = false;
			}
		}
	}

	[HarmonyPatch(typeof(VisEquipment), "SetHelmetItem")]
	private static class HelmetScaleOnEquipPatch
	{
		private static void Postfix(VisEquipment __instance)
		{
			//IL_0045: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && !IsHeadless())
			{
				Player component = ((Component)__instance).GetComponent<Player>();
				if (!((Object)(object)component == (Object)null))
				{
					string race = GetRace(component);
					Vector3 targetScale = ((!string.IsNullOrWhiteSpace(race) && IsRaceEnabled(race)) ? GetRaceScale(race) : HumanScale);
					ScaleCurrentHelmetForRace(component, targetScale);
				}
			}
		}
	}

	[HarmonyPatch(typeof(VisEquipment), "SetHairColor")]
	private static class ForceRaceHairColorPatch
	{
		private static void Prefix(VisEquipment __instance, ref Vector3 __0)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || IsApplyingRace)
			{
				return;
			}
			Player component = ((Component)__instance).GetComponent<Player>();
			if (!((Object)(object)component == (Object)null))
			{
				string race = GetRace(component);
				if (RaceUsesForcedHair(race))
				{
					__0 = GetRaceHairColor(race);
				}
			}
		}
	}

	[HarmonyPatch(typeof(VisEquipment), "SetSkinColor")]
	private static class PreventForcedRaceSkinOverridePatch
	{
		private static bool Prefix(VisEquipment __instance, Vector3 __0)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || IsApplyingRace)
			{
				return true;
			}
			Player component = ((Component)__instance).GetComponent<Player>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			string race = GetRace(component);
			if (!RaceUsesForcedSkin(race))
			{
				return true;
			}
			CacheOriginalSkinValue(component, __0);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.ForceSkinAfterVanilla(component, race);
			}
			return false;
		}
	}

	[HarmonyPatch]
	private static class VisEquipmentOnlineRefreshPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] names = new string[9] { "UpdateEquipmentVisuals", "UpdateVisuals", "SetChestItem", "SetLegItem", "SetHelmetItem", "SetShoulderItem", "SetUtilityItem", "SetLeftItem", "SetRightItem" };
			string[] array = names;
			foreach (string name in array)
			{
				MethodInfo method = AccessTools.Method(typeof(VisEquipment), name, (Type[])null, (Type[])null);
				if (method != null)
				{
					yield return method;
				}
			}
		}

		private static void Postfix(VisEquipment __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Instance == (Object)null) && !IsApplyingRace && !IsHeadless())
			{
				Instance.ForceSkinAfterVanilla(__instance);
			}
		}
	}

	[HarmonyPatch]
	private static class PlayerOnlineRefreshPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] names = new string[3] { "UpdateEquipment", "OnInventoryChanged", "UpdateVisual" };
			string[] array = names;
			foreach (string name in array)
			{
				MethodInfo method = AccessTools.Method(typeof(Player), name, (Type[])null, (Type[])null);
				if (method != null)
				{
					yield return method;
				}
			}
		}

		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Instance == (Object)null) && !IsApplyingRace && !IsHeadless())
			{
				Instance.RequestOnlineVisualRefresh(__instance);
			}
		}
	}

	[HarmonyPatch(typeof(Character), "SetVisible")]
	private static class CharacterSetVisiblePatch
	{
		private static void Postfix(Character __instance, bool visible)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (!visible || (Object)(object)__instance == (Object)null || (Object)(object)Instance == (Object)null || IsHeadless())
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null))
			{
				string race = GetRace(val);
				if (!string.IsNullOrWhiteSpace(race) && IsRaceEnabled(race))
				{
					ApplyScale(val, GetRaceScale(race));
				}
			}
		}
	}

	[HarmonyPatch(typeof(Chat), "InputText")]
	private static class ChatPatch
	{
		private static bool Prefix(Chat __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			string text = "";
			try
			{
				FieldInfo field = typeof(Chat).GetField("m_input", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					return true;
				}
				object value = field.GetValue(__instance);
				if (value == null)
				{
					return true;
				}
				text = (value.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(value) as string)?.Trim() ?? "";
			}
			catch
			{
				return true;
			}
			string raceFromSecretCode = GetRaceFromSecretCode(text);
			if (raceFromSecretCode == null)
			{
				return true;
			}
			TransformLocalPlayerFromMarketplace(raceFromSecretCode);
			try
			{
				object obj2 = typeof(Chat).GetField("m_input", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(__instance);
				PropertyInfo propertyInfo = obj2?.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
				if (propertyInfo != null && propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(obj2, "");
				}
			}
			catch
			{
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Player), "SetMaxHealth")]
	private static class RaceMaxHealthPatch
	{
		private static Dictionary<Biome, float> ParseBiomeShifts(string value)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Biome, float> dictionary = new Dictionary<Biome, float>();
			if (string.IsNullOrWhiteSpace(value))
			{
				return dictionary;
			}
			string[] array = value.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length == 2 && Enum.TryParse<Biome>(array2[0].Trim(), ignoreCase: true, out Biome result) && float.TryParse(array2[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
				{
					dictionary[result] = result2;
				}
			}
			return dictionary;
		}

		private static void Prefix(Player __instance, ref float health)
		{
			//IL_007d: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetRaceConfig(__instance, out var cfg))
			{
				return;
			}
			health += cfg.HealthBonus.Value;
			if (cfg.DayNightHealthShift.Value != 0f)
			{
				health += (IsNightSafe() ? cfg.DayNightHealthShift.Value : (0f - cfg.DayNightHealthShift.Value));
			}
			if (!string.IsNullOrWhiteSpace(cfg.BiomeHealthShifts.Value))
			{
				try
				{
					Biome currentBiome = __instance.GetCurrentBiome();
					Dictionary<Biome, float> dictionary = ParseBiomeShifts(cfg.BiomeHealthShifts.Value);
					if (dictionary.TryGetValue(currentBiome, out var value))
					{
						health += value;
					}
				}
				catch (Exception ex)
				{
					GlitnirRacesPlugin instance = Instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogWarning((object)("[GlitnirRaces] Falha ao ler bioma atual pra oscilação de vida: " + ex.Message));
					}
				}
			}
			if (health < 10f)
			{
				health = 10f;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "SetMaxStamina")]
	private static class RaceMaxStaminaPatch
	{
		private static void Prefix(Player __instance, ref float stamina)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				stamina += cfg.StaminaBonus.Value;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "SetMaxEitr")]
	private static class RaceMaxEitrPatch
	{
		private static void Prefix(Player __instance, ref float eitr)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				eitr += cfg.EitrBonus.Value;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
	private static class RaceMaxCarryWeightPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += cfg.CarryWeightBonus.Value;
			}
		}
	}

	[HarmonyPatch(typeof(Character), "Heal")]
	private static class RaceHealthRegenPatch
	{
		private static void Prefix(Character __instance, ref float hp)
		{
			if (!(hp <= 0f))
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg) && (!cfg.NightOnlyBonuses.Value || IsNightSafe()))
				{
					hp *= cfg.HealthRegenMultiplier.Value;
				}
			}
		}
	}

	[HarmonyPatch(typeof(Character), "AddStamina")]
	private static class RaceStaminaRegenPatch
	{
		private static void Prefix(Character __instance, ref float v)
		{
			if (!(v <= 0f))
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg) && (!cfg.NightOnlyBonuses.Value || IsNightSafe()))
				{
					v *= cfg.StaminaRegenMultiplier.Value;
				}
			}
		}
	}

	[HarmonyPatch(typeof(Character), "AddEitr")]
	private static class RaceEitrRegenPatch
	{
		private static void Prefix(Character __instance, ref float v)
		{
			if (!(v <= 0f))
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg) && (!cfg.NightOnlyBonuses.Value || IsNightSafe()))
				{
					v *= cfg.EitrRegenMultiplier.Value;
				}
			}
		}
	}

	[HarmonyPatch(typeof(Player), "UseStamina")]
	private static class RaceNoStaminaCostPatch
	{
		private static void Prefix(Player __instance, ref float v)
		{
			if (TryGetRaceConfig(__instance, out var cfg) && cfg.NoResourceCost.Value)
			{
				v = 0f;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "UseEitr")]
	private static class RaceNoEitrCostPatch
	{
		private static void Prefix(Player __instance, ref float v)
		{
			if (TryGetRaceConfig(__instance, out var cfg) && cfg.NoResourceCost.Value)
			{
				v = 0f;
			}
		}
	}

	[HarmonyPatch(typeof(Character), "TryUseEitr")]
	private static class RaceNoEitrCostTryPatch
	{
		private static bool Prefix(Character __instance, ref bool __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null || !TryGetRaceConfig(val, out var cfg) || !cfg.NoResourceCost.Value)
			{
				return true;
			}
			__result = true;
			return false;
		}
	}

	[HarmonyPatch(typeof(Character), "GetDamageModifier")]
	private static class RaceResistancePatch
	{
		private static void Postfix(Character __instance, DamageType damageType, ref DamageModifier __result)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected I4, but got Unknown
			if (__result)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg))
			{
				Dictionary<DamageType, DamageModifier> dictionary = ParseResistances(cfg.Resistances.Value);
				if (dictionary.TryGetValue(damageType, out var value))
				{
					__result = (DamageModifier)(int)value;
				}
			}
		}
	}

	[HarmonyPatch(typeof(Character), "Damage")]
	private static class RaceDamagePatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			if (hit == null)
			{
				return;
			}
			_ = hit.m_damage;
			if (false)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val != (Object)null && TryGetRaceConfig(val, out var cfg))
			{
				if (cfg.DamageTakenMultiplier.Value != 1f)
				{
					ScaleDamage(ref hit.m_damage, cfg.DamageTakenMultiplier.Value);
				}
				if (RacialAbilitiesEnabled.Value && cfg.IronStanceEnabled != null && cfg.IronStanceEnabled.Value && cfg.IronStanceExtraDamageReduction.Value > 0f && ((Character)val).IsBlocking())
				{
					ScaleDamage(ref hit.m_damage, 1f - cfg.IronStanceExtraDamageReduction.Value);
					hit.m_pushForce = 0f;
				}
			}
			Character attacker = hit.GetAttacker();
			Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
			if (!((Object)(object)val2 != (Object)null) || !TryGetRaceConfig(val2, out var cfg2))
			{
				return;
			}
			float num = cfg2.DamageDoneMultiplier.Value;
			Dictionary<SkillType, float> dictionary = ParseSkillMultipliers(cfg2.SkillDamageMultipliers.Value);
			if (dictionary.TryGetValue(hit.m_skill, out var value))
			{
				num *= value;
			}
			if (RacialAbilitiesEnabled.Value && cfg2.LowHealthThreshold != null && cfg2.LowHealthThreshold.Value > 0f && ((Character)val2).GetMaxHealth() > 0f && ((Character)val2).GetHealth() / ((Character)val2).GetMaxHealth() <= cfg2.LowHealthThreshold.Value)
			{
				num *= cfg2.LowHealthDamageBonus.Value;
			}
			if (num != 1f)
			{
				ScaleDamage(ref hit.m_damage, num);
			}
			Dictionary<DamageType, float> dictionary2 = ParseTypeMultipliers(cfg2.AttackDamageTypeMultipliers.Value);
			if (dictionary2.Count > 0)
			{
				ScaleDamageByType(ref hit.m_damage, dictionary2);
			}
			if (cfg2.BackstabMultiplier.Value != 1f)
			{
				hit.m_backstabBonus *= cfg2.BackstabMultiplier.Value;
			}
			if (cfg2.KnockbackMultiplier.Value != 1f)
			{
				hit.m_pushForce *= cfg2.KnockbackMultiplier.Value;
			}
			if (cfg2.XpGainMultiplier.Value != 1f)
			{
				hit.m_skillRaiseAmount *= cfg2.XpGainMultiplier.Value;
			}
			if (cfg2.LifestealPercent.Value > 0f)
			{
				float totalDamage = hit.GetTotalDamage();
				if (totalDamage > 0f)
				{
					((Character)val2).Heal(totalDamage * cfg2.LifestealPercent.Value, false);
				}
			}
		}

		private static void ScaleDamageByType(ref DamageTypes dmg, Dictionary<DamageType, float> mults)
		{
			if (mults.TryGetValue((DamageType)1, out var value))
			{
				dmg.m_blunt *= value;
			}
			if (mults.TryGetValue((DamageType)2, out var value2))
			{
				dmg.m_slash *= value2;
			}
			if (mults.TryGetValue((DamageType)4, out var value3))
			{
				dmg.m_pierce *= value3;
			}
			if (mults.TryGetValue((DamageType)8, out var value4))
			{
				dmg.m_chop *= value4;
			}
			if (mults.TryGetValue((DamageType)16, out var value5))
			{
				dmg.m_pickaxe *= value5;
			}
			if (mults.TryGetValue((DamageType)32, out var value6))
			{
				dmg.m_fire *= value6;
			}
			if (mults.TryGetValue((DamageType)64, out var value7))
			{
				dmg.m_frost *= value7;
			}
			if (mults.TryGetValue((DamageType)128, out var value8))
			{
				dmg.m_lightning *= value8;
			}
			if (mults.TryGetValue((DamageType)256, out var value9))
			{
				dmg.m_poison *= value9;
			}
			if (mults.TryGetValue((DamageType)512, out var value10))
			{
				dmg.m_spirit *= value10;
			}
		}

		private static void ScaleDamage(ref DamageTypes dmg, float mult)
		{
			dmg.m_blunt *= mult;
			dmg.m_slash *= mult;
			dmg.m_pierce *= mult;
			dmg.m_chop *= mult;
			dmg.m_pickaxe *= mult;
			dmg.m_fire *= mult;
			dmg.m_frost *= mult;
			dmg.m_lightning *= mult;
			dmg.m_poison *= mult;
			dmg.m_spirit *= mult;
		}
	}

	[HarmonyPatch(typeof(Character), "GetStealthFactor")]
	private static class RaceStealthPatch
	{
		private static void Postfix(Character __instance, ref float __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg))
			{
				__result *= cfg.StealthMultiplier.Value;
			}
		}
	}

	[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
	{
		typeof(int),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	private static class RaceBlockFreezingStatusPatch
	{
		private static bool Prefix(SEMan __instance, int nameHash, ref StatusEffect __result)
		{
			if (nameHash != SEMan.s_statusEffectFreezing && nameHash != SEMan.s_statusEffectCold)
			{
				return true;
			}
			Character sEManOwner = GetSEManOwner(__instance);
			Player val = (Player)(object)((sEManOwner is Player) ? sEManOwner : null);
			if ((Object)(object)val == (Object)null || !TryGetRaceConfig(val, out var cfg) || cfg.HeatResistanceBonus.Value == 0f)
			{
				return true;
			}
			__result = null;
			return false;
		}
	}

	[HarmonyPatch(typeof(Character), "GetJogSpeedFactor")]
	private static class RaceJogSpeedPatch
	{
		private static void Postfix(Character __instance, ref float __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg) && (!cfg.NightOnlyBonuses.Value || IsNightSafe()))
			{
				__result *= cfg.SpeedMultiplier.Value;
			}
		}
	}

	[HarmonyPatch(typeof(Character), "GetRunSpeedFactor")]
	private static class RaceRunSpeedPatch
	{
		private static void Postfix(Character __instance, ref float __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg) && (!cfg.NightOnlyBonuses.Value || IsNightSafe()))
			{
				__result *= cfg.SpeedMultiplier.Value;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentAttackStaminaModifier")]
	private static class RaceStaminaCostAttackPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Attack");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentBlockStaminaModifier")]
	private static class RaceStaminaCostBlockPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Block");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentDodgeStaminaModifier")]
	private static class RaceStaminaCostDodgePatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Dodge");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentJumpStaminaModifier")]
	private static class RaceStaminaCostJumpPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Jump");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentRunStaminaModifier")]
	private static class RaceStaminaCostRunPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Run");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentSneakStaminaModifier")]
	private static class RaceStaminaCostSneakPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Sneak");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "GetEquipmentSwimStaminaModifier")]
	private static class RaceStaminaCostSwimPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (TryGetRaceConfig(__instance, out var cfg))
			{
				__result += GetStaminaCostModifier(cfg, "Swim");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "UseEitr")]
	private static class RaceEitrCostPatch
	{
		private static void Prefix(Player __instance, ref float v)
		{
			if (TryGetRaceConfig(__instance, out var cfg) && !cfg.NoResourceCost.Value && cfg.EitrCostMultiplier.Value != 1f)
			{
				v *= cfg.EitrCostMultiplier.Value;
			}
		}
	}

	[HarmonyPatch(typeof(Character), "TryUseEitr")]
	private static class RaceEitrCostTryPatch
	{
		private static void Prefix(Character __instance, ref float eitrUse)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && TryGetRaceConfig(val, out var cfg) && !cfg.NoResourceCost.Value && cfg.EitrCostMultiplier.Value != 1f)
			{
				eitrUse *= cfg.EitrCostMultiplier.Value;
			}
		}
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static ConsoleEventFailable <>9__35_0;

		internal object <RegisterOfflineResetCommand>b__35_0(ConsoleEventArgs args)
		{
			if (!HasDevCommands())
			{
				args.Context.AddString(Text_SemPermissao.Value);
				return false;
			}
			if (args.Args.Length < 2)
			{
				args.Context.AddString(Text_UsoOfflineReset.Value);
				return false;
			}
			if (!long.TryParse(args.Args[1], out var result))
			{
				args.Context.AddString(Text_IdInvalido.Value);
				return false;
			}
			bool flag = ResetOfflinePlayer(result);
			args.Context.AddString(flag ? Text_OfflineResetado.Value : Text_OfflineNaoEncontrado.Value);
			return flag;
		}
	}

	private static readonly HashSet<long> PendingAltarSwitch = new HashSet<long>();

	private static readonly FieldInfo HelmetInstanceField = AccessTools.Field(typeof(VisEquipment), "m_helmetItemInstance");

	private static readonly HashSet<string> RegisteredSecretCodes = new HashSet<string>(StringComparer.Ordinal);

	public static readonly Dictionary<string, RaceConfig> Races = new Dictionary<string, RaceConfig>();

	public static ConfigEntry<float> EyeGlowPeak;

	public static ConfigEntry<bool> EmoteOnTransform;

	public static ConfigEntry<bool> FillVitalsOnTransform;

	public static ConfigEntry<bool> RacialAbilitiesEnabled;

	public static ConfigEntry<string> AltarSwitchCostItem;

	public static ConfigEntry<int> AltarSwitchCostAmount;

	public static ConfigEntry<string> AltarRecipe;

	private const string ZdoRagdollRaceKey = "GlitnirRagdollRace";

	private const string ModGUID = "glitnir.races";

	private const string ModName = "Glitnir Races";

	private const string ModVersion = "1.0.0";

	private const string RaceOrc = "orc";

	private const string RaceElf = "elfo";

	private const string RaceDwarf = "anao";

	private const string RaceHuman = "humano";

	private const string ZdoRaceKey = "GlitnirRace";

	private const string ZdoRaceVersionKey = "GlitnirRaceVer";

	private const int CurrentRaceDataVersion = 1;

	private const string ZdoLockedKey = "GlitnirRaceLocked";

	private const string ZdoEarColorKey = "GlitnirRaceEarColor";

	private const string ZdoSkinColorKey = "GlitnirRaceSkinColor";

	private const string RpcSetRace = "GlitnirRaces_SetRace";

	private const string RpcVersionCheck = "GlitnirRaces_VersionCheck";

	private const string RpcAdminSetRaceRequest = "GlitnirRaces_AdminSetRaceRequest";

	private const int FindHeadMaxDepth = 12;

	private const float RaceApplyMaxDistance = 60f;

	private static readonly WaitForSeconds Wait01 = new WaitForSeconds(0.1f);

	private static readonly WaitForSeconds Wait05 = new WaitForSeconds(0.5f);

	private static readonly WaitForSeconds Wait1 = new WaitForSeconds(1f);

	private static readonly WaitForSeconds Wait2 = new WaitForSeconds(2f);

	private static readonly WaitForSeconds WaitSkin005 = new WaitForSeconds(0.05f);

	private static readonly WaitForSeconds WaitSkin020 = new WaitForSeconds(0.2f);

	private static readonly WaitForSeconds WaitSkin050 = new WaitForSeconds(0.5f);

	private static readonly WaitForSeconds WaitSkin100 = new WaitForSeconds(1f);

	private readonly Harmony harmony = new Harmony("glitnir.races");

	public static GlitnirRacesPlugin Instance;

	public ConfigFile MainConfigFile;

	public ConfigFile TextConfigFile;

	private static GameObject ElfEarsMalePrefab;

	private static GameObject ElfEarsFemalePrefab;

	private static GameObject OrcEarsMalePrefab;

	private static GameObject OrcEarsFemalePrefab;

	private static GameObject OrcTuskPrefab;

	private static GameObject HighElfEyesPrefab;

	private static GameObject AltarPrefab;

	private static bool IsApplyingRace = false;

	private static bool RpcRegistered = false;

	private static ZRoutedRpc RegisteredRpcInstance = null;

	private static readonly Dictionary<long, string> NetworkRaces = new Dictionary<long, string>();

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

	private static readonly Dictionary<string, Vector3> OriginalSkinColors = new Dictionary<string, Vector3>();

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

	private static readonly HashSet<long> KnownAdmins = new HashSet<long>();

	private static readonly Dictionary<int, Transform> HeadCache = new Dictionary<int, Transform>();

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

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

	private static readonly Dictionary<int, ZNetView> ZNetViewCache = new Dictionary<int, ZNetView>();

	private float timer = 0f;

	private float syncTimer = 0f;

	private float localSaveTimer = 0f;

	private float adminCacheTimer = 0f;

	private float configWatchTimer = 0f;

	private DateTime _lastConfigWrite = DateTime.MinValue;

	private bool isRestoringLocalRace = false;

	private string lastBroadcastRace = null;

	private static string _cachedSavePrefix = null;

	private static long _cachedCharacterId = 0L;

	private static string _cachedServerKey = null;

	private static ZNet _cachedZNetInstance = null;

	private static readonly Vector3 HumanScale = Vector3.one;

	private static readonly Vector3 InvalidColor = new Vector3(-1f, -1f, -1f);

	private static readonly Vector3 ElfEarPosition = Vector3.zero;

	private static readonly Vector3 ElfEarRotation = Vector3.zero;

	private static readonly Vector3 ElfEarScale = Vector3.one;

	private static readonly Vector3 OrcEarPosition = new Vector3(0.00024f, 0.001475f, 0.001121f);

	private static readonly Vector3 OrcEarRotation = new Vector3(24.5f, 81.89f, -2.969f);

	private static readonly Vector3 OrcEarScale = new Vector3(1.137024f, 1.637663f, 1.083586f);

	private static readonly Vector3 HighElfEyesPosition = new Vector3(-0.0063f, -0.0194f, -0.00017f);

	private static readonly Vector3 HighElfEyesRotation = new Vector3(-18.776f, -89.449f, -0.691f);

	private static readonly Vector3 HighElfEyesScale = new Vector3(0.00993f, 0.011539f, 0.01272f);

	private static readonly Vector3 OrcTuskPosition = new Vector3(-0.00137f, 0.00114f, -0.00014f);

	private static readonly Vector3 OrcTuskRotation = new Vector3(-16.932f, -110.817f, 8.571f);

	private static readonly Vector3 OrcTuskScale = new Vector3(0.00494f, 0.005076f, 0.00559f);

	private bool _isReloadingConfig;

	private DateTime _lastTextConfigWrite = DateTime.MinValue;

	private static readonly Dictionary<string, Vector3> StillLastPos = new Dictionary<string, Vector3>();

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

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

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

	private static bool _envNightWarningLogged;

	private const float BonusRegenSeconds = 30f;

	private static readonly Dictionary<string, DamageType> DamageTypeNames = new Dictionary<string, DamageType>(StringComparer.OrdinalIgnoreCase)
	{
		{
			"Blunt",
			(DamageType)1
		},
		{
			"Slash",
			(DamageType)2
		},
		{
			"Pierce",
			(DamageType)4
		},
		{
			"Chop",
			(DamageType)8
		},
		{
			"Pickaxe",
			(DamageType)16
		},
		{
			"Fire",
			(DamageType)32
		},
		{
			"Frost",
			(DamageType)64
		},
		{
			"Lightning",
			(DamageType)128
		},
		{
			"Poison",
			(DamageType)256
		},
		{
			"Spirit",
			(DamageType)512
		}
	};

	private static readonly Dictionary<string, DamageModifier> ModifierNames = new Dictionary<string, DamageModifier>(StringComparer.OrdinalIgnoreCase)
	{
		{
			"Normal",
			(DamageModifier)0
		},
		{
			"Resistant",
			(DamageModifier)1
		},
		{
			"Weak",
			(DamageModifier)2
		},
		{
			"Immune",
			(DamageModifier)3
		},
		{
			"Ignore",
			(DamageModifier)4
		},
		{
			"VeryResistant",
			(DamageModifier)5
		},
		{
			"VeryWeak",
			(DamageModifier)6
		},
		{
			"SlightlyResistant",
			(DamageModifier)7
		},
		{
			"SlightlyWeak",
			(DamageModifier)8
		}
	};

	private static FieldInfo _semanCharacterField;

	private static readonly Dictionary<string, StatusEffect> RaceStatusEffects = new Dictionary<string, StatusEffect>();

	private static Sprite _placeholderIcon;

	private static bool _placeholderIconResolved;

	private const string RaceIconResourceName = "race_icon.rgba";

	private const int RaceIconSize = 128;

	private const string ColorAccent = "#E8A33D";

	private const string ColorAbilityBuff = "#8CFF8C";

	private const string ColorAbilityDebuff = "#FF8C8C";

	public static ConfigEntry<string> Text_AltarNome;

	public static ConfigEntry<string> Text_AltarDescricao;

	public static ConfigEntry<string> Text_AltarHoverEscolher;

	public static ConfigEntry<string> Text_AltarHoverTrocar;

	public static ConfigEntry<string> Text_AltarJaEscolheu;

	public static ConfigEntry<string> Text_UiEscolhaTitulo;

	public static ConfigEntry<string> Text_UiFechar;

	public static ConfigEntry<string> Text_UiTrocarBotao;

	public static ConfigEntry<string> Text_UiJaEscolheuTitulo;

	public static ConfigEntry<string> Text_UiMoedaInsuficiente;

	public static ConfigEntry<string> Text_RaceName_Orc;

	public static ConfigEntry<string> Text_RaceName_Elf;

	public static ConfigEntry<string> Text_RaceName_Dwarf;

	public static ConfigEntry<string> Text_RaceName_Human;

	public static ConfigEntry<string> Text_SemPermissao;

	public static ConfigEntry<string> Text_UsoRaca;

	public static ConfigEntry<string> Text_RacaDesativadaAdmin;

	public static ConfigEntry<string> Text_PlayerNaoEncontrado;

	public static ConfigEntry<string> Text_PlayerNaoCarregado;

	public static ConfigEntry<string> Text_AgoraERaca;

	public static ConfigEntry<string> Text_RacaRemovida;

	public static ConfigEntry<string> Text_UsoOfflineReset;

	public static ConfigEntry<string> Text_IdInvalido;

	public static ConfigEntry<string> Text_OfflineResetado;

	public static ConfigEntry<string> Text_OfflineNaoEncontrado;

	public static ConfigEntry<string> Text_EssaRacaDesativada;

	public static ConfigEntry<string> Text_AgoraEuSou;

	public static ConfigEntry<string> Text_ConfigRecarregado;

	public static ConfigEntry<string> Text_CodigosSecretosAtivos;

	public static ConfigEntry<string> Text_VersaoDiferente;

	public static ConfigEntry<string> Text_StatNomeRaca;

	public static ConfigEntry<string> Text_StatVidaMaxima;

	public static ConfigEntry<string> Text_StatStaminaMaxima;

	public static ConfigEntry<string> Text_StatEitrMaximo;

	public static ConfigEntry<string> Text_StatCapacidadeCarga;

	public static ConfigEntry<string> Text_StatRegenVida;

	public static ConfigEntry<string> Text_StatRegenStamina;

	public static ConfigEntry<string> Text_StatRegenEitr;

	public static ConfigEntry<string> Text_StatDanoCausado;

	public static ConfigEntry<string> Text_StatDanoComSkill;

	public static ConfigEntry<string> Text_StatDanoTipo;

	public static ConfigEntry<string> Text_StatDanoAtaqueFurtivo;

	public static ConfigEntry<string> Text_StatForcaEmpurrao;

	public static ConfigEntry<string> Text_StatDanoRecebido;

	public static ConfigEntry<string> Text_StatVelocidade;

	public static ConfigEntry<string> Text_StatFurtividade;

	public static ConfigEntry<string> Text_StatRouboVida;

	public static ConfigEntry<string> Text_StatGanhoXP;

	public static ConfigEntry<string> Text_StatRegenVelocidadeNoite;

	public static ConfigEntry<string> Text_StatSoANoite;

	public static ConfigEntry<string> Text_StatVidaNoiteDia;

	public static ConfigEntry<string> Text_StatVidaBioma;

	public static ConfigEntry<string> Text_StatFrioAmbiente;

	public static ConfigEntry<string> Text_StatNaoCongela;

	public static ConfigEntry<string> Text_StatModificadorDano;

	public static ConfigEntry<string> Text_StatSemBonus;

	public static ConfigEntry<string> Text_NivelImune;

	public static ConfigEntry<string> Text_NivelMuitoResistente;

	public static ConfigEntry<string> Text_NivelResistente;

	public static ConfigEntry<string> Text_NivelLevementeResistente;

	public static ConfigEntry<string> Text_NivelLevementeFraco;

	public static ConfigEntry<string> Text_NivelFraco;

	public static ConfigEntry<string> Text_NivelMuitoFraco;

	public static ConfigEntry<string> Text_NivelIgnora;

	public static ConfigEntry<string> Text_DanoBlunt;

	public static ConfigEntry<string> Text_DanoSlash;

	public static ConfigEntry<string> Text_DanoPierce;

	public static ConfigEntry<string> Text_DanoChop;

	public static ConfigEntry<string> Text_DanoPickaxe;

	public static ConfigEntry<string> Text_DanoFire;

	public static ConfigEntry<string> Text_DanoFrost;

	public static ConfigEntry<string> Text_DanoLightning;

	public static ConfigEntry<string> Text_DanoPoison;

	public static ConfigEntry<string> Text_DanoSpirit;

	public static ConfigEntry<string> Text_BiomaMeadows;

	public static ConfigEntry<string> Text_BiomaBlackForest;

	public static ConfigEntry<string> Text_BiomaSwamp;

	public static ConfigEntry<string> Text_BiomaMountain;

	public static ConfigEntry<string> Text_BiomaPlains;

	public static ConfigEntry<string> Text_BiomaMistlands;

	public static ConfigEntry<string> Text_BiomaAshLands;

	public static ConfigEntry<string> Text_BiomaDeepNorth;

	public static ConfigEntry<string> Text_BiomaOcean;

	public static ConfigEntry<string> Text_HabFuriaSangue;

	public static ConfigEntry<string> Text_HabFuriaSangueDesc;

	public static ConfigEntry<string> Text_HabAuraCura;

	public static ConfigEntry<string> Text_HabAuraCuraDesc;

	public static ConfigEntry<string> Text_HabAuraCuraSoDeDia;

	public static ConfigEntry<string> Text_HabPosturaFerro;

	public static ConfigEntry<string> Text_HabPosturaFerroDesc;

	public static ConfigEntry<string> Text_HabSegundoFolego;

	public static ConfigEntry<string> Text_HabSegundoFolegoDesc;

	private static bool _modelIndexFieldWarningLogged;

	private static readonly FieldInfo ModelIndexField = AccessTools.Field(typeof(VisEquipment), "m_modelIndex");

	private void RegisterAltarPiece()
	{
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Expected O, but got Unknown
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Expected O, but got Unknown
		if ((Object)(object)AltarPrefab == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[GlitnirRaces] Prefab 'altar_racas' não encontrado — peça do Altar das Raças não registrada.");
			return;
		}
		if ((Object)(object)AltarPrefab.GetComponent<Piece>() == (Object)null)
		{
			AltarPrefab.AddComponent<Piece>();
		}
		if ((Object)(object)AltarPrefab.GetComponent<ZNetView>() == (Object)null)
		{
			AltarPrefab.AddComponent<ZNetView>();
		}
		if ((Object)(object)AltarPrefab.GetComponent<WearNTear>() == (Object)null)
		{
			AltarPrefab.AddComponent<WearNTear>();
		}
		if ((Object)(object)AltarPrefab.GetComponent<RaceAltar>() == (Object)null)
		{
			AltarPrefab.AddComponent<RaceAltar>();
		}
		if ((Object)(object)AltarPrefab.GetComponentInChildren<Collider>() == (Object)null)
		{
			Renderer componentInChildren = AltarPrefab.GetComponentInChildren<Renderer>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				BoxCollider val = AltarPrefab.AddComponent<BoxCollider>();
				Bounds localBounds = componentInChildren.localBounds;
				val.center = ((Bounds)(ref localBounds)).center;
				val.size = ((Bounds)(ref localBounds)).size;
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[GlitnirRaces] 'altar_racas' não tinha Collider — criei um BoxCollider automático baseado na malha. Se ficar com hitbox estranha, adiciona um Collider certinho no Unity e ele passa a usar esse em vez do automático.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[GlitnirRaces] 'altar_racas' não tem Collider nem Renderer — o altar provavelmente não vai responder a interação.");
			}
		}
		PieceConfig val2 = new PieceConfig
		{
			Name = Text_AltarNome.Value,
			Description = Text_AltarDescricao.Value,
			PieceTable = "Hammer",
			CraftingStation = "piece_workbench",
			Category = "Misc",
			Icon = (LoadIconFromEmbedded("altar_icon.rgba") ?? GetPlaceholderIcon()),
			Requirements = ParseAltarRecipe(AltarRecipe?.Value ?? "Wood:10,Stone:5")
		};
		CustomPiece val3 = new CustomPiece(AltarPrefab, true, val2);
		PieceManager.Instance.AddPiece(val3);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[GlitnirRaces] Peça 'Altar das Raças' registrada.");
	}

	private static RequirementConfig[] ParseAltarRecipe(string recipe)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Expected O, but got Unknown
		//IL_00ca: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Expected O, but got Unknown
		List<RequirementConfig> list = new List<RequirementConfig>();
		if (string.IsNullOrWhiteSpace(recipe))
		{
			return (RequirementConfig[])(object)new RequirementConfig[1]
			{
				new RequirementConfig
				{
					Item = "Wood",
					Amount = 10,
					Recover = true
				}
			};
		}
		string[] array = recipe.Split(new char[1] { ',' });
		foreach (string text in array)
		{
			string[] array2 = text.Split(new char[1] { ':' });
			if (array2.Length == 2)
			{
				string text2 = array2[0].Trim();
				if (!string.IsNullOrWhiteSpace(text2) && int.TryParse(array2[1].Trim(), out var result) && result > 0)
				{
					list.Add(new RequirementConfig
					{
						Item = text2,
						Amount = result,
						Recover = true
					});
				}
			}
		}
		if (list.Count == 0)
		{
			list.Add(new RequirementConfig
			{
				Item = "Wood",
				Amount = 10,
				Recover = true
			});
		}
		return list.ToArray();
	}

	public static bool HasChosenRaceViaAltar(Player p)
	{
		return IsPlayerLocked(p);
	}

	public static bool HasPendingAltarSwitch(Player p)
	{
		if ((Object)(object)p == (Object)null)
		{
			return false;
		}
		long playerIdLong = GetPlayerIdLong(p);
		return playerIdLong != 0L && PendingAltarSwitch.Contains(playerIdLong);
	}

	public static void ChooseRaceFromAltar(string race)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || (Object)(object)Instance == (Object)null)
		{
			return;
		}
		long playerIdLong = GetPlayerIdLong(localPlayer);
		bool flag = playerIdLong != 0L && PendingAltarSwitch.Contains(playerIdLong);
		if (!HasChosenRaceViaAltar(localPlayer) || flag)
		{
			if (flag)
			{
				PendingAltarSwitch.Remove(playerIdLong);
			}
			TransformLocalPlayerFromMarketplace(race);
		}
	}

	public static void SwitchRaceFromAltar(Player p)
	{
		if (!((Object)(object)Instance == (Object)null) && !((Object)(object)p == (Object)null))
		{
			long playerIdLong = GetPlayerIdLong(p);
			if (playerIdLong != 0)
			{
				PendingAltarSwitch.Add(playerIdLong);
			}
		}
	}

	private void ApplyRaceWithDelays(Player p)
	{
		if (!((Object)(object)p == (Object)null))
		{
			ApplyRace(p);
			if (!IsHeadless())
			{
				int instanceID = ((Object)p).GetInstanceID();
				int value;
				int num = ((!ApplyRaceCoroutineGeneration.TryGetValue(instanceID, out value)) ? 1 : (value + 1));
				ApplyRaceCoroutineGeneration[instanceID] = num;
				((MonoBehaviour)this).StartCoroutine(ApplyRaceDelayed(p, Wait01, num));
				((MonoBehaviour)this).StartCoroutine(ApplyRaceDelayed(p, Wait05, num));
				((MonoBehaviour)this).StartCoroutine(ApplyRaceDelayed(p, Wait1, num));
				((MonoBehaviour)this).StartCoroutine(ApplyRaceDelayed(p, Wait2, num));
			}
		}
	}

	private IEnumerator ApplyRaceDelayed(Player p, WaitForSeconds wait, int generation)
	{
		yield return wait;
		if (!((Object)(object)p == (Object)null))
		{
			int instanceId = ((Object)p).GetInstanceID();
			if (ApplyRaceCoroutineGeneration.TryGetValue(instanceId, out var current) && current == generation)
			{
				ApplyRace(p);
			}
		}
	}

	private void QueueSkinRefresh(Player p, string race)
	{
		if (!IsHeadless() && !((Object)(object)p == (Object)null) && !string.IsNullOrWhiteSpace(race))
		{
			string text = GetVisualKey(p);
			if (string.IsNullOrWhiteSpace(text))
			{
				text = ((Object)p).GetInstanceID().ToString();
			}
			float time = Time.time;
			if (!LastSkinRefreshRequest.TryGetValue(text, out var value) || !(time - value < 1.25f))
			{
				LastSkinRefreshRequest[text] = time;
				((MonoBehaviour)this).StartCoroutine(ForceSkinRefresh(p, race));
			}
		}
	}

	private IEnumerator ForceSkinRefresh(Player p, string race)
	{
		yield return null;
		ForceSkinNow(p, race);
		yield return WaitSkin005;
		ForceSkinNow(p, race);
		yield return WaitSkin020;
		ForceSkinNow(p, race);
		yield return WaitSkin050;
		ForceSkinNow(p, race);
		yield return WaitSkin100;
		ForceSkinNow(p, race);
	}

	private void ForceSkinNow(Player p, string race)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)p == (Object)null || string.IsNullOrWhiteSpace(race) || GetRace(p) != race)
		{
			return;
		}
		bool isApplyingRace = IsApplyingRace;
		IsApplyingRace = true;
		try
		{
			if (RaceUsesForcedSkin(race))
			{
				ApplySkin(p, GetNetworkSkinColor(p, race));
			}
			Color color = ToColor(GetRaceEarColor(p, race));
			if (race == "orc")
			{
				ApplyOrcEars(p, color);
			}
			else if (race == "elfo")
			{
				ApplyEars(p, color);
			}
			Transform cachedHead = GetCachedHead(p, race);
			Transform val = ((cachedHead != null) ? cachedHead.Find("HighElfEyes") : null);
			if ((Object)(object)val != (Object)null)
			{
				ApplyEyeColor(val, GetRaceEyeColor(race));
			}
		}
		finally
		{
			IsApplyingRace = isApplyingRace;
		}
	}

	public void RequestOnlineVisualRefresh(Player p)
	{
		if (IsHeadless() || (Object)(object)p == (Object)null)
		{
			return;
		}
		string race = GetRace(p);
		if (!string.IsNullOrWhiteSpace(race))
		{
			ApplyRaceWithDelays(p);
			if (RaceUsesForcedSkin(race) || race == "elfo")
			{
				QueueSkinRefresh(p, race);
			}
		}
	}

	public void RequestOnlineVisualRefresh(VisEquipment vis)
	{
		if (!IsHeadless() && !((Object)(object)vis == (Object)null))
		{
			Player val = ((Component)vis).GetComponent<Player>() ?? ((Component)vis).GetComponentInParent<Player>();
			if ((Object)(object)val != (Object)null)
			{
				RequestOnlineVisualRefresh(val);
			}
		}
	}

	public void ForceSkinAfterVanilla(Player p, string race)
	{
		if (!IsHeadless() && !((Object)(object)p == (Object)null) && !string.IsNullOrWhiteSpace(race))
		{
			QueueSkinRefresh(p, race);
		}
	}

	public void ForceSkinAfterVanilla(VisEquipment vis)
	{
		if (IsHeadless() || (Object)(object)vis == (Object)null)
		{
			return;
		}
		Player val = ((Component)vis).GetComponent<Player>() ?? ((Component)vis).GetComponentInParent<Player>();
		if (!((Object)(object)val == (Object)null))
		{
			string race = GetRace(val);
			if (RaceUsesForcedSkin(race) || race == "elfo")
			{
				QueueSkinRefresh(val, race);
			}
		}
	}

	private Transform GetCachedHead(Player p, string race)
	{
		if ((Object)(object)p == (Object)null)
		{
			return null;
		}
		int instanceID = ((Object)p).GetInstanceID();
		if (HeadCache.TryGetValue(instanceID, out var value) && HeadCacheRace.TryGetValue(instanceID, out var value2) && value2 == race && (Object)(object)value != (Object)null)
		{
			return value;
		}
		Transform val = FindHead(((Component)p).transform, 0);
		HeadCache[instanceID] = val;
		HeadCacheRace[instanceID] = race;
		return val;
	}

	private static void InvalidateHeadCache(Player p)
	{
		if (!((Object)(object)p == (Object)null))
		{
			int instanceID = ((Object)p).GetInstanceID();
			HeadCache.Remove(instanceID);
			HeadCacheRace.Remove(instanceID);
		}
	}

	private static ZNetView GetCachedZNetView(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return null;
		}
		int instanceID = ((Object)player).GetInstanceID();
		if (ZNetViewCache.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		ZNetView component = ((Component)player).GetComponent<ZNetView>();
		if ((Object)(object)component != (Object)null)
		{
			ZNetViewCache[instanceID] = component;
		}
		return component;
	}

	public void ApplyRace(Player p)
	{
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0252: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02df: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)p == (Object)null || IsApplyingRace || GetPlayerIdLong(p) == 0)
		{
			return;
		}
		bool flag = IsHeadless();
		string race = GetRace(p);
		string visualKey = GetVisualKey(p);
		string value = "";
		if (!string.IsNullOrWhiteSpace(visualKey))
		{
			LastAppliedRace.TryGetValue(visualKey, out value);
		}
		bool flag2 = value != race;
		IsApplyingRace = true;
		try
		{
			if (flag2)
			{
				InvalidateHeadCache(p);
				if (!flag)
				{
					ResetScaledHelmet(p);
				}
				ApplyScale(p, HumanScale);
				if (!flag)
				{
					if (RaceUsesForcedSkin(value))
					{
						ResetSkin(p);
					}
					Transform cachedHead = GetCachedHead(p, race);
					RemoveElfEarsAnywhere(cachedHead);
					RemoveOrcEarsAnywhere(cachedHead);
					RemoveHeadAttachmentDirect(cachedHead, "HighElfEyes");
					RemoveHeadAttachmentDirect(cachedHead, "OrcTusk");
				}
				if (!string.IsNullOrWhiteSpace(visualKey))
				{
					LastAppliedRace[visualKey] = race;
				}
				UpdateRaceStatusEffect(p, race);
			}
			if (string.IsNullOrWhiteSpace(race) || !IsRaceEnabled(race))
			{
				ApplyScale(p, HumanScale);
				if (!flag && flag2)
				{
					string visualKey2 = GetVisualKey(p);
					if (!string.IsNullOrWhiteSpace(visualKey2) && OriginalSkinColors.TryGetValue(visualKey2, out var value2))
					{
						ApplySkin(p, value2);
					}
					else
					{
						ApplySkin(p, Vector3.one);
					}
					ResetHelmet(p);
					Transform head = FindHead(((Component)p).transform, 0);
					RemoveElfEarsAnywhere(head);
					RemoveOrcEarsAnywhere(head);
					RemoveHeadAttachmentDirect(head, "HighElfEyes");
					RemoveHeadAttachmentDirect(head, "OrcTusk");
				}
				return;
			}
			ApplyScale(p, GetRaceScale(race));
			ScaleCurrentHelmetForRace(p, GetRaceScale(race));
			if (!flag)
			{
				if (RaceUsesForcedSkin(race))
				{
					CacheOriginalSkinIfNeeded(p);
					ApplySkin(p, GetNetworkSkinColor(p, race));
					QueueSkinRefresh(p, race);
				}
				if (RaceUsesForcedHair(race))
				{
					ApplyHair(p, GetRaceHairColor(race));
				}
				Transform cachedHead2 = GetCachedHead(p, race);
				switch (race)
				{
				case "orc":
					ApplyOrcEarsDirect(cachedHead2, ToColor(GetRaceEarColor(p, race)), IsPlayerFemale(p));
					ApplyHighElfEyesDirect(p, cachedHead2);
					ApplyOrcTuskDirect(cachedHead2);
					break;
				case "elfo":
					ApplyEarsDirect(cachedHead2, ToColor(GetRaceEarColor(p, race)), IsPlayerFemale(p));
					QueueSkinRefresh(p, race);
					ApplyHighElfEyesDirect(p, cachedHead2);
					break;
				case "anao":
					ApplyHighElfEyesDirect(p, cachedHead2);
					break;
				case "humano":
					ApplyHighElfEyesDirect(p, cachedHead2);
					break;
				}
			}
		}
		finally
		{
			IsApplyingRace = false;
		}
	}

	public void SpawnEffect(Player p)
	{
		//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_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		if (!IsHeadless() && !((Object)(object)p == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null))
		{
			Vector3 val = ((Component)p).transform.position + Vector3.up;
			Quaternion rotation = ((Component)p).transform.rotation;
			GameObject prefab = ZNetScene.instance.GetPrefab("vfx_spawn_large");
			GameObject prefab2 = ZNetScene.instance.GetPrefab("fx_Fader_Roar_Projectile_Hit");
			if ((Object)(object)prefab != (Object)null)
			{
				Object.Instantiate<GameObject>(prefab, val, rotation);
			}
			if ((Object)(object)prefab2 != (Object)null)
			{
				Object.Instantiate<GameObject>(prefab2, val, rotation);
			}
		}
	}

	private static void ScaleCurrentHelmetForRace(Player p, Vector3 targetScale)
	{
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: 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_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)p == (Object)null || IsHeadless())
		{
			return;
		}
		try
		{
			VisEquipment component = ((Component)p).GetComponent<VisEquipment>();
			object? obj = HelmetInstanceField?.GetValue(component);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				HelmetScaleMarker helmetScaleMarker = val.GetComponent<HelmetScaleMarker>();
				if ((Object)(object)helmetScaleMarker == (Object)null)
				{
					helmetScaleMarker = val.AddComponent<HelmetScaleMarker>();
					helmetScaleMarker.OriginalScale = val.transform.localScale;
					helmetScaleMarker.OriginalPosition = val.transform.localPosition;
					helmetScaleMarker.AppliedScale = Vector3.one;
				}
				if (!(helmetScaleMarker.AppliedScale == targetScale))
				{
					val.transform.localScale = Vector3.Scale(helmetScaleMarker.OriginalScale, targetScale);
					val.transform.localPosition = helmetScaleMarker.OriginalPosition;
					helmetScaleMarker.AppliedScale = targetScale;
				}
			}
		}
		catch (Exception ex)
		{
			GlitnirRacesPlugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)("[GlitnirRaces] Falha ao escalar capacete: " + ex.Message));
			}
		}
	}

	private void ResetHelmet(Player p)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		ScaleCurrentHelmetForRace(p, Vector3.one);
	}

	private void ResetScaledHelmet(Player p)
	{
		ResetHelmet(p);
	}

	private static void ApplyScale(Player p, Vector3 scale)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)p == (Object)null))
		{
			((Component)p).transform.localScale = scale;
		}
	}

	private static void RemovePlayerAttachment(Player p, string objectName)
	{
		if (!((Object)(object)p == (Object)null))
		{
			Transform val = ((Component)p).transform.Find(objectName);
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
		}
	}

	private void RegisterCommands()
	{
		RegisterRaceCommand("orc");
		RegisterRaceCommand("elfo");
		RegisterRaceCommand("anao");
		RegisterRaceCommand("humano");
		RegisterResetRaceCommand();
		RegisterOfflineResetCommand();
		ReRegisterSecretCodeCommands();
		RegisterReloadCommand();
	}

	private void RegisterReloadCommand()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		new ConsoleCommand("reloadracas", "Recarrega os codigos secretos do config", (ConsoleEventFailable)delegate(ConsoleEventArgs args)
		{
			if (!HasDevCommands())
			{
				args.Context.AddString(Text_SemPermissao.Value);
				return false;
			}
			Instance.MainConfigFile.Reload();
			Instance.TextConfigFile.Reload();
			NetworkRaces.Clear();
			ZNetViewCache.Clear();
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null))
				{
					string visualKey = GetVisualKey(allPlayer);
					if (!string.IsNullOrWhiteSpace(visualKey))
					{
						LastAppliedRace[visualKey] = "";
					}
					InvalidateHeadCache(allPlayer);
					Instance.ApplyRaceWithDelays(allPlayer);
				}
			}
			ReRegisterSecretCodeCommands();
			args.Context.AddString(Text_ConfigRecarregado.Value);
			args.Context.AddString(Text_CodigosSecretosAtivos.Value);
			foreach (KeyValuePair<string, RaceConfig> race in Races)
			{
				string value = race.Value.SecretCode.Value;
				if (!string.IsNullOrWhiteSpace(value))
				{
					args.Context.AddString("  " + race.Value.DisplayName + " → " + value);
				}
			}
			return true;
		}, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private void ReRegisterSecretCodeCommands()
	{
		foreach (KeyValuePair<string, RaceConfig> race in Races)
		{
			string value = race.Value.SecretCode.Value;
			string key = race.Key;
			if (!string.IsNullOrWhiteSpace(value) && !RegisteredSecretCodes.Contains(value))
			{
				RegisterSecretCodeCommand(value, key);
				RegisteredSecretCodes.Add(value);
			}
		}
	}

	private void RegisterSecretCodeCommand(string code, string race)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		new ConsoleCommand(code, "", (ConsoleEventFailable)delegate
		{
			TransformLocalPlayerFromMarketplace(race);
			return true;
		}, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private void RegisterRaceCommand(string race)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		new ConsoleCommand(race, race + " me  ou  " + race + " NomeDoPlayer", (ConsoleEventFailable)delegate(ConsoleEventArgs args)
		{
			if (!HasDevCommands())
			{
				args.Context.AddString(Text_SemPermissao.Value);
				return false;
			}
			if (!IsRaceEnabled(race))
			{
				args.Context.AddString(Fmt(Text_RacaDesativadaAdmin.Value, GetRaceName(race)));
				return false;
			}
			if (args.Args.Length < 2)
			{
				args.Context.AddString(Fmt(Text_UsoRaca.Value, race));
				return false;
			}
			Player targetPlayer = GetTargetPlayer(args);
			if ((Object)(object)targetPlayer == (Object)null)
			{
				args.Context.AddString(Text_PlayerNaoEncontrado.Value);
				return false;
			}
			AdminSetRace(targetPlayer, race);
			args.Context.AddString(Fmt(Text_AgoraERaca.Value, targetPlayer.GetPlayerName(), GetRaceName(race)));
			return true;
		}, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private void RegisterResetRaceCommand()
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Expected O, but got Unknown
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		ConsoleEventFailable val = (ConsoleEventFailable)delegate(ConsoleEventArgs args)
		{
			if (!HasDevCommands())
			{
				args.Context.AddString(Text_SemPermissao.Value);
				return false;
			}
			if (args.Args.Length < 2)
			{
				args.Context.AddString(Fmt(Text_UsoRaca.Value, args.Args[0]));
				return false;
			}
			Player targetPlayer = GetTargetPlayer(args);
			if ((Object)(object)targetPlayer == (Object)null)
			{
				args.Context.AddString(Text_PlayerNaoEncontrado.Value);
				return false;
			}
			long playerIdLong = GetPlayerIdLong(targetPlayer);
			if (playerIdLong == 0)
			{
				args.Context.AddString(Fmt(Text_PlayerNaoCarregado.Value, targetPlayer.GetPlayerName()));
				return false;
			}
			ResetRace(targetPlayer, broadcast: true);
			args.Context.AddString(Fmt(Text_RacaRemovida.Value, targetPlayer.GetPlayerName()));
			return true;
		};
		new ConsoleCommand("racareset", "racareset me  ou  racareset NomeDoPlayer", val, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private Player GetTargetPlayer(ConsoleEventArgs args)
	{
		string typedName = string.Join(" ", args.Args.Skip(1)).Trim();
		if (typedName.Equals("me", StringComparison.OrdinalIgnoreCase))
		{
			return Player.m_localPlayer;
		}
		return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => (Object)(object)p != (Object)null && p.GetPlayerName().Equals(typedName, StringComparison.OrdinalIgnoreCase)));
	}

	private void RegisterOfflineResetCommand()
	{
		//IL_0033: 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_002a: Expected O, but got Unknown
		object obj = <>c.<>9__35_0;
		if (obj == null)
		{
			ConsoleEventFailable val = delegate(ConsoleEventArgs args)
			{
				if (!HasDevCommands())
				{
					args.Context.AddString(Text_SemPermissao.Value);
					return false;
				}
				if (args.Args.Length < 2)
				{
					args.Context.AddString(Text_UsoOfflineReset.Value);
					return false;
				}
				if (!long.TryParse(args.Args[1], out var result))
				{
					args.Context.AddString(Text_IdInvalido.Value);
					return false;
				}
				bool flag = ResetOfflinePlayer(result);
				args.Context.AddString(flag ? Text_OfflineResetado.Value : Text_OfflineNaoEncontrado.Value);
				return flag;
			};
			<>c.<>9__35_0 = val;
			obj = (object)val;
		}
		new ConsoleCommand("glitnir_reset_offline", "glitnir_reset_offline <playerID/steamID>", (ConsoleEventFailable)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static bool ResetOfflinePlayer(long playerID)
	{
		if (ZDOMan.instance == null)
		{
			return false;
		}
		List<ZDO> list = new List<ZDO>();
		int num = 0;
		while (ZDOMan.instance.GetAllZDOsWithPrefabIterative("Player", list, ref num))
		{
		}
		foreach (ZDO item in list)
		{
			if (item != null)
			{
				long num2 = item.GetLong(ZDOVars.s_playerID, 0L);
				if (num2 == playerID)
				{
					item.Set("GlitnirRace", "");
					item.Set("GlitnirRaceLocked", false);
					item.Set("GlitnirRaceEarColor_x", -1f);
					item.Set("GlitnirRaceEarColor_y", -1f);
					item.Set("GlitnirRaceEarColor_z", -1f);
					item.Set("GlitnirRaceSkinColor_x", -1f);
					item.Set("GlitnirRaceSkinColor_y", -1f);
					item.Set("GlitnirRaceSkinColor_z", -1f);
					NetworkRaces.Remove(playerID);
					return true;
				}
			}
		}
		return false;
	}

	private static void TransformLocalPlayerFromMarketplace(string race)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || (Object)(object)Instance == (Object)null)
		{
			return;
		}
		if (!IsRaceEnabled(race))
		{
			((Character)localPlayer).Message((MessageType)2, Text_EssaRacaDesativada.Value, 0, (Sprite)null);
			return;
		}
		if (RaceUsesForcedSkin(race))
		{
			Instance.CacheOriginalSkinIfNeeded(localPlayer);
		}
		Instance.SetRace(localPlayer, race, lockPlayer: true, broadcast: true);
		Instance.ApplyRaceWithDelays(localPlayer);
		TopOffVitals(localPlayer);
		if (!IsHeadless())
		{
			Instance.SpawnEffect(localPlayer);
		}
		PlayTransformEmote();
		((Character)localPlayer).Message((MessageType)2, Fmt(Text_AgoraEuSou.Value, Instance.GetRaceName(race)), 0, (Sprite)null);
	}

	private static void PlayTransformEmote()
	{
		if (!EmoteOnTransform.Value)
		{
			return;
		}
		try
		{
			Emote.DoEmote((Emotes)11);
		}
		catch (Exception ex)
		{
			GlitnirRacesPlugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)("Falha ao tocar emote de transformação: " + ex.Message));
			}
		}
	}

	private void CreateRaceConfigs()
	{
		Races.Clear();
		CreateRaceConfig("orc", Text_RaceName_Orc.Value, enabled: true, "1,1,1", "#3FA86B", "#3FA86B", "#FFFF00", "X9K7Q2M4Z8P1", RaceAbility.Orc, "-1", 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 0.9f, "Blunt:Resistant,Frost:Weak", 0f, 0.3f, 1.3f);
		CreateRaceConfig("elfo", Text_RaceName_Elf.Value, enabled: true, "1,1,1", "-1", "-1", "#00FF00", "R3T8Y1U6I4O9", RaceAbility.Elf, "-1", 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 1.15f, "Spirit:Resistant,Poison:Weak", 0f, 0f, 1f, stillHealAura: true, 5f, 2f, 2f, stillHealDayOnly: true);
		CreateRaceConfig("anao", Text_RaceName_Dwarf.Value, enabled: true, "0.8,0.8,0.8", "-1", "-1", "#9B30FF", "Z4P9X2C7B8N1", RaceAbility.Dwarf, "-1", 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 0.9f, "Blunt:VeryResistant,Slash:Resistant,Pierce:Resistant,Fire:Weak", 0f, 0f, 1f, stillHealAura: false, 5f, 2f, 2f, stillHealDayOnly: false, ironStanceEnabled: true, 0.3f);
		CreateRaceConfig("humano", Text_RaceName_Human.Value, enabled: true, "1,1,1", "-1", "-1", "#00F0FF", "T2Y7U4I8O1P6", RaceAbility.Human, "-1", 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 1f, "", 0f, 0f, 1f, stillHealAura: false, 5f, 2f, 2f, stillHealDayOnly: false, ironStanceEnabled: false, 0f, secondWindEnabled: true, 0.2f, 30f, 30f);
	}

	private void CreateRaceConfig(string raceId, string displayName, bool enabled, string scale, string skinColor, string earColor, string eyeColor, string secretCode, RaceAbility ability = RaceAbility.None, string hairColor = "-1", float healthBonus = 0f, float staminaBonus = 0f, float eitrBonus = 0f, float carryWeightBonus = 0f, float healthRegenMult = 1f, float staminaRegenMult = 1f, float eitrRegenMult = 1f, float damageDoneMult = 1f, float backstabMult = 1f, float damageTakenMult = 1f, float lifestealPercent = 0f, float stealthMult = 1f, float speedMult = 1f, string resistances = "", float heatResistanceBonus = 0f, float lowHealthThreshold = 0f, float lowHealthDamageBonus = 1f, bool stillHealAura = false, float stillHealAmount = 5f, float stillHealInterval = 2f, float stillHealRequiredStillTime = 2f, bool stillHealDayOnly = false, bool ironStanceEnabled = false, float ironStanceExtraDamageReduction = 0f, bool secondWindEnabled = false, float secondWindThreshold = 0.2f, float secondWindHealAmount = 0f, float secondWindStaminaAmount = 0f, float secondWindCooldown = 300f, string skillDamageMultipliers = "", string attackDamageTypeMultipliers = "", bool noResourceCost = false, float xpGainMult = 1f, float knockbackMult = 1f, float eitrCostMult = 1f, string staminaCostModifiers = "", bool nightOnlyBonuses = false, float dayNightHealthShift = 0f, string biomeHealthShifts = "")
	{
		string text = "Aparencia - " + displayName;
		string text2 = "Habilidades - " + displayName;
		RaceConfig raceConfig = new RaceConfig
		{
			RaceId = raceId,
			DisplayName = displayName,
			Enabled = SyncedConfig(text, "Usar", enabled, "Ativa ou desativa a raça " + displayName + "."),
			Scale = SyncedConfig(text, "Tamanho", scale, "Tamanho da raça no formato X,Y,Z."),
			SkinColor = SyncedConfig(text, "Cor", skinColor, "Cor da pele em HEX. Use -1 para não alterar."),
			HairColor = SyncedConfig(text, "CorCabeloBarba", hairColor, "Cor do cabelo e barba em HEX (os dois juntos, o jogo usa a mesma cor pros dois). Use -1 para não alterar."),
			EarColor = SyncedConfig(text, "CorOrelha", earColor, "Cor da orelha em HEX. Use -1 para usar a cor da pele."),
			EyeColor = SyncedConfig(text, "CorOlho", eyeColor, "Cor dos olhos em HEX."),
			SecretCode = SyncedConfig(text, "CodigoSecreto", secretCode, "Código secreto digitado no chat para escolher a raça."),
			HealthBonus = SyncedConfig(text2, "BonusVida", healthBonus, "Vida máxima extra, somada em cima do vanilla (nunca abaixo)."),
			StaminaBonus = SyncedConfig(text2, "BonusStamina", staminaBonus, "Stamina máxima extra, somada em cima do vanilla."),
			EitrBonus = SyncedConfig(text2, "BonusEitr", eitrBonus, "Eitr máximo extra, somado em cima do vanilla."),
			CarryWeightBonus = SyncedConfig(text2, "BonusPeso", carryWeightBonus, "Peso máximo extra que pode carregar, somado em cima do vanilla."),
			HealthRegenMultiplier = SyncedConfig(text2, "MultiplicadorRegenVida", healthRegenMult, "Velocidade de regeneração de vida (e cura em geral). 1.0 = vanilla, 1.4 = 40% mais rápido."),
			StaminaRegenMultiplier = SyncedConfig(text2, "MultiplicadorRegenStamina", staminaRegenMult, "Velocidade de regeneração de stamina. 1.0 = vanilla."),
			EitrRegenMultiplier = SyncedConfig(text2, "MultiplicadorRegenEitr", eitrRegenMult, "Velocidade de regeneração de eitr. 1.0 = vanilla."),
			DamageDoneMultiplier = SyncedConfig(text2, "MultiplicadorDanoCausado", damageDoneMult, "Multiplicador de dano causado em geral. 1.0 = vanilla, 1.15 = +15%."),
			BackstabMultiplier = SyncedConfig(text2, "MultiplicadorAtaqueFurtivo", backstabMult, "Multiplicador extra no bônus de dano de ataque furtivo (backstab). 1.0 = vanilla."),
			DamageTakenMultiplier = SyncedConfig(text2, "MultiplicadorDanoRecebido", damageTakenMult, "Multiplicador de dano recebido em geral. 1.0 = vanilla, 0.85 = -15%."),
			LifestealPercent = SyncedConfig(text2, "RouboDeVida", lifestealPercent, "Fração do dano causado que vira cura pro atacante. 0 = desativado, 0.12 = 12%."),
			StealthMultiplier = SyncedConfig(text2, "MultiplicadorFurtividade", stealthMult, "Multiplicador do fator de furtividade. Menor que 1.0 = mais furtivo."),
			SpeedMultiplier = SyncedConfig(text2, "MultiplicadorVelocidade", speedMult, "Multiplicador de velocidade de movimento (andar/correr). 1.0 = vanilla."),
			Resistances = SyncedConfig(text2, "Resistencias", resistances, "Resistências a dano por tipo, formato 'Tipo:Nivel,Tipo:Nivel'. Tipos: Blunt, Slash, Pierce, Chop, Pickaxe, Fire, Frost, Lightning, Poison, Spirit. Niveis: Normal, Resistant, VeryResistant, SlightlyResistant, Weak, VeryWeak, SlightlyWeak, Immune, Ignore. Ex: 'Frost:Immune,Fire:Weak'."),
			HeatResistanceBonus = SyncedConfig(text2, "BonusResistenciaFrio", heatResistanceBonus, "Se diferente de 0, essa raça é IMUNE ao frio/calor ambiente (status 'Congelando' e dano de exposição a lava). O valor em si não importa (só precisa ser != 0) — é liga/desliga, não intensidade."),
			SkillDamageMultipliers = SyncedConfig(text2, "MultiplicadorDanoPorSkill", skillDamageMultipliers, "[Uso raro] Bônus de dano extra quando o golpe usa um skill específico, formato 'Skill:Mult,Skill:Mult'. Skills comuns: Swords, Knives, Clubs, Polearms, Spears, Axes, Bows, Crossbows, Unarmed, ElementalMagic, BloodMagic. Ex: 'BloodMagic:1.3,Bows:1.15'."),
			AttackDamageTypeMultipliers = SyncedConfig(text2, "MultiplicadorDanoPorTipo", attackDamageTypeMultipliers, "[Uso raro] Bônus de dano causado, só na fatia daquele TIPO de dano específico. Formato 'Tipo:Mult,Tipo:Mult'."),
			NoResourceCost = SyncedConfig(text2, "SemGastoStaminaEitr", noResourceCost, "[Uso raro] Se true, não gasta stamina nem eitr ao usar ataques/habilidades (a barra só existe pra visual)."),
			XpGainMultiplier = SyncedConfig(text2, "MultiplicadorGanhoXP", xpGainMult, "[Uso raro] Multiplicador de XP ganho em skills ao acertar golpes. 1.0 = vanilla."),
			KnockbackMultiplier = SyncedConfig(text2, "MultiplicadorEmpurrao", knockbackMult, "[Uso raro] Multiplicador da força de empurrão (knockback) que os golpes causam. 1.0 = vanilla."),
			EitrCostMultiplier = SyncedConfig(text2, "MultiplicadorCustoEitr", eitrCostMult, "[Uso raro] Multiplicador do custo de Eitr ao usar magia. Ignorado se SemGastoStaminaEitr estiver ligado."),
			StaminaCostModifiers = SyncedConfig(text2, "ModificadoresCustoStamina", staminaCostModifiers, "[Uso raro] Reduz (ou aumenta) o custo de stamina por ação específica, formato 'Acao:Valor,Acao:Valor'. Ações: Attack, Block, Dodge, Jump, Run, Sneak, Swim. Valor positivo reduz custo, negativo aumenta."),
			NightOnlyBonuses = SyncedConfig(text2, "BonusSoDeNoite", nightOnlyBonuses, "[Uso raro] Se true, os multiplicadores de regen e de velocidade dessa raça só valem durante a noite."),
			DayNightHealthShift = SyncedConfig(text2, "OscilacaoVidaDiaNoite", dayNightHealthShift, "[Uso raro] Faz a vida máxima subir de noite e cair de dia, em cima do BonusVida normal."),
			BiomeHealthShifts = SyncedConfig(text2, "OscilacaoVidaPorBioma", biomeHealthShifts, "[Uso raro] Soma/subtrai vida máxima dependendo do bioma atual, formato 'Bioma:Valor,Bioma:Valor'.")
		};
		switch (ability)
		{
		case RaceAbility.Orc:
			raceConfig.LowHealthThreshold = SyncedConfig(text2, "Habilidade_LimiarVida", lowHealthThreshold, "Fúria de Sangue: abaixo de que % de vida (0 a 1) o bônus de dano extra ativa. 0 = desativado.");
			raceConfig.LowHealthDamageBonus = SyncedConfig(text2, "Habilidade_BonusDano", lowHealthDamageBonus, "Fúria de Sangue: multiplicador de dano causado quando a vida está abaixo do limiar acima.");
			break;
		case RaceAbility.Elf:
			raceConfig.StillHealAura = SyncedConfig(text2, "Habilidade_Ativo", stillHealAura, "Aura de Cura: se true, ficar parado por um tempo regenera vida extra periodicamente.");
			raceConfig.StillHealAmount = SyncedConfig(text2, "Habilidade_QuantidadeCura", stillHealAmount, "Quantidade de vida curada por tick enquanto parado.");
			raceConfig.StillHealInterval = SyncedConfig(text2, "Habilidade_Intervalo", stillHealInterval, "Intervalo em segundos entre cada tick de cura enquanto parado.");
			raceConfig.StillHealRequiredStillTime = SyncedConfig(text2, "Habilidade_TempoParado", stillHealRequiredStillTime, "Quantos segundos parado até a cura começar a ativar.");
			raceConfig.StillHealDayOnly = SyncedConfig(text2, "Habilidade_SoDeDia", stillHealDayOnly, "Se true, a Aura de Cura só funciona durante o DIA.");
			break;
		case RaceAbility.Dwarf:
			raceConfig.IronStanceEnabled = SyncedConfig(text2, "Habilidade_Ativo", ironStanceEnabled, "Postura de Ferro: reduz dano extra e zera empurrão enquanto estiver bloqueando.");
			raceConfig.IronStanceExtraDamageReduction = SyncedConfig(text2, "Habilidade_ReducaoExtra", ironStanceExtraDamageReduction, "Redução de dano EXTRA (fração 0-1) enquanto bloqueando, somada ao bloqueio normal do jogo.");
			break;
		case RaceAbility.Human:
			raceConfig.SecondWindEnabled = SyncedConfig(text2, "Habilidade_Ativo", secondWindEnabled, "Segundo Fôlego: quando a vida cai abaixo do limiar, recupera vida/stamina uma vez, com cooldown.");
			raceConfig.SecondWindThreshold = SyncedConfig(text2, "Habilidade_LimiarVida", secondWindThreshold, "Abaixo de que % de vida (0 a 1) o Segundo Fôlego ativa.");
			raceConfig.SecondWindHealAmount = SyncedConfig(text2, "Habilidade_QuantidadeVida", secondWindHealAmount, "Quantidade de vida recuperada quando o Segundo Fôlego ativa.");
			raceConfig.SecondWindStaminaAmount = SyncedConfig(text2, "Habilidade_QuantidadeStamina", secondWindStaminaAmount, "Quantidade de stamina recuperada quando o Segundo Fôlego ativa.");
			raceConfig.SecondWindCooldown = SyncedConfig(text2, "Habilidade_Cooldown", secondWindCooldown, "Cooldown em segundos antes do Segundo Fôlego poder ativar de novo.");
			break;
		}
		Races[raceId] = raceConfig;
	}

	private ConfigEntry<T> SyncedConfig<T>(string group, string name, T value, string description)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Expected O, but got Unknown
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		};
		return MainConfigFile.Bind<T>(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { val }));
	}

	private void OnConfigurationSynchronized(object sender, EventArgs args)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Configuração sincronizada pelo Jotunn.");
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		NetworkRaces.Clear();
		ZNetViewCache.Clear();
		foreach (Player allPlayer in Player.GetAllPlayers())
		{
			if (!((Object)(object)allPlayer == (Object)null))
			{
				string visualKey = GetVisualKey(allPlayer);
				if (!string.IsNullOrWhiteSpace(visualKey))
				{
					LastAppliedRace[visualKey] = "";
				}
				InvalidateHeadCache(allPlayer);
			}
		}
		ReRegisterSecretCodeCommands();
		foreach (Player allPlayer2 in Player.GetAllPlayers())
		{
			ApplyRaceWithDelays(allPlayer2);
		}
	}

	private void LoadAssets()
	{
		ElfEarsMalePrefab = LoadPrefabFromEmbedded("elf_ears_m");
		ElfEarsFemalePrefab = LoadPrefabFromEmbedded("elf_ears_f");
		OrcEarsMalePrefab = LoadPrefabFromEmbedded("orc_ears_m");
		OrcEarsFemalePrefab = LoadPrefabFromEmbedded("orc_ears_f");
		OrcTuskPrefab = LoadPrefabFromEmbedded("orc_tusk");
		HighElfEyesPrefab = LoadPrefabFromEmbedded("high_elf_eyes");
	}

	private GameObject LoadPrefabFromEmbedded(string assetName)
	{
		try
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string x) => x.EndsWith(assetName));
			if (text == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Asset embutido '" + assetName + "' não encontrado na DLL."));
				string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
				foreach (string text2 in manifestResourceNames)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("RESOURCE: " + text2));
				}
				return null;
			}
			using Stream stream = executingAssembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Stream '" + assetName + "' nulo."));
				return null;
			}
			byte[] array;
			using (MemoryStream memoryStream = new MemoryStream())
			{
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			AssetBundle val = AssetBundle.LoadFromMemory(array);
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao carregar AssetBundle '" + assetName + "'."));
				return null;
			}
			GameObject val2 = val.LoadAsset<GameObject>(assetName) ?? val.LoadAllAssets<GameObject>().FirstOrDefault();
			if ((Object)(object)val2 == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Nenhum prefab em '" + assetName + "'."));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Prefab '" + assetName + "' carregado."));
			}
			return val2;
		}
		catch (Exception arg)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)$"Erro ao carregar asset '{assetName}': {arg}");
			return null;
		}
	}

	private static bool IsRaceEnabled(string race)
	{
		RaceConfig value;
		return !string.IsNullOrWhiteSpace(race) && Races.TryGetValue(race, out value) && value.Enabled.Value;
	}

	private static bool RaceUsesForcedSkin(string race)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: 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_0054: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrWhiteSpace(race) || !Races.TryGetValue(race, out var value))
		{
			return false;
		}
		Vector3 val = ParseColorConfig(value.SkinColor.Value, InvalidColor);
		return val.x >= 0f && val.y >= 0f && val.z >= 0f;
	}

	private static bool RaceUsesForcedHair(string race)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: 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_0054: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrWhiteSpace(race) || !Races.TryGetValue(race, out var value))
		{
			return false;
		}
		Vector3 val = ParseColorConfig(value.HairColor.Value, InvalidColor);
		return val.x >= 0f && val.y >= 0f && val.z >= 0f;
	}

	private static Vector3 GetRaceHairColor(string race)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		RaceConfig value;
		return Races.TryGetValue(race, out value) ? ParseColorConfig(value.HairColor.Value, InvalidColor) : InvalidColor;
	}

	private static Vector3 GetRaceScale(string race)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		RaceConfig value;
		return Races.TryGetValue(race, out value) ? ParseVector(value.Scale.Value, HumanScale) : HumanScale;
	}

	private static Vector3 GetRaceSkinColor(string race)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		RaceConfig value;
		return Races.TryGetValue(race, out value) ? ParseColorConfig(value.SkinColor.Value, Vector3.one) : Vector3.one;
	}

	private static Vector3 ParseVector(string value, Vector3 fallback)
	{
		//IL_0073: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: 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_002f: 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)
		try
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			string[] array = value.Split(new char[1] { ',' });
			if (array.Length != 3)
			{
				return fallback;
			}
			return new Vector3(float.Parse(array[0].Trim(), CultureInfo.InvariantCulture), float.Parse(array[1].Trim(), CultureInfo.InvariantCulture), float.Parse(array[2].Trim(), CultureInfo.InvariantCulture));
		}
		catch
		{
			return fallback;
		}
	}

	private static Vector3 ParseColorConfig(string value, Vector3 fallback)
	{
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			value = value.Trim();
			if (value == "-1" || value.Equals("none", StringComparison.OrdinalIgnoreCase) || value.Equals("nao", StringComparison.OrdinalIgnoreCase) || value.Equals("não", StringComparison.OrdinalIgnoreCase) || value == "-1,-1,-1")
			{
				return InvalidColor;
			}
			if (value.StartsWith("#"))
			{
				Color val = default(Color);
				if (ColorUtility.TryParseHtmlString(value, ref val))
				{
					return new Vector3(val.r, val.g, val.b);
				}
				return fallback;
			}
			return ParseVector(value, fallback);
		}
		catch
		{
			return fallback;
		}
	}

	private static string GetRaceFromSecretCode(string text)
	{
		if (string.IsNullOrWhiteSpace(text))
		{
			return null;
		}
		foreach (KeyValuePair<string, RaceConfig> race in Races)
		{
			RaceConfig value = race.Value;
			if (value?.SecretCode == null || !value.Enabled.Value || !text.Equals(value.SecretCode.Value, StringComparison.Ordinal))
			{
				continue;
			}
			return race.Key;
		}
		return null;
	}

	private string GetRaceName(string race)
	{
		RaceConfig value;
		return Races.TryGetValue(race, out value) ? value.DisplayName : race;
	}

	private static string GetPlayerId(Player player)
	{
		long playerIdLong = GetPlayerIdLong(player);
		return (playerIdLong != 0L) ? playerIdLong.ToString() : "";
	}

	private static string GetVisualKey(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return "";
		}
		long playerIdLong = GetPlayerIdLong(player);
		return (playerIdLong != 0L) ? playerIdLong.ToString() : "";
	}

	private static long GetPlayerIdLong(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return 0L;
		}
		try
		{
			long playerID = player.GetPlayerID();
			if (playerID != 0)
			{
				return playerID;
			}
		}
		catch
		{
		}
		return 0L;
	}

	private void RestoreLocalSavedRaceIfNeeded()
	{
		if (isRestoringLocalRace)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || GetPlayerIdLong(localPlayer) == 0)
		{
			return;
		}
		string savedLocalRace = GetSavedLocalRace();
		if (string.IsNullOrWhiteSpace(savedLocalRace) || !IsRaceEnabled(savedLocalRace))
		{
			return;
		}
		string race = GetRace(localPlayer);
		if (!string.IsNullOrWhiteSpace(race))
		{
			return;
		}
		ZNetView cachedZNetView = GetCachedZNetView(localPlayer);
		if ((Object)(object)cachedZNetView == (Object)null || cachedZNetView.GetZDO() == null)
		{
			return;
		}
		isRestoringLocalRace = true;
		try
		{
			long playerIdLong = GetPlayerIdLong(localPlayer);
			if (playerIdLong != 0)
			{
				NetworkRaces[playerIdLong] = savedLocalRace;
			}
			ForceSetZdoRace(localPlayer, savedLocalRace);
			ForceSetRaceVisualZdo(localPlayer, savedLocalRace);
			SetZdoLocked(localPlayer, locked: true);
			BroadcastRace(localPlayer, savedLocalRace);
			lastBroadcastRace = savedLocalRace;
			ApplyRaceWithDelays(localPlayer);
		}
		finally
		{
			isRestoringLocalRace = false;
		}
	}

	private static string GetLocalSavePrefix()
	{
		long num = 0L;
		try
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				num = Player.m_localPlayer.GetPlayerID();
			}
		}
		catch
		{
		}
		if (_cachedSavePrefix != null && (Object)(object)_cachedZNetInstance == (Object)(object)ZNet.instance && _cachedCharacterId == num)
		{
			return _cachedSavePrefix;
		}
		string text = ((num != 0L) ? num.ToString() : "unknown");
		string text2 = "local";
		try
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				if (!ZNet.instance.IsServer())
				{
					string text3 = "";
					int result = 0;
					FieldInfo fieldInfo = AccessTools.Field(typeof(ZNet), "m_serverHost");
					FieldInfo fieldInfo2 = AccessTools.Field(typeof(ZNet), "m_serverPort");
					if (fieldInfo != null)
					{
						text3 = fieldInfo.GetValue(ZNet.instance)?.ToString() ?? "";
					}
					if (fieldInfo2 != null)
					{
						int.TryParse(fieldInfo2.GetValue(ZNet.instance)?.ToString() ?? "", out result);
					}
					if (!string.IsNullOrWhiteSpace(text3))
					{
						text2 = text3 + ((result > 0) ? ("_" + result) : "");
					}
				}
				else
				{
					MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "GetWorldName", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						text2 = methodInfo.Invoke(ZNet.instance, null)?.ToString() ?? "local";
					}
				}
			}
		}
		catch
		{
		}
		_cachedSavePrefix = "GlitnirRaces_c" + text + "_s" + SanitizeKey(text2) + "_";
		_cachedZNetInstance = ZNet.instance;
		_cachedCharacterId = num;
		_cachedServerKey = text2;
		return _cachedSavePrefix;
	}

	private static string SanitizeKey(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return "unknown";
		}
		char[] array = value.ToCharArray();
		for (int i = 0; i < array.Length; i++)
		{
			if (!char.IsLetterOrDigit(array[i]) && array[i] != '-')
			{
				array[i] = '_';
			}
		}
		return new string(array);
	}

	private static string GetSavedLocalRace()
	{
		try
		{
			return PlayerPrefs.GetString(GetLocalSavePrefix() + "Race", "");
		}
		catch
		{
			return "";
		}
	}

	private static bool HasSavedLocalRaceLocked()
	{
		try
		{
			return PlayerPrefs.GetInt(GetLocalSavePrefix() + "Locked", 0) == 1;
		}
		catch
		{
			return false;
		}
	}

	private static void SaveLocalRace(string race, bool locked)
	{
		if (string.IsNullOrWhiteSpace(race))
		{
			return;
		}
		try
		{
			PlayerPrefs.SetString(GetLocalSavePrefix() + "Race", race);
			PlayerPrefs.SetInt(GetLocalSavePrefix() + "Locked", locked ? 1 : 0);
			PlayerPrefs.Save();
		}
		catch
		{
		}
		long localCharacterId = GetLocalCharacterId();
		if (localCharacterId == 0)
		{
			return;
		}
		try
		{
			PlayerPrefs.SetString(GetGlobalRaceKey(localCharacterId), race);
			PlayerPrefs.Save();
		}
		catch
		{
		}
	}

	private static long GetLocalCharacterId()
	{
		try
		{
			return ((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID() : 0;
		}
		catch
		{
			return 0L;
		}
	}

	private static string GetGlobalRaceKey(long characterId)
	{
		return $"GlitnirRaces_c{characterId}_LastRaceGlobal";
	}

	private static string GetLastRaceGlobal(long characterId)
	{
		if (characterId == 0)
		{
			return "";
		}
		try
		{
			return PlayerPrefs.GetString(GetGlobalRaceKey(characterId), "");
		}
		catch
		{
			return "";
		}
	}

	private static void ClearLocalSavedRace()
	{
		try
		{
			PlayerPrefs.DeleteKey(GetLocalSavePrefix() + "Race");
			PlayerPrefs.DeleteKey(GetLocalSavePrefix() + "Locked");
			PlayerPrefs.Save();
		}
		catch
		{
		}
	}

	private static bool IsClientConnectedToRemoteServer()
	{
		try
		{
			return (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer();
		}
		catch
		{
			return false;
		}
	}

	private static bool IsSinglePlayer()
	{
		try
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			return ZNet.instance.IsServer() && ZNet.instance.GetNrOfPlayers() <= 1;
		}
		catch
		{
			return false;
		}
	}

	private static void ApplyPreviewRace(FejdStartup fejd, PlayerProfile profile)
	{
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)fejd == (Object)null || profile == null || (Object)(object)Instance == (Object)null)