Decompiled source of Master of Disguise v1.0.0

YouDied.MasterOfDisguise.dll

Decompiled 3 days 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 System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("YouDied.MasterOfDisguise")]
[assembly: AssemblyProduct("Master of Disguise")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace YouDied.MasterOfDisguise
{
	internal static class BossBiomes
	{
		public const string DefaultMap = "TrophyEikthyr:Meadows, TrophyTheElder:BlackForest, TrophyBonemass:Swamp, TrophyDragonQueen:Mountain, TrophyGoblinKing:Plains, TrophySeekerQueen:Mistlands, TrophyFader:AshLands";

		private static string s_raw;

		private static readonly Dictionary<string, Biome> s_map = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase);

		public static bool TryGet(string trophy, out Biome biome)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected I4, but got Unknown
			Parse();
			if (s_map.TryGetValue(trophy, out biome))
			{
				return true;
			}
			TrophyInfo trophyInfo = TrophyDatabase.Get(trophy);
			if (trophyInfo != null && trophyInfo.FromBoss && Plugin.UnlistedBossBiome.Value != "None")
			{
				try
				{
					biome = (Biome)(int)(Biome)Enum.Parse(typeof(Biome), Plugin.UnlistedBossBiome.Value, ignoreCase: true);
					return true;
				}
				catch
				{
				}
			}
			biome = (Biome)0;
			return false;
		}

		public static bool IsListed(string trophy)
		{
			Parse();
			return s_map.ContainsKey(trophy);
		}

		public static bool Fools(string trophy, Character creature)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			if (!TryGet(trophy, out var biome))
			{
				return false;
			}
			WorldGenerator instance = WorldGenerator.instance;
			if (instance == null)
			{
				return false;
			}
			return (instance.GetBiome(((Component)creature).transform.position) & biome) > 0;
		}

		public unsafe static string DisplayName(string trophy)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if (!TryGet(trophy, out var biome))
			{
				return "boss's biome (not set, see config)";
			}
			if ((int)biome != 8)
			{
				if ((int)biome != 32)
				{
					if ((int)biome == 64)
					{
						return "Deep North";
					}
					return ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString();
				}
				return "Ashlands";
			}
			return "Black Forest";
		}

		private static void Parse()
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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)
			string text = Plugin.BossBiomeMap.Value ?? "";
			if (text == s_raw)
			{
				return;
			}
			s_raw = text;
			s_map.Clear();
			string[] array = text.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' });
				if (array2.Length == 2)
				{
					string text2 = array2[0].Trim();
					string text3 = array2[1].Trim();
					try
					{
						Biome value = (Biome)Enum.Parse(typeof(Biome), text3, ignoreCase: true);
						s_map[text2] = value;
					}
					catch
					{
						Plugin.Warn("Boss trophy biomes: '" + text3 + "' isn't a biome (for " + text2 + ").");
					}
				}
			}
		}
	}
	internal static class Cost
	{
		internal const string EffectName = "YouDied_Disguised";

		private static readonly int EffectHash = StringExtensionMethods.GetStableHashCode("YouDied_Disguised");

		private const string UsedKeyPrefix = "YouDied.MasterOfDisguise.Used.";

		private static readonly FieldRef<StatusEffect, float> SeTime = AccessTools.FieldRefAccess<StatusEffect, float>("m_time");

		private static SE_Stats s_template;

		private static float s_used;

		private static string s_usedFor;

		private static float s_nextSave;

		internal static SE_Stats Template
		{
			get
			{
				if ((Object)(object)s_template == (Object)null)
				{
					s_template = ScriptableObject.CreateInstance<SE_Stats>();
					((Object)s_template).name = "YouDied_Disguised";
					((StatusEffect)s_template).m_name = "Disguised";
					((StatusEffect)s_template).m_tooltip = "Creatures fooled by your trophy treat you as one of their own.";
				}
				return s_template;
			}
		}

		public static float LifetimeSeconds(string trophy)
		{
			if (!Plugin.NeverBreaks.Value && Plugin.Enabled.Value)
			{
				return (float)Plugin.Lifetime.Value * 60f;
			}
			return 0f;
		}

		public static float UsedSeconds(Player player, string trophy)
		{
			if (trophy == s_usedFor && (Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				return s_used;
			}
			if (!player.m_customData.TryGetValue("YouDied.MasterOfDisguise.Used." + trophy, out var value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return 0f;
			}
			return result;
		}

		public static void Update(Player player, string trophy)
		{
			if (s_usedFor != trophy)
			{
				Save(player);
				s_usedFor = trophy;
				s_used = UsedSeconds(player, trophy);
			}
			float num = LifetimeSeconds(trophy);
			if (num > 0f)
			{
				if (!FitMode.Active)
				{
					s_used += Time.deltaTime;
				}
				if (Time.time >= s_nextSave)
				{
					s_nextSave = Time.time + 2f;
					Save(player);
				}
				if (s_used >= num)
				{
					Consume(player, trophy);
					return;
				}
			}
			ApplyEffect(player, trophy, num);
		}

		private static void ApplyEffect(Player player, string trophy, float lifetime)
		{
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan == null)
			{
				return;
			}
			bool num = Plugin.ShowStatusIcon.Value || Plugin.StaminaRegen.Value < 100 || Plugin.MoveSpeed.Value < 100;
			StatusEffect statusEffect = sEMan.GetStatusEffect(EffectHash);
			SE_Stats val = (SE_Stats)(object)((statusEffect is SE_Stats) ? statusEffect : null);
			if (!num)
			{
				if ((Object)(object)val != (Object)null)
				{
					sEMan.RemoveStatusEffect(EffectHash, true);
				}
				return;
			}
			if ((Object)(object)val == (Object)null)
			{
				sEMan.AddStatusEffect((StatusEffect)(object)Template, false, 0, 0f, (short)(-1));
				StatusEffect statusEffect2 = sEMan.GetStatusEffect(EffectHash);
				val = (SE_Stats)(object)((statusEffect2 is SE_Stats) ? statusEffect2 : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
			}
			((StatusEffect)val).m_icon = (Plugin.ShowStatusIcon.Value ? TrophyIcon(player, trophy) : null);
			val.m_staminaRegenMultiplier = (float)Plugin.StaminaRegen.Value / 100f;
			val.m_speedModifier = (float)(Plugin.MoveSpeed.Value - 100) / 100f;
			((StatusEffect)val).m_ttl = ((lifetime > 0f) ? (lifetime + 1f) : 0f);
			SeTime.Invoke((StatusEffect)(object)val) = ((lifetime > 0f) ? s_used : 0f);
		}

		public static void Clear(Player player)
		{
			Save(player);
			s_usedFor = null;
			SEMan val = (((Object)(object)player != (Object)null) ? ((Character)player).GetSEMan() : null);
			if (val != null && val.HaveStatusEffect(EffectHash))
			{
				val.RemoveStatusEffect(EffectHash, true);
			}
		}

		private static void Consume(Player player, string trophy)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData val = null;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if ((Object)(object)allItem.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == trophy)
				{
					val = allItem;
					break;
				}
			}
			string text = ((val != null) ? Loc.T(val.m_shared.m_name) : "trophy");
			if (val != null)
			{
				inventory.RemoveItem(val, 1);
			}
			player.m_customData.Remove("YouDied.MasterOfDisguise.Used." + trophy);
			s_usedFor = null;
			s_used = 0f;
			Disguise.Remove(player, "Your " + text + " crumbles to dust. Your disguise is gone.");
		}

		private static void Save(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !string.IsNullOrEmpty(s_usedFor))
			{
				player.m_customData["YouDied.MasterOfDisguise.Used." + s_usedFor] = s_used.ToString("0.#", CultureInfo.InvariantCulture);
			}
		}

		private static Sprite TrophyIcon(Player player, string trophy)
		{
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				if ((Object)(object)allItem.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == trophy)
				{
					return allItem.GetIcon();
				}
			}
			return null;
		}

		public static string FormatTime(float seconds)
		{
			int num = Mathf.Max(0, Mathf.CeilToInt(seconds));
			if (num < 60)
			{
				return $"{num}s";
			}
			return $"{num / 60}m {num % 60:00}s";
		}

		public static void Register(ObjectDB db)
		{
			if (!((Object)(object)db == (Object)null) && db.m_StatusEffects != null && !db.m_StatusEffects.Contains((StatusEffect)(object)Template))
			{
				db.m_StatusEffects.Add((StatusEffect)(object)Template);
			}
		}
	}
	internal static class Disguise
	{
		internal static readonly int ZTrophy = StringExtensionMethods.GetStableHashCode("YouDied.MoD.Trophy");

		internal static readonly int ZExposed = StringExtensionMethods.GetStableHashCode("YouDied.MoD.Exposed");

		internal static readonly int ZFit = StringExtensionMethods.GetStableHashCode("YouDied.MoD.Fit");

		internal const string SaveKey = "YouDied.MasterOfDisguise.Trophy";

		internal static readonly FieldRef<Character, ZNetView> CharNView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");

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

		private static Player s_initFor;

		private static float s_exposedUntil;

		private static float s_nextInventoryCheck;

		private static readonly FieldRef<Character, Faction> FactionField = AccessTools.FieldRefAccess<Character, Faction>("m_faction");

		internal static bool Probing;

		public static ZDO GetZDO(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			ZNetView val = CharNView.Invoke(c);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return null;
			}
			return val.GetZDO();
		}

		public static string GetWorn(Player p)
		{
			ZDO zDO = GetZDO((Character)(object)p);
			return ((zDO != null) ? zDO.GetString(ZTrophy, "") : null) ?? "";
		}

		public static bool IsExposed(Player p)
		{
			ZDO zDO = GetZDO((Character)(object)p);
			return zDO != null && zDO.GetInt(ZExposed, 0) != 0;
		}

		private static string PrefabName(Character c)
		{
			int instanceID = ((Object)c).GetInstanceID();
			if (!s_prefabNames.TryGetValue(instanceID, out var value))
			{
				value = Utils.GetPrefabName(((Component)c).gameObject);
				s_prefabNames[instanceID] = value;
			}
			return value;
		}

		public static bool Fools(Player player, Character creature, bool ignoreExposed = false)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value || (Object)(object)player == (Object)null || (Object)(object)creature == (Object)null)
			{
				return false;
			}
			if (creature.IsPlayer() || (int)creature.GetFaction() == 8 || creature.IsTamed())
			{
				return false;
			}
			ZDO zDO = GetZDO((Character)(object)player);
			if (zDO == null)
			{
				return false;
			}
			string text = zDO.GetString(ZTrophy, "");
			if (text.Length == 0)
			{
				return false;
			}
			if (!ignoreExposed && zDO.GetInt(ZExposed, 0) != 0)
			{
				return false;
			}
			TrophyInfo trophyInfo = TrophyDatabase.Get(text);
			if (trophyInfo == null || TrophyDatabase.IsExcluded(text))
			{
				return false;
			}
			if (trophyInfo.FromBoss && !Plugin.BossFoolsAll.Value)
			{
				return false;
			}
			Faction faction = creature.GetFaction();
			if (!TrophyDatabase.IsDisguisableFaction(faction))
			{
				return false;
			}
			BaseAI baseAI = creature.GetBaseAI();
			if ((Object)(object)baseAI != (Object)null && baseAI.IsAggravated())
			{
				return false;
			}
			if (trophyInfo.FromBoss)
			{
				return BossBiomes.Fools(text, creature);
			}
			switch (Plugin.Scope.Value)
			{
			case DisguiseScope.Creature:
				return trophyInfo.Creatures.Contains(PrefabName(creature));
			case DisguiseScope.Faction:
				return trophyInfo.Factions.Contains(faction);
			default:
				if (trophyInfo.Factions.Contains(faction))
				{
					return true;
				}
				foreach (Faction faction2 in trophyInfo.Factions)
				{
					if (TrophyDatabase.IsDisguisableFaction(faction2) && GameSaysFriendly(creature, player, faction2))
					{
						return true;
					}
				}
				return false;
			}
		}

		private static bool GameSaysFriendly(Character creature, Player player, Faction asFaction)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected I4, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected I4, but got Unknown
			ref Faction reference = ref FactionField.Invoke((Character)(object)player);
			Faction val = reference;
			Probing = true;
			reference = (Faction)(int)asFaction;
			try
			{
				return !BaseAI.IsEnemy(creature, (Character)(object)player);
			}
			finally
			{
				reference = (Faction)(int)val;
				Probing = false;
			}
		}

		internal static IEnumerable<Faction> AlliesOf(IEnumerable<Faction> trophyFactions)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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)
			HashSet<Faction> hashSet = new HashSet<Faction>();
			foreach (Faction trophyFaction in trophyFactions)
			{
				if (!TrophyDatabase.IsDisguisableFaction(trophyFaction))
				{
					continue;
				}
				hashSet.Add(trophyFaction);
				foreach (Faction value in Enum.GetValues(typeof(Faction)))
				{
					if (TrophyDatabase.IsDisguisableFaction(value) && !TableIsEnemy(value, trophyFaction))
					{
						hashSet.Add(value);
					}
				}
			}
			return hashSet;
		}

		private static bool TableIsEnemy(Faction a, Faction b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_0044: Expected I4, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Invalid comparison between Unknown and I4
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Invalid comparison between Unknown and I4
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Invalid comparison between Unknown and I4
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Invalid comparison between Unknown and I4
			//IL_0083: 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_0090: Invalid comparison between Unknown and I4
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Invalid comparison between Unknown and I4
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Invalid comparison between Unknown and I4
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Invalid comparison between Unknown and I4
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Invalid comparison between Unknown and I4
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Invalid comparison between Unknown and I4
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Invalid comparison between Unknown and I4
			if (a == b)
			{
				return false;
			}
			switch ((int)a)
			{
			case 0:
				return (int)b != 10;
			case 1:
			case 11:
				return true;
			case 2:
			case 9:
			case 13:
				if ((int)b != 1)
				{
					return (int)b != 8;
				}
				return false;
			case 3:
				if ((int)b != 4)
				{
					return (int)b != 8;
				}
				return false;
			case 4:
				if ((int)b != 3)
				{
					return (int)b != 8;
				}
				return false;
			case 5:
			case 6:
			case 7:
				return (int)b != 8;
			case 8:
				if ((int)b != 0)
				{
					return (int)b == 11;
				}
				return true;
			case 10:
				if ((int)b != 1 && (int)b != 8)
				{
					return (int)b > 0;
				}
				return false;
			case 12:
				return (int)b == 0;
			default:
				return false;
			}
		}

		public static void Wear(Player player, ItemData item)
		{
			string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
			if (!string.IsNullOrEmpty(text))
			{
				if (GetWorn(player) == text)
				{
					Remove(player);
					return;
				}
				if (FitStore.IsSkipped(text))
				{
					Message(player, "The " + Loc.T(item.m_shared.m_name) + " can't be worn.");
					return;
				}
				SetWorn(player, text);
				Message(player, DescribeDisguise(text, item));
			}
		}

		public static void Remove(Player player, string reason = null)
		{
			if (GetWorn(player).Length != 0)
			{
				FitMode.Exit(player, save: true);
				Cost.Clear(player);
				SetWorn(player, "");
				Message(player, reason ?? "You take off your disguise.");
			}
		}

		private static void SetWorn(Player player, string trophy)
		{
			ZDO zDO = GetZDO((Character)(object)player);
			if (zDO != null)
			{
				zDO.Set(ZTrophy, trophy);
				zDO.Set(ZFit, (trophy.Length > 0) ? FitStore.Get(trophy).Serialize() : "");
				zDO.Set(ZExposed, 0, false);
				s_exposedUntil = 0f;
				if (trophy.Length > 0)
				{
					player.m_customData["YouDied.MasterOfDisguise.Trophy"] = trophy;
				}
				else
				{
					player.m_customData.Remove("YouDied.MasterOfDisguise.Trophy");
				}
			}
		}

		private static string DescribeDisguise(string trophy, ItemData item)
		{
			return "You put on the " + Loc.T(item.m_shared.m_name) + ".";
		}

		internal static IEnumerable<string> Fmt(IEnumerable<Faction> factions)
		{
			foreach (Faction faction in factions)
			{
				if (TrophyDatabase.IsDisguisableFaction(faction))
				{
					yield return TrophyDatabase.FactionDisplayName(faction);
				}
			}
		}

		internal static string JoinNames(TrophyInfo info)
		{
			List<string> list = new List<string>();
			foreach (string creatureDisplayName in info.CreatureDisplayNames)
			{
				list.Add(Loc.T(creatureDisplayName));
			}
			return string.Join(", ", list);
		}

		private static string Cap(string s)
		{
			if (!string.IsNullOrEmpty(s))
			{
				return char.ToUpperInvariant(s[0]) + s.Substring(1);
			}
			return s;
		}

		public static void OnLocalAttack(Player player, Character target)
		{
			if (Plugin.BreakOnAttack.Value && !((Object)(object)target == (Object)null) && !target.IsDead() && GetWorn(player).Length != 0 && Fools(player, target, ignoreExposed: true))
			{
				ZDO zDO = GetZDO((Character)(object)player);
				bool flag = zDO.GetInt(ZExposed, 0) != 0;
				zDO.Set(ZExposed, 1, false);
				s_exposedUntil = Time.time + (float)Plugin.BreakSeconds.Value;
				if (!flag)
				{
					Message(player, $"Your disguise is broken! ({Plugin.BreakSeconds.Value}s)");
				}
			}
		}

		public static void LocalUpdate(Player player)
		{
			ZDO zDO = GetZDO((Character)(object)player);
			if (zDO == null)
			{
				return;
			}
			if ((Object)(object)s_initFor != (Object)(object)player)
			{
				s_initFor = player;
				s_exposedUntil = 0f;
				zDO.Set(ZExposed, 0, false);
				if (player.m_customData.TryGetValue("YouDied.MasterOfDisguise.Trophy", out var value) && !string.IsNullOrEmpty(value))
				{
					if (!FitStore.IsSkipped(value) && HasTrophy(player, value))
					{
						zDO.Set(ZTrophy, value);
						zDO.Set(ZFit, FitStore.Get(value).Serialize());
					}
					else
					{
						player.m_customData.Remove("YouDied.MasterOfDisguise.Trophy");
						zDO.Set(ZTrophy, "");
					}
				}
			}
			string text = zDO.GetString(ZTrophy, "");
			if (text.Length == 0)
			{
				return;
			}
			Cost.Update(player, text);
			text = zDO.GetString(ZTrophy, "");
			if (text.Length == 0)
			{
				return;
			}
			if (zDO.GetInt(ZExposed, 0) != 0 && Time.time >= s_exposedUntil)
			{
				zDO.Set(ZExposed, 0, false);
				Message(player, "Your disguise holds again.");
			}
			if (Time.time >= s_nextInventoryCheck)
			{
				s_nextInventoryCheck = Time.time + 1f;
				if (!HasTrophy(player, text))
				{
					Remove(player, "Your disguise is gone. The trophy left your inventory.");
				}
			}
		}

		internal static bool HasTrophy(Player player, string prefabName)
		{
			Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return false;
			}
			foreach (ItemData allItem in val.GetAllItems())
			{
				if ((Object)(object)allItem.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == prefabName)
				{
					return true;
				}
			}
			return false;
		}

		private static void Message(Player player, string msg)
		{
			if (Plugin.ShowMessages.Value && (Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				((Character)player).Message((MessageType)2, msg, 0, (Sprite)null, false);
			}
		}
	}
	internal enum AttachPoint
	{
		Head,
		Back,
		Hips,
		LeftArm,
		RightArm,
		LeftLeg,
		RightLeg
	}
	internal struct Fit
	{
		public AttachPoint Bone;

		public Vector3 Offset;

		public Vector3 Euler;

		public float Scale;

		public static Fit Default => new Fit
		{
			Bone = AttachPoint.Head,
			Offset = Vector3.zero,
			Euler = Vector3.zero,
			Scale = 1f
		};

		public string Serialize()
		{
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			return string.Join(",", Offset.x.ToString("0.###", invariantCulture), Offset.y.ToString("0.###", invariantCulture), Offset.z.ToString("0.###", invariantCulture), Euler.x.ToString("0.#", invariantCulture), Euler.y.ToString("0.#", invariantCulture), Euler.z.ToString("0.#", invariantCulture), Scale.ToString("0.###", invariantCulture)) + ((Bone != AttachPoint.Head) ? ("," + Bone) : "");
		}

		public static Fit Parse(string s)
		{
			//IL_006f: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			Fit result = Default;
			if (string.IsNullOrEmpty(s))
			{
				return result;
			}
			string[] array = s.Split(new char[1] { ',' });
			if (array.Length != 7 && array.Length != 8)
			{
				return result;
			}
			float[] array2 = new float[7];
			for (int i = 0; i < 7; i++)
			{
				if (!float.TryParse(array[i], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]))
				{
					return Default;
				}
			}
			result.Offset = new Vector3(array2[0], array2[1], array2[2]);
			result.Euler = new Vector3(array2[3], array2[4], array2[5]);
			result.Scale = Mathf.Clamp(array2[6], 0.1f, 5f);
			if (array.Length == 8)
			{
				try
				{
					result.Bone = (AttachPoint)Enum.Parse(typeof(AttachPoint), array[7].Trim(), ignoreCase: true);
				}
				catch
				{
					result.Bone = AttachPoint.Head;
				}
			}
			return result;
		}
	}
	internal static class FitStore
	{
		private static readonly Dictionary<string, Fit> s_fits = new Dictionary<string, Fit>();

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

		private static readonly HashSet<string> BuiltInSkips = new HashSet<string> { "TrophyAbomination", "TrophyBarka", "TrophyKvastur", "TrophyHare", "TrophyMole", "TrophyCharredArcher", "TrophyCharredMage", "TrophyWraith", "TrophyFenring" };

		private static readonly Dictionary<string, string> BuiltInFits = new Dictionary<string, string>
		{
			{ "TrophyEikthyr", "0.007,0.379,0.322,2.8,-179.8,0,1" },
			{ "TrophyTheElder", "0.157,-0.123,0.267,-33.4,-175.2,0,1" },
			{ "TrophyAbomination", "-0.269,-0.502,0.087,27.1,-136.5,-80.3,0.954" },
			{ "TrophyAsksvin", "0.016,-0.221,0.177,-57.5,-0.2,-178.1,1.137" },
			{ "TrophyBjorn", "0.027,0,0.135,-52.6,5.6,177.9,0.976" },
			{ "TrophyBlob", "-0.001,-0.294,0.18,-15.6,169.4,0,1" },
			{ "TrophyBoar", "0.012,-0.301,0.327,-11.6,-178.1,0,1" },
			{ "TrophyBonemawSerpent", "-0.009,0.924,0.587,46.2,179.1,0,0.863" },
			{ "TrophyCultist", "0.014,-0.09,0.323,0,-178.6,0,1" },
			{ "TrophyDeathsquito", "0.003,-0.302,0.571,-36.4,-179.4,0,0.998" },
			{ "TrophyDeer", "0.015,0.039,0.241,38.3,0,0,1" },
			{ "TrophyHatchling", "0,-0.085,0.159,-1.2,178.8,0,1" },
			{ "TrophyDraugrElite", "0.019,-0.344,0.235,0,179.4,0,1" },
			{ "TrophyDraugr", "0.001,-0.384,0.309,0,174.4,0,1" },
			{ "TrophyDraugrFem", "0.019,-0.402,0.109,0,175.8,0,1" },
			{ "TrophyDvergr", "0.002,-0.602,0.286,-114,178.9,0,0.997" },
			{ "TrophyElaking", "0,-0.273,0.213,-89.9,-179.9,0,1.126" },
			{ "TrophyFader", "0,0.164,0.871,170.9,1.1,0,1" },
			{ "TrophyFallenValkyrie", "0,-0.201,0.311,67.5,178.8,0,1" },
			{ "TrophyFenring", "0.199,-0.263,0.032,140.7,7.1,5.1,0.464,RightArm" },
			{ "TrophyGoblinBrute", "0,-0.05,0.189,0,-179,0,1" },
			{ "TrophyGoblinShaman", "0,-0.162,0.082,44.5,-179.7,0,1" },
			{ "TrophyGoblin", "0,-0.263,0.185,-35.8,-179.3,0,1" },
			{ "TrophyCultist_Hildir", "0.003,-0.14,0.287,0,-179.7,0,1" },
			{ "TrophyGhost", "0,-0.277,0.233,-116.8,0.8,0,1.124" },
			{ "TrophyGjall", "-0.012,-0.872,0.351,-116.2,-176.1,0,1" },
			{ "TrophyGreydwarfBrute", "-0.031,-0.099,0.156,-31,-177,0,1" },
			{ "TrophyGreydwarfShaman", "0.003,0.001,0.175,-35.7,178.4,0,1" },
			{ "TrophyGreydwarf", "0,-0.309,0.204,-29.5,-179.9,0,1" },
			{ "TrophyGrowth", "0.018,-0.139,0.307,-9.4,-177.9,0,1" },
			{ "TrophyJotunWitch", "0,-0.19,0.471,-71.4,177,0,1" },
			{ "TrophyJotunWarrior", "0,-0.255,0.205,-65.5,-179.8,0,1" },
			{ "TrophyLox", "0,0,0.063,-46.3,180,0,1" },
			{ "TrophyDragonQueen", "0.021,0.322,-0.446,12.5,176.5,0,1" },
			{ "TrophyMoose", "0,0,0.291,-123,178.8,0,1" },
			{ "TrophyNeck", "0.011,-0.279,0.152,0,178.9,0,1" },
			{ "TrophySkeletonPoison", "0,-0.291,0.169,-28.9,-178.7,0,1" },
			{ "TrophySeal", "0,-0.155,0.213,-88.5,-179.4,0,1" },
			{ "TrophySeekerBrute", "0,-0.43,0.137,-89.1,-177.8,0,1" },
			{ "TrophySeeker", "0,0.916,-0.288,-52.4,0,0,1,Back" },
			{ "TrophySerpent", "-0.028,0.551,1.011,-13.5,-178.8,0,1" },
			{ "TrophySkeleton", "0,-0.28,0.189,-35.8,-178.5,0,1" },
			{ "TrophySeekerQueen", "0.022,0.548,0.563,-175,-3,0,1" },
			{ "TrophyGoblinBruteBrosBrute", "0,-0.026,0.184,0,176.8,0,1" },
			{ "TrophyTick", "-0.003,-0.389,0.321,59.3,0,0,1" },
			{ "TrophyFrostTroll", "0.012,0,0,60,-178.2,0,1" },
			{ "TrophyUlv", "0.542,-0.159,-0.263,0,-3.5,0,1,Hips" },
			{ "TrophyBjornUndead", "0,-0.123,0.29,-45.9,177.5,0,1" },
			{ "TrophyVolture", "0,-0.278,0.402,-129.6,179.6,0,1" },
			{ "TrophyCharredMelee", "0,-0.279,0.217,-35,178.7,0,1" },
			{ "TrophyWolf", "0,-0.213,0.291,-29.7,178.9,0,1" },
			{ "TrophyWraith", "-0.038,-0.343,0.266,-16.8,176.5,0,1,Back" },
			{ "TrophyGoblinKing", "0,0.52,0.033,40.3,-178.3,0,1" },
			{ "TrophyGoblinBruteBrosShaman", "0.008,-0.276,0.192,0,-176.2,0,1" },
			{ "TrophyDeerWhite", "0.021,0.022,0.306,46.9,3.6,0,1" },
			{ "TrophyBarka", "-0.047,0.32,0.015,-96.9,-180,0,1" },
			{ "TrophyBonemass", "0.056,-0.254,0.024,-19.6,177.2,0,1" },
			{ "TrophySkeletonHildir", "0.005,-0.289,0.195,-30.2,178.2,0,1" },
			{ "TrophyBlob_Frost", "-0.003,-0.262,0.242,6.7,167.9,0,1" },
			{ "TrophyBlob_Lava", "0.014,-0.254,0.24,-15.6,-174.1,0,1" },
			{ "TrophyLeech", "-0.028,-0.236,0.173,0,-172.3,0,1" },
			{ "TrophyCharredArcher", "0,-0.286,0.07,-22.1,-173.5,0,0.762,Hips" },
			{ "TrophyMorgen", "0,0.055,0.154,2.3,172.6,0,1,Back" },
			{ "TrophyBlob_Morkhalla", "0.021,-0.293,0.221,-20.5,178.5,0,1" },
			{ "TrophySGolem", "-0.074,0.403,0.409,0,0,0,1" },
			{ "TrophySurtling", "0,-0.221,0.226,-25.4,-179.6,0,1" },
			{ "TrophyCharredMage", "0,-0.322,0.144,-26.6,-178.2,0,1,Back" },
			{ "TrophyWrithan", "0,-0.253,0.288,56.9,0,0,1" }
		};

		private static Dictionary<string, Fit> s_builtInParsed;

		private static string FilePath => Path.Combine(Paths.ConfigPath, "YouDied.MasterOfDisguise.fits.txt");

		public static bool IsSkipped(string trophy)
		{
			if (string.IsNullOrEmpty(trophy))
			{
				return false;
			}
			if (!s_skip.TryGetValue(trophy, out var value))
			{
				return BuiltInSkips.Contains(trophy);
			}
			return value;
		}

		public static void ToggleSkip(string trophy)
		{
			bool flag = !IsSkipped(trophy);
			if (flag == BuiltInSkips.Contains(trophy))
			{
				s_skip.Remove(trophy);
			}
			else
			{
				s_skip[trophy] = flag;
			}
			Save();
		}

		public static Fit Get(string trophy)
		{
			if (!s_fits.TryGetValue(trophy, out var value))
			{
				return BuiltIn(trophy);
			}
			return value;
		}

		public static Fit BuiltIn(string trophy)
		{
			if (s_builtInParsed == null)
			{
				s_builtInParsed = new Dictionary<string, Fit>();
				foreach (KeyValuePair<string, string> builtInFit in BuiltInFits)
				{
					s_builtInParsed[builtInFit.Key] = Fit.Parse(builtInFit.Value);
				}
			}
			if (trophy == null || !s_builtInParsed.TryGetValue(trophy, out var value))
			{
				return Fit.Default;
			}
			return value;
		}

		public static void Set(string trophy, Fit fit)
		{
			s_fits[trophy] = fit;
			Save();
		}

		public static void Reset(string trophy)
		{
			s_fits.Remove(trophy);
			Save();
		}

		public static void Load()
		{
			s_fits.Clear();
			s_skip.Clear();
			try
			{
				if (!File.Exists(FilePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(FilePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length == 0 || text.StartsWith("#"))
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num > 0)
					{
						string key = text.Substring(0, num).Trim();
						string text2 = text.Substring(num + 1).Trim();
						if (text2.Equals("skip", StringComparison.OrdinalIgnoreCase))
						{
							s_skip[key] = true;
						}
						else if (text2.Equals("wear", StringComparison.OrdinalIgnoreCase))
						{
							s_skip[key] = false;
						}
						else
						{
							s_fits[key] = Fit.Parse(text2);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("Could not read trophy fits: " + ex.Message);
			}
		}

		private static void Save()
		{
			try
			{
				List<string> list = new List<string> { "# Master of Disguise trophy fits. Made with fit mode in game (default F7).", "# TrophyName = offsetX,offsetY,offsetZ (m), rotX,rotY,rotZ (deg), scale[, bodyPart]", "# TrophyName = skip   (can't be worn: arms, legs, cloaks...)   TrophyName = wear   (undo a default skip)" };
				foreach (KeyValuePair<string, Fit> s_fit in s_fits)
				{
					list.Add(s_fit.Key + "=" + s_fit.Value.Serialize());
				}
				foreach (KeyValuePair<string, bool> item in s_skip)
				{
					list.Add(item.Key + "=" + (item.Value ? "skip" : "wear"));
				}
				File.WriteAllLines(FilePath, list.ToArray());
			}
			catch (Exception ex)
			{
				Plugin.Warn("Could not save trophy fits: " + ex.Message);
			}
		}
	}
	internal static class FitMode
	{
		private static readonly KeyboardShortcut Fwd = new KeyboardShortcut((KeyCode)273, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut Back = new KeyboardShortcut((KeyCode)274, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut Left = new KeyboardShortcut((KeyCode)276, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut Right = new KeyboardShortcut((KeyCode)275, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut Up = new KeyboardShortcut((KeyCode)273, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 });

		private static readonly KeyboardShortcut Down = new KeyboardShortcut((KeyCode)274, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 });

		private static readonly KeyboardShortcut PitchUp = new KeyboardShortcut((KeyCode)273, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 });

		private static readonly KeyboardShortcut PitchDown = new KeyboardShortcut((KeyCode)274, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 });

		private static readonly KeyboardShortcut YawLeft = new KeyboardShortcut((KeyCode)276, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 });

		private static readonly KeyboardShortcut YawRight = new KeyboardShortcut((KeyCode)275, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 });

		private static readonly KeyboardShortcut RollLeft = new KeyboardShortcut((KeyCode)276, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 });

		private static readonly KeyboardShortcut RollRight = new KeyboardShortcut((KeyCode)275, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 });

		private static readonly KeyboardShortcut Grow = new KeyboardShortcut((KeyCode)273, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 });

		private static readonly KeyboardShortcut Shrink = new KeyboardShortcut((KeyCode)274, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 });

		private static readonly KeyboardShortcut ResetKey = new KeyboardShortcut((KeyCode)8, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut BoneKey = new KeyboardShortcut((KeyCode)98, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut SlowerKey = new KeyboardShortcut((KeyCode)91, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut FasterKey = new KeyboardShortcut((KeyCode)93, Array.Empty<KeyCode>());

		private static readonly int[] Speeds = new int[6] { 5, 10, 25, 50, 100, 200 };

		private static int s_speedIndex = 3;

		private const float MoveSpeed = 0.12f;

		private const float TurnSpeed = 60f;

		private const float ScaleSpeed = 0.6f;

		private static bool s_active;

		private static string s_trophy;

		private static Fit s_fit;

		private static float s_nextBroadcast;

		public static int SpeedPercent => Speeds[s_speedIndex];

		public static bool Active => s_active;

		public static void Update(Player player)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			string worn = Disguise.GetWorn(player);
			if (s_active && worn != s_trophy)
			{
				Exit(player, save: true);
			}
			KeyboardShortcut val = Plugin.FitModeKey.Value;
			if (((KeyboardShortcut)(ref val)).IsDown())
			{
				if (s_active)
				{
					Exit(player, save: true);
				}
				else if (worn.Length > 0)
				{
					Enter(player, worn);
				}
				else
				{
					((Character)player).Message((MessageType)2, "Wear a trophy first, then fit it.", 0, (Sprite)null, false);
				}
			}
			if (!s_active)
			{
				return;
			}
			Fit fit = Nudge(s_fit);
			val = ResetKey;
			if (((KeyboardShortcut)(ref val)).IsDown())
			{
				fit = FitStore.BuiltIn(s_trophy);
				((Character)player).Message((MessageType)1, "Trophy fit reset.", 0, (Sprite)null, false);
			}
			s_fit = fit;
			Visual(player)?.SetLive(s_fit);
			if (Time.time >= s_nextBroadcast)
			{
				s_nextBroadcast = Time.time + 0.25f;
				ZDO zDO = Disguise.GetZDO((Character)(object)player);
				if (zDO != null)
				{
					zDO.Set(Disguise.ZFit, s_fit.Serialize());
				}
			}
		}

		internal static Fit Nudge(Fit f)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: 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_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut val = SlowerKey;
			if (((KeyboardShortcut)(ref val)).IsDown() && s_speedIndex > 0)
			{
				s_speedIndex--;
			}
			val = FasterKey;
			if (((KeyboardShortcut)(ref val)).IsDown() && s_speedIndex < Speeds.Length - 1)
			{
				s_speedIndex++;
			}
			float num = Time.unscaledDeltaTime * (float)SpeedPercent / 100f;
			val = BoneKey;
			if (((KeyboardShortcut)(ref val)).IsDown())
			{
				AttachPoint bone = (AttachPoint)((int)(f.Bone + 1) % Enum.GetValues(typeof(AttachPoint)).Length);
				f = Fit.Default;
				f.Bone = bone;
			}
			val = Fwd;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Offset.z += 0.12f * num;
			}
			val = Back;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Offset.z -= 0.12f * num;
			}
			val = Left;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Offset.x -= 0.12f * num;
			}
			val = Right;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Offset.x += 0.12f * num;
			}
			val = Up;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Offset.y += 0.12f * num;
			}
			val = Down;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Offset.y -= 0.12f * num;
			}
			val = PitchUp;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Euler.x -= 60f * num;
			}
			val = PitchDown;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Euler.x += 60f * num;
			}
			val = YawLeft;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Euler.y -= 60f * num;
			}
			val = YawRight;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Euler.y += 60f * num;
			}
			val = RollLeft;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Euler.z += 60f * num;
			}
			val = RollRight;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Euler.z -= 60f * num;
			}
			val = Grow;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Scale *= 1f + 0.6f * num;
			}
			val = Shrink;
			if (((KeyboardShortcut)(ref val)).IsPressed())
			{
				f.Scale /= 1f + 0.6f * num;
			}
			f.Scale = Mathf.Clamp(f.Scale, 0.1f, 5f);
			f.Euler = new Vector3(Wrap(f.Euler.x), Wrap(f.Euler.y), Wrap(f.Euler.z));
			return f;
		}

		internal static bool ResetPressed()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut resetKey = ResetKey;
			return ((KeyboardShortcut)(ref resetKey)).IsDown();
		}

		private static void Enter(Player player, string trophy)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			s_active = true;
			s_trophy = trophy;
			s_fit = FitStore.Get(trophy);
			((Character)player).Message((MessageType)2, "Fit mode: arrows move, Shift+Up/Down height, Ctrl+arrows turn, B body part, [ ] speed,\n" + $"Alt+Left/Right roll, Alt+Up/Down size, Backspace reset. {Plugin.FitModeKey.Value} saves.\nDisguise timer paused.", 0, (Sprite)null, false);
		}

		public static void Exit(Player player, bool save)
		{
			if (!s_active)
			{
				return;
			}
			s_active = false;
			if (save && !string.IsNullOrEmpty(s_trophy))
			{
				if (s_fit.Serialize() == FitStore.BuiltIn(s_trophy).Serialize())
				{
					FitStore.Reset(s_trophy);
				}
				else
				{
					FitStore.Set(s_trophy, s_fit);
				}
				ZDO zDO = Disguise.GetZDO((Character)(object)player);
				if (zDO != null)
				{
					zDO.Set(Disguise.ZFit, s_fit.Serialize());
				}
				((Character)player).Message((MessageType)2, "Trophy fit saved.", 0, (Sprite)null, false);
			}
			Visual(player)?.ClearLive();
			s_trophy = null;
		}

		private static TrophyVisual Visual(Player player)
		{
			VisEquipment component = ((Component)player).GetComponent<VisEquipment>();
			if (!((Object)(object)component != (Object)null))
			{
				return null;
			}
			return ((Component)component).GetComponent<TrophyVisual>();
		}

		private static float Wrap(float deg)
		{
			deg %= 360f;
			if (deg > 180f)
			{
				deg -= 360f;
			}
			if (deg < -180f)
			{
				deg += 360f;
			}
			return deg;
		}
	}
	internal static class TrophyVisualLiveExt
	{
		public static void SetLive(this TrophyVisual tv, Fit fit)
		{
			tv.LiveFit = fit;
		}

		public static void ClearLive(this TrophyVisual tv)
		{
			tv.LiveFit = null;
			tv.ForceRefit();
		}
	}
	internal static class Loc
	{
		private static bool s_resolved;

		private static PropertyInfo s_instance;

		private static MethodInfo s_localize;

		public static string T(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return token;
			}
			if (!s_resolved)
			{
				s_resolved = true;
				Type type = AccessTools.TypeByName("Localization");
				s_instance = ((type != null) ? AccessTools.Property(type, "instance") : null);
				s_localize = ((type != null) ? AccessTools.Method(type, "Localize", new Type[1] { typeof(string) }, (Type[])null) : null);
			}
			try
			{
				object obj = s_instance?.GetValue(null, null);
				if (obj != null && s_localize != null)
				{
					return (string)s_localize.Invoke(obj, new object[1] { token });
				}
			}
			catch
			{
			}
			return token.TrimStart(new char[1] { '$' });
		}
	}
	internal static class MenuFitting
	{
		private static readonly KeyboardShortcut PrevKey = new KeyboardShortcut((KeyCode)44, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut NextKey = new KeyboardShortcut((KeyCode)46, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut TurnLeft = new KeyboardShortcut((KeyCode)113, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut TurnRight = new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut SkipKey = new KeyboardShortcut((KeyCode)127, Array.Empty<KeyCode>());

		private static readonly KeyboardShortcut RefKey = new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>());

		private static bool s_showRef = true;

		private static GameObject s_reference;

		private static string s_referenceFor;

		private const float SpinSpeed = 90f;

		private static bool s_active;

		private static GameObject s_preview;

		private static List<KeyValuePair<string, string>> s_trophies;

		private static int s_index;

		private static Fit s_fit;

		private static readonly FieldRef<FejdStartup, GameObject> PlayerInstance = AccessTools.FieldRefAccess<FejdStartup, GameObject>("m_playerInstance");

		private static bool s_guiResolved;

		private static MethodInfo s_box;

		public static bool Active => s_active;

		private static string Current
		{
			get
			{
				if (!s_active || s_trophies == null || s_trophies.Count <= 0)
				{
					return null;
				}
				return s_trophies[s_index].Key;
			}
		}

		private static GameObject Preview()
		{
			if (!Plugin.FittingRoom.Value)
			{
				return null;
			}
			FejdStartup instance = FejdStartup.instance;
			GameObject val = (((Object)(object)instance != (Object)null) ? PlayerInstance.Invoke(instance) : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if ((Object)(object)instance.m_characterSelectScreen == (Object)null || !instance.m_characterSelectScreen.activeInHierarchy)
			{
				return null;
			}
			return val;
		}

		public static void Drive(VisEquipment vis)
		{
			FejdStartup instance = FejdStartup.instance;
			GameObject val = (((Object)(object)instance != (Object)null) ? PlayerInstance.Invoke(instance) : null);
			if ((Object)(object)val == (Object)null || (Object)(object)((Component)vis).gameObject != (Object)(object)val)
			{
				return;
			}
			TrophyVisual trophyVisual = TrophyVisual.For(vis);
			if (s_active && (Object)(object)s_preview == (Object)(object)((Component)vis).gameObject && Current != null)
			{
				trophyVisual.SyncLocal(Current, s_fit);
				return;
			}
			Player component = ((Component)vis).GetComponent<Player>();
			string value = null;
			if ((Object)(object)component != (Object)null)
			{
				component.m_customData.TryGetValue("YouDied.MasterOfDisguise.Trophy", out value);
			}
			if (string.IsNullOrEmpty(value) || FitStore.IsSkipped(value) || !Disguise.HasTrophy(component, value))
			{
				value = "";
			}
			trophyVisual.SyncLocal(value, (value.Length == 0) ? Fit.Default : FitStore.Get(value));
		}

		public static void Update()
		{
			//IL_0050: 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_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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: 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)
			GameObject val = Preview();
			if ((Object)(object)val == (Object)null)
			{
				if (s_active)
				{
					Exit();
				}
				return;
			}
			if (s_active && (Object)(object)val != (Object)(object)s_preview)
			{
				Exit();
			}
			VisEquipment component = val.GetComponent<VisEquipment>();
			if ((Object)(object)component != (Object)null)
			{
				Drive(component);
			}
			KeyboardShortcut val2 = Plugin.FitModeKey.Value;
			if (((KeyboardShortcut)(ref val2)).IsDown())
			{
				if (s_active)
				{
					Exit();
				}
				else
				{
					Enter(val);
				}
			}
			if (s_active && Current != null)
			{
				val2 = NextKey;
				if (((KeyboardShortcut)(ref val2)).IsDown())
				{
					Step(1);
				}
				val2 = PrevKey;
				if (((KeyboardShortcut)(ref val2)).IsDown())
				{
					Step(-1);
				}
				val2 = TurnLeft;
				if (((KeyboardShortcut)(ref val2)).IsPressed())
				{
					val.transform.Rotate(0f, 90f * Time.unscaledDeltaTime, 0f, (Space)0);
				}
				val2 = TurnRight;
				if (((KeyboardShortcut)(ref val2)).IsPressed())
				{
					val.transform.Rotate(0f, -90f * Time.unscaledDeltaTime, 0f, (Space)0);
				}
				val2 = SkipKey;
				if (((KeyboardShortcut)(ref val2)).IsDown())
				{
					FitStore.ToggleSkip(Current);
				}
				val2 = RefKey;
				if (((KeyboardShortcut)(ref val2)).IsDown())
				{
					s_showRef = !s_showRef;
				}
				UpdateReference(val);
				s_fit = FitMode.Nudge(s_fit);
				if (FitMode.ResetPressed())
				{
					s_fit = FitStore.BuiltIn(Current);
				}
			}
		}

		private static void Enter(GameObject preview)
		{
			BuildList();
			if (s_trophies.Count == 0)
			{
				return;
			}
			s_active = true;
			s_preview = preview;
			Player component = preview.GetComponent<Player>();
			s_index = 0;
			if ((Object)(object)component != (Object)null && component.m_customData.TryGetValue("YouDied.MasterOfDisguise.Trophy", out var saved))
			{
				int num = s_trophies.FindIndex((KeyValuePair<string, string> t) => t.Key == saved);
				if (num >= 0)
				{
					s_index = num;
				}
			}
			s_fit = FitStore.Get(Current);
		}

		private static void Step(int dir)
		{
			SaveCurrent();
			s_index = (s_index + dir + s_trophies.Count) % s_trophies.Count;
			s_fit = FitStore.Get(Current);
		}

		private static void Exit()
		{
			ClearReference();
			SaveCurrent();
			s_active = false;
			s_preview = null;
		}

		private static void UpdateReference(GameObject preview)
		{
			//IL_0070: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (!s_showRef || Current == null)
			{
				ClearReference();
				return;
			}
			if ((Object)(object)s_reference == (Object)null || s_referenceFor != Current)
			{
				ClearReference();
				s_reference = BuildReference(Current);
				s_referenceFor = Current;
			}
			if (!((Object)(object)s_reference == (Object)null))
			{
				Transform transform = preview.transform;
				s_reference.transform.position = transform.position + transform.right * 0.9f + Vector3.up * 1.5f;
				s_reference.transform.rotation = transform.rotation;
			}
		}

		private static GameObject BuildReference(string trophy)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(trophy) : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			try
			{
				return TrophyVisual.MeshCopy(val);
			}
			catch (Exception ex)
			{
				Plugin.Warn("Couldn't build a size reference for " + trophy + ": " + ex.Message);
				return null;
			}
		}

		private static void ClearReference()
		{
			if ((Object)(object)s_reference != (Object)null)
			{
				Object.Destroy((Object)(object)s_reference);
			}
			s_reference = null;
			s_referenceFor = null;
		}

		private static void SaveCurrent()
		{
			string current = Current;
			if (current != null && !(s_fit.Serialize() == FitStore.Get(current).Serialize()))
			{
				if (s_fit.Serialize() == FitStore.BuiltIn(current).Serialize())
				{
					FitStore.Reset(current);
				}
				else
				{
					FitStore.Set(current, s_fit);
				}
			}
		}

		private static void BuildList()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Invalid comparison between Unknown and I4
			s_trophies = new List<KeyValuePair<string, string>>();
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			foreach (GameObject item in instance.m_items)
			{
				if (!((Object)(object)item == (Object)null))
				{
					ItemDrop component = item.GetComponent<ItemDrop>();
					SharedData val = ((!((Object)(object)component != (Object)null)) ? null : component.m_itemData?.m_shared);
					if (val != null && (int)val.m_itemType == 13 && item.GetComponentsInChildren<Renderer>(true).Length != 0)
					{
						s_trophies.Add(new KeyValuePair<string, string>(((Object)item).name, Loc.T(val.m_name)));
					}
				}
			}
			s_trophies.Sort((KeyValuePair<string, string> a, KeyValuePair<string, string> b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase));
		}

		public static void OnGUI()
		{
			//IL_001f: 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_01bb: 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)
			if (!((Object)(object)Preview() == (Object)null))
			{
				string text;
				if (!s_active)
				{
					text = $"Master of Disguise: press {Plugin.FitModeKey.Value} to open the trophy fitting room";
					Draw(new Rect(20f, 20f, 520f, 28f), text);
					return;
				}
				KeyValuePair<string, string> keyValuePair = s_trophies[s_index];
				bool flag = FitStore.Get(keyValuePair.Key).Serialize() != FitStore.BuiltIn(keyValuePair.Key).Serialize() || s_fit.Serialize() != FitStore.BuiltIn(keyValuePair.Key).Serialize();
				text = $"TROPHY FITTING ROOM   {s_index + 1}/{s_trophies.Count}\n" + keyValuePair.Value + "   (" + keyValuePair.Key + ")" + (flag ? "   [custom fit]" : "") + "\n" + (FitStore.IsSkipped(keyValuePair.Key) ? "*** SKIPPED: can't be worn in game (Delete to undo) ***\n\n" : "\n") + ",  /  .       previous / next trophy\nQ  /  E       turn your character\n" + $"[  /  ]       slower / faster adjusting: {FitMode.SpeedPercent}%\n" + "R             size reference beside you: " + (s_showRef ? "on" : "off") + " (true in-game size)\n" + $"B             body part: {s_fit.Bone}  (Head, Back, Hips, arms, legs)\n" + "Arrows        move forward/back/left/right\nShift+Up/Dn   raise / lower\nCtrl+arrows   tilt / turn\nAlt+Lt/Rt     roll\nAlt+Up/Dn     bigger / smaller\nBackspace     reset this trophy\nDelete        skip this trophy (not a head) / undo\n" + $"{Plugin.FitModeKey.Value}            save and close (switching trophies also saves)";
				Draw(new Rect(20f, 20f, 600f, 350f), text);
			}
		}

		private static void Draw(Rect rect, string text)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (!s_guiResolved)
			{
				s_guiResolved = true;
				s_box = Type.GetType("UnityEngine.GUI, UnityEngine.IMGUIModule")?.GetMethod("Box", new Type[2]
				{
					typeof(Rect),
					typeof(string)
				});
			}
			try
			{
				s_box?.Invoke(null, new object[2] { rect, text });
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[]
	{
		typeof(Character),
		typeof(Character)
	})]
	internal static class IsEnemyPatch
	{
		private static void Postfix(Character a, Character b, ref bool __result)
		{
			if (__result && !((Object)(object)a == (Object)null) && !((Object)(object)b == (Object)null) && !Disguise.Probing)
			{
				Player val = (Player)(object)((b is Player) ? b : null);
				if (val != null && !(a is Player) && Disguise.Fools(val, a))
				{
					__result = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class DamagePatch
	{
		private static void Postfix(Character __instance, HitData hit)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && hit != null && !((Object)(object)__instance == (Object)(object)localPlayer) && !((Object)(object)hit.GetAttacker() != (Object)(object)localPlayer))
			{
				Disguise.OnLocalAttack(localPlayer, __instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerUpdatePatch
	{
		private unsafe static void Postfix(Player __instance)
		{
			//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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Disguise.LocalUpdate(__instance);
			if (InputBlocked())
			{
				return;
			}
			KeyboardShortcut value = Plugin.RemoveKey.Value;
			object obj = KeyboardShortcut.Empty;
			if (!((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).Equals(obj))
			{
				value = Plugin.RemoveKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					Disguise.Remove(__instance);
				}
			}
			FitMode.Update(__instance);
		}

		internal static bool InputBlocked()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible())
			{
				if ((Object)(object)Minimap.instance != (Object)null)
				{
					return Minimap.IsOpen();
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class InventoryKeyPatch
	{
		private static readonly MethodInfo HoveredMethod = AccessTools.Method(typeof(InventoryGrid), "GetHoveredElement", (Type[])null, (Type[])null);

		private static InventoryElement GetHovered(InventoryGrid grid)
		{
			object? obj = HoveredMethod?.Invoke(grid, null);
			return (InventoryElement)((obj is InventoryElement) ? obj : null);
		}

		private static void Postfix(InventoryGui __instance)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Invalid comparison between Unknown and I4
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !InventoryGui.IsVisible())
			{
				return;
			}
			KeyboardShortcut value = Plugin.WearKey.Value;
			if (!((KeyboardShortcut)(ref value)).IsDown() || PlayerUpdatePatch.InputBlocked())
			{
				return;
			}
			InventoryGrid playerGrid = __instance.m_playerGrid;
			if ((Object)(object)playerGrid == (Object)null)
			{
				return;
			}
			InventoryElement hovered = GetHovered(playerGrid);
			if (!((Object)(object)hovered == (Object)null))
			{
				Vector2i position = hovered.Position;
				Inventory inventory = playerGrid.GetInventory();
				ItemData val = ((inventory != null) ? inventory.GetItemAt(position.x, position.y) : null);
				if (val?.m_shared != null && (int)val.m_shared.m_itemType == 13)
				{
					Disguise.Wear(localPlayer, val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class InventoryMarkerPatch
	{
		private static readonly FieldInfo ElementsField = AccessTools.Field(typeof(InventoryGrid), "m_elements");

		private static readonly FieldInfo Equipped = AccessTools.Field(typeof(InventoryElement), "m_equiped");

		private static void Postfix(InventoryGrid __instance, Player player)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || Equipped == null || (Object)(object)InventoryGui.instance == (Object)null || (Object)(object)__instance != (Object)(object)InventoryGui.instance.m_playerGrid)
			{
				return;
			}
			string worn = Disguise.GetWorn(player);
			if (worn.Length == 0)
			{
				return;
			}
			Inventory inventory = __instance.GetInventory();
			IList list = ElementsField?.GetValue(__instance) as IList;
			if (inventory == null || list == null)
			{
				return;
			}
			foreach (object item in list)
			{
				InventoryElement val = (InventoryElement)((item is InventoryElement) ? item : null);
				if (val == null)
				{
					continue;
				}
				ItemData itemAt = inventory.GetItemAt(val.Position.x, val.Position.y);
				if ((Object)(object)itemAt?.m_dropPrefab == (Object)null || ((Object)itemAt.m_dropPrefab).name != worn)
				{
					continue;
				}
				object value = Equipped.GetValue(val);
				Behaviour val2 = (Behaviour)((value is Behaviour) ? value : null);
				if (val2 != null)
				{
					val2.enabled = true;
					continue;
				}
				GameObject val3 = (GameObject)((value is GameObject) ? value : null);
				if (val3 != null)
				{
					val3.SetActive(true);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class TooltipPatch
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			if (item?.m_shared == null || (int)item.m_shared.m_itemType != 13 || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return;
			}
			string name = ((Object)item.m_dropPrefab).name;
			TrophyInfo trophyInfo = TrophyDatabase.Get(name);
			StringBuilder stringBuilder = new StringBuilder(__result);
			stringBuilder.Append("\n\n<color=orange>Master of Disguise</color>");
			if (FitStore.IsSkipped(name))
			{
				stringBuilder.Append("\nCan't be worn.");
				__result = stringBuilder.ToString();
				return;
			}
			if (!Plugin.Enabled.Value)
			{
				bool flag = (Object)(object)Player.m_localPlayer != (Object)null && Disguise.GetWorn(Player.m_localPlayer) == name;
				stringBuilder.Append("\nCosmetic (disguise power is off)");
				stringBuilder.Append($"\n<color=yellow>[{Plugin.WearKey.Value}]</color> ").Append(flag ? "Take off" : "Wear");
				__result = stringBuilder.ToString();
				return;
			}
			if (trophyInfo == null || TrophyDatabase.IsExcluded(name) || (trophyInfo.FromBoss && !Plugin.BossFoolsAll.Value))
			{
				stringBuilder.Append("\nCosmetic only. Nobody is fooled by this one.");
			}
			else if (trophyInfo.FromBoss)
			{
				stringBuilder.Append("\nFools: every creature in the ").Append(BossBiomes.DisplayName(name)).Append(" (bosses excepted)");
			}
			else if (Plugin.Scope.Value == DisguiseScope.Creature)
			{
				stringBuilder.Append("\nFools: ").Append(Disguise.JoinNames(trophyInfo));
			}
			else if (Plugin.Scope.Value == DisguiseScope.Allies)
			{
				stringBuilder.Append("\nFools: ").Append(string.Join(", ", Disguise.Fmt(Disguise.AlliesOf(trophyInfo.Factions))));
			}
			else
			{
				stringBuilder.Append("\nFools: ").Append(string.Join(", ", Disguise.Fmt(trophyInfo.Factions)));
			}
			Player localPlayer = Player.m_localPlayer;
			bool flag2 = (Object)(object)localPlayer != (Object)null && Disguise.GetWorn(localPlayer) == name;
			float num = Cost.LifetimeSeconds(name);
			if (num > 0f)
			{
				float num2 = (((Object)(object)localPlayer != (Object)null) ? Cost.UsedSeconds(localPlayer, name) : 0f);
				stringBuilder.Append("\nDisguise time: ").Append(Cost.FormatTime(num - num2));
				if (num2 > 0f)
				{
					stringBuilder.Append(" left");
				}
				stringBuilder.Append(" (crumbles when it runs out)");
			}
			stringBuilder.Append($"\n<color=yellow>[{Plugin.WearKey.Value}]</color> ").Append(flag2 ? "Take off" : "Wear as disguise");
			__result = stringBuilder.ToString();
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "UpdateEquipmentVisuals")]
	internal static class VisualPatch
	{
		private static readonly FieldRef<VisEquipment, ZNetView> VisNView = AccessTools.FieldRefAccess<VisEquipment, ZNetView>("m_nview");

		private static void Postfix(VisEquipment __instance)
		{
			if (TrophyVisual.IsPlayer.Invoke(__instance))
			{
				ZNetView val = VisNView.Invoke(__instance);
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					MenuFitting.Drive(__instance);
				}
				else
				{
					TrophyVisual.For(__instance).Sync(val.GetZDO());
				}
			}
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "UpdateKeyboard")]
	internal static class MenuKeyboardPatch
	{
		private static bool Prefix()
		{
			return !MenuFitting.Active;
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnCharacterLeft")]
	internal static class MenuCharacterLeftPatch
	{
		private static bool Prefix()
		{
			return !MenuFitting.Active;
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnCharacterRight")]
	internal static class MenuCharacterRightPatch
	{
		private static bool Prefix()
		{
			return !MenuFitting.Active;
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDBAwakePatch
	{
		private static void Postfix(ObjectDB __instance)
		{
			Cost.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class SceneAwakePatch
	{
		private static void Postfix()
		{
			TrophyDatabase.Invalidate();
		}
	}
	public enum DisguiseScope
	{
		Allies,
		Faction,
		Creature
	}
	[BepInPlugin("YouDied.MasterOfDisguise", "Master of Disguise", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "YouDied.MasterOfDisguise";

		public const string Name = "Master of Disguise";

		public const string Version = "1.0.0";

		internal static Plugin Instance;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<DisguiseScope> Scope;

		internal static ConfigEntry<bool> AnimalDisguises;

		internal static ConfigEntry<string> ExcludedTrophies;

		internal static ConfigEntry<bool> BossFoolsAll;

		internal static ConfigEntry<string> BossBiomeMap;

		internal static ConfigEntry<string> UnlistedBossBiome;

		internal static ConfigEntry<bool> BreakOnAttack;

		internal static ConfigEntry<int> BreakSeconds;

		internal static ConfigEntry<bool> ShowMessages;

		internal static ConfigEntry<bool> ShowOnHead;

		internal static ConfigEntry<bool> HideHelmet;

		internal static ConfigEntry<int> TrophySize;

		internal static ConfigEntry<int> MaxTrophySize;

		internal static ConfigEntry<int> Facing;

		internal static ConfigEntry<int> Tilt;

		internal static ConfigEntry<int> ForwardOffset;

		internal static ConfigEntry<int> AutoFitHeight;

		internal static ConfigEntry<int> Lifetime;

		internal static ConfigEntry<bool> NeverBreaks;

		internal static ConfigEntry<int> StaminaRegen;

		internal static ConfigEntry<int> MoveSpeed;

		internal static ConfigEntry<bool> ShowStatusIcon;

		internal static ConfigEntry<KeyboardShortcut> WearKey;

		internal static ConfigEntry<KeyboardShortcut> RemoveKey;

		internal static ConfigEntry<KeyboardShortcut> FitModeKey;

		internal static ConfigEntry<bool> FittingRoom;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Expected O, but got Unknown
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Expected O, but got Unknown
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Expected O, but got Unknown
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Expected O, but got Unknown
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Expected O, but got Unknown
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_034b: Expected O, but got Unknown
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Expected O, but got Unknown
			//IL_0443: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Expected O, but got Unknown
			//IL_0477: Unknown result type (might be due to invalid IL or missing references)
			//IL_0481: Expected O, but got Unknown
			//IL_05f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fa: Expected O, but got Unknown
			Instance = this;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Disguise power", true, "On: wearing a trophy fools creatures. Off: trophies are cosmetic only (you can still wear and fit them, nobody is fooled, and they never wear out).");
			Scope = ((BaseUnityPlugin)this).Config.Bind<DisguiseScope>("1 - General", "Disguise mode", DisguiseScope.Allies, "Allies: uses the game's own faction rules. A trophy fools its faction and every creature that wouldn't attack that creature (a Charred trophy fools whatever doesn't fight the Charred; a Skeleton trophy still won't fool Greydwarfs, because they fight skeletons).\nFaction: a trophy only fools its own faction (a Greydwarf trophy fools Greydwarfs, Brutes, Shamans and Trolls).\nCreature: a trophy only fools the creature that drops it (a Troll trophy only fools Trolls).");
			AnimalDisguises = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Animal disguises", true, "Deer, boar, wolves-as-animals and other AnimalsVeg creatures stop fleeing from you while you wear their trophy.");
			ExcludedTrophies = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "Excluded trophies", "", "Comma-separated trophy prefab names that are cosmetic only (example: TrophyTroll,TrophySerpent).");
			BossFoolsAll = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Boss trophies fool their biome", true, "Wearing a boss trophy makes every creature in that boss's biome friendly, whatever its faction. It does nothing in other biomes. Bosses themselves are never fooled.");
			BossBiomeMap = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "Boss trophy biomes", "TrophyEikthyr:Meadows, TrophyTheElder:BlackForest, TrophyBonemass:Swamp, TrophyDragonQueen:Mountain, TrophyGoblinKing:Plains, TrophySeekerQueen:Mistlands, TrophyFader:AshLands", "Which biome each boss trophy rules, as Trophy:Biome pairs. Add the Deep North boss or modded bosses here (the log lists every boss trophy found). Biomes: Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth, Ocean.");
			UnlistedBossBiome = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "Modded boss trophies", "None", new ConfigDescription("Biome for boss trophies missing from the list above (all 7 vanilla boss trophies are listed; Kall Fimbulbringer drops none). Only matters for modded bosses. None = their trophies are cosmetic only. Better: add them to Boss trophy biomes.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[10] { "DeepNorth", "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "AshLands", "Ocean", "None" }), Array.Empty<object>()));
			BreakOnAttack = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Breaking the disguise", "Break on attack", true, "Hitting a creature your disguise is fooling exposes you, just like the masks in Breath of the Wild.");
			BreakSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Breaking the disguise", "Exposed seconds", 60, new ConfigDescription("How long the disguise stays broken after you attack.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 600), Array.Empty<object>()));
			ShowMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Breaking the disguise", "Show messages", true, "Show on-screen messages when your disguise breaks or holds again.");
			ShowOnHead = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Visuals", "Show trophy on head", true, "Draw the worn trophy on your head. Turn off to keep the disguise effect invisible.");
			HideHelmet = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Visuals", "Hide helmet while disguised", true, "Hide your real helmet so it doesn't poke through the trophy. Armor stats are unaffected.");
			TrophySize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Visuals", "Trophy size (%)", 100, new ConfigDescription("Size of worn trophies. 100 = exactly the size the trophy is on the ground or on an item stand.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(25, 300), Array.Empty<object>()));
			MaxTrophySize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Visuals", "Size cap (cm)", 0, new ConfigDescription("0 = trophies are worn at their actual size. Set a number to shrink huge trophies (bosses, Trolls, Lox) so their largest side is no bigger than this.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 500), Array.Empty<object>()));
			Facing = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Visuals", "Facing (degrees)", 0, new ConfigDescription("Turns every worn trophy around your vertical axis. Only needed if trophies don't face forward.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-180, 180), Array.Empty<object>()));
			Tilt = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Visuals", "Tilt forward (degrees)", 30, new ConfigDescription("Tips every worn trophy forward. Wall trophies are mounted nose-up, so this levels them out.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-90, 90), Array.Empty<object>()));
			ForwardOffset = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Visuals", "Forward offset (cm)", -20, new ConfigDescription("Moves every worn trophy forward (+) or back (-) so the skull sits on your head instead of in front of it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-60, 60), Array.Empty<object>()));
			AutoFitHeight = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Visuals", "Height (cm)", 28, new ConfigDescription("How far above the head joint the trophy is centered, before your own fit adjustments.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-30, 60), Array.Empty<object>()));
			WearKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("4 - Keys", "Wear key", new KeyboardShortcut((KeyCode)121, Array.Empty<KeyCode>()), "Press while hovering a trophy in your inventory to wear it. Press on the worn trophy to take it off.");
			RemoveKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("4 - Keys", "Remove disguise key", KeyboardShortcut.Empty, "Optional key to take off your disguise from anywhere.");
			FitModeKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("4 - Keys", "Fit mode key", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Toggle fit mode for the worn trophy. Arrows move it, Shift+Up/Down raise/lower, Ctrl+arrows rotate, Alt+Left/Right roll, Alt+Up/Down resize, Backspace resets. Press again to save.");
			Lifetime = ((BaseUnityPlugin)this).Config.Bind<int>("5 - The cost", "Disguise time (minutes)", 3, new ConfigDescription("How long a trophy can be worn before it crumbles and is used up. Applies to every trophy, bosses included. Time only passes while it's worn and is saved per trophy.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>()));
			NeverBreaks = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - The cost", "Trophies never break", false, "Turn on to wear trophies forever. They never crumble.");
			StaminaRegen = ((BaseUnityPlugin)this).Config.Bind<int>("5 - The cost", "Stamina regen while disguised (%)", 100, new ConfigDescription("Hunching around in a trophy is tiring. 100 = no penalty.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>()));
			MoveSpeed = ((BaseUnityPlugin)this).Config.Bind<int>("5 - The cost", "Move speed while disguised (%)", 100, new ConfigDescription("Slows you down while disguised. 100 = no penalty.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 100), Array.Empty<object>()));
			ShowStatusIcon = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - The cost", "Show Disguised status icon", true, "Show a Disguised status effect with the trophy's icon and a countdown on the HUD.");
			FittingRoom = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Keys", "Main menu fitting room", false, "On the character select screen, press the fit key to try on and fit every trophy in the game on your preview character.");
			ShowOnHead.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			TrophySize.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			MaxTrophySize.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			Facing.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			Tilt.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			ForwardOffset.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			AutoFitHeight.SettingChanged += delegate
			{
				TrophyVisual.RefreshAll();
			};
			FitStore.Load();
			_harmony = new Harmony("YouDied.MasterOfDisguise");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Master of Disguise 1.0.0 loaded.");
		}

		private void Update()
		{
			MenuFitting.Update();
		}

		private void OnGUI()
		{
			MenuFitting.OnGUI();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static void Log(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)msg);
			}
		}

		internal static void Warn(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)msg);
			}
		}
	}
	internal class TrophyInfo
	{
		public string TrophyName;

		public readonly HashSet<Faction> Factions = new HashSet<Faction>();

		public readonly HashSet<string> Creatures = new HashSet<string>();

		public readonly List<string> CreatureDisplayNames = new List<string>();

		public bool FromBoss;
	}
	internal static class TrophyDatabase
	{
		private static readonly Dictionary<string, TrophyInfo> s_byTrophy = new Dictionary<string, TrophyInfo>();

		private static ZNetScene s_builtFor;

		private static HashSet<string> s_excluded = new HashSet<string>();

		private static string s_excludedRaw;

		public static void Invalidate()
		{
			s_builtFor = null;
		}

		public static TrophyInfo Get(string trophyName)
		{
			if (string.IsNullOrEmpty(trophyName))
			{
				return null;
			}
			EnsureBuilt();
			if (!s_byTrophy.TryGetValue(trophyName, out var value))
			{
				return null;
			}
			return value;
		}

		public static bool IsExcluded(string trophyName)
		{
			string text = Plugin.ExcludedTrophies.Value ?? "";
			if (text != s_excludedRaw)
			{
				s_excludedRaw = text;
				s_excluded = new HashSet<string>(from s in text.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
					select s.Trim(), StringComparer.OrdinalIgnoreCase);
			}
			return s_excluded.Contains(trophyName);
		}

		public static bool IsDisguisableFaction(Faction faction)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			if ((int)faction <= 1)
			{
				if ((int)faction == 0)
				{
					goto IL_0018;
				}
				if ((int)faction == 1)
				{
					return Plugin.AnimalDisguises.Value;
				}
			}
			else if ((int)faction == 8 || faction - 11 <= 1)
			{
				goto IL_0018;
			}
			return true;
			IL_0018:
			return false;
		}

		private static void EnsureBuilt()
		{
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Invalid comparison between Unknown and I4
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Invalid comparison between Unknown and I4
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance == (Object)(object)s_builtFor)
			{
				return;
			}
			s_builtFor = instance;
			s_byTrophy.Clear();
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Character component = prefab.GetComponent<Character>();
				if ((Object)(object)component == (Object)null || component is Player)
				{
					continue;
				}
				CharacterDrop component2 = prefab.GetComponent<CharacterDrop>();
				if ((Object)(object)component2 == (Object)null || component2.m_drops == null)
				{
					continue;
				}
				foreach (Drop drop in component2.m_drops)
				{
					if ((Object)(object)drop?.m_prefab == (Object)null)
					{
						continue;
					}
					ItemDrop component3 = drop.m_prefab.GetComponent<ItemDrop>();
					if (!((Object)(object)component3 == (Object)null) && component3.m_itemData?.m_shared != null && (int)component3.m_itemData.m_shared.m_itemType == 13)
					{
						string name = ((Object)drop.m_prefab).name;
						if (!s_byTrophy.TryGetValue(name, out var value))
						{
							value = new TrophyInfo
							{
								TrophyName = name
							};
							s_byTrophy[name] = value;
						}
						if ((int)component.m_faction == 8)
						{
							value.FromBoss = true;
						}
						value.Factions.Add(component.m_faction);
						if (value.Creatures.Add(((Object)prefab).name) && !string.IsNullOrEmpty(component.m_name))
						{
							value.CreatureDisplayNames.Add(component.m_name);
						}
					}
				}
			}
			Plugin.Log($"Mapped {s_byTrophy.Count} trophies to creatures.");
			LogCoverage();
			foreach (TrophyInfo value2 in s_byTrophy.Values)
			{
				if (!value2.FromBoss)
				{
					continue;
				}
				if (BossBiomes.TryGet(value2.TrophyName, out var biome))
				{
					if (!BossBiomes.IsListed(value2.TrophyName))
					{
						Plugin.Log($"Modded boss trophy {value2.TrophyName} isn't listed; treating it as {biome} (Modded boss trophies setting).");
					}
					continue;
				}
				Plugin.Warn("Modded boss trophy " + value2.TrophyName + " has no biome, so it's cosmetic only. Add \"" + value2.TrophyName + ":Biome\" to 'Boss trophy biomes' to make it work.");
			}
		}

		private static void LogCoverage()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Expected I4, but got Unknown
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Faction, List<string>> dictionary = new Dictionary<Faction, List<string>>();
			Dictionary<Faction, HashSet<string>> dictionary2 = new Dictionary<Faction, HashSet<string>>();
			foreach (TrophyInfo value3 in s_byTrophy.Values)
			{
				if (value3.FromBoss)
				{
					continue;
				}
				foreach (Faction faction in value3.Factions)
				{
					if (!dictionary.TryGetValue(faction, out var value))
					{
						value = (dictionary[faction] = new List<string>());
					}
					value.Add(value3.TrophyName);
					if (!dictionary2.TryGetValue(faction, out var value2))
					{
						value2 = (dictionary2[faction] = new HashSet<string>());
					}
					foreach (string creature in value3.Creatures)
					{
						value2.Add(creature);
					}
				}
			}
			foreach (KeyValuePair<Faction, List<string>> kv in dictionary)
			{
				if (IsDisguisableFaction(kv.Key))
				{
					List<string> list2 = kv.Value.Where((string t) => !FitStore.IsSkipped(t) && !IsExcluded(t)).ToList();
					List<string> list3 = kv.Value.Where((string t) => FitStore.IsSkipped(t)).ToList();
					string text = string.Join(", ", from f in Disguise.AlliesOf((IEnumerable<Faction>)(object)new Faction[1] { (Faction)(int)kv.Key })
						where f != kv.Key
						select f);
					string text2 = string.Format("Faction {0}{1}: worn as {2}", kv.Key, (text.Length > 0) ? (" (also fools " + text + ")") : "", (list2.Count > 0) ? string.Join(", ", list2) : "NOTHING") + ((list3.Count > 0) ? (" | skipped: " + string.Join(", ", list3)) : "") + " | creatures: " + string.Join(", ", dictionary2[kv.Key]);
					if (list2.Count == 0)
					{
						Plugin.Warn(text2 + "  <- no wearable trophy fools this faction");
					}
				}
			}
		}

		public unsafe static string FactionDisplayName(Faction faction)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected I4, but got Unknown
			return (faction - 1) switch
			{
				0 => "animals", 
				1 => "forest creatures", 
				2 => "the undead", 
				3 => "demons", 
				4 => "mountain creatures", 
				5 => "sea creatures", 
				6 => "plains creatures", 
				8 => "Mistlands creatures", 
				9 => "the Dvergr", 
				12 => "Deep North creatures", 
				_ => ((object)(*(Faction*)(&faction))/*cast due to .constrained prefix*/).ToString(), 
			};
		}
	}
	internal class TrophyVisual : MonoBehaviour
	{
		internal static readonly FieldRef<VisEquipment, Transform> HelmetJoint = AccessTools.FieldRefAccess<VisEquipment, Transform>("m_helmet");

		internal static readonly FieldRef<VisEquipment, GameObject> HelmetInstance = AccessTools.FieldRefAccess<VisEquipment, GameObject>("m_helmetItemInstance");

		internal static readonly FieldRef<VisEquipment, bool> IsPlayer = AccessTools.FieldRefAccess<VisEquipment, bool>("m_isPlayer");

		private static readonly MethodInfo CleanupInstance = AccessTools.Method(typeof(VisEquipment), "CleanupInstance", (Type[])null, (Type[])null);

		private VisEquipment _vis;

		private Transform _pivot;

		private GameObject _model;

		private string _trophy = "";

		private string _fitRaw;

		private Fit _parsedFit = Fit.Default;

		private AttachPoint _bone;

		private float _autoScale = 1f;

		private float _groundRatio = 1f;

		private float _jointScale = 1f;

		private Quaternion _charToJoint = Quaternion.identity;

		private GameObject _hiddenHelmet;

		internal Fit? LiveFit;

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

		public static TrophyVisual For(VisEquipment vis)
		{
			TrophyVisual trophyVisual = ((Component)vis).GetComponent<TrophyVisual>();
			if ((Object)(object)trophyVisual == (Object)null)
			{
				trophyVisual = ((Component)vis).gameObject.AddComponent<TrophyVisual>();
				trophyVisual._vis = vis;
			}
			return trophyVisual;
		}

		public void Sync(ZDO zdo)
		{
			string trophy = (Plugin.ShowOnHead.Value ? zdo.GetString(Disguise.ZTrophy, "") : "");
			Fit fit;
			if (LiveFit.HasValue)
			{
				fit = LiveFit.Value;
			}
			else
			{
				string text = zdo.GetString(Disguise.ZFit, "");
				if (text != _fitRaw)
				{
					_fitRaw = text;
					_parsedFit = Fit.Parse(text);
				}
				fit = _parsedFit;
			}
			Show(trophy, fit);
		}

		public void ForceRefit()
		{
			_fitRaw = null;
		}

		public void SyncLocal(string trophy, Fit fit)
		{
			Show(Plugin.ShowOnHead.Value ? (trophy ?? "") : "", fit);
		}

		private void Show(string trophy, Fit fit)
		{
			if (trophy != _trophy || fit.Bone != _bone)
			{
				_trophy = trophy;
				_bone = fit.Bone;
				Rebuild();
			}
			ApplyHelmetHiding();
			if ((Object)(object)_model != (Object)null)
			{
				Apply(fit);
			}
		}

		private void Rebuild()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			Clear();
			if (_trophy.Length == 0)
			{
				return;
			}
			Transform val = FindJoint(_bone);
			GameObject val2 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(_trophy) : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return;
			}
			_pivot = new GameObject("MoD_TrophyPivot").transform;
			_pivot.SetParent(val, false);
			_charToJoint = RestCharToJoint(val);
			_jointScale = Mathf.Max(val.lossyScale.x, 0.0001f);
			Transform val3 = val2.transform.Find("attach");
			if ((Object)(object)val3 != (Object)null)
			{
				_model = Object.Instantiate<GameObject>(((Component)val3).gameObject, _pivot, false);
				_model.transform.localScale = val3.lossyScale;
				_groundRatio = GroundRatio(val2, val3);
			}
			else
			{
				_model = MeshCopy(val2);
				if ((Object)(object)_model == (Object)null)
				{
					Plugin.Warn(_trophy + " has nothing to draw; it can still disguise you.");
					Object.Destroy((Object)(object)((Component)_pivot).gameObject);
					_pivot = null;
					return;
				}
				_model.transform.SetParent(_pivot, false);
				_groundRatio = 1f;
			}
			((Object)_model).name = "MoD_Trophy";
			_model.transform.localPosition = Vector3.zero;
			_model.transform.localRotation = Quaternion.identity;
			_model.layer = ((Component)val).gameObject.layer;
			Transform[] componentsInChildren = _model.GetComponentsInChildren<Transform>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Component)componentsInChildren[i]).gameObject.layer = ((Component)val).gameObject.layer;
			}
			try
			{
				CleanupInstance?.Invoke(CleanupInstance.IsStatic ? null : _vis, new object[1] { _model });
			}
			catch (Exception ex)
			{
				Plugin.Warn("Trophy cleanup failed: " + ex.Message);
			}
			AutoFit();
			_fitRaw = null;
		}

		private void AutoFit()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			_pivot.localPosition = Vector3.zero;
			_pivot.localRotation = BaseRotation(Vector3.zero);
			_pivot.localScale = Vector3.one;
			if (!WorldMeshBounds(_model.transform, out var bounds))
			{
				_autoScale = 1f;
				return;
			}
			float num = Mathf.Max(_pivot.lossyScale.x, 0.0001f);
			float num2 = Mathf.Max(((Bounds)(ref bounds)).size.x, Mathf.Max(((Bounds)(ref bounds)).size.y, ((Bounds)(ref bounds)).size.z)) / num;
			float num3 = ((Plugin.MaxTrophySize.Value > 0 && num2 > (float)Plugin.MaxTrophySize.Value / 100f) ? ((float)Plugin.MaxTrophySize.Value / 100f / num2) : 1f);
			float num4 = num2 * _groundRatio;
			num3 = ((Plugin.MaxTrophySize.Value > 0 && num4 > (float)Plugin.MaxTrophySize.Value / 100f) ? ((float)Plugin.MaxTrophySize.Value / 100f / num4) : 1f);
			_autoScale = 1f / num * _groundRatio * num3 * ((float)Plugin.TrophySize.Value / 100f);
			_model.transform.localPosition = -_pivot.InverseTransformPoint(((Bounds)(ref bounds)).center);
		}

		private static bool WorldMeshBounds(Transform root, out Bounds bounds)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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)
			bool any = false;
			bounds = default(Bounds);
			MeshFilter[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val in componentsInChildren)
			{
				if ((Object)(object)val.sharedMesh != (Object)null)
				{
					Add(ref bounds, ref any, ((Component)val).transform.localToWorldMatrix, val.sharedMesh.bounds);
				}
			}
			SkinnedMeshRenderer[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val2 in componentsInChildren2)
			{
				if ((Object)(object)val2.sharedMesh != (Object)null)
				{
					Add(ref bounds, ref any, ((Component)val2).transform.localToWorldMatrix, val2.sharedMesh.bounds);
				}
			}
			return any;
		}

		internal static GameObject MeshCopy(GameObject prefab)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0019: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = prefab.transform;
			GameObject val = new GameObject("MoD_MeshCopy");
			val.transform.localScale = transform.lossyScale;
			Matrix4x4 worldToLocalMatrix = transform.worldToLocalMatrix;
			int num = 0;
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val2 in componentsInChildren)
			{
				MeshRenderer component = ((Component)val2).GetComponent<MeshRenderer>();
				if (!((Object)(object)val2.sharedMesh == (Object)null) && !((Object)(object)component == (Object)null) && ((Renderer)component).enabled && ActiveUpTo(((Component)val2).transform, transform))
				{
					AddPart(val.transform, worldToLocalMatrix * ((Component)val2).transform.localToWorldMatrix, val2.sharedMesh, ((Renderer)component).sharedMaterials);
					num++;
				}
			}
			SkinnedMeshRenderer[] componentsInChildren2 = prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val3 in componentsInChildr