Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MixAndMatchCosmetics v1.1.1
MixAndMatchCosmetics.dll
Decompiled 21 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("MixAndMatchCosmetics")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("MixAndMatchCosmetics")] [assembly: AssemblyTitle("MixAndMatchCosmetics")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.0")] [module: UnverifiableCode] [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 MixAndMatchCosmetics { internal static class ItemColors { [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsLogic")] private static class SetupColorsLogicPatch { private static void Postfix(PlayerCosmetics __instance) { Apply(__instance); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsAllLogic")] private static class SetupColorsAllLogicPatch { private static void Postfix(PlayerCosmetics __instance) { Apply(__instance); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupCosmeticsLogic")] private static class SetupCosmeticsLogicPatch { private static void Postfix(PlayerCosmetics __instance) { Apply(__instance); } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "ChangeColorButton")] private static class ItemColorButtonPatch { private static void Prefix(MenuElementCosmeticButton __instance) { editing = (MixAndMatch.Active ? __instance.cosmeticAsset : null); } } [HarmonyPatch(typeof(MenuElementCosmeticSection), "ChangeColorButton")] private static class SectionColorButtonPatch { private static void Prefix() { editing = null; } } [HarmonyPatch(typeof(MenuPageColor), "OnDestroy")] private static class ColorPageClosedPatch { private static void Postfix() { editing = null; } } [HarmonyPatch(typeof(MenuPageColor), "SelectedColorIDGet")] private static class SelectedColorPatch { private static void Postfix(MenuPageColor __instance, ref int __result) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between I4 and Unknown if (!((Object)(object)editing == (Object)null) && __instance.colorKey == (int)editing.type) { EnsureLoaded(); if (overrides.TryGetValue(((Object)editing).name, out var value)) { __result = value; } } } } [HarmonyPatch(typeof(MetaManager), "CosmeticColorSet")] private static class CosmeticColorSetPatch { private static bool Prefix(MetaManager __instance, int _index, int _colorID) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 if (!MixAndMatch.Active || applyingPreset >= 0) { return true; } EnsureLoaded(); if ((Object)(object)editing != (Object)null && (int)editing.type == _index) { overrides[((Object)editing).name] = _colorID; Changed(); RefreshLocalAvatars(); return false; } bool flag = false; foreach (CosmeticAsset cosmeticAsset in __instance.cosmeticAssets) { if ((Object)(object)cosmeticAsset != (Object)null && (int)cosmeticAsset.type == _index && overrides.Remove(((Object)cosmeticAsset).name)) { flag = true; } } if (flag) { Changed(); RefreshLocalAvatars(); } return true; } } [HarmonyPatch(typeof(MetaManager), "CosmeticEquip")] private static class CosmeticEquipPatch { private static void Prefix(MetaManager __instance, CosmeticAsset _cosmeticAssetNew, bool _isPreview, out int __state) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected I4, but got Unknown __state = -1; if (!_isPreview && !((Object)(object)_cosmeticAssetNew == (Object)null) && __instance.colorsEquipped != null) { int num = (int)_cosmeticAssetNew.type; if (num >= 0 && num < __instance.colorsEquipped.Length) { __state = __instance.colorsEquipped[num]; } } } private static void Postfix(MetaManager __instance, CosmeticAsset _cosmeticAssetNew, bool _isPreview, bool __result, int __state) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (!__result || _isPreview || __state < 0 || !MixAndMatch.Active || applyingPreset >= 0) { return; } EnsureLoaded(); bool flag = false; foreach (int item in __instance.cosmeticEquipped) { if (item >= 0 && item < __instance.cosmeticAssets.Count) { CosmeticAsset val = __instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_cosmeticAssetNew) && val.type == _cosmeticAssetNew.type && val.tintable && !HasOverride(val)) { overrides[((Object)val).name] = __state; flag = true; } } } if (flag) { Changed(); } } } internal class Callbacks : MonoBehaviourPunCallbacks { public override void OnJoinedRoom() { Publish(); } public override void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { if (changedProps != null && ((Dictionary<object, object>)(object)changedProps).ContainsKey((object)"MAMC.itemColors")) { RefreshPlayer(targetPlayer); } } } [HarmonyPatch(typeof(MetaManager), "CosmeticPresetSet")] private static class PresetSetPatch { private static void Postfix(MetaManager __instance, int _index, List<int> _cosmeticEquipped) { EnsureLoaded(); EnsurePresetsLoaded(); if (_cosmeticEquipped == null || _cosmeticEquipped.Count == 0) { presetColors.Remove(_index); } else { Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (CosmeticAsset item in PresetAssets(__instance, _index)) { if (overrides.TryGetValue(((Object)item).name, out var value)) { dictionary[((Object)item).name] = value; } } presetColors[_index] = dictionary; } SavePresets(); } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "TogglePreset")] private static class TogglePresetPatch { private static void Prefix(MenuElementCosmeticPreset __instance, out bool __state) { MetaManager instance = MetaManager.instance; int presetIndex = __instance.presetIndex; __state = MixAndMatch.Active && (Object)(object)instance != (Object)null && (instance.cosmeticPresets[presetIndex].Count > 0 || instance.colorPresets[presetIndex].Count > 0) && !__instance.IsEquipped(); if (__state) { applyingPreset = presetIndex; } } private static void Postfix(MenuElementCosmeticPreset __instance, bool __state) { if (!__state) { return; } applyingPreset = -1; MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } EnsureLoaded(); Dictionary<string, int> dictionary = PresetMap(__instance.presetIndex); foreach (CosmeticAsset item in PresetAssets(instance, __instance.presetIndex)) { if (dictionary.TryGetValue(((Object)item).name, out var value)) { overrides[((Object)item).name] = value; } else { overrides.Remove(((Object)item).name); } } Changed(); RefreshLocalAvatars(); } private static Exception Finalizer(Exception __exception) { applyingPreset = -1; return __exception; } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "IsEquipped")] private static class PresetIsEquippedPatch { private static void Postfix(MenuElementCosmeticPreset __instance, ref bool __result) { if (!__result || !MixAndMatch.Active || (Object)(object)MetaManager.instance == (Object)null) { return; } EnsureLoaded(); Dictionary<string, int> dictionary = PresetMap(__instance.presetIndex); foreach (CosmeticAsset item in PresetAssets(MetaManager.instance, __instance.presetIndex)) { int value; bool flag = overrides.TryGetValue(((Object)item).name, out value); int value2; bool flag2 = dictionary.TryGetValue(((Object)item).name, out value2); if (flag != flag2 || (flag && value != value2)) { __result = false; break; } } } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "Update")] private static class PresetHoverPatch { private static void Prefix(MenuElementCosmeticPreset __instance) { MenuButton menuButton = __instance.menuButton; if (!((Object)(object)menuButton == (Object)null)) { if (menuButton.hovering) { hoveredPreset = __instance.presetIndex; } else if (hoveredPreset == __instance.presetIndex) { hoveredPreset = -1; } } } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "OnDisable")] private static class PresetDisablePatch { private static void Postfix(MenuElementCosmeticPreset __instance) { if (hoveredPreset == __instance.presetIndex) { hoveredPreset = -1; } } } private const string PropKey = "MAMC.itemColors"; private static readonly Dictionary<string, int> overrides = new Dictionary<string, int>(); private static Dictionary<int, int> localMap; private static bool loaded; private static CosmeticAsset editing; private static readonly int AlbedoColor = Shader.PropertyToID("_AlbedoColor"); private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); private static readonly int FresnelColor = Shader.PropertyToID("_FresnelColor"); private static readonly Dictionary<int, Dictionary<string, int>> presetColors = new Dictionary<int, Dictionary<string, int>>(); private static bool presetsLoaded; private static int applyingPreset = -1; private static int hoveredPreset = -1; private static string FilePath => Path.Combine(Application.persistentDataPath, "MixAndMatchCosmetics_ItemColors.txt"); private static string PresetFilePath => Path.Combine(Application.persistentDataPath, "MixAndMatchCosmetics_PresetColors.txt"); private static void EnsureLoaded() { if (loaded) { return; } loaded = true; try { if (!File.Exists(FilePath)) { return; } string[] array = File.ReadAllLines(FilePath); foreach (string text in array) { int num = text.LastIndexOf('='); if (num > 0 && int.TryParse(text.Substring(num + 1), out var result)) { overrides[text.Substring(0, num)] = result; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not read item colors: {arg}"); } } private static void Changed() { localMap = null; try { string text = FilePath + ".tmp"; File.WriteAllLines(text, from p in overrides orderby p.Key select p.Key + "=" + p.Value); if (File.Exists(FilePath)) { File.Delete(FilePath); } File.Move(text, FilePath); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not save item colors: {arg}"); } Publish(); } private static Dictionary<int, int> LocalMap() { EnsureLoaded(); MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return null; } if (localMap != null) { return localMap; } localMap = new Dictionary<int, int>(); for (int i = 0; i < instance.cosmeticAssets.Count; i++) { CosmeticAsset val = instance.cosmeticAssets[i]; if ((Object)(object)val != (Object)null && overrides.TryGetValue(((Object)val).name, out var value)) { localMap[i] = value; } } return localMap; } private static bool HasOverride(CosmeticAsset asset) { if ((Object)(object)asset != (Object)null) { return overrides.ContainsKey(((Object)asset).name); } return false; } internal static void Publish() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_0088: Expected O, but got Unknown if (!PhotonNetwork.InRoom) { return; } Dictionary<int, int> obj = LocalMap() ?? new Dictionary<int, int>(); int[] array = new int[obj.Count * 2]; int num = 0; foreach (KeyValuePair<int, int> item in obj) { array[num++] = item.Key; array[num++] = item.Value; } Player localPlayer = PhotonNetwork.LocalPlayer; Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"MAMC.itemColors", (object)array); localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } private static Dictionary<int, int> MapFor(PlayerCosmetics pc) { PlayerAvatar val = null; if ((Object)(object)pc.deathHead != (Object)null) { val = pc.deathHead.playerAvatar; } else if ((Object)(object)pc.playerAvatarVisuals != (Object)null && !pc.playerAvatarVisuals.isMenuAvatar) { val = pc.playerAvatarVisuals.playerAvatar; } if ((Object)(object)val == (Object)null) { Dictionary<int, int> dictionary = PresetPreviewMap(); if (dictionary != null) { return dictionary; } } if ((Object)(object)val == (Object)null || val.isLocal || !SemiFunc.IsMultiplayer() || (Object)(object)val.photonView == (Object)null) { return LocalMap(); } Player owner = val.photonView.Owner; if (owner == null || !((Dictionary<object, object>)(object)owner.CustomProperties).TryGetValue((object)"MAMC.itemColors", out object value) || !(value is int[] array)) { return null; } Dictionary<int, int> dictionary2 = new Dictionary<int, int>(); for (int i = 0; i + 1 < array.Length; i += 2) { dictionary2[array[i]] = array[i + 1]; } return dictionary2; } internal static void RefreshPlayer(Player target) { foreach (PlayerAvatar item in SemiFunc.PlayerGetList()) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.photonView == (Object)null) && item.photonView.Owner == target) { Refresh(item.playerCosmetics); if ((Object)(object)item.playerDeathHead != (Object)null) { Refresh(item.playerDeathHead.playerCosmetics); } } } } private static void RefreshLocalAvatars() { PlayerAvatar val = SemiFunc.PlayerGetLocal(); if ((Object)(object)val != (Object)null) { Refresh(val.playerCosmetics); if ((Object)(object)val.playerDeathHead != (Object)null) { Refresh(val.playerDeathHead.playerCosmetics); } } if ((Object)(object)PlayerAvatarMenu.instance != (Object)null) { Refresh(PlayerAvatarMenu.instance.playerCosmetics); } } private static void Refresh(PlayerCosmetics pc) { if (!((Object)(object)pc == (Object)null) && pc.colorsEquipped != null) { if (pc.colorsEquipped.All((int c) => c >= 0)) { pc.SetupColorsLogic(pc.colorsEquipped.ToArray()); } else { Apply(pc); } } } internal static void Apply(PlayerCosmetics pc) { if (!MixAndMatch.Active || (Object)(object)pc == (Object)null || pc.playerMaterials == null) { return; } MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null || instance.colors == null || instance.colors.Count == 0) { return; } Dictionary<int, int> dictionary = MapFor(pc); if (dictionary == null || dictionary.Count == 0) { return; } foreach (PlayerMaterial playerMaterial in pc.playerMaterials) { if (!((Object)(object)playerMaterial == (Object)null) && !((Object)(object)playerMaterial.material == (Object)null) && !((Object)(object)playerMaterial.cosmetic == (Object)null) && !((Object)(object)playerMaterial.cosmetic.cosmeticAsset == (Object)null)) { int num = instance.cosmeticAssets.IndexOf(playerMaterial.cosmetic.cosmeticAsset); if (num >= 0 && dictionary.TryGetValue(num, out var value) && value >= 0 && value < instance.colors.Count) { playerMaterial.ColorSet(AlbedoColor, EmissionColor, FresnelColor, value); } } } } internal static void CreateCallbacks() { //IL_0005: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown GameObject val = new GameObject("MixAndMatchCosmetics.Callbacks") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); val.AddComponent<Callbacks>(); } private static void EnsurePresetsLoaded() { if (presetsLoaded) { return; } presetsLoaded = true; try { if (!File.Exists(PresetFilePath)) { return; } string[] array = File.ReadAllLines(PresetFilePath); foreach (string text in array) { int num = text.IndexOf('|'); int num2 = text.LastIndexOf('='); if (num > 0 && num2 > num + 1 && int.TryParse(text.Substring(0, num), out var result) && int.TryParse(text.Substring(num2 + 1), out var result2)) { if (!presetColors.TryGetValue(result, out var value)) { value = (presetColors[result] = new Dictionary<string, int>()); } value[text.Substring(num + 1, num2 - num - 1)] = result2; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not read preset colors: {arg}"); } } private static void SavePresets() { try { IEnumerable<string> contents = presetColors.OrderBy((KeyValuePair<int, Dictionary<string, int>> p) => p.Key).SelectMany((KeyValuePair<int, Dictionary<string, int>> p) => from q in p.Value orderby q.Key select $"{p.Key}|{q.Key}={q.Value}"); string text = PresetFilePath + ".tmp"; File.WriteAllLines(text, contents); if (File.Exists(PresetFilePath)) { File.Delete(PresetFilePath); } File.Move(text, PresetFilePath); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not save preset colors: {arg}"); } } private static Dictionary<string, int> PresetMap(int index) { EnsurePresetsLoaded(); if (!presetColors.TryGetValue(index, out var value)) { return new Dictionary<string, int>(); } return value; } private static IEnumerable<CosmeticAsset> PresetAssets(MetaManager mm, int index) { if (index < 0 || index >= mm.cosmeticPresets.Count) { yield break; } foreach (int item in mm.cosmeticPresets[index]) { if (item >= 0 && item < mm.cosmeticAssets.Count && (Object)(object)mm.cosmeticAssets[item] != (Object)null) { yield return mm.cosmeticAssets[item]; } } } private static Dictionary<int, int> PresetPreviewMap() { MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null || hoveredPreset < 0 || !instance.cosmeticPreviewEnabled) { return null; } Dictionary<string, int> dictionary = PresetMap(hoveredPreset); Dictionary<int, int> dictionary2 = new Dictionary<int, int>(); for (int i = 0; i < instance.cosmeticAssets.Count; i++) { CosmeticAsset val = instance.cosmeticAssets[i]; if ((Object)(object)val != (Object)null && dictionary.TryGetValue(((Object)val).name, out var value)) { dictionary2[i] = value; } } return dictionary2; } } internal static class LobbyGate { [HarmonyPatch(typeof(DataDirector), "PhotonVersionGet")] private static class PhotonVersionGetPatch { private static void Postfix(ref string __result) { if (!__result.EndsWith(Tag)) { __result += Tag; } } } internal static readonly string Tag = "-MixAndMatch" + MajorMinor("1.1.1"); private static string MajorMinor(string version) { string[] array = version.Split('.'); if (array.Length < 2) { return version; } return array[0] + "." + array[1]; } } internal static class MixAndMatch { [HarmonyPatch(typeof(MetaManager), "GetCosmeticsToUnequip")] private static class GetCosmeticsToUnequipPatch { private static bool Prefix(MetaManager __instance, List<int> _cosmeticEquipped, CosmeticAsset _cosmeticAssetNew, ref List<CosmeticAsset> __result) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (!Active) { return true; } List<CosmeticAsset> list = new List<CosmeticAsset>(); if ((Object)(object)_cosmeticAssetNew != (Object)null) { CosmeticTypeAsset val = __instance.cosmeticTypeAssets[(int)_cosmeticAssetNew.type]; if ((Object)(object)val != (Object)null && val.meshSwitch && !OriginalCanEquipMultiple(val)) { foreach (int item in _cosmeticEquipped) { if (item >= 0 && item < __instance.cosmeticAssets.Count) { CosmeticAsset val2 = __instance.cosmeticAssets[item]; if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)_cosmeticAssetNew && val2.type == _cosmeticAssetNew.type) { list.Add(val2); } } } } } __result = list; return false; } } [HarmonyPatch(typeof(MetaManager), "Start")] private static class MetaManagerStartPatch { private static void Postfix() { Apply(refreshAvatar: false); UnlockAll.Apply(); } } private static readonly Dictionary<CosmeticTypeAsset, bool> originalFlags = new Dictionary<CosmeticTypeAsset, bool>(); internal static bool Active => Plugin.MixEnabled.Value; internal static void Apply(bool refreshAvatar) { MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } foreach (CosmeticTypeAsset cosmeticTypeAsset in instance.cosmeticTypeAssets) { if (!((Object)(object)cosmeticTypeAsset == (Object)null)) { if (!originalFlags.TryGetValue(cosmeticTypeAsset, out var value)) { value = cosmeticTypeAsset.canEquipMultiple; originalFlags[cosmeticTypeAsset] = value; } cosmeticTypeAsset.canEquipMultiple = ((!Active) ? value : (value || !cosmeticTypeAsset.meshSwitch)); } } if (refreshAvatar && instance.saveReady) { instance.CosmeticPlayerUpdateLocal(true, true); } } private static bool OriginalCanEquipMultiple(CosmeticTypeAsset typeAsset) { if (!originalFlags.TryGetValue(typeAsset, out var value)) { return typeAsset.canEquipMultiple; } return value; } } [BepInPlugin("Aori.MixAndMatchCosmetics", "MixAndMatchCosmetics", "1.1.1")] public class Plugin : BaseUnityPlugin { public const string Guid = "Aori.MixAndMatchCosmetics"; public const string Name = "MixAndMatchCosmetics"; public const string Version = "1.1.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> MixEnabled; internal static ConfigEntry<bool> UnlockAllEnabled; private void Awake() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; MixEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable mix and match", true, "Allow wearing any number of cosmetics from the same customization tab at once. Click an item to put it on or take it off."); UnlockAllEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Unlock all cosmetics", false, "On: remembers which cosmetics you have really unlocked, then unlocks everything. Off: goes back to exactly what you had unlocked before (plus anything you unlocked from the machine meanwhile). Turn this off before uninstalling the mod, otherwise the unlocks stay in your save."); MixEnabled.SettingChanged += delegate { MixAndMatch.Apply(refreshAvatar: true); }; UnlockAllEnabled.SettingChanged += delegate { UnlockAll.Apply(); }; new Harmony("Aori.MixAndMatchCosmetics").PatchAll(typeof(Plugin).Assembly); ItemColors.CreateCallbacks(); if ((Object)(object)MetaManager.instance != (Object)null) { MixAndMatch.Apply(refreshAvatar: false); UnlockAll.Apply(); } Log.LogInfo((object)("MixAndMatchCosmetics 1.1.1 loaded. Lobby tag: " + LobbyGate.Tag)); } } internal static class UnlockAll { [HarmonyPatch(typeof(MetaManager), "CosmeticUnlock")] private static class CosmeticUnlockPatch { private static void Prefix(CosmeticAsset _cosmeticAsset) { if (Active && !((Object)(object)_cosmeticAsset == (Object)null) && snapshot.Add(((Object)_cosmeticAsset).name)) { WriteSnapshot(); Plugin.Log.LogInfo((object)("Real unlock recorded: " + ((Object)_cosmeticAsset).name)); } } } [HarmonyPatch(typeof(MetaManager), "CosmeticLockedGet")] private static class CosmeticLockedGetPatch { private static void Prefix(MetaManager __instance, ref List<int> __state) { if (Active) { __state = __instance.cosmeticUnlocks; __instance.cosmeticUnlocks = SnapshotIndices(__instance); } } private static void Finalizer(MetaManager __instance, List<int> __state) { if (__state != null) { __instance.cosmeticUnlocks = __state; } } } private static HashSet<string> snapshot; private static bool snapshotLoaded; private static string SnapshotPath => Path.Combine(Application.persistentDataPath, "MixAndMatchCosmetics_UnlockSnapshot.txt"); internal static bool Active => snapshot != null; internal static void Apply() { MetaManager mm = MetaManager.instance; if ((Object)(object)mm == (Object)null || !mm.saveReady) { return; } LoadSnapshot(); if (Plugin.UnlockAllEnabled.Value) { if (snapshot == null) { snapshot = new HashSet<string>(from i in mm.cosmeticUnlocks where i >= 0 && i < mm.cosmeticAssets.Count && (Object)(object)mm.cosmeticAssets[i] != (Object)null select ((Object)mm.cosmeticAssets[i]).name); WriteSnapshot(); Plugin.Log.LogInfo((object)$"Saved snapshot of {snapshot.Count} real unlocks."); } int num = 0; for (int num2 = 0; num2 < mm.cosmeticAssets.Count; num2++) { CosmeticAsset val = mm.cosmeticAssets[num2]; if (!((Object)(object)val == (Object)null) && ((PrefabRef<GameObject>)(object)val.prefab).IsValid() && !mm.cosmeticUnlocks.Contains(num2)) { mm.cosmeticUnlocks.Add(num2); num++; } } if (num > 0) { mm.Save(); Plugin.Log.LogInfo((object)$"Unlocked {num} cosmetics."); } } else if (snapshot != null) { List<int> restored = new List<int>(); for (int num3 = 0; num3 < mm.cosmeticAssets.Count; num3++) { CosmeticAsset val2 = mm.cosmeticAssets[num3]; if ((Object)(object)val2 != (Object)null && snapshot.Contains(((Object)val2).name)) { restored.Add(num3); } } mm.cosmeticUnlocks = restored; int num4 = mm.cosmeticEquipped.RemoveAll((int id) => !restored.Contains(id)); mm.Save(); snapshot = null; DeleteSnapshot(); Plugin.Log.LogInfo((object)$"Rolled back to {restored.Count} real unlocks, took off {num4} locked items."); if (num4 > 0) { mm.CosmeticPlayerUpdateLocal(true, true); } } CosmeticTokenUI instance = CosmeticTokenUI.instance; if (instance != null) { instance.Setup(); } } private static List<int> SnapshotIndices(MetaManager mm) { List<int> list = new List<int>(); for (int i = 0; i < mm.cosmeticAssets.Count; i++) { CosmeticAsset val = mm.cosmeticAssets[i]; if ((Object)(object)val != (Object)null && snapshot.Contains(((Object)val).name)) { list.Add(i); } } return list; } private static void LoadSnapshot() { if (snapshotLoaded) { return; } snapshotLoaded = true; try { if (File.Exists(SnapshotPath)) { snapshot = new HashSet<string>(from l in File.ReadAllLines(SnapshotPath) where l.Length > 0 select l); Plugin.Log.LogInfo((object)$"Found unlock snapshot with {snapshot.Count} entries."); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not read unlock snapshot: {arg}"); } } private static void WriteSnapshot() { try { string text = SnapshotPath + ".tmp"; File.WriteAllLines(text, snapshot.OrderBy((string n) => n)); if (File.Exists(SnapshotPath)) { File.Delete(SnapshotPath); } File.Move(text, SnapshotPath); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not write unlock snapshot: {arg}"); } } private static void DeleteSnapshot() { try { if (File.Exists(SnapshotPath)) { File.Delete(SnapshotPath); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not delete unlock snapshot: {arg}"); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }