Decompiled source of QuickWardrobe v1.0.0

plugins/QuickWardrobe.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using QuickWardrobe.Game;
using QuickWardrobe.Net;
using QuickWardrobe.Profiles;
using QuickWardrobe.UI;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("QuickWardrobe")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("QuickWardrobe")]
[assembly: AssemblyTitle("QuickWardrobe")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace QuickWardrobe
{
	public enum TransitionMode
	{
		Smoke,
		Instant
	}
	[BepInPlugin("QuickWardrobe", "QuickWardrobe", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "QuickWardrobe";

		public const string PluginName = "QuickWardrobe";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> SlotCount;

		internal static ConfigEntry<bool> ConfirmOverwrite;

		internal static ConfigEntry<TransitionMode> Transition;

		internal static ConfigEntry<float> SmokeLeadIn;

		internal static ConfigEntry<float> SmokeMinDuration;

		internal static ConfigEntry<int> FramesBetweenChanges;

		internal static ConfigEntry<bool> SyncSmokeToOthers;

		internal static ConfigEntry<bool> ShowSmokeFromOthers;

		internal static ConfigEntry<float> TabOffsetX;

		internal static ConfigEntry<float> TabOffsetY;

		internal static ConfigEntry<float> TabScale;

		internal static ConfigEntry<float> TabSpacing;

		internal static ConfigEntry<bool> VerboseLogging;

		internal static ProfileStore Store;

		private void Awake()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Expected O, but got Unknown
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SlotCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SlotCount", 5, new ConfigDescription("Number of profile slots shown next to the passport.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 9), Array.Empty<object>()));
			ConfirmOverwrite = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ConfirmOverwrite", true, "Ask for confirmation before overwriting or clearing a slot that already holds a profile.");
			Transition = ((BaseUnityPlugin)this).Config.Bind<TransitionMode>("Transition", "Mode", TransitionMode.Smoke, "Smoke: play the game's own spawn 'poof' around your character while the outfit changes. Instant: no effect.\nBoth modes change cosmetics one slot at a time through the game's own passport logic.");
			SmokeLeadIn = ((BaseUnityPlugin)this).Config.Bind<float>("Transition", "SmokeLeadInSeconds", 0.12f, new ConfigDescription("Delay between the poof starting and the first cosmetic change, so the change happens inside the smoke.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			SmokeMinDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Transition", "SmokeMinDurationSeconds", 0.6f, new ConfigDescription("Minimum length of the whole Smoke transition. Profile buttons ignore clicks until it finishes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			FramesBetweenChanges = ((BaseUnityPlugin)this).Config.Bind<int>("Transition", "FramesBetweenChanges", 1, new ConfigDescription("Frames to wait between two cosmetic changes (both modes).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>()));
			SyncSmokeToOthers = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "SyncSmokeToOthers", true, "Tell other players (who also have QuickWardrobe) to show the poof on your character. Players without the mod simply ignore it.");
			ShowSmokeFromOthers = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "ShowSmokeFromOthers", true, "Show the poof when another QuickWardrobe user applies a profile.");
			TabOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "TabOffsetX", 30f, "Gap between the passport page's left edge and the profile tabs (passport UI units).");
			TabOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "TabOffsetY", 0f, "Vertical offset of the profile tabs relative to the passport page's centre.");
			TabScale = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "TabScale", 0.8f, new ConfigDescription("Scale of each profile tab (1 = same size as a cosmetic button).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.4f, 1.5f), Array.Empty<object>()));
			TabSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "TabSpacing", 54f, new ConfigDescription("Vertical distance between tabs (passport UI units).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 120f), Array.Empty<object>()));
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Log which game method was used for every slot change.");
			Store = new ProfileStore(Path.Combine(Paths.ConfigPath, "QuickWardrobe", "profiles.json"));
			Store.Load();
			GameObject val = new GameObject("QuickWardrobe")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<WardrobeRunner>();
			Log.LogInfo((object)("Loaded. Profiles file: " + Store.FilePath));
		}
	}
	internal sealed class WardrobeRunner : MonoBehaviour
	{
		private PassportProfileTabs tabs;

		private int uiErrorCount;

		internal static WardrobeRunner Instance { get; private set; }

		internal ProfileApplier Applier { get; private set; }

		private void Awake()
		{
			Instance = this;
			Applier = new ProfileApplier((MonoBehaviour)(object)this);
			tabs = new PassportProfileTabs(Applier, Plugin.Store);
			SmokeSync.Register();
		}

		private void OnDestroy()
		{
			SmokeSync.Unregister();
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			if (uiErrorCount >= 5)
			{
				return;
			}
			try
			{
				tabs.Tick();
			}
			catch (Exception ex)
			{
				uiErrorCount++;
				Plugin.Log.LogError((object)("Passport profile tabs error: " + ex));
				if (uiErrorCount >= 5)
				{
					Plugin.Log.LogError((object)"Too many UI errors; profile tabs disabled for this session.");
				}
			}
		}
	}
}
namespace QuickWardrobe.UI
{
	internal sealed class PassportProfileTabs
	{
		private sealed class SlotView
		{
			internal int Slot;

			internal RectTransform Rect;

			internal Image Border;

			internal TextMeshProUGUI Label;
		}

		private const float CosmeticButtonSize = 60f;

		private readonly ProfileApplier applier;

		private readonly ProfileStore store;

		private PassportManager boundManager;

		private bool buildFailed;

		private RectTransform root;

		private readonly List<SlotView> views = new List<SlotView>();

		private TextMeshProUGUI status;

		private RectTransform confirmPanel;

		private TextMeshProUGUI confirmText;

		private Action confirmAction;

		private float statusUntil;

		private float nextRefresh;

		private int builtSlotCount;

		private TMP_FontAsset font;

		private Color textColor = new Color(0.2f, 0.16f, 0.12f);

		private Color activeBorder = new Color(0.9f, 0.45f, 0.1f);

		private Color inactiveBorder = new Color(0.35f, 0.3f, 0.25f);

		private Color paper = new Color(0.96f, 0.93f, 0.85f);

		internal PassportProfileTabs(ProfileApplier applier, ProfileStore store)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			this.applier = applier;
			this.store = store;
		}

		internal void Tick()
		{
			PassportManager instance = PassportManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Unbind();
				return;
			}
			if ((Object)(object)instance != (Object)(object)boundManager || builtSlotCount != Plugin.SlotCount.Value)
			{
				Unbind();
				boundManager = instance;
			}
			if ((Object)(object)root == (Object)null)
			{
				if (buildFailed)
				{
					return;
				}
				Build(instance);
				if ((Object)(object)root == (Object)null)
				{
					return;
				}
			}
			bool isOpen = ((MenuWindow)instance).isOpen;
			if (((Component)root).gameObject.activeSelf != isOpen)
			{
				((Component)root).gameObject.SetActive(isOpen);
				HideConfirm();
				if (isOpen)
				{
					nextRefresh = 0f;
				}
			}
			if (isOpen)
			{
				float unscaledTime = Time.unscaledTime;
				if (unscaledTime >= nextRefresh)
				{
					nextRefresh = unscaledTime + 0.25f;
					Refresh();
				}
				if ((Object)(object)status != (Object)null && ((TMP_Text)status).text.Length > 0 && unscaledTime >= statusUntil)
				{
					((TMP_Text)status).text = "";
				}
			}
		}

		private void Unbind()
		{
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)root).gameObject);
			}
			root = null;
			views.Clear();
			status = null;
			confirmPanel = null;
			confirmText = null;
			confirmAction = null;
			boundManager = null;
			buildFailed = false;
		}

		private static RectTransform FindPassportPage(PassportManager pm)
		{
			PassportButton[] buttons = pm.buttons;
			object obj;
			if (buttons == null)
			{
				obj = null;
			}
			else
			{
				PassportButton? obj2 = ((IEnumerable<PassportButton>)buttons).FirstOrDefault((Func<PassportButton, bool>)((PassportButton b) => (Object)(object)b != (Object)null));
				obj = ((obj2 != null) ? ((Component)obj2).transform : null);
			}
			Transform val = (Transform)obj;
			if ((Object)(object)val == (Object)null || (Object)(object)pm.uiObject == (Object)null)
			{
				return null;
			}
			Transform val2 = pm.uiObject.transform.Find("Canvas/Panel/Panel");
			if ((Object)(object)val2 != (Object)null && val.IsChildOf(val2))
			{
				return (RectTransform)(object)((val2 is RectTransform) ? val2 : null);
			}
			Transform val3 = null;
			Transform parent = val.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)((Component)parent).GetComponent<Canvas>() == (Object)null)
			{
				if ((Object)(object)((Component)parent).GetComponent<Mask>() != (Object)null)
				{
					val3 = parent;
				}
				parent = parent.parent;
			}
			if ((Object)(object)val3 != (Object)null)
			{
				Transform parent2 = val3.parent;
				RectTransform val4 = (RectTransform)(object)((parent2 is RectTransform) ? parent2 : null);
				if (val4 != null)
				{
					Plugin.Log.LogWarning((object)("Passport page not at the expected path; attaching tabs to '" + ((Object)val4).name + "' instead."));
					return val4;
				}
			}
			return null;
		}

		private void Build(PassportManager pm)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//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_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			builtSlotCount = Plugin.SlotCount.Value;
			RectTransform val = FindPassportPage(pm);
			PassportButton val2 = pm.buttons?.FirstOrDefault((Func<PassportButton, bool>)((PassportButton b) => (Object)(object)b != (Object)null));
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				buildFailed = true;
				Plugin.Log.LogWarning((object)"Could not locate the passport page; profile tabs are unavailable in this scene.");
				return;
			}
			if ((Object)(object)pm.nameText != (Object)null)
			{
				font = ((TMP_Text)pm.nameText).font;
				textColor = ((Graphic)pm.nameText).color;
			}
			activeBorder = pm.activeBorderColor;
			inactiveBorder = pm.inactiveBorderColor;
			Transform obj = ((Transform)val).Find("BG");
			Graphic val3 = ((obj != null) ? ((Component)obj).GetComponent<Graphic>() : null);
			if ((Object)(object)val3 != (Object)null)
			{
				paper = new Color(val3.color.r, val3.color.g, val3.color.b, 1f);
			}
			int num = builtSlotCount;
			GameObject val4 = new GameObject("QuickWardrobe_ProfileTabs", new Type[1] { typeof(RectTransform) });
			root = (RectTransform)val4.transform;
			((Transform)root).SetParent((Transform)(object)val, false);
			RectTransform obj2 = root;
			RectTransform obj3 = root;
			Vector2 val5 = default(Vector2);
			((Vector2)(ref val5))..ctor(0f, 0.5f);
			obj3.anchorMax = val5;
			obj2.anchorMin = val5;
			root.pivot = new Vector2(1f, 0.5f);
			for (int num2 = 0; num2 < num; num2++)
			{
				views.Add(CreateSlot(val2, num2 + 1));
			}
			status = NewText((Transform)(object)root, "Status", 20f, (TextAlignmentOptions)260);
			RectTransform rectTransform = ((TMP_Text)status).rectTransform;
			((Vector2)(ref val5))..ctor(1f, 0f);
			rectTransform.anchorMax = val5;
			rectTransform.anchorMin = val5;
			rectTransform.pivot = new Vector2(1f, 1f);
			rectTransform.anchoredPosition = new Vector2(0f, -6f);
			rectTransform.sizeDelta = new Vector2(300f, 80f);
			((TMP_Text)status).text = "";
			CreateConfirmPanel();
			ApplyLayout();
			val4.SetActive(false);
			Plugin.Log.LogInfo((object)("Profile tabs attached to passport page '" + GetPath((Transform)(object)val) + "'."));
		}

		private SlotView CreateSlot(PassportButton template, int slot)
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, (Transform)(object)root, false);
			((Object)val).name = "ProfileButton" + slot;
			val.SetActive(true);
			PassportButton component = val.GetComponent<PassportButton>();
			Image border = (((Object)(object)component != (Object)null) ? component.border : null);
			if ((Object)(object)component != (Object)null)
			{
				if ((Object)(object)component.icon != (Object)null)
				{
					((Component)component.icon).gameObject.SetActive(false);
				}
				if ((Object)(object)component.lockedIcon != (Object)null)
				{
					((Component)component.lockedIcon).gameObject.SetActive(false);
				}
				((Behaviour)component).enabled = false;
			}
			Button component2 = val.GetComponent<Button>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.onClick = new ButtonClickedEvent();
				((UnityEvent)component2.onClick).AddListener((UnityAction)delegate
				{
					OnLeftClick(slot);
				});
				((Selectable)component2).interactable = true;
			}
			val.AddComponent<SlotClickRelay>().Init(slot, OnRightClick);
			RectTransform val2 = (RectTransform)val.transform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 1f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.sizeDelta = new Vector2(60f, 60f);
			Transform parent = val.transform.Find("Box") ?? val.transform;
			TextMeshProUGUI val4 = NewText(parent, "Number", 34f, (TextAlignmentOptions)514);
			((TMP_Text)val4).rectTransform.anchorMin = Vector2.zero;
			((TMP_Text)val4).rectTransform.anchorMax = Vector2.one;
			((TMP_Text)val4).rectTransform.sizeDelta = Vector2.zero;
			((TMP_Text)val4).text = slot.ToString();
			return new SlotView
			{
				Slot = slot,
				Rect = val2,
				Border = border,
				Label = val4
			};
		}

		private void CreateConfirmPanel()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Confirm", new Type[1] { typeof(RectTransform) });
			confirmPanel = (RectTransform)val.transform;
			((Transform)confirmPanel).SetParent((Transform)(object)root, false);
			RectTransform obj = confirmPanel;
			RectTransform obj2 = confirmPanel;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			obj2.anchorMax = val2;
			obj.anchorMin = val2;
			confirmPanel.pivot = new Vector2(1f, 0.5f);
			confirmPanel.sizeDelta = new Vector2(250f, 96f);
			((Graphic)val.AddComponent<Image>()).color = paper;
			Outline obj3 = val.AddComponent<Outline>();
			((Shadow)obj3).effectColor = inactiveBorder;
			((Shadow)obj3).effectDistance = new Vector2(2f, -2f);
			confirmText = NewText((Transform)(object)confirmPanel, "Question", 22f, (TextAlignmentOptions)514);
			RectTransform rectTransform = ((TMP_Text)confirmText).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0.5f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.offsetMin = new Vector2(8f, 0f);
			rectTransform.offsetMax = new Vector2(-8f, -4f);
			NewTextButton((Transform)(object)confirmPanel, "Confirm", activeBorder, new Vector2(0.27f, 0.27f), delegate
			{
				Action action = confirmAction;
				HideConfirm();
				action?.Invoke();
			});
			NewTextButton((Transform)(object)confirmPanel, "Cancel", inactiveBorder, new Vector2(0.73f, 0.27f), HideConfirm);
			val.SetActive(false);
		}

		private void ApplyLayout()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			float value = Plugin.TabSpacing.Value;
			float value2 = Plugin.TabScale.Value;
			root.anchoredPosition = new Vector2(0f - Plugin.TabOffsetX.Value, Plugin.TabOffsetY.Value);
			root.sizeDelta = new Vector2(60f * value2, value * (float)views.Count);
			for (int i = 0; i < views.Count; i++)
			{
				views[i].Rect.anchoredPosition = new Vector2(0f, (0f - value) * ((float)i + 0.5f));
			}
		}

		private void Refresh()
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: 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)
			ApplyLayout();
			CharacterCustomizationData localData = CosmeticSlots.GetLocalData();
			Customization customization = CosmeticSlots.GetCustomization();
			int num = 0;
			int lastAppliedSlot = store.LastAppliedSlot;
			if (lastAppliedSlot > 0 && lastAppliedSlot <= views.Count && CosmeticSlots.IsWorn(store.Get(lastAppliedSlot), localData, customization))
			{
				num = lastAppliedSlot;
			}
			int num2 = 1;
			while (num == 0 && num2 <= views.Count)
			{
				if (CosmeticSlots.IsWorn(store.Get(num2), localData, customization))
				{
					num = num2;
				}
				num2++;
			}
			float value = Plugin.TabScale.Value;
			foreach (SlotView view in views)
			{
				bool flag = store.Get(view.Slot) == null;
				bool flag2 = view.Slot == num;
				((TMP_Text)view.Label).text = (flag ? $"{view.Slot}<size=55%> -</size>" : view.Slot.ToString());
				Color color = textColor;
				color.a = (flag ? 0.35f : 1f);
				((Graphic)view.Label).color = color;
				((TMP_Text)view.Label).fontStyle = (FontStyles)(flag2 ? 1 : 0);
				if ((Object)(object)view.Border != (Object)null)
				{
					((Graphic)view.Border).color = (flag2 ? activeBorder : inactiveBorder);
				}
				((Transform)view.Rect).localScale = Vector3.one * (flag2 ? (value * 1.1f) : value);
			}
		}

		private void ShowStatus(string text, float seconds = 3.5f)
		{
			if (!((Object)(object)status == (Object)null))
			{
				((TMP_Text)status).text = text;
				statusUntil = Time.unscaledTime + seconds;
			}
		}

		private void Ask(int slot, string question, Action onConfirm)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ConfirmOverwrite.Value)
			{
				onConfirm();
				return;
			}
			confirmAction = onConfirm;
			((TMP_Text)confirmText).text = question;
			float y = views[slot - 1].Rect.anchoredPosition.y;
			confirmPanel.anchoredPosition = new Vector2(-12f, y);
			((Component)confirmPanel).gameObject.SetActive(true);
		}

		private void HideConfirm()
		{
			confirmAction = null;
			if ((Object)(object)confirmPanel != (Object)null)
			{
				((Component)confirmPanel).gameObject.SetActive(false);
			}
		}

		private void OnLeftClick(int slot)
		{
			HideConfirm();
			if (applier.IsBusy)
			{
				return;
			}
			CosmeticProfile cosmeticProfile = store.Get(slot);
			if (cosmeticProfile == null)
			{
				ShowStatus($"Profile {slot} is empty.\nRight-click to save your current outfit.");
				return;
			}
			ShowStatus($"Applying Profile {slot}...");
			applier.Apply(cosmeticProfile, delegate(List<SlotResult> results)
			{
				store.MarkApplied(slot);
				ShowStatus(Summarize(slot, results), 5f);
				nextRefresh = 0f;
			});
		}

		private void OnRightClick(int slot)
		{
			if (applier.IsBusy || slot < 1 || slot > views.Count)
			{
				return;
			}
			HideConfirm();
			bool num = Keyboard.current != null && Keyboard.current.shiftKey.isPressed;
			CosmeticProfile cosmeticProfile = store.Get(slot);
			if (num)
			{
				if (cosmeticProfile == null)
				{
					ShowStatus($"Profile {slot} is already empty.");
					return;
				}
				Ask(slot, $"Clear Profile {slot}?", delegate
				{
					ClearSlot(slot);
				});
			}
			else if (cosmeticProfile != null)
			{
				Ask(slot, $"Overwrite Profile {slot}?", delegate
				{
					SaveSlot(slot);
				});
			}
			else
			{
				SaveSlot(slot);
			}
		}

		private void SaveSlot(int slot)
		{
			CosmeticProfile cosmeticProfile = store.SaveCurrent(slot);
			ShowStatus((cosmeticProfile != null) ? $"Saved current outfit to Profile {slot}." : "Could not read your current cosmetics.");
			if (cosmeticProfile != null)
			{
				Plugin.Log.LogInfo((object)$"Saved current cosmetics to slot {slot}.");
			}
			nextRefresh = 0f;
		}

		private void ClearSlot(int slot)
		{
			if (store.Clear(slot))
			{
				ShowStatus($"Profile {slot} cleared.");
			}
			nextRefresh = 0f;
		}

		private static string Summarize(int slot, List<SlotResult> results)
		{
			List<string> list = (from r in results
				where r.Status == SlotStatus.Missing || r.Status == SlotStatus.Locked || r.Status == SlotStatus.Error
				select string.Format("{0} {1}", r.Kind, (r.Status == SlotStatus.Missing) ? "missing" : ((r.Status == SlotStatus.Locked) ? "locked" : "failed"))).ToList();
			if (list.Count == 0)
			{
				return $"Profile {slot} applied.";
			}
			return string.Format("Profile {0} applied.\nSkipped: {1}", slot, string.Join(", ", list));
		}

		private TextMeshProUGUI NewText(Transform parent, string name, float size, TextAlignmentOptions alignment)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)font != (Object)null)
			{
				((TMP_Text)val2).font = font;
			}
			((TMP_Text)val2).fontSize = size;
			((TMP_Text)val2).alignment = alignment;
			((Graphic)val2).color = textColor;
			((Graphic)val2).raycastTarget = false;
			((TMP_Text)val2).richText = true;
			return val2;
		}

		private void NewTextButton(Transform parent, string caption, Color color, Vector2 anchor, Action onClick)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0091: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(caption, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			Vector2 anchorMin = (val2.anchorMax = anchor);
			val2.anchorMin = anchorMin;
			val2.sizeDelta = new Vector2(108f, 34f);
			Image val4 = val.AddComponent<Image>();
			((Graphic)val4).color = color;
			Button obj = val.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)val4;
			((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
			{
				onClick();
			});
			TextMeshProUGUI obj2 = NewText((Transform)(object)val2, "Text", 20f, (TextAlignmentOptions)514);
			((TMP_Text)obj2).rectTransform.anchorMin = Vector2.zero;
			((TMP_Text)obj2).rectTransform.anchorMax = Vector2.one;
			((TMP_Text)obj2).rectTransform.sizeDelta = Vector2.zero;
			((Graphic)obj2).color = ((((Color)(ref color)).grayscale > 0.6f) ? textColor : Color.white);
			((TMP_Text)obj2).text = caption;
		}

		private static string GetPath(Transform t)
		{
			string text = ((Object)t).name;
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}
	}
	internal sealed class SlotClickRelay : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
	{
		private int slot;

		private Action<int> onRightClick;

		internal void Init(int slotNumber, Action<int> rightClick)
		{
			slot = slotNumber;
			onRightClick = rightClick;
		}

		public void OnPointerClick(PointerEventData eventData)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)eventData.button == 1)
			{
				onRightClick?.Invoke(slot);
			}
		}
	}
}
namespace QuickWardrobe.Profiles
{
	[Serializable]
	public class SavedCosmetic
	{
		public int index = -1;

		public string name = "";

		public bool IsSet => index >= 0;
	}
	[Serializable]
	public class CosmeticSet
	{
		public SavedCosmetic skin = new SavedCosmetic();

		public SavedCosmetic eyes = new SavedCosmetic();

		public SavedCosmetic mouth = new SavedCosmetic();

		public SavedCosmetic accessory = new SavedCosmetic();

		public SavedCosmetic outfit = new SavedCosmetic();

		public SavedCosmetic hat = new SavedCosmetic();

		public SavedCosmetic sash = new SavedCosmetic();

		public SavedCosmetic medal = new SavedCosmetic();

		public SavedCosmetic Get(SlotKind kind)
		{
			return kind switch
			{
				SlotKind.Skin => skin, 
				SlotKind.Eyes => eyes, 
				SlotKind.Mouth => mouth, 
				SlotKind.Accessory => accessory, 
				SlotKind.Outfit => outfit, 
				SlotKind.Hat => hat, 
				SlotKind.Sash => sash, 
				SlotKind.Medal => medal, 
				_ => null, 
			};
		}

		public void Set(SlotKind kind, SavedCosmetic value)
		{
			switch (kind)
			{
			case SlotKind.Skin:
				skin = value;
				break;
			case SlotKind.Eyes:
				eyes = value;
				break;
			case SlotKind.Mouth:
				mouth = value;
				break;
			case SlotKind.Accessory:
				accessory = value;
				break;
			case SlotKind.Outfit:
				outfit = value;
				break;
			case SlotKind.Hat:
				hat = value;
				break;
			case SlotKind.Sash:
				sash = value;
				break;
			case SlotKind.Medal:
				medal = value;
				break;
			}
		}
	}
	[Serializable]
	public class CosmeticProfile
	{
		public int slot;

		public string id = "";

		public string name = "";

		public string createdUtc = "";

		public string modifiedUtc = "";

		public CosmeticSet cosmetics = new CosmeticSet();
	}
	[Serializable]
	public class ProfileFile
	{
		public int version = 1;

		public int lastAppliedSlot;

		public List<CosmeticProfile> profiles = new List<CosmeticProfile>();
	}
	internal sealed class ProfileStore
	{
		private ProfileFile data = new ProfileFile();

		internal string FilePath { get; }

		internal int LastAppliedSlot => data.lastAppliedSlot;

		internal ProfileStore(string filePath)
		{
			FilePath = filePath;
		}

		internal void Load()
		{
			if (!File.Exists(FilePath))
			{
				data = new ProfileFile();
				return;
			}
			try
			{
				data = JsonUtility.FromJson<ProfileFile>(File.ReadAllText(FilePath)) ?? new ProfileFile();
				if (data.profiles == null)
				{
					data.profiles = new List<CosmeticProfile>();
				}
				data.profiles.RemoveAll((CosmeticProfile p) => p == null || p.slot <= 0);
				foreach (CosmeticProfile profile in data.profiles)
				{
					if (profile.cosmetics == null)
					{
						profile.cosmetics = new CosmeticSet();
					}
					SlotKind[] applyOrder = CosmeticSlots.ApplyOrder;
					foreach (SlotKind kind in applyOrder)
					{
						if (profile.cosmetics.Get(kind) == null)
						{
							profile.cosmetics.Set(kind, new SavedCosmetic());
						}
					}
				}
			}
			catch (Exception ex)
			{
				string text = FilePath + ".broken-" + DateTime.Now.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture);
				Plugin.Log.LogError((object)("Could not read " + FilePath + " (" + ex.Message + "). Moving it to " + text + " and starting with empty profiles."));
				try
				{
					File.Move(FilePath, text);
				}
				catch
				{
				}
				data = new ProfileFile();
			}
		}

		internal void Save()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
				string text = FilePath + ".tmp";
				File.WriteAllText(text, JsonUtility.ToJson((object)data, true));
				if (File.Exists(FilePath))
				{
					File.Delete(FilePath);
				}
				File.Move(text, FilePath);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not write {FilePath}: {arg}");
			}
		}

		internal CosmeticProfile Get(int slot)
		{
			return data.profiles.Find((CosmeticProfile p) => p.slot == slot);
		}

		internal CosmeticProfile SaveCurrent(int slot)
		{
			CosmeticSet cosmeticSet = CosmeticSlots.CaptureCurrent();
			if (cosmeticSet == null)
			{
				return null;
			}
			string text = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture);
			CosmeticProfile cosmeticProfile = Get(slot);
			if (cosmeticProfile == null)
			{
				cosmeticProfile = new CosmeticProfile
				{
					slot = slot,
					id = Guid.NewGuid().ToString("N"),
					name = "Profile " + slot,
					createdUtc = text
				};
				data.profiles.Add(cosmeticProfile);
				data.profiles.Sort((CosmeticProfile a, CosmeticProfile b) => a.slot.CompareTo(b.slot));
			}
			cosmeticProfile.cosmetics = cosmeticSet;
			cosmeticProfile.modifiedUtc = text;
			Save();
			return cosmeticProfile;
		}

		internal bool Clear(int slot)
		{
			if (data.profiles.RemoveAll((CosmeticProfile p) => p.slot == slot) == 0)
			{
				return false;
			}
			if (data.lastAppliedSlot == slot)
			{
				data.lastAppliedSlot = 0;
			}
			Save();
			return true;
		}

		internal void MarkApplied(int slot)
		{
			if (data.lastAppliedSlot != slot)
			{
				data.lastAppliedSlot = slot;
				Save();
			}
		}
	}
}
namespace QuickWardrobe.Net
{
	internal static class SmokeSync
	{
		internal const byte EventCode = 187;

		private const string Magic = "QuickWardrobe.Smoke";

		private const int ProtocolVersion = 1;

		private const float MinInterval = 0.3f;

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

		private static bool registered;

		internal static void Register()
		{
			if (!registered && PhotonNetwork.NetworkingClient != null)
			{
				PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
				registered = true;
			}
		}

		internal static void Unregister()
		{
			if (registered)
			{
				if (PhotonNetwork.NetworkingClient != null)
				{
					PhotonNetwork.NetworkingClient.EventReceived -= OnEvent;
				}
				registered = false;
			}
		}

		internal static void Send()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode && PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount >= 2)
			{
				PhotonNetwork.RaiseEvent((byte)187, (object)new object[2] { "QuickWardrobe.Smoke", 1 }, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendReliable);
			}
		}

		private static void OnEvent(EventData e)
		{
			if (e.Code != 187 || !(e.CustomData is object[] array) || array.Length < 1 || !(array[0] is string text) || text != "QuickWardrobe.Smoke" || !Plugin.ShowSmokeFromOthers.Value || PhotonNetwork.LocalPlayer == null || e.Sender == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (!lastReceived.TryGetValue(e.Sender, out var value) || !(unscaledTime - value < 0.3f))
			{
				lastReceived[e.Sender] = unscaledTime;
				Character character = default(Character);
				if (PlayerHandler.TryGetCharacter(e.Sender, ref character))
				{
					Smoke.PlayOn(character);
				}
			}
		}
	}
}
namespace QuickWardrobe.Game
{
	public enum SlotKind
	{
		Skin,
		Eyes,
		Mouth,
		Accessory,
		Outfit,
		Hat,
		Sash,
		Medal
	}
	internal enum SlotStatus
	{
		Ok,
		Unchanged,
		NotSaved,
		Missing,
		Locked,
		Error
	}
	internal struct SlotResult
	{
		internal SlotKind Kind;

		internal SlotStatus Status;

		internal string Detail;

		internal bool Changed => Status == SlotStatus.Ok;

		internal static SlotResult Of(SlotKind kind, SlotStatus status, string detail = null)
		{
			return new SlotResult
			{
				Kind = kind,
				Status = status,
				Detail = detail
			};
		}

		public override string ToString()
		{
			string arg = Status switch
			{
				SlotStatus.Ok => "OK", 
				SlotStatus.Unchanged => "OK (already worn)", 
				SlotStatus.NotSaved => "not in profile - skipped", 
				SlotStatus.Missing => "MISSING - skipped", 
				SlotStatus.Locked => "LOCKED - skipped", 
				_ => "ERROR - skipped", 
			};
			if (!string.IsNullOrEmpty(Detail))
			{
				return $"{Kind,-10}{arg} ({Detail})";
			}
			return $"{Kind,-10}{arg}";
		}
	}
	internal static class CosmeticSlots
	{
		internal static readonly SlotKind[] ApplyOrder = new SlotKind[8]
		{
			SlotKind.Skin,
			SlotKind.Eyes,
			SlotKind.Mouth,
			SlotKind.Accessory,
			SlotKind.Outfit,
			SlotKind.Hat,
			SlotKind.Sash,
			SlotKind.Medal
		};

		internal static Type ToGameType(SlotKind kind)
		{
			return (Type)(kind switch
			{
				SlotKind.Skin => 0, 
				SlotKind.Eyes => 20, 
				SlotKind.Mouth => 30, 
				SlotKind.Accessory => 10, 
				SlotKind.Outfit => 40, 
				SlotKind.Hat => 50, 
				SlotKind.Sash => 60, 
				_ => 70, 
			});
		}

		internal static Customization GetCustomization()
		{
			Customization instance = Singleton<Customization>.Instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return null;
			}
			return instance;
		}

		internal static CharacterCustomizationData GetLocalData()
		{
			try
			{
				if (PhotonNetwork.LocalPlayer == null)
				{
					return null;
				}
				PersistentPlayerDataService service = GameHandler.GetService<PersistentPlayerDataService>();
				return (service == null) ? null : service.GetPlayerData(PhotonNetwork.LocalPlayer)?.customizationData;
			}
			catch (Exception)
			{
				return null;
			}
		}

		internal static int GetIndex(CharacterCustomizationData d, SlotKind kind)
		{
			return kind switch
			{
				SlotKind.Skin => d.currentSkin, 
				SlotKind.Eyes => d.currentEyes, 
				SlotKind.Mouth => d.currentMouth, 
				SlotKind.Accessory => d.currentAccessory, 
				SlotKind.Outfit => d.currentOutfit, 
				SlotKind.Hat => d.currentHat, 
				SlotKind.Sash => d.currentSash, 
				_ => d.currentMedal, 
			};
		}

		internal static CosmeticSet CaptureCurrent()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			CharacterCustomizationData localData = GetLocalData();
			Customization customization = GetCustomization();
			if (localData == null || (Object)(object)customization == (Object)null)
			{
				return null;
			}
			CosmeticSet cosmeticSet = new CosmeticSet();
			SlotKind[] applyOrder = ApplyOrder;
			foreach (SlotKind kind in applyOrder)
			{
				int index = GetIndex(localData, kind);
				CustomizationOption[] list = customization.GetList(ToGameType(kind));
				string name = ((list != null && index >= 0 && index < list.Length && (Object)(object)list[index] != (Object)null) ? ((Object)list[index]).name : "");
				cosmeticSet.Set(kind, new SavedCosmetic
				{
					index = index,
					name = name
				});
			}
			return cosmeticSet;
		}

		internal static int Resolve(CustomizationOption[] list, SavedCosmetic saved)
		{
			if (list == null || saved == null || !saved.IsSet)
			{
				return -1;
			}
			bool flag = saved.index < list.Length && (Object)(object)list[saved.index] != (Object)null;
			if (string.IsNullOrEmpty(saved.name))
			{
				if (!flag)
				{
					return -1;
				}
				return saved.index;
			}
			if (flag && ((Object)list[saved.index]).name == saved.name)
			{
				return saved.index;
			}
			for (int i = 0; i < list.Length; i++)
			{
				if ((Object)(object)list[i] != (Object)null && ((Object)list[i]).name == saved.name)
				{
					return i;
				}
			}
			return -1;
		}

		internal static bool IsSelectable(SlotKind kind, CustomizationOption[] list, int index)
		{
			if (kind == SlotKind.Medal)
			{
				AchievementManager instance = Singleton<AchievementManager>.Instance;
				if ((Object)(object)instance == (Object)null || !instance.AllSixtyFourAchievementsUnlocked())
				{
					return false;
				}
				PassportManager instance2 = PassportManager.instance;
				CustomizationOption val = (((Object)(object)instance2 != (Object)null && (Object)(object)instance2.medalOption != (Object)null) ? instance2.medalOption : list[index]);
				if (!((Object)(object)val == (Object)null))
				{
					return !val.IsLocked;
				}
				return true;
			}
			return !list[index].IsLocked;
		}

		internal static SlotResult ApplyOne(SlotKind kind, SavedCosmetic saved)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (saved == null || !saved.IsSet)
				{
					return SlotResult.Of(kind, SlotStatus.NotSaved);
				}
				Customization customization = GetCustomization();
				CharacterCustomizationData localData = GetLocalData();
				if ((Object)(object)customization == (Object)null || localData == null)
				{
					return SlotResult.Of(kind, SlotStatus.Error, "customization system not loaded");
				}
				CustomizationOption[] list = customization.GetList(ToGameType(kind));
				int num = Resolve(list, saved);
				if (num < 0)
				{
					return SlotResult.Of(kind, SlotStatus.Missing, $"saved #{saved.index} '{saved.name}'");
				}
				string detail = ((num != saved.index) ? $"found '{saved.name}' at #{num}" : null);
				if (GetIndex(localData, kind) == num)
				{
					return SlotResult.Of(kind, SlotStatus.Unchanged, detail);
				}
				if (!IsSelectable(kind, list, num))
				{
					return SlotResult.Of(kind, SlotStatus.Locked, ((Object)(object)list[num] != (Object)null) ? ((Object)list[num]).name : null);
				}
				string text = InvokeGame(kind, list[num], num);
				CharacterCustomizationData localData2 = GetLocalData();
				if (localData2 == null || GetIndex(localData2, kind) != num)
				{
					return SlotResult.Of(kind, SlotStatus.Error, "game did not accept the change");
				}
				if (Plugin.VerboseLogging.Value)
				{
					Plugin.Log.LogInfo((object)$"{kind} -> #{num} '{((Object)list[num]).name}' via {text}");
				}
				return SlotResult.Of(kind, SlotStatus.Ok, detail);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"Applying {kind} failed: {ex}");
				return SlotResult.Of(kind, SlotStatus.Error, ex.GetType().Name);
			}
		}

		private static string InvokeGame(SlotKind kind, CustomizationOption option, int index)
		{
			PassportManager instance = PassportManager.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)Character.localCharacter != (Object)null && (Object)(object)option != (Object)null)
			{
				if (kind == SlotKind.Medal)
				{
					instance.SetOption(((Object)(object)instance.medalOption != (Object)null) ? instance.medalOption : option, 0);
				}
				else
				{
					instance.SetOption(option, index);
				}
				return "PassportManager.SetOption";
			}
			switch (kind)
			{
			case SlotKind.Skin:
				CharacterCustomization.SetCharacterSkinColor(index);
				break;
			case SlotKind.Eyes:
				CharacterCustomization.SetCharacterEyes(index);
				break;
			case SlotKind.Mouth:
				CharacterCustomization.SetCharacterMouth(index);
				break;
			case SlotKind.Accessory:
				CharacterCustomization.SetCharacterAccessory(index);
				break;
			case SlotKind.Outfit:
				CharacterCustomization.SetCharacterOutfit(index);
				break;
			case SlotKind.Hat:
				CharacterCustomization.SetCharacterHat(index);
				break;
			case SlotKind.Sash:
				CharacterCustomization.SetCharacterSash(index);
				break;
			case SlotKind.Medal:
				CharacterCustomization.SetCharacterMedal(index);
				break;
			}
			return "CharacterCustomization.SetCharacter" + kind;
		}

		internal static bool IsWorn(CosmeticProfile profile, CharacterCustomizationData data, Customization cust)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (profile?.cosmetics == null || data == null || (Object)(object)cust == (Object)null)
			{
				return false;
			}
			bool result = false;
			SlotKind[] applyOrder = ApplyOrder;
			foreach (SlotKind kind in applyOrder)
			{
				SavedCosmetic savedCosmetic = profile.cosmetics.Get(kind);
				if (savedCosmetic == null || !savedCosmetic.IsSet)
				{
					continue;
				}
				CustomizationOption[] list = cust.GetList(ToGameType(kind));
				int num = Resolve(list, savedCosmetic);
				if (num >= 0)
				{
					if (GetIndex(data, kind) == num)
					{
						result = true;
					}
					else if (IsSelectable(kind, list, num))
					{
						return false;
					}
				}
			}
			return result;
		}
	}
	internal sealed class ProfileApplier
	{
		private readonly MonoBehaviour host;

		internal bool IsBusy { get; private set; }

		internal ProfileApplier(MonoBehaviour host)
		{
			this.host = host;
		}

		internal bool Apply(CosmeticProfile profile, Action<List<SlotResult>> onDone)
		{
			if (IsBusy || profile == null)
			{
				return false;
			}
			host.StartCoroutine(ApplyRoutine(profile, onDone));
			return true;
		}

		private IEnumerator ApplyRoutine(CosmeticProfile profile, Action<List<SlotResult>> onDone)
		{
			IsBusy = true;
			List<SlotResult> results = new List<SlotResult>();
			try
			{
				float start = Time.unscaledTime;
				bool smoke = Plugin.Transition.Value == TransitionMode.Smoke;
				if (smoke)
				{
					Smoke.PlayOn(Character.localCharacter);
					if (Plugin.SyncSmokeToOthers.Value)
					{
						SmokeSync.Send();
					}
					if (Plugin.SmokeLeadIn.Value > 0f)
					{
						yield return (object)new WaitForSecondsRealtime(Plugin.SmokeLeadIn.Value);
					}
				}
				SlotKind[] applyOrder = CosmeticSlots.ApplyOrder;
				foreach (SlotKind kind in applyOrder)
				{
					SlotResult item = CosmeticSlots.ApplyOne(kind, profile.cosmetics.Get(kind));
					results.Add(item);
					if (item.Changed)
					{
						for (int j = 0; j < Plugin.FramesBetweenChanges.Value; j++)
						{
							yield return null;
						}
					}
				}
				if (smoke)
				{
					float num = Plugin.SmokeMinDuration.Value - (Time.unscaledTime - start);
					if (num > 0f)
					{
						yield return (object)new WaitForSecondsRealtime(num);
					}
				}
				LogReport(profile, results);
			}
			finally
			{
				IsBusy = false;
			}
			try
			{
				onDone?.Invoke(results);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Profile apply callback failed: " + ex));
			}
		}

		private static void LogReport(CosmeticProfile profile, List<SlotResult> results)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("Applying \"").Append(profile.name).Append("\" (slot ")
				.Append(profile.slot)
				.AppendLine(")...");
			foreach (SlotResult result in results)
			{
				stringBuilder.Append("  ").AppendLine(result.ToString());
			}
			Plugin.Log.LogInfo((object)stringBuilder.ToString().TrimEnd());
		}
	}
	internal static class Smoke
	{
		internal static void PlayOn(Character character)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.poof == (Object)null)
			{
				return;
			}
			try
			{
				character.PlayPoofVFX(character.Center);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not play poof VFX: " + ex.Message));
			}
		}
	}
}