Decompiled source of GooCosmetics v0.0.2

BepInEx/plugins/GooCosmetics/GooCosmetics.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("GooCosmetics")]
[assembly: AssemblyProduct("GooCosmetics")]
[assembly: AssemblyTitle("GooCosmetics")]
[assembly: AssemblyFileVersion("0.0.2.0")]
[assembly: AssemblyInformationalVersion("0.0.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.2.0")]
namespace GooCosmetics;

internal enum ItemParticleMode
{
	Donor,
	Original,
	None,
	Selected
}
internal sealed class Appearance
{
	internal const string ItemKey = "gnls.GooCosmetics.illusion.v1";

	internal const int MaxLength = 2100;

	internal readonly string Original;

	internal readonly string Donor;

	internal readonly int Variant;

	internal readonly int Quality;

	internal readonly string ParticleDonor;

	internal readonly int ParticleVariant;

	internal readonly int ParticleQuality;

	internal readonly bool PreserveParticles;

	internal readonly bool NoParticles;

	internal ItemParticleMode ParticleMode
	{
		get
		{
			if (!NoParticles)
			{
				if (!PreserveParticles)
				{
					if (ParticleDonor == null)
					{
						return ItemParticleMode.Donor;
					}
					return ItemParticleMode.Selected;
				}
				return ItemParticleMode.Original;
			}
			return ItemParticleMode.None;
		}
	}

	internal CollectedAppearance ParticleLook
	{
		get
		{
			if (ParticleDonor != null)
			{
				return new CollectedAppearance(ParticleDonor, ParticleVariant, ParticleQuality);
			}
			return null;
		}
	}

	internal Appearance(string original, string donor, int variant, int quality, bool preserveParticles = false, bool noParticles = false, string particleDonor = null, int particleVariant = 0, int particleQuality = 1)
	{
		Original = original;
		Donor = donor;
		Variant = variant;
		Quality = quality;
		PreserveParticles = preserveParticles;
		NoParticles = noParticles;
		ParticleDonor = particleDonor;
		ParticleVariant = particleVariant;
		ParticleQuality = particleQuality;
	}

	private static bool ValidName(string name)
	{
		if (string.IsNullOrWhiteSpace(name) || name.Length > 160)
		{
			return false;
		}
		for (int i = 0; i < name.Length; i++)
		{
			if (char.IsControl(name[i]))
			{
				return false;
			}
		}
		return true;
	}

	private static string Encode(string value)
	{
		return Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
	}

	internal string Encode()
	{
		string[] obj = new string[12]
		{
			(ParticleDonor == null) ? "2|" : "3|",
			Encode(Original),
			"|",
			Encode(Donor),
			"|",
			null,
			null,
			null,
			null,
			null,
			null,
			null
		};
		int variant = Variant;
		obj[5] = variant.ToString(CultureInfo.InvariantCulture);
		obj[6] = "|";
		variant = Quality;
		obj[7] = variant.ToString(CultureInfo.InvariantCulture);
		obj[8] = "|";
		obj[9] = (PreserveParticles ? "1" : "0");
		obj[10] = "|";
		obj[11] = (NoParticles ? "1" : "0");
		string text = string.Concat(obj);
		if (ParticleDonor != null)
		{
			string[] obj2 = new string[7]
			{
				text,
				"|",
				Encode(ParticleDonor),
				"|",
				null,
				null,
				null
			};
			variant = ParticleVariant;
			obj2[4] = variant.ToString(CultureInfo.InvariantCulture);
			obj2[5] = "|";
			variant = ParticleQuality;
			obj2[6] = variant.ToString(CultureInfo.InvariantCulture);
			return string.Concat(obj2);
		}
		return text;
	}

	internal static bool TryParse(string text, out Appearance result)
	{
		result = null;
		if (string.IsNullOrEmpty(text) || text.Length > 2100)
		{
			return false;
		}
		string[] array = text.Split(new char[1] { '|' });
		bool num = array.Length == 6 && array[0] == "1";
		bool flag = array.Length == 7 && array[0] == "2";
		bool flag2 = array.Length == 10 && array[0] == "3";
		if ((!num && !flag && !flag2) || (array[5] != "0" && array[5] != "1") || ((flag || flag2) && array[6] != "0" && array[6] != "1") || (flag2 && (array[5] != "0" || array[6] != "0")))
		{
			return false;
		}
		if (!int.TryParse(array[3], NumberStyles.None, CultureInfo.InvariantCulture, out var result2) || result2 > 65535 || !int.TryParse(array[4], NumberStyles.None, CultureInfo.InvariantCulture, out var result3) || result3 < 1 || result3 > 65535)
		{
			return false;
		}
		try
		{
			UTF8Encoding uTF8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
			string text2 = uTF8Encoding.GetString(Convert.FromBase64String(array[1]));
			string text3 = uTF8Encoding.GetString(Convert.FromBase64String(array[2]));
			if (!ValidName(text2) || !ValidName(text3))
			{
				return false;
			}
			string text4 = null;
			int result4 = 0;
			int result5 = 1;
			if (flag2)
			{
				text4 = uTF8Encoding.GetString(Convert.FromBase64String(array[7]));
				if (!ValidName(text4) || !int.TryParse(array[8], NumberStyles.None, CultureInfo.InvariantCulture, out result4) || result4 > 65535 || !int.TryParse(array[9], NumberStyles.None, CultureInfo.InvariantCulture, out result5) || result5 < 1 || result5 > 65535)
				{
					return false;
				}
			}
			result = new Appearance(text2, text3, result2, result3, array[5] == "1", flag && array[6] == "1", text4, result4, result5);
			return true;
		}
		catch (FormatException)
		{
			return false;
		}
		catch (DecoderFallbackException)
		{
			return false;
		}
	}
}
internal static class AppearanceCollection
{
	private sealed class State
	{
		internal AppearanceJournal Journal;

		internal bool Writable;

		internal bool Dirty;

		internal int Revision;

		internal int ParticleJournalRevision = -1;

		internal int ParticleVisualRevision = -1;

		internal bool HasParticles;
	}

	internal sealed class LoadState
	{
		internal string Previous;
	}

	private static readonly ConditionalWeakTable<Player, State> States = new ConditionalWeakTable<Player, State>();

	private static readonly HashSet<Player> Loading = new HashSet<Player>();

	internal static LoadState BeginLoad(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player))
		{
			return null;
		}
		player.m_customData.TryGetValue("gnls.GooCosmetics.collection.v1", out var value);
		LoadState result = new LoadState
		{
			Previous = value
		};
		player.m_customData.Remove("gnls.GooCosmetics.collection.v1");
		States.Remove(player);
		Loading.Add(player);
		return result;
	}

	internal static void EndLoad(Player player, LoadState snapshot, bool succeeded)
	{
		if (Object.op_Implicit((Object)(object)player) && snapshot != null)
		{
			Loading.Remove(player);
			States.Remove(player);
			if (succeeded)
			{
				Loaded(player);
			}
			else if (snapshot.Previous == null)
			{
				player.m_customData.Remove("gnls.GooCosmetics.collection.v1");
			}
			else
			{
				player.m_customData["gnls.GooCosmetics.collection.v1"] = snapshot.Previous;
			}
		}
	}

	private static State Get(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player))
		{
			return null;
		}
		if (States.TryGetValue(player, out var value))
		{
			return value;
		}
		player.m_customData.TryGetValue("gnls.GooCosmetics.collection.v1", out var value2);
		AppearanceJournal journal;
		bool flag = AppearanceJournal.TryDecode(value2, out journal);
		value = new State
		{
			Journal = journal,
			Writable = flag
		};
		States.Add(player, value);
		if (!flag)
		{
			Plugin.WarnOnce("Saved appearances", new FormatException("The character's appearance collection could not be read. Its saved value has been preserved."));
		}
		return value;
	}

	internal static int Revision(Player player)
	{
		return Get(player)?.Revision ?? 0;
	}

	internal static bool Readable(Player player)
	{
		return Get(player)?.Writable ?? false;
	}

	internal static bool HasParticles(Player player)
	{
		State state = Get(player);
		if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return false;
		}
		if (state.ParticleJournalRevision != state.Revision || state.ParticleVisualRevision != Plugin.VisualRevision)
		{
			state.HasParticles = false;
			foreach (CollectedAppearance entry in state.Journal.Entries)
			{
				if (ParticleSources.Available(entry))
				{
					state.HasParticles = true;
					break;
				}
			}
			state.ParticleJournalRevision = state.Revision;
			state.ParticleVisualRevision = Plugin.VisualRevision;
		}
		return state.HasParticles;
	}

	internal static void Loaded(Player player)
	{
		if (Object.op_Implicit((Object)(object)player))
		{
			States.Remove(player);
			ObserveInventory(player);
			Flush(player);
		}
	}

	internal static void ObserveInventory(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player) || ((Humanoid)player).GetInventory() == null)
		{
			return;
		}
		foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
		{
			Observe(player, allItem);
		}
	}

	internal static void Observe(Player player, ItemData item)
	{
		if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player) || item == null || item.m_stack != 1 || !((Humanoid)player).GetInventory().ContainsItem(item) || (!Classification.Eligible(item) && ParticleSources.Plan(item.m_dropPrefab) == null))
		{
			return;
		}
		State state = Get(player);
		if (state.Writable)
		{
			Add(state, NativeLook(item));
			if (Illusions.TryResolve(Illusions.Raw(item), StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), out var appearance, out var _))
			{
				Add(state, Normalize(new CollectedAppearance(appearance.Donor, appearance.Variant, appearance.Quality)));
			}
		}
	}

	private static void Add(State state, CollectedAppearance look)
	{
		if (state.Journal.Add(look))
		{
			state.Dirty = true;
			state.Revision++;
		}
	}

	internal static void Flush(Player player)
	{
		if (Object.op_Implicit((Object)(object)player) && !Loading.Contains(player))
		{
			State state = Get(player);
			if (state != null && state.Writable && state.Dirty)
			{
				player.m_customData["gnls.GooCosmetics.collection.v1"] = state.Journal.Encode();
				state.Dirty = false;
			}
		}
	}

	internal static void Saving(Player player)
	{
		ObserveInventory(player);
		Flush(player);
	}

	internal static CollectedAppearance Normalize(CollectedAppearance look)
	{
		ItemData val = ((look == null) ? null : Native.PrefabItem(look.Prefab));
		GameObject prefab = ((look != null && Object.op_Implicit((Object)(object)ObjectDB.instance)) ? ObjectDB.instance.GetItemPrefab(look.Prefab) : null);
		if (val?.m_shared == null || val.m_shared.m_scaleByQuality != 0f || ParticleSources.QualityChanges(prefab))
		{
			return look;
		}
		return new CollectedAppearance(look.Prefab, look.Variant, 1);
	}

	internal static CollectedAppearance NativeLook(ItemData item)
	{
		if ((Object)(object)item?.m_dropPrefab == (Object)null)
		{
			return null;
		}
		return Normalize(new CollectedAppearance(((Object)item.m_dropPrefab).name, item.m_variant, item.m_quality));
	}

	internal static CollectedAppearance VisibleLook(ItemData item)
	{
		if ((Object)(object)item?.m_dropPrefab == (Object)null)
		{
			return null;
		}
		if (!Illusions.TryResolve(Illusions.Raw(item), StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), out var appearance, out var _))
		{
			return NativeLook(item);
		}
		return Normalize(new CollectedAppearance(appearance.Donor, appearance.Variant, appearance.Quality));
	}

	internal static bool Contains(Player player, CollectedAppearance look)
	{
		State state = Get(player);
		if (look == null || !look.Valid || state == null || !state.Writable)
		{
			return false;
		}
		CollectedAppearance collectedAppearance = Normalize(look);
		if (state.Journal.Contains(collectedAppearance))
		{
			return true;
		}
		foreach (CollectedAppearance entry in state.Journal.Entries)
		{
			if (collectedAppearance.Equals(Normalize(entry)))
			{
				return true;
			}
		}
		return false;
	}

	internal static List<CollectedAppearance> Matching(Player player, ItemData target, bool particles = false)
	{
		List<CollectedAppearance> list = new List<CollectedAppearance>();
		State state = Get(player);
		if (state == null || !state.Writable || !Classification.Eligible(target) || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return list;
		}
		HashSet<CollectedAppearance> hashSet = new HashSet<CollectedAppearance>();
		foreach (CollectedAppearance entry in state.Journal.Entries)
		{
			CollectedAppearance collectedAppearance = Normalize(entry);
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(collectedAppearance.Prefab);
			if (!Object.op_Implicit((Object)(object)itemPrefab) || !hashSet.Add(collectedAppearance))
			{
				continue;
			}
			string reason;
			bool num;
			if (!particles)
			{
				if (!Classification.PairPrefabs(target.m_dropPrefab, itemPrefab, out reason))
				{
					continue;
				}
				num = !VisualCapabilities.Equipment(itemPrefab, Classification.Of(itemPrefab).Kind);
			}
			else
			{
				num = !ParticleSources.Compatible(target.m_dropPrefab, collectedAppearance, out reason);
			}
			if (!num)
			{
				list.Add(collectedAppearance);
			}
		}
		AppearanceOrder.Sort(list);
		return list;
	}

	internal static ItemData Preview(CollectedAppearance look)
	{
		if (look == null || !look.Valid || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return null;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(look.Prefab);
		ItemData val = ((!Object.op_Implicit((Object)(object)itemPrefab)) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData);
		if (val?.m_shared == null)
		{
			return null;
		}
		ItemData obj = val.Clone();
		obj.m_customData.Clear();
		obj.m_dropPrefab = itemPrefab;
		obj.m_stack = 1;
		obj.m_variant = look.Variant;
		obj.m_quality = look.Quality;
		obj.m_equipped = false;
		obj.m_durability = obj.GetMaxDurability();
		return obj;
	}

	internal static string Name(CollectedAppearance look)
	{
		if (look == null)
		{
			return "";
		}
		string text = Native.PrefabItem(look.Prefab)?.m_shared?.m_name ?? look.Prefab;
		if (Localization.instance == null)
		{
			return text;
		}
		return Localization.instance.Localize(text);
	}

	internal static Sprite Icon(CollectedAppearance look)
	{
		GameObject val = ((look != null && Object.op_Implicit((Object)(object)ObjectDB.instance)) ? ObjectDB.instance.GetItemPrefab(look.Prefab) : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return null;
		}
		return VisualCapabilities.Icon(val, look.Variant);
	}
}
internal static class AppearanceOrder
{
	private static readonly HashSet<string> vanilla = new HashSet<string>(StringComparer.Ordinal);

	private static readonly Dictionary<string, string[]> recipes = new Dictionary<string, string[]>(StringComparer.Ordinal);

	private static readonly Dictionary<string, int> resolved = new Dictionary<string, int>(StringComparer.Ordinal);

	private static bool captured;

	private static readonly Regex Formatting = new Regex("<[^>]*>", RegexOptions.CultureInvariant);

	internal static void CaptureVanilla(ObjectDB database)
	{
		if (captured || !Object.op_Implicit((Object)(object)database) || database.m_items == null)
		{
			return;
		}
		bool flag = false;
		foreach (GameObject item in database.m_items)
		{
			if (Object.op_Implicit((Object)(object)item) && ((Object)item).name == "AxeStone")
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		foreach (GameObject item2 in database.m_items)
		{
			if (Object.op_Implicit((Object)(object)item2))
			{
				vanilla.Add(((Object)item2).name);
			}
		}
		foreach (Recipe recipe in database.m_recipes)
		{
			if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item) || recipe.m_resources == null)
			{
				continue;
			}
			List<string> list = new List<string>();
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (Object.op_Implicit((Object)(object)val?.m_resItem))
				{
					list.Add(((Object)val.m_resItem).name);
				}
			}
			string name = ((Object)recipe.m_item).name;
			if (!recipes.ContainsKey(name))
			{
				recipes.Add(name, list.ToArray());
			}
		}
		captured = true;
	}

	private static int MaterialRank(string name)
	{
		switch (name)
		{
		case "Gold":
		case "GoldBar":
		case "GoldOre":
		case "AncientGemstoneBlack":
		case "AncientGemstoneGreen":
		case "AncientGemstoneOrange":
		case "AncientGemstonePurple":
			return 800;
		case "AskHide":
		case "GemstoneRed":
		case "MorgenHeart":
		case "FlametalNew":
		case "GemstoneBlue":
		case "GemstoneGreen":
		case "FlametalOreNew":
			return 700;
		case "Eitr":
		case "YggdrasilWood":
		case "Softtissue":
		case "Mandible":
		case "Carapace":
		case "ScaleHide":
		case "BlackCore":
			return 600;
		case "LoxPelt":
		case "LinenThread":
		case "BlackMetal":
		case "BlackMetalScrap":
			return 500;
		case "Crystal":
		case "FenringHair":
		case "FenringClaw":
		case "FreezeGland":
		case "WolfFang":
		case "WolfPelt":
		case "SilverOre":
		case "Silver":
			return 400;
		case "Iron":
		case "Root":
		case "IronScrap":
		case "ElderBark":
		case "Chain":
			return 300;
		case "SurtlingCore":
		case "RoundLog":
		case "FineWood":
		case "TrollHide":
		case "Bronze":
		case "Copper":
		case "Tin":
			return 200;
		default:
			return 100;
		}
	}

	private static int Rank(string name, HashSet<string> path)
	{
		int num = AppearanceProgression.Rank(name);
		if (num > 0)
		{
			return num;
		}
		if (resolved.TryGetValue(name, out var value))
		{
			return value;
		}
		int num2 = MaterialRank(name);
		if (path.Count >= 32 || !path.Add(name))
		{
			return num2;
		}
		if (recipes.TryGetValue(name, out var value2))
		{
			string[] array = value2;
			foreach (string name2 in array)
			{
				num2 = Math.Max(num2, Rank(name2, path) + 1);
			}
		}
		path.Remove(name);
		resolved[name] = num2;
		return num2;
	}

	internal static void Sort(List<CollectedAppearance> looks)
	{
		Dictionary<CollectedAppearance, string> names = new Dictionary<CollectedAppearance, string>();
		Dictionary<CollectedAppearance, int> ranks = new Dictionary<CollectedAppearance, int>();
		foreach (CollectedAppearance look in looks)
		{
			names[look] = Formatting.Replace(AppearanceCollection.Name(look), "");
			ranks[look] = Rank(look.Prefab, new HashSet<string>(StringComparer.Ordinal));
		}
		looks.Sort((CollectedAppearance left, CollectedAppearance right) => AppearanceProgression.Compare(IsVanilla(left.Prefab), ranks[left], names[left], left, IsVanilla(right.Prefab), ranks[right], names[right], right));
	}

	private static bool IsVanilla(string name)
	{
		if (!captured)
		{
			return AppearanceProgression.Known(name);
		}
		return vanilla.Contains(name);
	}
}
internal static class AppearanceProgression
{
	private static readonly Dictionary<string, int> Ranks = Build();

	internal static bool Known(string name)
	{
		if (name != null)
		{
			return Ranks.ContainsKey(name);
		}
		return false;
	}

	internal static int Rank(string name)
	{
		if (name == null || !Ranks.TryGetValue(name, out var value))
		{
			return 0;
		}
		return value;
	}

	private static void Add(Dictionary<string, int> ranks, int stage, params string[] names)
	{
		for (int i = 0; i < names.Length; i++)
		{
			ranks[names[i]] = stage * 100 + i;
		}
	}

	private static Dictionary<string, int> Build()
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
		Add(dictionary, 1, "AxeStone", "AxeFlint", "Club", "KnifeFlint", "SpearFlint", "Bow", "ShieldWood", "ShieldWoodTower", "SledgeStagbreaker", "ArmorRagsChest", "ArmorRagsLegs", "ArmorLeatherChest", "ArmorLeatherLegs", "HelmetLeather", "CapeDeerHide");
		Add(dictionary, 2, "PickaxeAntler", "AxeBronze", "SwordBronze", "MaceBronze", "KnifeCopper", "SpearBronze", "AtgeirBronze", "BowFineWood", "ShieldBronzeBuckler", "ShieldBoneTower", "PickaxeBronze", "ArmorTrollLeatherChest", "ArmorTrollLeatherLegs", "HelmetTrollLeather", "CapeTrollHide", "ArmorBronzeChest", "ArmorBronzeLegs", "HelmetBronze", "HelmetDverger", "HelmetYule", "CapeOdin", "HelmetOdin");
		Add(dictionary, 3, "AxeIron", "SwordIron", "MaceIron", "Battleaxe", "AtgeirIron", "SpearElderbark", "BowHuntsman", "SledgeIron", "PickaxeIron", "ShieldIronBuckler", "ShieldBanded", "ShieldIronTower", "ShieldSerpentscale", "ArmorRootChest", "ArmorRootLegs", "HelmetRoot", "ArmorIronChest", "ArmorIronLegs", "HelmetIron");
		Add(dictionary, 4, "SwordSilver", "MaceSilver", "KnifeSilver", "SpearWolfFang", "BowDraugrFang", "BattleaxeCrystal", "FistFenrirClaw", "ShieldSilver", "ArmorWolfChest", "ArmorWolfLegs", "HelmetDrake", "CapeWolf", "ArmorFenringChest", "ArmorFenringLegs", "HelmetFenring");
		Add(dictionary, 5, "AxeBlackMetal", "SwordBlackmetal", "MaceNeedle", "KnifeBlackMetal", "AtgeirBlackmetal", "ShieldBlackmetal", "ShieldBlackmetalTower", "ArmorPaddedCuirass", "ArmorPaddedGreaves", "HelmetPadded", "CapeLox", "CapeLinen");
		Add(dictionary, 6, "AxeJotunBane", "SwordMistwalker", "THSwordKrom", "SledgeDemolisher", "AtgeirHimminAfl", "SpearCarapace", "KnifeSkollAndHati", "BowSpineSnap", "CrossbowArbalest", "PickaxeBlackMetal", "ShieldCarapaceBuckler", "ShieldCarapace", "StaffFireball", "StaffIceShards", "StaffShield", "StaffSkeleton", "ArmorCarapaceChest", "ArmorCarapaceLegs", "HelmetCarapace", "ArmorMageChest", "ArmorMageLegs", "HelmetMage", "CapeFeather");
		Add(dictionary, 7, "AxeBerzerkr", "AxeBerzerkrBlood", "AxeBerzerkrLightning", "AxeBerzerkrNature", "SwordNiedhogg", "SwordNiedhoggBlood", "SwordNiedhoggLightning", "SwordNiedhoggNature", "THSwordSlayer", "THSwordSlayerBlood", "THSwordSlayerLightning", "THSwordSlayerNature", "SpearSplitner", "SpearSplitner_Blood", "SpearSplitner_Lightning", "SpearSplitner_Nature", "BowAshlands", "BowAshlandsBlood", "BowAshlandsLightning", "BowAshlandsNature", "CrossbowRipper", "CrossbowRipperBlood", "CrossbowRipperLightning", "CrossbowRipperNature", "MaceEldner", "MaceEldnerBlood", "MaceEldnerLightning", "MaceEldnerNature", "SwordDyrnwyn", "SwordIronFire", "ShieldFlametal", "ShieldFlametalTower", "StaffClusterbomb", "StaffLightning", "StaffRedTroll", "StaffGreenRoots", "ArmorFlametalChest", "ArmorFlametalLegs", "HelmetFlametal", "ArmorAshlandsMediumChest", "ArmorAshlandsMediumlegs", "HelmetAshlandsMediumHood", "ArmorMageAshlandsChest", "ArmorMageAshlandsLegs", "HelmetMageAshlands", "CapeAsksvin", "CapeAsh");
		Add(dictionary, 8, "AxeGold", "AxeGold_BloodLightning", "AxeGold_FrostFire", "BattleaxeGold", "BattleaxeGold_BloodLightning", "BattleaxeGold_FrostFire", "SwordGold", "SwordGold_BloodLightning", "SwordGold_FrostFire", "THSwordGold", "THSwordGold_BloodLightning", "THSwordGold_FrostFire", "MaceGold", "MaceGold_BloodLightning", "MaceGold_FrostFire", "SledgeGold", "SledgeGold_BloodLightning", "SledgeGold_FrostFire", "SpearGold", "SpearGold_BloodLightning", "SpearGold_FrostFire", "AtgeirGold", "AtgeirGold_BloodLightning", "AtgeirGold_FrostFire", "KnifeGold", "KnifeGold_BloodLightning", "KnifeGold_FrostFire", "BowGold", "BowGold_BloodLightning", "BowGold_FrostFire", "CrossbowGold", "CrossbowGold_BloodLightning", "CrossbowGold_FrostFire", "PickaxeGold", "ShieldGold", "ShieldGoldTower", "ShieldGoldBuckler", "StaffFireFrost", "StaffThunderBlood", "StaffEchoSpike", "ArmorDeepNorthHeavyChest", "ArmorDeepNorthHeavylegs", "HelmetDeepNorthHeavy", "ArmorDeepNorthMediumChest", "ArmorDeepNorthMediumlegs", "HelmetDeepNorthMedium", "ArmorDeepNorthMageChest", "ArmorDeepNorthMagelegs", "HelmetDeepNorthMage");
		return dictionary;
	}

	internal static int Compare(bool leftVanilla, int leftRank, string leftName, CollectedAppearance left, bool rightVanilla, int rightRank, string rightName, CollectedAppearance right)
	{
		int num = rightVanilla.CompareTo(leftVanilla);
		if (num == 0 && leftVanilla)
		{
			num = leftRank.CompareTo(rightRank);
		}
		if (num == 0)
		{
			num = StringComparer.CurrentCultureIgnoreCase.Compare(leftName, rightName);
		}
		if (num == 0)
		{
			num = StringComparer.Ordinal.Compare(left.Prefab, right.Prefab);
		}
		if (num == 0)
		{
			int variant = left.Variant;
			num = variant.CompareTo(right.Variant);
		}
		if (num == 0)
		{
			int variant = left.Quality;
			num = variant.CompareTo(right.Quality);
		}
		return num;
	}
}
internal static class Classification
{
	private static readonly HashSet<string> Bucklers = new HashSet<string>(StringComparer.Ordinal) { "ShieldBronzeBuckler", "ShieldIronBuckler", "ShieldCarapaceBuckler", "ShieldGoldBuckler", "ShieldBucklerGold" };

	private static readonly HashSet<string> Towers = new HashSet<string>(StringComparer.Ordinal) { "ShieldWoodTower", "ShieldBoneTower", "ShieldIronTower", "ShieldSerpentscale", "ShieldBlackmetalTower", "ShieldFlametalTower", "ShieldGoldTower", "ShieldTowerGold" };

	private static readonly HashSet<string> Rounds = new HashSet<string>(StringComparer.Ordinal) { "ShieldWood", "ShieldBanded", "ShieldSilver", "ShieldBlackmetal", "ShieldCarapace", "ShieldGold", "ShieldRoundGold" };

	internal static bool AllowUnknown => Plugin.AllowUnknownTypes?.Value ?? false;

	internal static GearSignature Of(ItemData item)
	{
		if ((Object)(object)item?.m_dropPrefab == (Object)null)
		{
			return new GearSignature();
		}
		return Of(item.m_dropPrefab);
	}

	internal static GearSignature Of(GameObject prefab)
	{
		if (!Object.op_Implicit((Object)(object)prefab))
		{
			return new GearSignature();
		}
		return OfPrefab(prefab, prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared);
	}

	private static GearSignature OfPrefab(GameObject prefab, SharedData shared)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected I4, but got Unknown
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected I4, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected I4, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: 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_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Invalid comparison between Unknown and I4
		//IL_0287: Unknown result type (might be due to invalid IL or missing references)
		//IL_029e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_036c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0372: Invalid comparison between Unknown and I4
		GearSignature gearSignature = new GearSignature();
		if (shared == null || shared.m_maxStackSize != 1)
		{
			return gearSignature;
		}
		gearSignature.Stance = (int)shared.m_animationState;
		gearSignature.Skill = (int)shared.m_skillType;
		gearSignature.ItemType = (int)shared.m_itemType;
		gearSignature.Primary = shared.m_attack?.m_attackAnimation ?? "";
		gearSignature.Secondary = shared.m_secondaryAttack?.m_attackAnimation ?? "";
		gearSignature.PrimaryChain = shared.m_attack?.m_attackChainLevels ?? 0;
		gearSignature.SecondaryChain = shared.m_secondaryAttack?.m_attackChainLevels ?? 0;
		Attack attack = shared.m_attack;
		gearSignature.PrimaryType = ((attack == null) ? 3 : ((int)attack.m_attackType));
		Attack secondaryAttack = shared.m_secondaryAttack;
		gearSignature.SecondaryType = ((secondaryAttack == null) ? 3 : ((int)secondaryAttack.m_attackType));
		gearSignature.KnownSlot = (WeaponSlot(shared.m_itemType) ? gearSignature.ItemType : (-1));
		gearSignature.KnownStance = (Enum.IsDefined(typeof(AnimationState), shared.m_animationState) ? gearSignature.Stance : (-1));
		Compatibility.Gear(((Object)prefab).name, out var rule);
		GearKind gearKind = Kind(((Object)prefab).name, shared);
		GearKind gearKind2 = StanceKind(shared.m_animationState);
		if (gearKind == GearKind.Unsupported && !Enum.IsDefined(typeof(ItemType), shared.m_itemType) && gearSignature.Primary.Length > 0 && gearKind2 != GearKind.Unsupported && KnownWeaponSkill(shared.m_skillType) && SkillFits(gearKind2, shared.m_skillType))
		{
			gearKind = gearKind2;
		}
		gearSignature.Kind = gearKind;
		gearSignature.Evidence = "Native equipment data";
		if (gearKind != GearKind.Unsupported)
		{
			if (!Fits(gearKind, shared) || (rule != null && rule.Family != gearKind))
			{
				return Block(gearSignature, "Conflicting native equipment data or family registration");
			}
		}
		else if (rule != null)
		{
			if (!Fits(rule.Family, shared) || (GearSignature.Weapon(rule.Family) && (gearSignature.Primary.Length == 0 || !SkillFits(rule.Family, shared.m_skillType))))
			{
				return Block(gearSignature, "Registration conflicts with equipment data");
			}
			gearSignature.Kind = rule.Family;
			gearSignature.Evidence = "Explicit compatibility registration";
		}
		else if ((int)shared.m_itemType == 5)
		{
			GearKind kind = WeaponNameClues.Read(((Object)prefab).name, shared.m_name).Resolve(shared);
			if (!Shield(kind))
			{
				return Block(gearSignature, "Shield category could not be identified");
			}
			gearSignature.Kind = kind;
			gearSignature.Evidence = "Shield slot and name clues";
		}
		else
		{
			GearKind gearKind3 = StanceKind(shared.m_animationState);
			bool flag = !Enum.IsDefined(typeof(ItemType), shared.m_itemType) && gearKind3 != GearKind.Unsupported;
			if ((!WeaponSlot(shared.m_itemType) && !flag) || gearSignature.Primary.Length == 0)
			{
				return Block(gearSignature, "No supported weapon slot and attack setup");
			}
			if (KnownWeaponSkill(shared.m_skillType))
			{
				return Block(gearSignature, "Weapon skill conflicts with equipment data");
			}
			GearKind gearKind4 = WeaponNameClues.Read(((Object)prefab).name, shared.m_name).Resolve(shared);
			if (gearKind4 == GearKind.Unsupported || (gearKind4 != GearKind.UnknownWeapon && !Fits(gearKind4, shared)))
			{
				return Block(gearSignature, "Name clues are ambiguous or conflict with equipment data");
			}
			if (gearKind3 != GearKind.Unsupported && gearKind4 != GearKind.UnknownWeapon && gearKind3 != gearKind4)
			{
				return Block(gearSignature, "Weapon stance and name clues disagree");
			}
			gearSignature.Kind = ((gearKind3 != GearKind.Unsupported) ? gearKind3 : gearKind4);
			if (gearSignature.Kind == GearKind.UnknownWeapon && (int)shared.m_itemType == 4)
			{
				gearSignature.Kind = GearKind.Bow;
			}
			if (!Fits(gearSignature.Kind, shared))
			{
				return Block(gearSignature, "Weapon family conflicts with equipment position");
			}
			gearSignature.Evidence = ((gearSignature.Kind == GearKind.UnknownWeapon) ? "Unresolved weapon family" : ((gearKind3 != GearKind.Unsupported) ? "Weapon slot, attack and stance" : "Weapon slot, attack and name clues"));
		}
		if (rule != null && gearSignature.Issue.Length == 0)
		{
			gearSignature.Evidence = "Explicit compatibility registration; native boundaries checked";
		}
		if (rule != null && rule.Moveset != null && gearSignature.IsWeapon && gearSignature.Primary.Length > 0)
		{
			gearSignature.Primary = "registered:" + rule.Moveset + ":primary";
			if (gearSignature.Secondary.Length > 0)
			{
				gearSignature.Secondary = "registered:" + rule.Moveset + ":secondary";
			}
		}
		return gearSignature;
	}

	private static GearSignature Block(GearSignature signature, string issue)
	{
		signature.Kind = GearKind.Unsupported;
		signature.Issue = issue;
		signature.Evidence = "Unresolved or conflicting data";
		return signature;
	}

	private static bool Shield(GearKind kind)
	{
		if (kind != GearKind.Buckler && kind != GearKind.RoundShield)
		{
			return kind == GearKind.TowerShield;
		}
		return true;
	}

	private static bool WeaponSlot(ItemType type)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		if ((int)type != 3 && (int)type != 14 && (int)type != 22)
		{
			return (int)type == 4;
		}
		return true;
	}

	private static bool KnownWeaponSkill(SkillType skill)
	{
		//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_0040: Expected I4, but got Unknown
		switch (skill - 1)
		{
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 6:
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 13:
			return true;
		default:
			return false;
		}
	}

	private static bool SkillFits(GearKind kind, SkillType skill)
	{
		//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_0040: Expected I4, but got Unknown
		switch (skill - 1)
		{
		case 0:
			if (kind != GearKind.Sword1H)
			{
				return kind == GearKind.Sword2H;
			}
			return true;
		case 6:
			if (kind != GearKind.Axe1H && kind != GearKind.Axe2H)
			{
				return kind == GearKind.DualAxes;
			}
			return true;
		case 2:
			if (kind != GearKind.Mace1H)
			{
				return kind == GearKind.Sledge;
			}
			return true;
		case 1:
			if (kind != GearKind.Knife)
			{
				return kind == GearKind.DualKnives;
			}
			return true;
		case 4:
			return kind == GearKind.Spear;
		case 3:
			return kind == GearKind.Atgeir;
		case 10:
			return kind == GearKind.Fists;
		case 7:
			return kind == GearKind.Bow;
		case 13:
			return kind == GearKind.Crossbow;
		case 8:
			return kind == GearKind.ElementalStaff;
		case 9:
			return kind == GearKind.BloodStaff;
		case 11:
			return kind == GearKind.Pickaxe;
		default:
			return true;
		}
	}

	private static GearKind StanceKind(AnimationState stance)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Invalid comparison between Unknown and I4
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected I4, but got Unknown
		if ((int)stance != 2)
		{
			if ((int)stance != 3)
			{
				return (stance - 7) switch
				{
					6 => GearKind.Sword2H, 
					0 => GearKind.Atgeir, 
					8 => GearKind.DualAxes, 
					4 => GearKind.DualKnives, 
					3 => GearKind.Crossbow, 
					_ => GearKind.Unsupported, 
				};
			}
			return GearKind.Bow;
		}
		return GearKind.Sledge;
	}

	private static bool Fits(GearKind kind, SharedData shared)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Invalid comparison between Unknown and I4
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Invalid comparison between Unknown and I4
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Invalid comparison between Unknown and I4
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Invalid comparison between Unknown and I4
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Invalid comparison between Unknown and I4
		//IL_0060: 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_0091: Invalid comparison between Unknown and I4
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Invalid comparison between Unknown and I4
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Invalid comparison between Unknown and I4
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Invalid comparison between Unknown and I4
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Invalid comparison between Unknown and I4
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Invalid comparison between Unknown and I4
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Expected I4, but got Unknown
		if (Shield(kind))
		{
			return (int)shared.m_itemType == 5;
		}
		switch (kind)
		{
		case GearKind.Chest:
			return (int)shared.m_itemType == 7;
		case GearKind.Legs:
			return (int)shared.m_itemType == 11;
		case GearKind.Cape:
			return (int)shared.m_itemType == 17;
		case GearKind.Helmet:
			return (int)shared.m_itemType == 6;
		default:
		{
			if (!GearSignature.Weapon(kind) || (!WeaponSlot(shared.m_itemType) && Enum.IsDefined(typeof(ItemType), shared.m_itemType)))
			{
				return false;
			}
			bool flag = (int)shared.m_itemType == 3;
			bool flag2 = (int)shared.m_itemType == 14 || (int)shared.m_itemType == 22;
			if ((int)shared.m_animationState == 1 && flag2)
			{
				return false;
			}
			if ((int)shared.m_animationState == 1)
			{
				flag = true;
			}
			if (flag2 && (kind == GearKind.Sword1H || kind == GearKind.Axe1H || kind == GearKind.Mace1H || kind == GearKind.Knife))
			{
				return false;
			}
			if (flag && (kind == GearKind.Sword2H || kind == GearKind.Axe2H || kind == GearKind.Sledge || kind == GearKind.DualAxes || kind == GearKind.DualKnives))
			{
				return false;
			}
			if ((int)shared.m_itemType == 4 && kind != GearKind.Bow && kind != GearKind.Crossbow)
			{
				return false;
			}
			GearKind gearKind = StanceKind(shared.m_animationState);
			if (gearKind != GearKind.Unsupported && gearKind != kind)
			{
				return false;
			}
			AnimationState animationState = shared.m_animationState;
			switch (animationState - 4)
			{
			case 0:
			case 1:
			case 2:
			case 5:
				return false;
			case 4:
				if (kind != GearKind.Axe2H && kind != GearKind.Pickaxe)
				{
					return kind == GearKind.UnknownWeapon;
				}
				return true;
			case 8:
				if (kind != GearKind.ElementalStaff && kind != GearKind.BloodStaff)
				{
					return kind == GearKind.UnknownWeapon;
				}
				return true;
			default:
				return true;
			}
		}
		}
	}

	private static GearKind Kind(string prefab, SharedData shared)
	{
		//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_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected I4, but got Unknown
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Invalid comparison between Unknown and I4
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Invalid comparison between Unknown and I4
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Invalid comparison between Unknown and I4
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Invalid comparison between Unknown and I4
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Expected I4, but got Unknown
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Invalid comparison between Unknown and I4
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Invalid comparison between Unknown and I4
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Invalid comparison between Unknown and I4
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Invalid comparison between Unknown and I4
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Invalid comparison between Unknown and I4
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Invalid comparison between Unknown and I4
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Invalid comparison between Unknown and I4
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: 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_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Expected I4, but got Unknown
		ItemType itemType = shared.m_itemType;
		switch (itemType - 5)
		{
		default:
			if ((int)itemType != 17)
			{
				break;
			}
			return GearKind.Cape;
		case 2:
			return GearKind.Chest;
		case 6:
			return GearKind.Legs;
		case 1:
			return GearKind.Helmet;
		case 0:
			if (!Bucklers.Contains(prefab))
			{
				if (!Towers.Contains(prefab))
				{
					if (!Rounds.Contains(prefab))
					{
						return GearKind.Unsupported;
					}
					return GearKind.RoundShield;
				}
				return GearKind.TowerShield;
			}
			return GearKind.Buckler;
		case 3:
		case 4:
		case 5:
			break;
		}
		if ((int)shared.m_itemType != 3 && (int)shared.m_itemType != 14 && (int)shared.m_itemType != 22 && (int)shared.m_itemType != 4)
		{
			return GearKind.Unsupported;
		}
		SkillType skillType = shared.m_skillType;
		switch (skillType - 8)
		{
		case 0:
			return GearKind.Bow;
		case 6:
			return GearKind.Crossbow;
		case 1:
			return GearKind.ElementalStaff;
		case 2:
			return GearKind.BloodStaff;
		case 4:
			return GearKind.Pickaxe;
		default:
		{
			AnimationState animationState = shared.m_animationState;
			if ((int)animationState != 11)
			{
				if ((int)animationState == 15)
				{
					if ((int)shared.m_skillType != 7)
					{
						return GearKind.Unsupported;
					}
					return GearKind.DualAxes;
				}
				bool flag = (int)shared.m_itemType == 14 || (int)shared.m_itemType == 22;
				skillType = shared.m_skillType;
				switch (skillType - 1)
				{
				case 0:
					if (!flag)
					{
						return GearKind.Sword1H;
					}
					return GearKind.Sword2H;
				case 6:
					if (!flag)
					{
						return GearKind.Axe1H;
					}
					return GearKind.Axe2H;
				case 2:
					if (!flag)
					{
						return GearKind.Mace1H;
					}
					return GearKind.Sledge;
				case 1:
					if (!flag)
					{
						return GearKind.Knife;
					}
					return GearKind.Unsupported;
				case 4:
					return GearKind.Spear;
				case 3:
					return GearKind.Atgeir;
				case 10:
					return GearKind.Fists;
				default:
					return GearKind.Unsupported;
				}
			}
			if ((int)shared.m_skillType != 2)
			{
				return GearKind.Unsupported;
			}
			return GearKind.DualKnives;
		}
		}
	}

	internal static bool Eligible(ItemData item)
	{
		if (item == null || item.m_stack != 1 || item.m_shared == null || item.m_shared.m_maxStackSize != 1)
		{
			return false;
		}
		return Of(item).Kind != GearKind.Unsupported;
	}

	internal static bool Pair(ItemData original, ItemData donor, out string reason)
	{
		if (!Eligible(original) || !Eligible(donor))
		{
			reason = "This item can’t be used as an appearance.";
			return false;
		}
		return PairPrefabs(original.m_dropPrefab, donor.m_dropPrefab, out reason);
	}

	internal static bool PairPrefabs(GameObject original, GameObject donor, out string reason, bool savedAppearance = false)
	{
		GearSignature gearSignature = Of(original);
		GearSignature gearSignature2 = Of(donor);
		if ((!Object.op_Implicit((Object)(object)original) || !((Object)(object)original == (Object)(object)donor) || gearSignature.Kind == GearKind.Unsupported) && !FamilyRules.Compatible(gearSignature, gearSignature2, savedAppearance || AllowUnknown))
		{
			reason = ((gearSignature.Kind == GearKind.Unsupported || gearSignature2.Kind == GearKind.Unsupported) ? "This item’s type could not be matched safely. Choose a different appearance." : ((gearSignature.Kind == GearKind.UnknownWeapon && gearSignature2.Kind == GearKind.UnknownWeapon) ? ((!savedAppearance && !AllowUnknown) ? "These weapon types are unknown. Enable Allow mixing of unknown types in settings to use them together." : "These weapons use different equipment positions. Choose a different appearance.") : ((gearSignature.Kind != gearSignature2.Kind) ? FamilyRules.Requirement(gearSignature.Kind) : "These weapons use different attacks. Choose a different appearance.")));
			return false;
		}
		reason = "";
		return true;
	}
}
internal sealed class CollectedAppearance : IEquatable<CollectedAppearance>
{
	internal readonly string Prefab;

	internal readonly int Variant;

	internal readonly int Quality;

	internal bool Valid
	{
		get
		{
			if (string.IsNullOrWhiteSpace(Prefab) || Prefab.Length > 160 || Variant < 0 || Variant > 65535 || Quality < 1 || Quality > 65535)
			{
				return false;
			}
			string prefab = Prefab;
			for (int i = 0; i < prefab.Length; i++)
			{
				if (char.IsControl(prefab[i]))
				{
					return false;
				}
			}
			return true;
		}
	}

	internal CollectedAppearance(string prefab, int variant, int quality)
	{
		Prefab = prefab;
		Variant = variant;
		Quality = quality;
	}

	public bool Equals(CollectedAppearance other)
	{
		if (other != null && Prefab == other.Prefab && Variant == other.Variant)
		{
			return Quality == other.Quality;
		}
		return false;
	}

	public override bool Equals(object other)
	{
		return Equals(other as CollectedAppearance);
	}

	public override int GetHashCode()
	{
		return (((((Prefab != null) ? StringComparer.Ordinal.GetHashCode(Prefab) : 0) * 397) ^ Variant) * 397) ^ Quality;
	}

	internal string Encode()
	{
		string[] obj = new string[5]
		{
			Convert.ToBase64String(Encoding.UTF8.GetBytes(Prefab)),
			"|",
			null,
			null,
			null
		};
		int variant = Variant;
		obj[2] = variant.ToString(CultureInfo.InvariantCulture);
		obj[3] = "|";
		variant = Quality;
		obj[4] = variant.ToString(CultureInfo.InvariantCulture);
		return string.Concat(obj);
	}

	internal static bool TryDecode(string line, out CollectedAppearance value)
	{
		value = null;
		string[] array = line.Split(new char[1] { '|' });
		if (array.Length != 3 || array[0].Length > 860 || !int.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || !int.TryParse(array[2], NumberStyles.None, CultureInfo.InvariantCulture, out var result2))
		{
			return false;
		}
		try
		{
			CollectedAppearance collectedAppearance = new CollectedAppearance(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(array[0])), result, result2);
			if (!collectedAppearance.Valid)
			{
				return false;
			}
			value = collectedAppearance;
			return true;
		}
		catch (FormatException)
		{
			return false;
		}
		catch (DecoderFallbackException)
		{
			return false;
		}
	}
}
internal sealed class AppearanceJournal
{
	internal const string Key = "gnls.GooCosmetics.collection.v1";

	internal const int MaxEntries = 8192;

	internal const int MaxLength = 2097152;

	private readonly HashSet<CollectedAppearance> entries = new HashSet<CollectedAppearance>();

	private int encodedLength = 2;

	internal IEnumerable<CollectedAppearance> Entries => entries;

	internal int Count => entries.Count;

	internal bool Contains(CollectedAppearance entry)
	{
		if (entry != null)
		{
			return entries.Contains(entry);
		}
		return false;
	}

	internal bool Add(CollectedAppearance entry)
	{
		if (entry == null || !entry.Valid || entries.Count >= 8192 || entries.Contains(entry))
		{
			return false;
		}
		int num = entry.Encode().Length + ((entries.Count != 0) ? 1 : 0);
		if (encodedLength + num > 2097152)
		{
			return false;
		}
		entries.Add(entry);
		encodedLength += num;
		return true;
	}

	internal string Encode()
	{
		List<string> list = new List<string>();
		foreach (CollectedAppearance entry in entries)
		{
			list.Add(entry.Encode());
		}
		list.Sort(StringComparer.Ordinal);
		return "1\n" + string.Join("\n", list);
	}

	internal static bool TryDecode(string raw, out AppearanceJournal journal)
	{
		journal = new AppearanceJournal();
		if (string.IsNullOrEmpty(raw))
		{
			return true;
		}
		if (raw.Length > 2097152 || !raw.StartsWith("1\n", StringComparison.Ordinal))
		{
			return false;
		}
		if (raw.Length == 2)
		{
			return true;
		}
		string[] array = raw.Substring(2).Split(new char[1] { '\n' });
		if (array.Length > 8192)
		{
			return false;
		}
		AppearanceJournal appearanceJournal = new AppearanceJournal();
		string[] array2 = array;
		for (int i = 0; i < array2.Length; i++)
		{
			if (!CollectedAppearance.TryDecode(array2[i], out var value))
			{
				return false;
			}
			appearanceJournal.Add(value);
		}
		journal = appearanceJournal;
		return true;
	}
}
internal static class CollectionHooks
{
	internal static void CheckContracts()
	{
		Native.Method(typeof(Player), "Load", typeof(ZPackage));
		Native.Method(typeof(Player), "Save", typeof(ZPackage));
		Native.Method(typeof(Player), "AddKnownItem", typeof(ItemData));
		Native.Method(typeof(ObjectDB), "Awake");
		if (AccessTools.Field(typeof(Player), "m_customData") == null)
		{
			throw new MissingFieldException("Player.m_customData");
		}
	}
}
[HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })]
internal static class CollectionLoadPatch
{
	internal sealed class Snapshot
	{
		internal AppearanceCollection.LoadState Collection;

		internal string Favorites;

		internal bool FavoritesStarted;
	}

	[HarmonyPrefix]
	private static void Prefix(Player __instance, out Snapshot __state)
	{
		__state = new Snapshot();
		try
		{
			__state.Collection = AppearanceCollection.BeginLoad(__instance);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Prepare saved appearances", error);
		}
		try
		{
			__state.Favorites = FavoritesStore.BeginLoad(__instance);
			__state.FavoritesStarted = true;
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Prepare saved favorites", error2);
		}
	}

	[HarmonyFinalizer]
	private static Exception Finalizer(Player __instance, Snapshot __state, Exception __exception)
	{
		try
		{
			AppearanceCollection.EndLoad(__instance, __state?.Collection, __exception == null);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Load saved appearances", error);
		}
		try
		{
			if (__state != null && __state.FavoritesStarted)
			{
				FavoritesStore.EndLoad(__instance, __state.Favorites, __exception == null);
			}
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Load saved favorites", error2);
		}
		return __exception;
	}
}
[HarmonyPatch(typeof(Player), "Save", new Type[] { typeof(ZPackage) })]
internal static class CollectionSavePatch
{
	[HarmonyPrefix]
	private static void Prefix(Player __instance)
	{
		try
		{
			AppearanceCollection.Saving(__instance);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Save appearances", error);
		}
		try
		{
			FavoritesStore.Saving(__instance);
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Save favorites", error2);
		}
	}
}
[HarmonyPatch(typeof(Player), "AddKnownItem", new Type[] { typeof(ItemData) })]
internal static class CollectionObtainPatch
{
	[HarmonyPostfix]
	private static void Postfix(Player __instance, ItemData item)
	{
		try
		{
			AppearanceCollection.Observe(__instance, item);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Collect appearance", error);
		}
	}
}
[HarmonyPatch(typeof(ObjectDB), "Awake")]
internal static class VanillaAppearanceCatalogPatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void Prefix(ObjectDB __instance)
	{
		try
		{
			AppearanceOrder.CaptureVanilla(__instance);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Vanilla appearance order", error);
		}
	}
}
internal static class CollectionPages
{
	internal const int Columns = 4;

	internal const int Rows = 6;

	internal const int Size = 24;

	internal static int Count(int items)
	{
		return Math.Max(1, (Math.Max(0, items) + 24 - 1) / 24);
	}

	internal static int Clamp(int page, int items)
	{
		return Math.Max(0, Math.Min(page, Count(items) - 1));
	}

	internal static int Index(int page, int cell)
	{
		return page * 24 + cell;
	}
}
internal static class CollectionWindow
{
	private sealed class Panel
	{
		private readonly bool particleCollection;

		private GameObject root;

		private WindowDrag drag;

		private NativeItemCell[] cells;

		private Button[] stars;

		private TMP_InputField search;

		private Toggle favorites;

		private TMP_Text subtitle;

		private TMP_Text pageLabel;

		private TMP_Text empty;

		private Button previous;

		private Button next;

		private List<CollectedAppearance> entries = new List<CollectedAppearance>();

		private List<CollectedAppearance> matching = new List<CollectedAppearance>();

		private readonly Dictionary<CollectedAppearance, string> names = new Dictionary<CollectedAppearance, string>();

		private int page;

		private int journalRevision = -1;

		private int visualRevision = -1;

		private int wardrobeRevision = -1;

		private bool allowUnknown;

		private ItemData target;

		private CollectedAppearance Selected
		{
			get
			{
				if (!particleCollection)
				{
					return IllusionWindow.Selected;
				}
				return IllusionWindow.SelectedParticles;
			}
		}

		internal bool IsOpen => Object.op_Implicit((Object)(object)root);

		internal Panel(bool particles)
		{
			particleCollection = particles;
		}

		internal bool CoversPointer(Vector2 position)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)drag))
			{
				return drag.Contains(position);
			}
			return false;
		}

		internal void Toggle()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_015f: 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_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: 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_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: 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_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c9: Expected O, but got Unknown
			if (IsOpen)
			{
				Close();
			}
			else
			{
				if (!IllusionWindow.IsOpen || !UiSafety.Allowed() || IllusionWindow.Target == null || !Features.Has(Feature.Collection) || (particleCollection && !AppearanceCollection.HasParticles(Player.m_localPlayer)))
				{
					return;
				}
				InventoryGrid playerGrid = InventoryGui.instance.m_playerGrid;
				Rect rect = ((RectTransform)playerGrid.m_elementPrefab.transform).rect;
				Vector2 size = ((Rect)(ref rect)).size;
				float elementSpace = playerGrid.m_elementSpace;
				if (elementSpace <= 0f || size.x <= 0f || size.y <= 0f)
				{
					throw new InvalidOperationException("Native inventory cell geometry is unavailable.");
				}
				float num = 3f * elementSpace + size.x;
				float num2 = 5f * elementSpace + size.y;
				float num3 = num + 40f;
				float num4 = num2 + 216f;
				root = new GameObject(particleCollection ? "GooCosmetics Saved Particles" : "GooCosmetics Saved Appearances", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				root.SetActive(false);
				RectTransform val = (RectTransform)root.transform;
				((Transform)val).SetParent(InventoryGui.instance.m_inventoryRoot, false);
				Vector3 lossyScale = ((Transform)playerGrid.m_gridRoot).lossyScale;
				Vector3 lossyScale2 = ((Transform)val).parent.lossyScale;
				((Transform)val).localScale = new Vector3(lossyScale.x / lossyScale2.x, lossyScale.y / lossyScale2.y, 1f);
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(0.5f, 0.5f);
				val.pivot = val2;
				Vector2 anchorMin = (val.anchorMax = val2);
				val.anchorMin = anchorMin;
				val.sizeDelta = new Vector2(num3, num4);
				IllusionWindow.Skin.Panel(root.GetComponent<Image>());
				drag = IllusionWindow.Header(val, particleCollection ? "Saved particles" : "Saved appearances", num3, Close);
				subtitle = IllusionWindow.Label("Category", val, "", 0f, -68f, num3 - 40f, 28f, 15, heading: false, (TextAlignmentOptions)514);
				search = IllusionWindow.InputAt(val, "Search appearances…", 0f, -100f, num3 - 40f, delegate
				{
					Filter();
				});
				favorites = IllusionWindow.Choice(val, "Favorites only", (0f - num3) / 2f + 105f, -134f, delegate
				{
					Filter();
				}, 13, 170f);
				cells = new NativeItemCell[24];
				stars = (Button[])(object)new Button[cells.Length];
				for (int num5 = 0; num5 < 6; num5++)
				{
					for (int num6 = 0; num6 < 4; num6++)
					{
						int index = num5 * 4 + num6;
						cells[index] = new NativeItemCell(val, (0f - num) / 2f + size.x / 2f + (float)num6 * elementSpace, -156f - size.y / 2f - (float)num5 * elementSpace, delegate
						{
							Choose(index);
						}, delegate
						{
							Favorite(index);
						});
						stars[index] = IllusionWindow.ButtonAt("Favorite", (RectTransform)cells[index].Root.transform, "☆", size.x / 2f - 12f, -12f, 22f, 22f, delegate
						{
							Favorite(index);
						});
					}
				}
				empty = IllusionWindow.Label("Empty collection", val, "", 0f, -156f - num2 / 2f, num - 20f, 80f, 17, heading: false, (TextAlignmentOptions)514);
				float y = 0f - num4 + 32f;
				float num7 = Mathf.Min(130f, (num3 - 76f - 60f) / 2f);
				float num8 = num3 / 2f - 20f - num7 / 2f;
				previous = IllusionWindow.ButtonAt("Previous", val, "Previous", 0f - num8, y, num7, 34f, delegate
				{
					TurnPage(-1);
				});
				next = IllusionWindow.ButtonAt("Next", val, "Next", num8, y, num7, 34f, delegate
				{
					TurnPage(1);
				});
				pageLabel = IllusionWindow.Label("Page", val, "", 0f, y, 76f, 30f, 16, heading: false, (TextAlignmentOptions)514);
				drag.Initialize(val, particleCollection ? "particles" : "collection", allowScaling: false);
				page = 0;
				target = null;
				journalRevision = (visualRevision = -1);
				Refresh();
				((Transform)val).SetAsLastSibling();
				root.SetActive(true);
			}
		}

		internal void Close()
		{
			if (Object.op_Implicit((Object)(object)root))
			{
				root.SetActive(false);
				Object.Destroy((Object)(object)root);
			}
			root = null;
			drag = null;
			cells = null;
			target = null;
			entries.Clear();
			matching.Clear();
			names.Clear();
			stars = null;
			search = null;
			favorites = null;
			subtitle = (pageLabel = (empty = null));
			previous = (next = null);
			journalRevision = (visualRevision = (wardrobeRevision = -1));
			page = 0;
		}

		internal void Tick()
		{
			if (IsOpen)
			{
				drag.CheckScreenSize();
				if (journalRevision != AppearanceCollection.Revision(Player.m_localPlayer) || visualRevision != Plugin.VisualRevision || allowUnknown != Classification.AllowUnknown || wardrobeRevision != FavoritesStore.Revision(Player.m_localPlayer))
				{
					Refresh();
				}
			}
		}

		internal void Refresh()
		{
			if (!IsOpen || cells == null)
			{
				return;
			}
			ItemData val = IllusionWindow.Target;
			int num = AppearanceCollection.Revision(Player.m_localPlayer);
			if (target != val || journalRevision != num || visualRevision != Plugin.VisualRevision || allowUnknown != Classification.AllowUnknown)
			{
				if (target != val)
				{
					page = 0;
				}
				target = val;
				journalRevision = num;
				visualRevision = Plugin.VisualRevision;
				allowUnknown = Classification.AllowUnknown;
				matching = AppearanceCollection.Matching(Player.m_localPlayer, val, particleCollection);
				names.Clear();
				foreach (CollectedAppearance item in matching)
				{
					names[item] = AppearanceCollection.Name(item);
				}
				Filter(reset: false);
			}
			else if (wardrobeRevision != FavoritesStore.Revision(Player.m_localPlayer))
			{
				Filter(reset: false);
			}
			else
			{
				Highlight();
			}
		}

		private void Filter(bool reset = true)
		{
			if (cells == null || (Object)(object)favorites == (Object)null || (Object)(object)search == (Object)null || (Object)(object)empty == (Object)null)
			{
				return;
			}
			entries.Clear();
			foreach (CollectedAppearance item in matching)
			{
				if ((!favorites.isOn || FavoritesStore.Favorite(Player.m_localPlayer, item)) && CollectionFilter.Matches(names[item], search.text))
				{
					entries.Add(item);
				}
			}
			wardrobeRevision = FavoritesStore.Revision(Player.m_localPlayer);
			page = ((!reset) ? CollectionPages.Clamp(page, entries.Count) : 0);
			BindPage();
		}

		private void Favorite(int cell)
		{
			int num = CollectionPages.Index(page, cell);
			if (num < entries.Count && UiSafety.Allowed() && Native.DragItem.Invoke(InventoryGui.instance) == null)
			{
				if (!FavoritesStore.ToggleFavorite(Player.m_localPlayer, entries[num], out var message))
				{
					IllusionWindow.Report(message);
				}
				Filter(reset: false);
			}
		}

		private void BindPage()
		{
			((Selectable)favorites).interactable = FavoritesStore.Readable(Player.m_localPlayer);
			subtitle.text = ((target == null) ? "Choose an item first" : FamilyRules.Label(Classification.Of(target).Kind));
			for (int i = 0; i < cells.Length; i++)
			{
				int num = CollectionPages.Index(page, i);
				CollectedAppearance collectedAppearance = ((num < entries.Count) ? entries[num] : null);
				cells[i].Bind(AppearanceCollection.Preview(collectedAppearance), AppearanceCollection.Icon(collectedAppearance), collectedAppearance?.Equals(Selected) ?? false, collectedAppearance != null);
				((Component)stars[i]).gameObject.SetActive(collectedAppearance != null);
				((Selectable)stars[i]).interactable = FavoritesStore.Readable(Player.m_localPlayer);
				((Component)stars[i]).GetComponentInChildren<TMP_Text>().text = (FavoritesStore.Favorite(Player.m_localPlayer, collectedAppearance) ? "★" : "☆");
			}
			empty.text = ((target == null) ? "Choose the item you want to change." : ((!AppearanceCollection.Readable(Player.m_localPlayer)) ? "Saved appearances could not be read." : ((entries.Count != 0) ? "" : ((matching.Count > 0) ? "No results. Clear search or favorites." : (particleCollection ? "No matching particles collected yet." : "No matching appearances collected yet.")))));
			((Selectable)previous).interactable = page > 0;
			((Selectable)next).interactable = page + 1 < CollectionPages.Count(entries.Count);
			pageLabel.text = page + 1 + " / " + CollectionPages.Count(entries.Count);
		}

		private void Highlight()
		{
			for (int i = 0; i < cells.Length; i++)
			{
				int num = CollectionPages.Index(page, i);
				cells[i].SetSelected(num < entries.Count && entries[num].Equals(Selected));
			}
		}

		private void TurnPage(int direction)
		{
			if (!UiSafety.Allowed())
			{
				IllusionWindow.Close();
				return;
			}
			page = CollectionPages.Clamp(page + direction, entries.Count);
			BindPage();
		}

		private void Choose(int cell)
		{
			int num = CollectionPages.Index(page, cell);
			if (num < entries.Count)
			{
				if (particleCollection)
				{
					IllusionWindow.SelectParticles(entries[num]);
				}
				else
				{
					IllusionWindow.SelectAppearance(entries[num]);
				}
			}
		}
	}

	private static readonly Panel Models = new Panel(particles: false);

	private static readonly Panel Particles = new Panel(particles: true);

	internal static bool IsOpen
	{
		get
		{
			if (!Models.IsOpen)
			{
				return Particles.IsOpen;
			}
			return true;
		}
	}

	internal static bool CoversPointer(Vector2 position)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		if (!Models.CoversPointer(position))
		{
			return Particles.CoversPointer(position);
		}
		return true;
	}

	internal static void Toggle()
	{
		Models.Toggle();
	}

	internal static void ToggleParticles()
	{
		Particles.Toggle();
	}

	internal static void CloseParticles()
	{
		Particles.Close();
	}

	internal static void Close()
	{
		Models.Close();
		Particles.Close();
	}

	internal static void CloseLast()
	{
		if (Particles.IsOpen)
		{
			Particles.Close();
		}
		else
		{
			Models.Close();
		}
	}

	internal static void Tick()
	{
		Models.Tick();
		Particles.Tick();
	}

	internal static void Refresh()
	{
		Models.Refresh();
		Particles.Refresh();
	}
}
public enum ModelPurpose
{
	Equipped,
	Dropped,
	Thrown
}
public static class Compatibility
{
	internal sealed class GearRule
	{
		internal GearKind Family;

		internal string Moveset;
	}

	internal sealed class ModelRule
	{
		internal string Path;

		internal bool Skinned;

		internal Vector3 Offset;

		internal Vector3 Rotation;

		internal Vector3 Scale;
	}

	private static readonly Dictionary<string, GearRule> gear = new Dictionary<string, GearRule>(StringComparer.Ordinal);

	private static readonly Dictionary<string, ModelRule> models = new Dictionary<string, ModelRule>(StringComparer.Ordinal);

	private static readonly HashSet<Type> storage = new HashSet<Type>();

	private static readonly HashSet<Type> processors = new HashSet<Type>();

	private static readonly List<Func<bool>> windows = new List<Func<bool>>();

	internal static int Revision { get; private set; }

	private static void Changed()
	{
		Revision++;
		Plugin.InvalidateDefinitions();
	}

	private static void Name(string name)
	{
		if (string.IsNullOrWhiteSpace(name) || name.Length > 160)
		{
			throw new ArgumentException("An exact prefab name is required.");
		}
		for (int i = 0; i < name.Length; i++)
		{
			if (char.IsControl(name[i]))
			{
				throw new ArgumentException("Invalid prefab name.");
			}
		}
	}

	public static void RegisterGear(string prefabName, GearKind family, string moveset = null)
	{
		Name(prefabName);
		if (family == GearKind.Unsupported || family == GearKind.UnknownWeapon || !Enum.IsDefined(typeof(GearKind), family))
		{
			throw new ArgumentException("A supported equipment family is required.");
		}
		if (moveset != null)
		{
			Name(moveset);
		}
		gear[prefabName] = new GearRule
		{
			Family = family,
			Moveset = moveset
		};
		Changed();
	}

	public static void RegisterModel(string prefabName, ModelPurpose purpose, string path)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		RegisterModel(prefabName, purpose, path, Vector3.zero, Vector3.zero, Vector3.one);
	}

	public static void RegisterModel(string prefabName, ModelPurpose purpose, string path, Vector3 offset, Vector3 rotationDegrees, Vector3 scale, bool skinned = false)
	{
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: 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_015d: Unknown result type (might be due to invalid IL or missing references)
		Name(prefabName);
		if (!Enum.IsDefined(typeof(ModelPurpose), purpose))
		{
			throw new ArgumentException("Unknown model purpose.");
		}
		if (string.IsNullOrWhiteSpace(path) || path.Length > 512 || path.StartsWith("/", StringComparison.Ordinal))
		{
			throw new ArgumentException("A relative child model path is required.");
		}
		string[] array = path.Split(new char[1] { '/' });
		foreach (string text in array)
		{
			if (text.Length == 0 || text == "." || text == "..")
			{
				throw new ArgumentException("Model paths must stay beneath their prefab.");
			}
		}
		for (int i = 0; i < path.Length; i++)
		{
			if (char.IsControl(path[i]))
			{
				throw new ArgumentException("Invalid model path.");
			}
		}
		if (!Finite(offset) || !Finite(rotationDegrees) || !Finite(scale) || scale.x <= 0f || scale.y <= 0f || scale.z <= 0f)
		{
			throw new ArgumentException("Model transforms must be finite, with positive scale.");
		}
		models[prefabName + ":" + purpose] = new ModelRule
		{
			Path = path,
			Offset = offset,
			Rotation = rotationDegrees,
			Scale = scale,
			Skinned = skinned
		};
		Changed();
	}

	private static bool Finite(Vector3 value)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		if (!float.IsNaN(value.x) && !float.IsNaN(value.y) && !float.IsNaN(value.z) && !float.IsInfinity(value.x) && !float.IsInfinity(value.y))
		{
			return !float.IsInfinity(value.z);
		}
		return false;
	}

	private static void ComponentType(Type type)
	{
		if (type == null || type == typeof(MonoBehaviour) || !typeof(MonoBehaviour).IsAssignableFrom(type))
		{
			throw new ArgumentException("An exact MonoBehaviour component type is required.");
		}
	}

	public static void RegisterStorageComponent(Type type)
	{
		ComponentType(type);
		if (processors.Contains(type))
		{
			throw new InvalidOperationException("A processor cannot be approved as storage.");
		}
		storage.Add(type);
		Changed();
	}

	public static void RegisterProcessorComponent(Type type)
	{
		ComponentType(type);
		storage.Remove(type);
		processors.Add(type);
		Changed();
	}

	public static void RegisterBlockingWindow(Func<bool> isVisible)
	{
		if (isVisible == null)
		{
			throw new ArgumentNullException("isVisible");
		}
		if (!windows.Contains(isVisible))
		{
			windows.Add(isVisible);
			Changed();
		}
	}

	public static void UnregisterGear(string prefabName)
	{
		Name(prefabName);
		if (gear.Remove(prefabName))
		{
			Changed();
		}
	}

	public static void UnregisterModel(string prefabName, ModelPurpose purpose)
	{
		Name(prefabName);
		if (models.Remove(prefabName + ":" + purpose))
		{
			Changed();
		}
	}

	public static void UnregisterStorageComponent(Type type)
	{
		if (type != null && storage.Remove(type))
		{
			Changed();
		}
	}

	public static void UnregisterProcessorComponent(Type type)
	{
		if (type != null && processors.Remove(type))
		{
			Changed();
		}
	}

	public static void UnregisterBlockingWindow(Func<bool> isVisible)
	{
		if (isVisible != null && windows.Remove(isVisible))
		{
			Changed();
		}
	}

	internal static bool Gear(string name, out GearRule rule)
	{
		return gear.TryGetValue(name, out rule);
	}

	internal static bool Model(string name, ModelPurpose purpose, out ModelRule rule)
	{
		return models.TryGetValue(name + ":" + purpose, out rule);
	}

	internal static bool Storage(Type type)
	{
		return storage.Contains(type);
	}

	internal static bool Processor(Type type)
	{
		return processors.Contains(type);
	}

	internal static bool WindowVisible()
	{
		foreach (Func<bool> window in windows)
		{
			if (window())
			{
				return true;
			}
		}
		return false;
	}
}
internal static class DiagnosticReport
{
	private static string Cell(object value)
	{
		return (value?.ToString() ?? "").Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ');
	}

	internal static string Build(IEnumerable<GameObject> prefabs, string filter = "")
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("GooCosmetics 0.0.2 [release]");
		stringBuilder.AppendLine("Definition inspection only; rendering, gameplay and multiplayer checks require a live test.");
		foreach (Features.State state in Features.States)
		{
			stringBuilder.AppendLine("Feature\t" + state.Feature.ToString() + "\t" + (state.Available ? "Available" : "Unavailable") + "\t" + Cell(state.Failure));
		}
		stringBuilder.AppendLine("Prefab\tFamily\tClassification evidence\tPrimary\tSecondary\tWorn model\tDrop model\tThrown model\tIcons\tParticle systems\tLights\tRegistration\tIssue");
		foreach (GameObject prefab in prefabs)
		{
			if (!Object.op_Implicit((Object)(object)prefab) || (!string.IsNullOrEmpty(filter) && ((Object)prefab).name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) < 0))
			{
				continue;
			}
			try
			{
				ItemData val = prefab.GetComponent<ItemDrop>()?.m_itemData;
				if (val?.m_shared == null)
				{
					continue;
				}
				GearSignature gearSignature = Classification.Of(prefab);
				bool flag = VisualCapabilities.Equipment(prefab, gearSignature.Kind);
				ModelPlan modelPlan = VisualCapabilities.Dropped(prefab);
				ModelPlan modelPlan2 = ((gearSignature.Kind == GearKind.Spear) ? VisualCapabilities.Thrown(prefab) : null);
				Compatibility.GearRule rule;
				bool flag2 = Compatibility.Gear(((Object)prefab).name, out rule);
				string text = ((gearSignature.Kind == GearKind.Unsupported) ? gearSignature.Issue : ((!flag) ? "No worn model; application unavailable" : ""));
				foreach (ModelPurpose value in Enum.GetValues(typeof(ModelPurpose)))
				{
					if (Compatibility.Model(((Object)prefab).name, value, out var rule2))
					{
						flag2 = true;
						GameObject root = ((value != ModelPurpose.Thrown) ? prefab : val.m_shared.m_secondaryAttack?.m_attackProjectile);
						if (!Object.op_Implicit((Object)(object)VisualCapabilities.Registered(prefab, root, value, out var _)))
						{
							text = text + " Missing " + value.ToString() + " path: " + rule2.Path + ";";
						}
						if (value == ModelPurpose.Equipped && !rule2.Skinned && (gearSignature.Kind == GearKind.Cape || gearSignature.Kind == GearKind.Chest || gearSignature.Kind == GearKind.Legs))
						{
							text += " Armor registration requires skinned=true;";
						}
					}
				}
				string[] obj = new string[13]
				{
					Cell(((Object)prefab).name),
					gearSignature.Kind.ToString(),
					Cell(gearSignature.Evidence),
					Cell(gearSignature.Primary),
					Cell(gearSignature.Secondary),
					flag ? "Available" : "Missing",
					Cell(modelPlan?.Description ?? "Original fallback"),
					Cell(modelPlan2?.Description ?? ((gearSignature.Kind == GearKind.Spear) ? "Original fallback" : "N/A")),
					null,
					null,
					null,
					null,
					null
				};
				Sprite[] icons = val.m_shared.m_icons;
				obj[8] = ((icons != null) ? icons.Length : 0).ToString();
				obj[9] = prefab.GetComponentsInChildren<ParticleSystem>(true).Length.ToString();
				obj[10] = prefab.GetComponentsInChildren<Light>(true).Length.ToString();
				obj[11] = (flag2 ? "Yes" : "No");
				obj[12] = Cell(text);
				stringBuilder.AppendLine(string.Join("\t", obj));
			}
			catch (Exception ex)
			{
				stringBuilder.AppendLine(Cell(((Object)prefab).name) + "\tERROR\t" + Cell(ex.GetBaseException().Message));
			}
		}
		return stringBuilder.ToString();
	}
}
internal static class Diagnostics
{
	private static ConsoleCommand command;

	private static readonly FieldInfo Commands = AccessTools.Field(typeof(Terminal), "commands");

	internal static void Register()
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Expected O, but got Unknown
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		if (((Commands?.GetValue(null) as Dictionary<string, ConsoleCommand>) ?? throw new MissingFieldException("Terminal.commands")).ContainsKey("goocosmetics"))
		{
			throw new InvalidOperationException("Console command goocosmetics is already registered.");
		}
		command = new ConsoleCommand("goocosmetics", "goocosmetics report [prefab filter] - write a local diagnostic report", new ConsoleEvent(Run), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static void Run(ConsoleEventArgs args)
	{
		if (args.Length < 2 || !string.Equals(args[1], "report", StringComparison.OrdinalIgnoreCase))
		{
			args.Context.AddString("Usage: goocosmetics report [prefab filter]");
			return;
		}
		if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			args.Context.AddString("Load a world first.");
			return;
		}
		try
		{
			string contents = DiagnosticReport.Build(ObjectDB.instance.m_items, (args.Length > 2) ? args[2] : "");
			string text = Path.Combine(Paths.ConfigPath, "GooCosmetics", "diagnostics");
			Directory.CreateDirectory(text);
			string text2 = Path.Combine(text, "GooCosmetics-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fff") + ".tsv");
			File.WriteAllText(text2, contents, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
			args.Context.AddString("GooCosmetics report: " + text2);
			Plugin.LogInfo("Diagnostic report written: " + text2);
		}
		catch (Exception ex)
		{
			Plugin.WarnOnce("Diagnostic report", ex);
			args.Context.AddString("Report failed: " + ex.GetBaseException().Message);
		}
	}

	internal static void Unregister()
	{
		Dictionary<string, ConsoleCommand> dictionary = Commands?.GetValue(null) as Dictionary<string, ConsoleCommand>;
		if (command != null && dictionary != null && dictionary.TryGetValue("goocosmetics", out var value) && value == command)
		{
			dictionary.Remove("goocosmetics");
		}
		command = null;
	}
}
internal static class EpicLootBridge
{
	private static MethodInfo decoratedName;

	internal static bool Present => Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot");

	internal static void InstallNames(Harmony harmony)
	{
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("EpicLoot.ItemDataExtensions");
		decoratedName = ((type == null) ? null : AccessTools.Method(type, "GetDecoratedName", new Type[2]
		{
			typeof(ItemData),
			typeof(string)
		}, (Type[])null));
		if (Present && decoratedName == null)
		{
			throw new MissingMethodException("Epic Loot decorated-name method is unavailable.");
		}
		if (decoratedName != null)
		{
			harmony.Patch((MethodBase)decoratedName, (HarmonyMethod)null, new HarmonyMethod(typeof(EpicLootBridge), "DecoratedNamePostfix", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}

	internal static void InstallEffects(Harmony harmony)
	{
		//IL_0072: 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_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		//IL_009b: Expected O, but got Unknown
		if (Present)
		{
			Type type = AccessTools.TypeByName("EpicLoot.VisEquipment_Patch");
			if (type == null)
			{
				throw new TypeLoadException("Epic Loot equipment presentation type is unavailable.");
			}
			PatchEffectGate(harmony, type, "SetTextureOverrides", "AllowTextureOverride");
			MethodInfo methodInfo = AccessTools.Method(type, "SetItemFx", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException(type.FullName, "SetItemFx");
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(EpicLootBridge), "AllowItemFx", (Type[])null), new HarmonyMethod(typeof(EpicLootBridge), "SuppressItemParticles", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}

	private static void PatchEffectGate(Harmony harmony, Type type, string method, string prefix)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			throw new MissingMethodException(type.FullName, method);
		}
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(EpicLootBridge), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	internal static string DecoratedName(ItemData item)
	{
		if (!Features.Has(Feature.EpicLootNames) || decoratedName == null)
		{
			return null;
		}
		try
		{
			return (string)decoratedName.Invoke(null, new object[2] { item, null });
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Epic Loot item name", error);
			return null;
		}
	}

	private static void DecoratedNamePostfix(ItemData __0, ref string __result)
	{
		if (Illusions.Has(__0))
		{
			__result = Presentation.MarkName(__result);
		}
	}

	private static bool AllowTextureOverride()
	{
		if (Visuals.BuildingParticles == 0)
		{
			if (Visuals.Current != null)
			{
				return Visuals.Current.Appearance.Donor == Visuals.Current.Appearance.Original;
			}
			return true;
		}
		return false;
	}

	private static bool AllowItemFx()
	{
		if (Visuals.BuildingParticles == 0)
		{
			if (Visuals.Current != null && !Visuals.Current.Appearance.PreserveParticles)
			{
				return Visuals.Current.Appearance.NoParticles;
			}
			return true;
		}
		return false;
	}

	private static void SuppressItemParticles(GameObject __0)
	{
		if (Object.op_Implicit((Object)(object)__0) && Visuals.BuildingParticles == 0 && Visuals.Current != null && Visuals.Current.Appearance.NoParticles)
		{
			ParticleLayers.HideAmbientEffects(__0);
		}
	}
}
internal static class EquipmentModels
{
	private static GameObject ContextPrefab()
	{
		Visuals.RenderContext current = Visuals.Current;
		if (current == null)
		{
			return null;
		}
		if (Visuals.BuildingParticles <= 0)
		{
			return current.Donor;
		}
		if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return null;
		}
		return ObjectDB.instance.GetItemPrefab(current.OriginalHash);
	}

	private static GameObject Registered(Transform root, out Compatibility.ModelRule rule)
	{
		rule = null;
		GameObject val = ContextPrefab();
		if (!Object.op_Implicit((Object)(object)val) || (Object)(object)root != (Object)(object)val.transform)
		{
			return null;
		}
		GameObject val2 = VisualCapabilities.Registered(val, val, ModelPurpose.Equipped, out rule);
		GearKind kind = Classification.Of(val).Kind;
		if (Object.op_Implicit((Object)(object)val2) && !rule.Skinned && (kind == GearKind.Chest || kind == GearKind.Legs || kind == GearKind.Cape))
		{
			return null;
		}
		return val2;
	}

	internal static int ChildCount(Transform root)
	{
		if (!Object.op_Implicit((Object)(object)Registered(root, out var _)))
		{
			return root.childCount;
		}
		return 1;
	}

	internal static Transform Child(Transform root, int index)
	{
		Compatibility.ModelRule rule;
		GameObject val = Registered(root, out rule);
		if (!Object.op_Implicit((Object)(object)val) || index != 0)
		{
			return root.GetChild(index);
		}
		return val.transform;
	}

	internal static string ModelName(Object value)
	{
		GameObject val = ContextPrefab();
		if (Object.op_Implicit((Object)(object)val))
		{
			Compatibility.ModelRule rule;
			GameObject val2 = Registered(val.transform, out rule);
			if (Object.op_Implicit((Object)(object)val2) && (value == (Object)(object)val2 || value == (Object)(object)val2.transform))
			{
				if (!rule.Skinned)
				{
					return "attach";
				}
				return "attach_skin";
			}
		}
		return value.name;
	}

	internal static void Place(GameObject instance)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: 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)
		GameObject val = ContextPrefab();
		if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)Registered(val.transform, out var rule)))
		{
			Transform transform = instance.transform;
			transform.localPosition += rule.Offset;
			Transform transform2 = instance.transform;
			transform2.localRotation *= Quaternion.Euler(rule.Rotation);
			instance.transform.localScale = Vector3.Scale(instance.transform.localScale, rule.Scale);
		}
	}
}
internal enum GearSlot
{
	Right,
	Left,
	RightBack,
	LeftBack,
	Chest,
	Legs,
	Helmet,
	Cape
}
internal static class EquipmentNetwork
{
	internal sealed class PreviewSlots
	{
		internal readonly string[] Raw = new string[8];
	}

	private static readonly string[] ItemFields = new string[8] { "m_rightItem", "m_leftItem", "m_hiddenRightItem", "m_hiddenLeftItem", "m_chestItem", "m_legItem", "m_helmetItem", "m_shoulderItem" };

	private static readonly FieldInfo[] Fields = BuildFields();

	private static readonly int[] Keys = BuildKeys();

	internal static readonly ConditionalWeakTable<VisEquipment, PreviewSlots> Previews = new ConditionalWeakTable<VisEquipment, PreviewSlots>();

	internal static event Action LocalEquipmentChanged;

	internal static ItemData Item(Player player, GearSlot slot)
	{
		if (!Object.op_Implicit((Object)(object)player))
		{
			return null;
		}
		object? value = Fields[(int)slot].GetValue(player);
		return (ItemData)((value is ItemData) ? value : null);
	}

	private static FieldInfo[] BuildFields()
	{
		FieldInfo[] array = new FieldInfo[ItemFields.Length];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = AccessTools.Field(typeof(Humanoid), ItemFields[i]) ?? throw new MissingFieldException(ItemFields[i]);
		}
		return array;
	}

	private static int[] BuildKeys()
	{
		int[] array = new int[ItemFields.Length];
		for (int i = 0; i < array.Length; i++)
		{
			int num = i;
			GearSlot gearSlot = (GearSlot)i;
			array[num] = StringExtensionMethods.GetStableHashCode("gnls.GooCosmetics.slot." + gearSlot);
		}
		return array;
	}

	internal static string Read(VisEquipment visual, GearSlot slot)
	{
		if (Previews.TryGetValue(visual, out var value))
		{
			return value.Raw[(int)slot] ?? "";
		}
		Player component = ((Component)visual).GetComponent<Player>();
		if (Object.op_Implicit((Object)(object)component) && ((Object)(object)component == (Object)(object)Player.m_localPlayer || (Object.op_Implicit((Object)(object)FejdStartup.instance) && (Object)(object)component == (Object)(object)FejdStartup.instance.GetPreviewPlayer())))
		{
			object? value2 = Fields[(int)slot].GetValue(component);
			return Illusions.Raw((ItemData)((value2 is ItemData) ? value2 : null));
		}
		if (!Plugin.SeeOthers.Value)
		{
			return "";
		}
		ZNetView val = Native.Equipment.View.Invoke(visual);
		if (!Object.op_Implicit((Object)(object)val) || !val.IsValid())
		{
			return "";
		}
		return val.GetZDO().GetString(Keys[(int)slot], "");
	}

	internal static void Publish(Humanoid humanoid, VisEquipment visual)
	{
		if (Object.op_Implicit((Object)(object)visual) && Previews.TryGetValue(visual, out var _))
		{
			return;
		}
		if ((Object)(object)humanoid == (Object)(object)Player.m_localPlayer)
		{
			EquipmentNetwork.LocalEquipmentChanged?.Invoke();
		}
		if (!Features.Has(Feature.Equipment) || !(humanoid is Player) || !Object.op_Implicit((Object)(object)visual))
		{
			return;
		}
		ZNetView val = Native.Equipment.View.Invoke(visual);
		if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner())
		{
			return;
		}
		ZDO zDO = val.GetZDO();
		for (int i = 0; i < Fields.Length; i++)
		{
			object? value2 = Fields[i].GetValue(humanoid);
			string text = Illusions.Raw((ItemData)((value2 is ItemData) ? value2 : null));
			if (text.Length > 2100)
			{
				text = "";
			}
			if (zDO.GetString(Keys[i], "") != text)
			{
				zDO.Set(Keys[i], text);
			}
		}
	}
}
[HarmonyPatch(typeof(Humanoid), "SetupVisEquipment", new Type[]
{
	typeof(VisEquipment),
	typeof(bool)
})]
internal static class PublishEquipmentPatch
{
	[HarmonyPostfix]
	private static void Postfix(Humanoid __instance, VisEquipment visEq)
	{
		try
		{
			EquipmentNetwork.Publish(__instance, visEq);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Cosmetic equipment publication", error);
		}
	}
}
public enum GearKind
{
	Unsupported,
	Sword1H,
	Axe1H,
	Mace1H,
	Knife,
	Spear,
	Fists,
	Sword2H,
	Axe2H,
	Sledge,
	Atgeir,
	DualAxes,
	DualKnives,
	Bow,
	Crossbow,
	ElementalStaff,
	BloodStaff,
	Pickaxe,
	Buckler,
	RoundShield,
	TowerShield,
	Chest,
	Legs,
	Cape,
	Helmet,
	UnknownWeapon
}
internal sealed class GearSignature
{
	internal GearKind Kind;

	internal int Stance;

	internal int Skill;

	internal int ItemType;

	internal string Primary = "";

	internal string Secondary = "";

	internal int PrimaryChain;

	internal int SecondaryChain;

	internal int PrimaryType;

	internal int SecondaryType;

	internal int KnownSlot = -1;

	internal int KnownStance = -1;

	internal string Evidence = "Unsupported item";

	internal string Issue = "";

	internal bool IsWeapon => Weapon(Kind);

	internal static bool Weapon(GearKind kind)
	{
		if (kind < GearKind.Sword1H || kind > GearKind.Pickaxe)
		{
			return kind == GearKind.UnknownWeapon;
		}
		return true;
	}
}
internal static class FamilyRules
{
	internal static bool Compatible(GearSignature original, GearSignature donor, bool allowUnknown = false)
	{
		if (original == null || donor == null || original.Kind == GearKind.Unsupported || original.Kind != donor.Kind)
		{
			return false;
		}
		if (original.Kind == GearKind.UnknownWeapon)
		{
			if (allowUnknown && original.Primary.Length > 0 && donor.Primary.Length > 0 && (original.KnownSlot < 0 || donor.KnownSlot < 0 || original.KnownSlot == donor.KnownSlot))
			{
				if (original.KnownStance >= 0 && donor.KnownStance >= 0)
				{
					return original.KnownStance == donor.KnownStance;
				}
				return true;
			}
			return false;
		}
		if (!original.IsWeapon)
		{
			return true;
		}
		if (original.Stance == donor.Stance && original.Skill == donor.Skill && original.ItemType == donor.ItemType && original.Primary.Length > 0 && string.Equals(original.Primary, donor.Primary, StringComparison.Ordinal) && string.Equals(original.Secondary, donor.Secondary, StringComparison.Ordinal) && original.PrimaryChain == donor.PrimaryChain && original.SecondaryChain == donor.SecondaryChain && original.PrimaryType == donor.PrimaryType)
		{
			if (original.Secondary.Length != 0)
			{
				return original.SecondaryType == donor.SecondaryType;
			}
			return true;
		}
		return false;
	}

	internal static string Label(GearKind kind)
	{
		return kind switch
		{
			GearKind.Sword1H => "One-handed sword", 
			GearKind.Axe1H => "One-handed axe", 
			GearKind.Mace1H => "One-handed club / mace", 
			GearKind.Knife => "Knife / dagger", 
			GearKind.Spear => "Spear", 
			GearKind.Fists => "Fist weapon", 
			GearKind.Sword2H => "Two-handed sword", 
			GearKind.Axe2H => "Two-handed axe", 
			GearKind.Sledge => "Two-handed club / sledge", 
			GearKind.Atgeir => "Atgeir / polearm", 
			GearKind.DualAxes => "Paired axes", 
			GearKind.DualKnives => "Paired knives", 
			GearKind.Bow => "Bow", 
			GearKind.Crossbow => "Crossbow", 
			GearKind.ElementalStaff => "Elemental staff", 
			GearKind.BloodStaff => "Blood magic staff", 
			GearKind.Pickaxe => "Pickaxe", 
			GearKind.Buckler => "Buckler", 
			GearKind.RoundShield => "Round shield", 
			GearKind.TowerShield => "Tower shield", 
			GearKind.Chest => "Chest clothing", 
			GearKind.Legs => "Leg clothing", 
			GearKind.Cape => "Cape", 
			GearKind.Helmet => "Headwear", 
			GearKind.UnknownWeapon => "Unclassified weapon", 
			_ => "Unsupported gear", 
		};
	}

	internal static string Requirement(GearKind kind)
	{
		return kind switch
		{
			GearKind.Chest => "Choose another piece of chest armor or clothing.", 
			GearKind.Legs => "Choose another piece of leg armor or clothing.", 
			GearKind.Helmet => "Choose another helmet or hat.", 
			GearKind.DualAxes => "Choose another pair of axes.", 
			GearKind.DualKnives => "Choose another pair of knives.", 
			GearKind.Mace1H => "Choose another one-handed club or mace.", 
			GearKind.Knife => "Choose another knife or dagger.", 
			GearKind.Sledge => "Choose another two-handed club or sledge.", 
			GearKind.Atgeir => "Choose another atgeir or polearm.", 
			GearKind.Unsupported => "This item can’t be used as an appearance.", 
			GearKind.UnknownWeapon => "Choose another unclassified weapon with the same equipment position.", 
			_ => "Choose another " + Label(kind).ToLowerInvariant() + ".", 
		};
	}
}
internal enum Feature
{
	Core,
	Equipment,
	Icons,
	Indicators,
	Drops,
	Spears,
	EpicLootNames,
	EpicLootEffects,
	ExternalUi,
	Diagnostics,
	Collection,
	Preview
}
internal static class Features
{
	internal sealed class State
	{
		internal Feature Feature;

		internal bool Available;

		internal string Failure;

		internal Harmony Owner;
	}

	private static readonly Dictionary<Feature, State> states = new Dictionary<Feature, State>();

	private static bool rollbackFailed;

	internal static IEnumerable<State> States => states.Values;

	internal static bool EquipmentReady
	{
		get
		{
			if (Has(Feature.Equipment))
			{
				if (EpicLootBridge.Present)
				{
					return Has(Feature.EpicLootEffects);
				}
				return true;
			}
			return false;
		}
	}

	internal static bool Has(Feature feature)
	{
		if (states.TryGetValue(feature, out var value))
		{
			return value.Available;
		}
		return false;
	}

	internal static bool Install(Feature feature, Action validate, Action<Harmony> install)
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		if (states.ContainsKey(feature))
		{
			throw new InvalidOperationException("Feature already initialized: " + feature);
		}
		State state = new State
		{
			Feature = feature,
			Owner = new Harmony("gnls.GooCosmetics." + feature)
		};
		states.Add(feature, state);
		try
		{
			validate?.Invoke();
			install(state.Owner);
			state.Available = true;
			return true;
		}
		catch (Exception ex)
		{
			state.Failure = ex.GetBaseException().Message;
			try
			{
				state.Owner.UnpatchSelf();
			}
			catch (Exception ex2)
			{
				rollbackFailed = true;
				state.Failure = state.Failure + "; rollback failed: " + ex2.Message;
				Plugin.WarnOnce(feature.ToString() + " patch rollback", ex2);
			}
			Plugin.WarnOnce(feature.ToString() + " unavailable", ex);
			return false;
		}
	}

	internal static void PatchClasses(Harmony owner, params Type[] classes)
	{
		List<PatchClassProcessor> list = new List<PatchClassProcessor>();
		foreach (Type type in classes)
		{
			list.Add(owner.CreateClassProcessor(type));
		}
		foreach (PatchClassProcessor item in list)
		{
			item.Patch();
		}
	}

	internal static string ApplicationProblem()
	{
		if (!Has(Feature.Core))
		{
			return "Item application is unavailable. See BepInEx/LogOutput.log.";
		}
		if (rollbackFailed)
		{
			return "Patch recovery failed. Restart Valheim and check BepInEx/LogOutput.log.";
		}
		if (!Has(Feature.Equipment))
		{
			return "Equipment rendering is unavailable. See BepInEx/LogOutput.log.";
		}
		if (!EquipmentReady)
		{
			return "Epic Loot visual compatibility is unavailable. See BepInEx/LogOutput.log.";
		}
		return null;
	}

	internal static void Stop()
	{
		foreach (State value in states.Values)
		{
			value.Available = false;
			try
			{
				value.Owner.UnpatchSelf();
			}
			catch (Exception error)
			{
				Plugin.WarnOnce(value.Feature.ToString() + " cleanup", error);
			}
		}
		states.Clear();
		rollbackFailed = false;
	}
}
internal static class IllusionWindow
{
	private static GameObject root;

	private static ItemData original;

	private static readonly AppearanceSelection appearance = new AppearanceSelection();

	private static readonly ParticleSelection particles = new ParticleSelection();

	private static NativeItemCell originalCell;

	private static NativeItemCell appearanceCell;

	private static NativeItemCell particleCell;

	private static TMP_Text originalName;

	private static TMP_Text originalType;

	private static TMP_Text appearanceName;

	private static TMP_Text particleName;

	private static TMP_Text particleDetail;

	private static TMP_Text iconScope;

	private static TMP_Text feedback;

	private static Button apply;

	private static Button remove;

	private static Button collection;

	private static Button particleCollection;

	private static Button clearParticles;

	private static Button preview;

	private static Button undo;

	private static Toggle hideParticles;

	private static Toggle originalLook;

	private static Toggle originalParticles;

	private static Toggle donorIcons;

	private static GameObject particleSection;

	private static Color feedbackColor