using System; using System.Collections; 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 System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Photon.Pun; using REPOLib; using REPOLib.Modules; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Xuaun")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Registers MoreHead .hhh cosmetics into the vanilla REPO cosmetics system via REPOLib.")] [assembly: AssemblyFileVersion("2.2.0.0")] [assembly: AssemblyInformationalVersion("2.2.0+d5ea3830f0322aae515765e2e2c6866d4975638b")] [assembly: AssemblyProduct("MoreHeadBridge")] [assembly: AssemblyTitle("MoreHeadBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.2.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 MoreHeadBridge { internal static class BceConsole { private static readonly MethodInfo? _writeLine; private static readonly MethodInfo? _write; internal static bool IsAvailable => _writeLine != null; static BceConsole() { Type type = Type.GetType("BCE.console, BCE"); if (!(type == null)) { _writeLine = type.GetMethod("WriteLine", new Type[2] { typeof(string), typeof(ConsoleColor) }); _write = type.GetMethod("Write", new Type[2] { typeof(string), typeof(ConsoleColor) }); } } internal static void WriteLine(string msg, ConsoleColor color) { _writeLine?.Invoke(null, new object[2] { msg, color }); } internal static void Write(string msg, ConsoleColor color) { _write?.Invoke(null, new object[2] { msg, color }); } } internal static class BridgeIds { internal const string Prefix = "morehead-bridge:"; internal static bool IsBridgeAsset(string? assetId) { if (!string.IsNullOrEmpty(assetId)) { return assetId.StartsWith("morehead-bridge:", StringComparison.Ordinal); } return false; } internal static bool IsBridgeAsset(CosmeticAsset? asset) { if ((Object)(object)asset != (Object)null) { return IsBridgeAsset(asset.assetId); } return false; } } internal static class HhhCosmeticLoader { internal static readonly List RegisteredAssetIds = new List(); internal static readonly HashSet WorldAssetIds = new HashSet(); internal static readonly Dictionary BridgeIconTextures = new Dictionary(); private static readonly Dictionary TagToType = new Dictionary { ["head"] = (CosmeticType)0, ["neck"] = (CosmeticType)30, ["body"] = (CosmeticType)20, ["hip"] = (CosmeticType)21, ["rightarm"] = (CosmeticType)1, ["leftarm"] = (CosmeticType)2, ["rightleg"] = (CosmeticType)3, ["leftleg"] = (CosmeticType)4, ["world"] = (CosmeticType)0 }; private static readonly HashSet ValidTags; private static readonly HashSet _usedPrefabIds; private static readonly HashSet _usedInternalNames; private static bool _moreHeadFixDone; private static readonly Dictionary _originalTypes; private static readonly Dictionary TagToOverrideType; public static void LoadAll() { string pluginPath = Paths.PluginPath; string[] files = Directory.GetFiles(pluginPath, "*.hhh", SearchOption.AllDirectories); string text = Plugin.SpecificFolders.Value ?? ""; if (!string.IsNullOrWhiteSpace(text)) { char[] invalidChars = Path.GetInvalidPathChars(); string[] array = (from s in (from s in text.Split(',') select s.Trim() into s where s.Length > 0 select s).Select(delegate(string s) { string text2 = new string(s.Where((char c) => !invalidChars.Contains(c)).ToArray()); if (text2 != s) { Plugin.Logger.LogWarning((object)("SpecificFolders: '" + s + "' contained invalid path characters — changed to '" + text2 + "'.")); } return text2; }) where s.Length > 0 select s).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); string[] matched = array.Where((string a) => files.Any((string f) => f.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray(); string[] array2 = array.Except(matched, StringComparer.OrdinalIgnoreCase).ToArray(); if (matched.Length == 0) { Plugin.Logger.LogWarning((object)("SpecificFolders: none of the specified folders were found (" + string.Join(", ", array) + "). Loading all .hhh files instead.")); } else { if (array2.Length != 0) { Plugin.Logger.LogWarning((object)("SpecificFolders: folder(s) not found and skipped: " + string.Join(", ", array2) + ".")); } int num = files.Length; files = files.Where((string f) => matched.Any((string a) => f.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray(); LogInfo(string.Format("SpecificFolders: loaded from {0} — kept {1}/{2} files.", string.Join(", ", matched), files.Length, num)); } } LogInfo($"Found {files.Length} .hhh file(s). Translating cosmetics from MoreHead to Vanilla REPO..."); int num2 = 0; string[] array3 = files; foreach (string path in array3) { if (TryRegister(path)) { num2++; } } int num3 = files.Length; int num4 = num3 - num2; LogInfo($"Done — {num2}/{num3} registered, {num4} error(s)."); } private static bool TryRegister(string path) { //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists || fileInfo.Length < 1024) { Plugin.Logger.LogWarning((object)("Skipped (too small/missing): " + Path.GetFileName(path))); return false; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); ParseFileName(fileNameWithoutExtension, out string name, out string tag); if (!TagToType.TryGetValue(tag, out var value)) { return false; } AssetBundle val = AssetBundle.LoadFromFile(path); if ((Object)(object)val == (Object)null) { Plugin.Logger.LogError((object)("Failed to load bundle: " + fileNameWithoutExtension)); return false; } GameObject val2 = null; string[] allAssetNames = val.GetAllAssetNames(); foreach (string text in allAssetNames) { GameObject val3 = val.LoadAsset(text); if ((Object)(object)val3 != (Object)null) { val2 = val3; break; } } val.Unload(false); if ((Object)(object)val2 == (Object)null) { Plugin.Logger.LogError((object)("No GameObject in bundle: " + fileNameWithoutExtension)); return false; } if (Plugin.FixBridgedCosmetics.Value) { FixPrefab(val2, fileNameWithoutExtension); } string name2 = ((Object)val2).name; ((Object)val2).name = EnsureUniqueId(name2, _usedPrefabIds); if (((Object)val2).name != name2) { Plugin.Logger.LogWarning((object)("Duplicate prefab name '" + name2 + "' → renamed to '" + ((Object)val2).name + "'")); } string text2 = name; name = EnsureUniqueId(name, _usedInternalNames); if (name != text2) { Plugin.Logger.LogWarning((object)("Duplicate internal name '" + text2 + "' → renamed to '" + name + "'")); } Cosmetic val4 = val2.GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = val2.AddComponent(); } val4.type = value; PrefabRef val5 = NetworkPrefabs.RegisterNetworkPrefab("Cosmetics/" + ((Object)val2).name, val2); if (val5 == null) { Plugin.Logger.LogError((object)("Failed to register network prefab: " + name)); return false; } string text3 = "morehead-bridge:" + name.ToLowerInvariant(); CosmeticAsset val6 = ScriptableObject.CreateInstance(); ((Object)val6).name = name; val6.assetName = ((Object)val2).name; val6.type = value; val6.prefab = val5; val6.assetId = text3; val6.rarity = Plugin.DefaultRarity.Value; val6.customTypeList = new List(); val6.tintable = false; if (TagToOverrideType.TryGetValue(tag, out var value2)) { _originalTypes[text3] = value2; } PerCosmeticOverrides.ApplyIfPresent(val6); Cosmetics.RegisterCosmetic(val6); RegisteredAssetIds.Add(text3); if (tag == "world") { WorldAssetIds.Add(text3); } Texture2D val7 = TryExtractIconTexture(val2); if ((Object)(object)val7 != (Object)null) { BridgeIconTextures[text3] = val7; } return true; } private static Texture2D? TryExtractIconTexture(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren(true); string[] array = new string[5] { "_MainTex", "_BaseMap", "_BaseColorMap", "_Albedo", "_AlbedoMap" }; Renderer[] array2 = componentsInChildren; foreach (Renderer val in array2) { if ((Object)(object)val == (Object)null) { continue; } Material[] sharedMaterials = val.sharedMaterials; foreach (Material val2 in sharedMaterials) { if ((Object)(object)val2 == (Object)null) { continue; } string[] array3 = array; foreach (string text in array3) { if (val2.HasProperty(text)) { Texture texture = val2.GetTexture(text); Texture2D val3 = (Texture2D)(object)((texture is Texture2D) ? texture : null); if (val3 != null && (Object)(object)val3 != (Object)null) { return val3; } } } } } return null; } private static void ParseFileName(string fileName, out string name, out string tag) { int num = fileName.LastIndexOf('_'); if (num >= 0) { int num2 = num + 1; string text = fileName.Substring(num2, fileName.Length - num2).ToLowerInvariant(); if (ValidTags.Contains(text)) { name = fileName.Substring(0, num); tag = text; return; } } name = fileName; tag = "head"; } private static string EnsureUniqueId(string baseName, HashSet used) { string text = baseName; int num = 1; while (!used.Add(text)) { text = $"{baseName}({num})"; num++; } return text; } private static void LogInfo(string msg) { if (BceConsole.IsAvailable) { BceConsole.WriteLine("[Info : MoreHead Bridge] " + msg, ConsoleColor.Cyan); } else { Plugin.Logger.LogInfo((object)(msg ?? "")); } } internal static bool IsWorldAsset(CosmeticAsset? asset) { if ((Object)(object)asset != (Object)null && BridgeIds.IsBridgeAsset(asset)) { return WorldAssetIds.Contains(asset.assetId); } return false; } internal static void ReapplyDefaultRarityToAll() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)MetaManager.instance == (Object)null) { return; } Rarity value = Plugin.DefaultRarity.Value; foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets) { if (!((Object)(object)cosmeticAsset == (Object)null) && BridgeIds.IsBridgeAsset(cosmeticAsset) && (!PerCosmeticOverrides.TryGet(cosmeticAsset.assetId, out CosmeticOverrideData data) || !data.Rarity.HasValue)) { cosmeticAsset.rarity = value; } } if (Plugin.FixBridgedCosmetics.Value && !_moreHeadFixDone) { _moreHeadFixDone = true; TryFixMoreHeadPrefabs(); } } private static void FixPrefab(GameObject prefab, string label = "", Type? partShrinkerType = null, bool verbose = true) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Invalid comparison between Unknown and I4 int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; Collider[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { num++; try { Object.DestroyImmediate((Object)(object)val); } catch { try { val.enabled = false; } catch { } } } Rigidbody[] componentsInChildren2 = prefab.GetComponentsInChildren(true); foreach (Rigidbody val2 in componentsInChildren2) { num2++; try { Object.DestroyImmediate((Object)(object)val2); } catch { try { val2.isKinematic = true; val2.useGravity = false; } catch { } } } Animation[] componentsInChildren3 = prefab.GetComponentsInChildren(true); foreach (Animation val3 in componentsInChildren3) { if ((Object)(object)val3.clip != (Object)null && (int)val3.clip.wrapMode != 2) { val3.clip.wrapMode = (WrapMode)2; num3++; } val3.wrapMode = (WrapMode)2; } Animator[] componentsInChildren4 = prefab.GetComponentsInChildren(true); foreach (Animator val4 in componentsInChildren4) { if ((Object)(object)val4.runtimeAnimatorController == (Object)null) { continue; } bool flag = false; AnimationClip[] animationClips = val4.runtimeAnimatorController.animationClips; foreach (AnimationClip val5 in animationClips) { if ((Object)(object)val5 != (Object)null && !((Motion)val5).isLooping) { flag = true; break; } } if (flag && (Object)(object)((Component)val4).GetComponent() == (Object)null) { ((Component)val4).gameObject.AddComponent(); num4++; } } string text = (string.IsNullOrEmpty(label) ? ((Object)prefab).name : label); if (num > 0 || num2 > 0 || num3 > 0 || num4 > 0) { string text2 = "'" + text + "': " + $"removed {num} Collider(s), {num2} Rigidbody(s); " + $"looped {num3} Animation clip(s), {num4} Animator(s)."; if (verbose) { Plugin.Logger.LogInfo((object)text2); } else { Plugin.Logger.LogDebug((object)text2); } } Type type = partShrinkerType ?? FindPartShrinkerType(); if (type != null) { Component[] componentsInChildren5 = prefab.GetComponentsInChildren(type, true); if (componentsInChildren5.Length != 0) { string text3 = $"'{text}': {componentsInChildren5.Length} component(s) — body-part hiding active."; if (verbose) { Plugin.Logger.LogInfo((object)text3); } else { Plugin.Logger.LogDebug((object)text3); } } return; } int num5 = 0; MonoBehaviour[] componentsInChildren6 = prefab.GetComponentsInChildren(true); foreach (MonoBehaviour val6 in componentsInChildren6) { if ((Object)(object)val6 == (Object)null) { num5++; } } if (num5 > 0) { Plugin.Logger.LogWarning((object)($"'{text}': {num5} missing script(s) detected — " + "install MoreHeadUtilities if body-part hiding is needed.")); } } private static Type? FindPartShrinkerType() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "MoreHeadUtilities") { return assembly.GetType("MoreHeadUtilities.PartShrinker"); } } return null; } private static void TryFixMoreHeadPrefabs() { try { Type type = Type.GetType("MoreHead.HeadDecorationManager, MoreHead"); if (type == null) { return; } PropertyInfo property = type.GetProperty("Decorations", BindingFlags.Static | BindingFlags.Public); if (property == null || !(property.GetValue(null) is IList list) || list.Count == 0) { return; } PropertyInfo property2 = list[0].GetType().GetProperty("Prefab"); if (property2 == null) { return; } Type partShrinkerType = FindPartShrinkerType(); int num = 0; foreach (object item in list) { object? value = property2.GetValue(item); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { FixPrefab(val, "", partShrinkerType, verbose: false); num++; } } Plugin.Logger.LogInfo((object)$"Applied fixes to {num} bridged cosmetics."); } catch (Exception ex) { Plugin.Logger.LogDebug((object)("Bridged cosmetic pass skipped: " + ex.Message)); } } internal static void ReapplyDefaults(CosmeticAsset asset) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) asset.rarity = Plugin.DefaultRarity.Value; if (_originalTypes.TryGetValue(asset.assetId, out var value)) { (CosmeticType cosmeticType, bool isWorld) tuple = PerCosmeticOverrides.ResolveType(value); CosmeticType item = tuple.cosmeticType; bool item2 = tuple.isWorld; asset.type = item; if (item2) { WorldAssetIds.Add(asset.assetId); } else { WorldAssetIds.Remove(asset.assetId); } } } static HhhCosmeticLoader() { HashSet hashSet = new HashSet(); foreach (string key in TagToType.Keys) { hashSet.Add(key); } ValidTags = hashSet; _usedPrefabIds = new HashSet(); _usedInternalNames = new HashSet(); _originalTypes = new Dictionary(); TagToOverrideType = new Dictionary { ["head"] = OverrideCosmeticType.Hat, ["neck"] = OverrideCosmeticType.HeadBottom, ["body"] = OverrideCosmeticType.BodyTop, ["hip"] = OverrideCosmeticType.BodyBottom, ["rightarm"] = OverrideCosmeticType.ArmRight, ["leftarm"] = OverrideCosmeticType.ArmLeft, ["rightleg"] = OverrideCosmeticType.LegRight, ["leftleg"] = OverrideCosmeticType.LegLeft, ["world"] = OverrideCosmeticType.World }; } } internal static class BatchIconGenerator { [CompilerGenerated] private sealed class d__7 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private List 5__2; private bool 5__3; private List.Enumerator <>7__wrap3; private CosmeticAsset 5__5; private int 5__6; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if ((uint)(num - -4) <= 1u || (uint)(num - 2) <= 2u) { try { if (num == -4 || (uint)(num - 2) <= 2u) { try { } finally { <>m__Finally2(); } } } finally { <>m__Finally1(); } } 5__2 = null; <>7__wrap3 = default(List.Enumerator); 5__5 = null; <>1__state = -2; } private bool MoveNext() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; _didStartOnce = true; <>2__current = (object)new WaitForSecondsRealtime(0.5f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)MetaManager.instance == (Object)null) { _isRunning = false; return false; } 5__2 = new List(); foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets) { if (!((Object)(object)cosmeticAsset == (Object)null) && cosmeticAsset.assetId != null && BridgeIds.IsBridgeAsset(cosmeticAsset) && !IconCapture.HasCache(cosmeticAsset)) { 5__2.Add(cosmeticAsset); } } Plugin.Logger.LogInfo((object)$"GenerateAllIcons: {5__2.Count} icon(s) to generate."); if (5__2.Count == 0) { _isRunning = false; Plugin.GenerateAllIcons.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); return false; } _progressDone = 0; _progressFailed = 0; _progressTotal = 5__2.Count; 5__3 = true; <>1__state = -3; <>7__wrap3 = 5__2.GetEnumerator(); <>1__state = -4; goto IL_03a4; case 2: <>1__state = -4; 5__6 = 0; goto IL_02af; case 3: <>1__state = -4; 5__6++; goto IL_02af; case 4: { <>1__state = -4; if ((Object)(object)MetaManager.instance == (Object)null) { break; } if (IconCapture.TryCapture(5__5)) { _progressDone++; } else { _progressFailed++; } MetaManager.instance.CosmeticPreviewSet(false); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true); int num = _progressDone + _progressFailed; if (num % 50 == 0) { Plugin.Logger.LogInfo((object)($"Batch progress: {num}/{5__2.Count} " + $"({_progressDone} ok, {_progressFailed} failed)")); } 5__5 = null; goto IL_03a4; } IL_03a4: while (<>7__wrap3.MoveNext()) { 5__5 = <>7__wrap3.Current; if ((Object)(object)MetaManager.instance == (Object)null) { break; } int num2 = MetaManager.instance.cosmeticAssets.IndexOf(5__5); if (num2 < 0) { _progressFailed++; continue; } MetaManager.instance.cosmeticEquippedPreview.Clear(); MetaManager.instance.cosmeticEquippedPreview.Add(num2); if (MetaManager.instance.colorsEquipped != null) { MetaManager.instance.colorsEquippedPreview = (int[])MetaManager.instance.colorsEquipped.Clone(); } MetaManager.instance.CosmeticPreviewSet(true); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: false); if (HhhCosmeticLoader.IsWorldAsset(5__5)) { WorldCosmeticsSetupPatch.SetWorldAssetActive(5__5, active: true); } SkipEquipAnimationFor(5__5); <>2__current = null; <>1__state = 2; return true; } break; IL_02af: if (5__6 < 3 && !IsAnimComplete(5__5)) { <>2__current = null; <>1__state = 3; return true; } <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 4; return true; } <>m__Finally2(); <>7__wrap3 = default(List.Enumerator); 5__3 = false; <>m__Finally1(); Plugin.GenerateAllIcons.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); Plugin.Logger.LogInfo((object)($"GenerateAllIcons done — {_progressDone} captured, " + $"{_progressFailed} failed. Flag reset to false.")); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (_isRunning) { _isRunning = false; if (5__3) { int num = _progressTotal - _progressDone - _progressFailed; Plugin.Logger.LogWarning((object)("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num} still to go). " + "Reopen the menu to continue.")); } } if ((Object)(object)MetaManager.instance != (Object)null) { MetaManager.instance.CosmeticPreviewSet(false); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); } WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true); } private void <>m__Finally2() { <>1__state = -3; ((IDisposable)<>7__wrap3).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _isRunning; private static bool _didStartOnce; private static int _progressDone; private static int _progressFailed; private static int _progressTotal; private static FieldInfo? _iconCreationAvatarField; private static FieldInfo? _equipLerpField; internal static void TryStart(MonoBehaviour host) { if (!_isRunning && Plugin.GenerateAllIcons.Value) { if (_didStartOnce) { Plugin.Logger.LogWarning((object)"GenerateAllIcons: previous batch was interrupted. Resuming — only icons still missing will be generated."); } _isRunning = true; host.StartCoroutine(Run()); } } internal static void NotifyMenuClosed() { if (_isRunning) { _isRunning = false; int num = _progressTotal - _progressDone - _progressFailed; Plugin.Logger.LogWarning((object)("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num} still to go). " + "Reopen the menu to continue. (Your equipped cosmetics were not modified.)")); } } [IteratorStateMachine(typeof(d__7))] private static IEnumerator Run() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__7(0); } private static void SkipEquipAnimationFor(CosmeticAsset asset) { if ((object)_iconCreationAvatarField == null) { _iconCreationAvatarField = AccessTools.Field(typeof(Cosmetic), "iconCreationAvatar"); } if (_iconCreationAvatarField == null) { return; } Cosmetic[] array = Object.FindObjectsOfType(); foreach (Cosmetic val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset) { _iconCreationAvatarField.SetValue(val, true); } } } private static bool IsAnimComplete(CosmeticAsset asset) { if ((object)_equipLerpField == null) { _equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp"); } if (_equipLerpField == null) { return true; } Cosmetic[] array = Object.FindObjectsOfType(); foreach (Cosmetic val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset && (float)(_equipLerpField.GetValue(val) ?? ((object)1f)) < 1f) { return false; } } return true; } } [HarmonyPatch(typeof(MenuPageCosmetics), "Start")] internal static class BatchIconGeneratorStartPatch { [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { BatchIconGenerator.TryStart((MonoBehaviour)(object)__instance); } } [HarmonyPatch(typeof(MenuPageCosmetics), "OnDestroy")] internal static class BatchIconGeneratorMenuClosePatch { [HarmonyPostfix] private static void Postfix() { BatchIconGenerator.NotifyMenuClosed(); CosmeticHoverPatch.OnMenuClosed(); CosmeticsMenuState.OnMenuClosed(); CosmeticsMenuLateUpdatePatch.OnMenuClosed(); } } internal static class FavHideIcons { private const string ResourcePrefix = "MoreHeadBridge.Icons.Resources."; private static Sprite? _star; private static Sprite? _hide; internal static Sprite? StarSprite => _star ?? (_star = LoadSprite("star.png")); internal static Sprite? HideSprite => _hide ?? (_hide = LoadSprite("hide.png")); private static Sprite? LoadSprite(string fileName) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) try { string text = "MoreHeadBridge.Icons.Resources." + fileName; using Stream stream = typeof(FavHideIcons).Assembly.GetManifestResourceStream(text); if (stream == null) { Plugin.Logger.LogWarning((object)("FavHideIcons: embedded resource '" + text + "' not found.")); return null; } byte[] array = new byte[stream.Length]; for (int i = 0; i < array.Length; i += stream.Read(array, i, array.Length - i)) { } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ((Object)val).name = "MoreHeadBridge_" + fileName; ((Texture)val).filterMode = (FilterMode)1; if (!ImageConversion.LoadImage(val, array)) { Plugin.Logger.LogWarning((object)("FavHideIcons: Texture2D.LoadImage failed for '" + text + "'.")); return null; } MaskWhitePixels(val); Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), (float)((Texture)val).width); ((Object)val2).name = "MoreHeadBridge_" + fileName; return val2; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("FavHideIcons: error loading '" + fileName + "': " + ex.Message)); return null; } } private static void MaskWhitePixels(Texture2D tex) { //IL_000d: 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_0013: 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_002d: Unknown result type (might be due to invalid IL or missing references) Color32[] pixels = tex.GetPixels32(); for (int i = 0; i < pixels.Length; i++) { Color32 val = pixels[i]; if (val.r > 220 && val.g > 220 && val.b > 220) { pixels[i].a = 0; } } tex.SetPixels32(pixels); tex.Apply(); } } internal static class IconCacheCleaner { internal static void Run() { if (!Plugin.DeleteIconCache.Value) { return; } try { string cacheDir = IconCapture.CacheDir; if (!Directory.Exists(cacheDir)) { Plugin.Logger.LogInfo((object)"DeleteIconCache: no cache directory, nothing to do."); ResetFlag(); return; } string text = Plugin.DeleteIconsMatching.Value ?? ""; string[] array = (from s in text.Split(',') select s.Trim().ToLowerInvariant() into s where s.Length > 0 select s).ToArray(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string registeredAssetId in HhhCosmeticLoader.RegisteredAssetIds) { int num = registeredAssetId.IndexOf(':'); if (num >= 0 && num + 1 < registeredAssetId.Length) { string text2 = registeredAssetId; int num2 = num + 1; hashSet.Add(text2.Substring(num2, text2.Length - num2)); } } int num3 = 0; int num4 = 0; string[] files = Directory.GetFiles(cacheDir, "*.png"); foreach (string text3 in files) { string name = Path.GetFileNameWithoutExtension(text3).ToLowerInvariant(); if (!hashSet.Contains(name)) { num4++; continue; } if (array.Length != 0 && !array.Any((string f) => name.Contains(f))) { num4++; continue; } try { File.Delete(text3); num3++; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to delete '" + text3 + "': " + ex.Message)); } } Plugin.Logger.LogInfo((object)($"DeleteIconCache: removed {num3} bridge icon(s), kept {num4}. " + "Filter: " + ((array.Length == 0) ? "(all bridge icons)" : string.Join(",", array)))); } catch (Exception ex2) { Plugin.Logger.LogError((object)("DeleteIconCache failed: " + ex2.Message)); } finally { ResetFlag(); } } private static void ResetFlag() { Plugin.DeleteIconCache.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); } } internal static class IconCapture { private const int OutSize = 128; private static string? _cacheDir; private static FieldInfo? _renderTextureInstanceField; private static readonly Rect CropHead = new Rect(0.22f, 0.62f, 0.56f, 0.35f); private static readonly Rect CropNeck = new Rect(0.22f, 0.5f, 0.56f, 0.38f); private static readonly Rect CropBody = new Rect(0.18f, 0.34f, 0.64f, 0.36f); private static readonly Rect CropArmR = new Rect(0.05f, 0.3f, 0.5f, 0.4f); private static readonly Rect CropArmL = new Rect(0.45f, 0.3f, 0.5f, 0.4f); private static readonly Rect CropLegR = new Rect(0.1f, 0f, 0.45f, 0.45f); private static readonly Rect CropLegL = new Rect(0.45f, 0f, 0.45f, 0.45f); private static readonly Rect CropFull = new Rect(0f, 0f, 1f, 1f); internal static string CacheDir { get { if (_cacheDir != null) { return _cacheDir; } _cacheDir = Path.Combine(Application.persistentDataPath, "Cache", "Icons", "CosmeticsModded", "MoreHeadBridge_CosmeticsIcons"); MigrateLegacyCache(_cacheDir); return _cacheDir; } } private static void MigrateLegacyCache(string newDir) { string path = Path.Combine(Application.persistentDataPath, "MoreHeadBridge_Icons"); if (!Directory.Exists(path)) { return; } Plugin.Logger.LogInfo((object)"IconCapture: migrating icon cache from legacy location..."); try { Directory.CreateDirectory(newDir); int num = 0; int num2 = 0; string[] files = Directory.GetFiles(path, "*.png"); foreach (string text in files) { string text2 = Path.Combine(newDir, Path.GetFileName(text)); try { if (!File.Exists(text2)) { File.Move(text, text2); } else { File.Delete(text); } num++; } catch (Exception ex) { num2++; Plugin.Logger.LogWarning((object)("IconCapture: could not migrate '" + Path.GetFileName(text) + "': " + ex.Message)); } } try { if (Directory.GetFiles(path).Length == 0) { Directory.Delete(path, recursive: false); } } catch { } Plugin.Logger.LogInfo((object)$"IconCapture: cache migration done — {num} moved, {num2} failed."); } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("IconCapture: cache migration failed: " + ex2.Message)); } } internal static string CachePathFor(CosmeticAsset asset) { string text = ((Object)asset).name.Replace("(Clone)", "").ToLowerInvariant(); return Path.Combine(CacheDir, text + ".png"); } internal static bool HasCache(CosmeticAsset asset) { return File.Exists(CachePathFor(asset)); } private static RenderTexture? FindActiveAvatarRT() { PlayerAvatarMenuHover val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { return null; } if ((object)_renderTextureInstanceField == null) { _renderTextureInstanceField = AccessTools.Field(typeof(PlayerAvatarMenuHover), "renderTextureInstance"); } if (_renderTextureInstanceField == null) { Plugin.Logger.LogWarning((object)"IconCapture: PlayerAvatarMenuHover.renderTextureInstance not found — update MoreHeadBridge."); } else { object? value = _renderTextureInstanceField.GetValue(val); RenderTexture val2 = (RenderTexture)((value is RenderTexture) ? value : null); if ((Object)(object)val2 != (Object)null) { return val2; } } RawImage component = ((Component)val).GetComponent(); if (!((Object)(object)component != (Object)null)) { return null; } Texture texture = component.texture; return (RenderTexture?)(object)((texture is RenderTexture) ? texture : null); } internal static bool TryCapture(CosmeticAsset asset) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return TryCapture(asset, (CosmeticType)(HhhCosmeticLoader.IsWorldAsset(asset) ? (-1) : ((asset != null) ? ((int)asset.type) : 0))); } internal static bool TryCapture(CosmeticAsset asset, CosmeticType type) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown if ((Object)(object)asset == (Object)null) { return false; } if (HasCache(asset)) { return false; } Texture2D val = null; Texture2D val2 = null; Texture2D val3 = null; RenderTexture active = RenderTexture.active; try { RenderTexture val4 = FindActiveAvatarRT(); if ((Object)(object)val4 == (Object)null) { return false; } Directory.CreateDirectory(CacheDir); RenderTexture.active = val4; val = new Texture2D(((Texture)val4).width, ((Texture)val4).height, (TextureFormat)4, false); val.ReadPixels(new Rect(0f, 0f, (float)((Texture)val4).width, (float)((Texture)val4).height), 0, 0); val.Apply(); Rect cropRect = GetCropRect(type); int num = Mathf.RoundToInt(((Rect)(ref cropRect)).x * (float)((Texture)val4).width); int num2 = Mathf.RoundToInt(((Rect)(ref cropRect)).y * (float)((Texture)val4).height); int num3 = Mathf.RoundToInt(((Rect)(ref cropRect)).width * (float)((Texture)val4).width); int num4 = Mathf.RoundToInt(((Rect)(ref cropRect)).height * (float)((Texture)val4).height); num3 = Mathf.Max(1, Mathf.Min(num3, ((Texture)val4).width - num)); num4 = Mathf.Max(1, Mathf.Min(num4, ((Texture)val4).height - num2)); Color[] pixels = val.GetPixels(num, num2, num3, num4); val2 = new Texture2D(num3, num4, (TextureFormat)4, false); val2.SetPixels(pixels); val2.Apply(); val3 = ResizeBilinear(val2, 128, 128); File.WriteAllBytes(CachePathFor(asset), ImageConversion.EncodeToPNG(val3)); if ((Object)(object)asset.icon != (Object)null) { Object.Destroy((Object)(object)asset.icon); asset.icon = null; } RefreshVisibleButtons(asset); return true; } catch (Exception ex) { Plugin.Logger.LogDebug((object)("Icon capture failed for '" + ((Object)asset).name + "': " + ex.Message)); return false; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); } if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } } } private static Rect GetCropRect(CosmeticType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected I4, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_0092: 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_00b6: Unknown result type (might be due to invalid IL or missing references) switch ((int)type) { case 0: case 5: case 14: case 15: case 17: case 18: case 24: case 31: case 32: return CropHead; case 6: case 25: case 30: return CropNeck; case 7: case 8: case 16: case 20: case 21: case 23: return CropBody; case 1: case 9: case 13: case 26: return CropArmR; case 2: case 10: case 27: return CropArmL; case 3: case 11: case 19: case 28: return CropLegR; case 4: case 12: case 22: case 29: return CropLegL; default: return CropFull; } } private static void RefreshVisibleButtons(CosmeticAsset asset) { try { MenuPageCosmetics val = Object.FindObjectOfType(); MenuElementCosmeticButton[] array = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentsInChildren(true) : Object.FindObjectsOfType()); MenuElementCosmeticButton[] array2 = array; foreach (MenuElementCosmeticButton val2 in array2) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.cosmeticAsset == (Object)(object)asset) { val2.UpdateIcon(false); } } } catch (Exception ex) { Plugin.Logger.LogDebug((object)("Button refresh failed: " + ex.Message)); } } private static Texture2D ResizeBilinear(Texture2D src, int w, int h) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) RenderTexture temporary = RenderTexture.GetTemporary(w, h); try { Graphics.Blit((Texture)(object)src, temporary); RenderTexture active = RenderTexture.active; RenderTexture.active = temporary; Texture2D val = new Texture2D(w, h, (TextureFormat)4, false); val.ReadPixels(new Rect(0f, 0f, (float)w, (float)h), 0, 0); val.Apply(); RenderTexture.active = active; return val; } finally { RenderTexture.ReleaseTemporary(temporary); } } } internal static class PlaceholderIcon { private const int Size = 64; private static Sprite? _cached; internal static Sprite Get() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_012d: 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_0165: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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) if ((Object)(object)_cached != (Object)null) { return _cached; } Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false); ((Object)val).name = "MoreHeadBridge_Placeholder"; ((Texture)val).filterMode = (FilterMode)0; Color val2 = default(Color); ((Color)(ref val2))..ctor(1f, 0.8f, 0f, 1f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.13f, 0.13f, 0.18f, 1f); Color val4 = default(Color); ((Color)(ref val4))..ctor(0.22f, 0.22f, 0.28f, 1f); Color val5 = default(Color); ((Color)(ref val5))..ctor(1f, 0.8f, 0f, 0.35f); Color[] array = (Color[])(object)new Color[4096]; for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { bool flag = j < 3 || j >= 61 || i < 3 || i >= 61; bool flag2 = (j + i) / 4 % 2 == 0; Color val6 = ((!flag) ? ((!flag2) ? val4 : Color.Lerp(val3, val5, 0.5f)) : val2); array[i * 64 + j] = val6; } } DrawM(array, 64, val2); val.SetPixels(array); val.Apply(); _cached = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 64f); ((Object)_cached).name = "MoreHeadBridge_Placeholder"; return _cached; } private static void DrawM(Color[] pixels, int size, Color color) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) for (int i = 14; i <= 50; i++) { Plot(pixels, size, 18, i, color); Plot(pixels, size, 19, i, color); Plot(pixels, size, 45, i, color); Plot(pixels, size, 44, i, color); } int num = 24; for (int j = 0; j <= num; j++) { int y = 50 - j; int num2 = 18 + j * 13 / num; int num3 = 45 - j * 14 / num; Plot(pixels, size, num2, y, color); Plot(pixels, size, num2 + 1, y, color); Plot(pixels, size, num3, y, color); Plot(pixels, size, num3 - 1, y, color); } } private static void Plot(Color[] pixels, int size, int x, int y, Color color) { //IL_0017: 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) if (x >= 0 && y >= 0 && x < size && y < size) { pixels[y * size + x] = color; } } } internal static class BridgeFavoritesManager { private sealed class SaveData { public List Favorites { get; set; } = new List(); public List Hidden { get; set; } = new List(); } private static readonly HashSet _favorites = new HashSet(); private static readonly HashSet _hidden = new HashSet(); private static bool _loaded; private static readonly string SavePath = Path.Combine(Paths.ConfigPath, "MoreHeadBridge_Favorites.json"); private static Task _lastWrite = Task.CompletedTask; internal static void EnsureLoaded() { if (!_loaded) { _loaded = true; Load(); } } internal static bool IsFavorite(CosmeticAsset? asset) { if ((Object)(object)asset != (Object)null) { return _favorites.Contains(KeyFor(asset)); } return false; } internal static bool IsHidden(CosmeticAsset? asset) { if ((Object)(object)asset != (Object)null) { return _hidden.Contains(KeyFor(asset)); } return false; } internal static bool HasAnyFavorite() { return _favorites.Count > 0; } internal static bool HasAnyHidden() { return _hidden.Count > 0; } internal static bool ToggleFavorite(CosmeticAsset asset) { string item = KeyFor(asset); if (_favorites.Remove(item)) { Save(); return false; } _hidden.Remove(item); _favorites.Add(item); Save(); return true; } internal static bool ToggleHidden(CosmeticAsset asset) { string item = KeyFor(asset); if (_hidden.Remove(item)) { Save(); return false; } _favorites.Remove(item); _hidden.Add(item); Save(); return true; } private static void Load() { try { if (!File.Exists(SavePath)) { return; } SaveData saveData = JsonConvert.DeserializeObject(File.ReadAllText(SavePath)); if (saveData == null) { return; } _favorites.Clear(); _hidden.Clear(); foreach (string item in saveData.Favorites ?? new List()) { _favorites.Add(item); } foreach (string item2 in saveData.Hidden ?? new List()) { _hidden.Add(item2); } Plugin.Logger.LogInfo((object)$"BridgeFavoritesManager: loaded {_favorites.Count} favorite(s), {_hidden.Count} hidden."); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("BridgeFavoritesManager: load failed: " + ex.Message)); } } private static void Save() { string json = JsonConvert.SerializeObject((object)new SaveData { Favorites = new List(_favorites), Hidden = new List(_hidden) }, (Formatting)1); _lastWrite = _lastWrite.ContinueWith(delegate { try { string text = SavePath + ".tmp"; File.WriteAllText(text, json, Encoding.UTF8); if (File.Exists(SavePath)) { File.Replace(text, SavePath, null); } else { File.Move(text, SavePath); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("BridgeFavoritesManager: save failed: " + ex.Message)); } }, TaskScheduler.Default); } private static string KeyFor(CosmeticAsset asset) { if (!string.IsNullOrEmpty(asset.assetId)) { return asset.assetId; } if (!string.IsNullOrEmpty(asset.assetName)) { return asset.assetName; } return ((Object)asset).name ?? ""; } } internal static class CosmeticOverridePopup { private const float PopupX = -120f; private const float TitleGap = 15f; private const float BtnRowH = 30f; private const float BtnTopGap = 10f; private const float BtnBackX = -137f; private const float BtnSaveX = 58f; private const float BtnResetX = 51f; private static readonly string[] RarityOptions = Enum.GetNames(typeof(Rarity)); private static readonly string[] MainOptions = Enum.GetNames(typeof(MainCosmeticCategory)); private static readonly string[] ModdedOptions = new string[3] { "Default", "Yes", "No" }; private static readonly Dictionary SubOptions = new Dictionary { [MainCosmeticCategory.Head] = new OverrideCosmeticType[6] { OverrideCosmeticType.Hat, OverrideCosmeticType.Eyewear, OverrideCosmeticType.FaceTop, OverrideCosmeticType.FaceBottom, OverrideCosmeticType.HeadBottom, OverrideCosmeticType.Ears }, [MainCosmeticCategory.Body] = new OverrideCosmeticType[2] { OverrideCosmeticType.BodyTop, OverrideCosmeticType.BodyBottom }, [MainCosmeticCategory.Arms] = new OverrideCosmeticType[2] { OverrideCosmeticType.ArmRight, OverrideCosmeticType.ArmLeft }, [MainCosmeticCategory.Legs] = new OverrideCosmeticType[4] { OverrideCosmeticType.LegRight, OverrideCosmeticType.LegLeft, OverrideCosmeticType.FootRight, OverrideCosmeticType.FootLeft }, [MainCosmeticCategory.World] = new OverrideCosmeticType[1] { OverrideCosmeticType.World } }; private static readonly Dictionary SubLabels = new Dictionary { [OverrideCosmeticType.Hat] = "Hat", [OverrideCosmeticType.Eyewear] = "Eyewear", [OverrideCosmeticType.FaceTop] = "Face Upper", [OverrideCosmeticType.FaceBottom] = "Face Middle", [OverrideCosmeticType.HeadBottom] = "Face Lower", [OverrideCosmeticType.Ears] = "Ears", [OverrideCosmeticType.BodyTop] = "Bodywear Top", [OverrideCosmeticType.BodyBottom] = "Bodywear Bottom", [OverrideCosmeticType.ArmRight] = "Armwear Right", [OverrideCosmeticType.ArmLeft] = "Armwear Left", [OverrideCosmeticType.LegRight] = "Legwear Right", [OverrideCosmeticType.LegLeft] = "Legwear Left", [OverrideCosmeticType.FootRight] = "Footwear Right", [OverrideCosmeticType.FootLeft] = "Footwear Left", [OverrideCosmeticType.World] = "World" }; private static readonly Dictionary LabelToType = SubLabels.ToDictionary, string, OverrideCosmeticType>((KeyValuePair kvp) => kvp.Value, (KeyValuePair kvp) => kvp.Key); internal static void Show(CosmeticAsset asset) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Expected O, but got Unknown CosmeticAsset asset2 = asset; bool flag = PerCosmeticOverrides.HasOverride(asset2); string text = asset2.assetName ?? ((Object)asset2).name ?? asset2.assetId; PerCosmeticOverrides.TryGet(asset2.assetId, out CosmeticOverrideData data); bool? pendingModded = data?.IsModded; Rarity pendingRarity = asset2.rarity; MainCosmeticCategory pendingMain = PerCosmeticOverrides.GetCurrentMain(asset2); OverrideCosmeticType pendingType = PerCosmeticOverrides.GetCurrentType(asset2); REPOPopupPage popup = MenuAPI.CreateREPOPopupPage(text, false, true, 5f, (Vector2?)new Vector2(-120f, 0f)); REPOSlider subSlider = null; popup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown REPOSlider val6 = MenuAPI.CreateREPOSlider("Main Category", "", (Action)delegate(string opt) { if (Enum.TryParse(opt, out var result2) && result2 != pendingMain) { pendingMain = result2; pendingType = SubOptions[result2][0]; if (!((Object)(object)subSlider == (Object)null)) { REPOScrollViewElement component = ((Component)subSlider).GetComponent(); if (result2 == MainCosmeticCategory.World) { if ((Object)(object)component != (Object)null) { component.visibility = false; } } else { subSlider.stringOptions = GetSubLabels(result2); subSlider.SetValue(0f, false); if ((Object)(object)component != (Object)null) { component.visibility = true; } } } } }, scrollView, MainOptions, pendingMain.ToString(), default(Vector2), "", "", (BarBehavior)0); return (RectTransform)((Component)val6).transform; }, 15f, 0f); popup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //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) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown MainCosmeticCategory mainCosmeticCategory = ((pendingMain != MainCosmeticCategory.World) ? pendingMain : MainCosmeticCategory.Head); string[] subLabels = GetSubLabels(mainCosmeticCategory); string text3 = ((Array.IndexOf(SubOptions[mainCosmeticCategory], pendingType) >= 0 && SubLabels.ContainsKey(pendingType)) ? SubLabels[pendingType] : subLabels[0]); subSlider = MenuAPI.CreateREPOSlider("Sub Category", "", (Action)delegate(string opt) { if (LabelToType.TryGetValue(opt, out var value)) { pendingType = value; } }, scrollView, subLabels, text3, default(Vector2), "", "", (BarBehavior)0); return (RectTransform)((Component)subSlider).transform; }, 0f, 0f); if (pendingMain == MainCosmeticCategory.World) { REPOSlider obj = subSlider; REPOScrollViewElement val = ((obj != null) ? ((Component)obj).GetComponent() : null); if ((Object)(object)val != (Object)null) { val.visibility = false; } } popup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //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) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown Action action = delegate(string opt) { bool? flag2 = ((opt == "Yes") ? new bool?(true) : ((!(opt == "No")) ? null : new bool?(false))); pendingModded = flag2; }; string text2 = ((!pendingModded.HasValue) ? "Default" : ((!pendingModded.GetValueOrDefault()) ? "No" : "Yes")); REPOSlider val5 = MenuAPI.CreateREPOSlider("Modded Rarity", "", action, scrollView, ModdedOptions, text2, default(Vector2), "", "", (BarBehavior)0); return (RectTransform)((Component)val5).transform; }, 0f, 0f); popup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown REPOSlider val4 = MenuAPI.CreateREPOSlider("Rarity", "", (Action)delegate(string opt) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse(opt, out Rarity result)) { pendingRarity = result; } }, scrollView, RarityOptions, ((object)(Rarity)(ref pendingRarity)).ToString(), default(Vector2), "", "", (BarBehavior)0); return (RectTransform)((Component)val4).transform; }, 0f, 0f); popup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) RectTransform val3 = MakeRow(scrollView); MenuAPI.CreateREPOButton("Back", (Action)delegate { popup.ClosePage(false); }, (Transform)(object)val3, new Vector2(-137f, 0f)); MenuAPI.CreateREPOButton("Save", (Action)delegate { //IL_000d: Unknown result type (might be due to invalid IL or missing references) PerCosmeticOverrides.SetAndApply(asset2, pendingModded, pendingRarity, pendingType); RefreshMenu(); popup.ClosePage(false); }, (Transform)(object)val3, new Vector2(58f, 0f)); return val3; }, 10f, 0f); if (flag) { popup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) RectTransform val2 = MakeRow(scrollView); MenuAPI.CreateREPOButton("Reset", (Action)delegate { PerCosmeticOverrides.Reset(asset2); RefreshMenu(); popup.ClosePage(false); }, (Transform)(object)val2, new Vector2(51f, 0f)); return val2; }, 5f, 0f); } popup.OpenPage(false); } private static string[] GetSubLabels(MainCosmeticCategory main) { return Array.ConvertAll(SubOptions[main], (OverrideCosmeticType t) => SubLabels[t]); } private static RectTransform MakeRow(Transform scrollView) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) RectTransform component = new GameObject("Button Row", new Type[1] { typeof(RectTransform) }).GetComponent(); ((Transform)component).SetParent(scrollView, false); component.sizeDelta = new Vector2(0f, 30f); return component; } private static void RefreshMenu() { MenuPageCosmetics? activePage = CosmeticsMenuState.ActivePage; if (activePage != null) { activePage.RefreshScrollContent(); } } } internal static class CosmeticsMenuState { [CompilerGenerated] private sealed class d__51 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public MenuPageCosmetics page; object? IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object? IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__51(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; return true; case 1: <>1__state = -1; _searchDebounce = null; page.RefreshScrollContent(); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static Coroutine? _searchDebounce; private const float SearchDebounceDelay = 0.25f; private static Dictionary? _assetIndexCache; internal static CosmeticCategoryAsset? SelectedCategory { get; private set; } internal static CosmeticCategoryAsset? SearchCategory { get; private set; } internal static CosmeticCategoryAsset? FavoritesCategory { get; private set; } internal static CosmeticCategoryAsset? HiddenCategory { get; private set; } internal static TextMeshProUGUI? StatusLabel { get; private set; } internal static TMP_InputField? SearchField { get; private set; } internal static GameObject? EmptyStateLabel { get; private set; } internal static bool SearchMode { get; private set; } internal static string SearchText { get; private set; } = ""; internal static MenuPageCosmetics? ActivePage { get; private set; } internal static bool IsVirtual(CosmeticCategoryAsset? c) { if ((Object)(object)c != (Object)null) { if (!((Object)(object)c == (Object)(object)SelectedCategory) && !((Object)(object)c == (Object)(object)SearchCategory) && !((Object)(object)c == (Object)(object)FavoritesCategory)) { return (Object)(object)c == (Object)(object)HiddenCategory; } return true; } return false; } internal static bool IsSelected(CosmeticCategoryAsset? c) { if ((Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)SelectedCategory; } return false; } internal static bool IsSearch(CosmeticCategoryAsset? c) { if ((Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)SearchCategory; } return false; } internal static bool IsFavCategory(CosmeticCategoryAsset? c) { if ((Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)FavoritesCategory; } return false; } internal static bool IsHideCategory(CosmeticCategoryAsset? c) { if ((Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)HiddenCategory; } return false; } internal static void EnsureCategories() { if (SelectedCategory == null) { SelectedCategory = MakeCategory("MHB_Selected", "SELECTED"); } if (SearchCategory == null) { SearchCategory = MakeCategory("MHB_Search", "SEARCH"); } if (FavoritesCategory == null) { FavoritesCategory = MakeCategory("MHB_Favorites", "FAV"); } if (HiddenCategory == null) { HiddenCategory = MakeCategory("MHB_Hidden", "HIDE"); } } internal static void SetStatusLabel(TextMeshProUGUI? v) { StatusLabel = v; } internal static void SetSearchField(TMP_InputField? v) { SearchField = v; } internal static void SetEmptyStateLabel(GameObject? v) { EmptyStateLabel = v; } internal static void SetSearchMode(bool v) { SearchMode = v; } internal static void SetSearchText(string v) { SearchText = v; } internal static void ClearSearch() { SearchText = ""; SearchMode = false; if ((Object)(object)SearchField != (Object)null) { SearchField.SetTextWithoutNotify(""); } } internal static void ScheduleSearchRefresh() { MenuPageCosmetics activePage = ActivePage; if (!((Object)(object)activePage == (Object)null)) { if (_searchDebounce != null) { ((MonoBehaviour)activePage).StopCoroutine(_searchDebounce); } _searchDebounce = ((MonoBehaviour)activePage).StartCoroutine(SearchRefreshCoroutine(activePage)); } } [IteratorStateMachine(typeof(d__51))] private static IEnumerator SearchRefreshCoroutine(MenuPageCosmetics page) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__51(0) { page = page }; } internal static void SetActivePage(MenuPageCosmetics? v) { ActivePage = v; } internal static void OnMenuClosed() { if (_searchDebounce != null && (Object)(object)ActivePage != (Object)null) { ((MonoBehaviour)ActivePage).StopCoroutine(_searchDebounce); } _searchDebounce = null; ActivePage = null; _assetIndexCache = null; } internal static int GetAssetIndex(CosmeticAsset asset) { if ((Object)(object)MetaManager.instance == (Object)null) { return -1; } if (_assetIndexCache == null) { List cosmeticAssets = MetaManager.instance.cosmeticAssets; _assetIndexCache = new Dictionary(cosmeticAssets.Count); for (int i = 0; i < cosmeticAssets.Count; i++) { if ((Object)(object)cosmeticAssets[i] != (Object)null) { _assetIndexCache[cosmeticAssets[i]] = i; } } } if (!_assetIndexCache.TryGetValue(asset, out var value)) { return -1; } return value; } internal static bool IsPresetsCategory(CosmeticCategoryAsset? cat) { if ((Object)(object)cat == (Object)null) { return false; } string text = (cat.categoryName ?? ((Object)cat).name ?? "").ToUpperInvariant(); if (!text.Contains("PRESET")) { return text.Contains("OUTFIT"); } return true; } private static CosmeticCategoryAsset MakeCategory(string id, string label) { CosmeticCategoryAsset val = ScriptableObject.CreateInstance(); ((Object)val).name = id; val.categoryName = label; val.typeList = Enum.GetValues(typeof(CosmeticType)).Cast().ToList(); return val; } } internal static class FavHideMarkerHelper { private const string MarkerName = "MHB_FavHideMarker"; private const float OffsetX = -7f; private const float OffsetY = 7f; private const float Size = 9f; internal static void UpdateMarker(MenuElementCosmeticButton btn) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)btn == (Object)null || (Object)(object)btn.cosmeticAsset == (Object)null) { return; } bool flag = BridgeFavoritesManager.IsFavorite(btn.cosmeticAsset); bool flag2 = BridgeFavoritesManager.IsHidden(btn.cosmeticAsset); Transform val = ((Component)btn).transform.Find("MHB_FavHideMarker"); if (!flag && !flag2) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } return; } Image val4; if ((Object)(object)val == (Object)null) { GameObject val2 = new GameObject("MHB_FavHideMarker"); RectTransform val3 = val2.AddComponent(); val2.transform.SetParent(((Component)btn).transform, false); val2.transform.SetAsLastSibling(); val3.anchorMin = new Vector2(1f, 0f); val3.anchorMax = new Vector2(1f, 0f); val3.pivot = new Vector2(1f, 0f); ApplyRect(val3); val4 = val2.AddComponent(); ((Graphic)val4).raycastTarget = false; val4.preserveAspect = true; } else { val.SetAsLastSibling(); ApplyRect(((Component)val).GetComponent()); val4 = ((Component)val).GetComponent(); } if (!((Object)(object)val4 == (Object)null)) { if (flag) { val4.sprite = FavHideIcons.StarSprite; ((Graphic)val4).color = Color.white; } else { val4.sprite = FavHideIcons.HideSprite; ((Graphic)val4).color = Color.white; } } } private static void ApplyRect(RectTransform? rt) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rt == (Object)null)) { rt.anchoredPosition = new Vector2(-7f, 7f); rt.sizeDelta = new Vector2(9f, 9f); } } } [HarmonyPatch] internal static class CosmeticsBulkActionRefreshPatch { [CompilerGenerated] private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "RandomizeAllButton", (Type[])null, (Type[])null); <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "RandomizeCosmeticsButton", (Type[])null, (Type[])null); <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "ResetAllButton", (Type[])null, (Type[])null); <>1__state = 3; return true; case 3: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "ResetCosmeticsButton", (Type[])null, (Type[])null); <>1__state = 4; return true; case 4: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [IteratorStateMachine(typeof(d__0))] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2); } [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.selectedTab == 0 && CosmeticsMenuState.IsSelected(__instance.selectedCategory)) { __instance.RefreshScrollContent(); } } } [HarmonyPatch(typeof(MenuPageCosmetics), "RefreshScrollContent")] internal static class CosmeticsFilterPatch { private const float SectionSpacing = 10f; private const float SectionHeader = 40f; internal const string WorldSectionName = "MHB_WorldSection"; internal const CosmeticType WorldSubCategory = 999; [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Invalid comparison between Unknown and I4 //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Invalid comparison between Unknown and I4 //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Expected O, but got Unknown //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_047f: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04cd: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.selectedTab == 1) { UpdateSearchFieldVisibility(isSearch: false); HideEmptyState(); return; } CosmeticCategoryAsset selectedCategory = __instance.selectedCategory; if ((Object)(object)selectedCategory == (Object)null) { return; } if (CosmeticsMenuState.IsPresetsCategory(selectedCategory)) { UpdateSearchFieldVisibility(isSearch: false); HideEmptyState(); return; } bool flag = CosmeticsMenuState.IsSelected(selectedCategory); bool flag2 = CosmeticsMenuState.IsSearch(selectedCategory); bool flag3 = CosmeticsMenuState.IsFavCategory(selectedCategory); bool flag4 = CosmeticsMenuState.IsHideCategory(selectedCategory); bool flag5 = CosmeticsMenuState.IsVirtual(selectedCategory); string text = (CosmeticsMenuState.SearchText?.Trim() ?? "").ToLowerInvariant(); bool flag6 = text.Length > 0; UpdateSearchFieldVisibility(flag2); BridgeFavoritesManager.EnsureLoaded(); bool flag7 = !flag4 && !flag && BridgeFavoritesManager.HasAnyHidden(); if (!flag5 && !flag6 && !flag7) { SortFavoritesInCategory(__instance); HideEmptyState(); } else { if ((Object)(object)MetaManager.instance == (Object)null) { return; } HashSet equippedSet = new HashSet(MetaManager.instance.cosmeticEquipped); HashSet unlocksSet = new HashSet(MetaManager.instance.cosmeticUnlocks); Dictionary assetIndexMap = BuildAssetIndexMap(MetaManager.instance); Dictionary dictionary = new Dictionary(); foreach (Transform item in __instance.subCategoriesTransform) { Transform val = item; MenuElementButtonCosmeticCategory component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && (int)component.buttonType == 1) { dictionary[component.subCategory] = ((Component)val).gameObject; } } foreach (GameObject value in dictionary.Values) { value.SetActive(!flag5); } float num = 0f; int num2 = 0; bool flag8 = flag5 && HhhCosmeticLoader.WorldAssetIds.Count > 0; MenuElementCosmeticSection val2 = null; foreach (MenuElementCosmeticSection item2 in __instance.sections.ToList()) { if (item2.isStickyHeader) { continue; } bool flag9 = flag8 && (int)item2.subCategory == 0; MenuElementCosmeticButton[] componentsInChildren = ((Component)item2.cosmeticListTransform).GetComponentsInChildren(true); MenuElementCosmeticButton[] array = componentsInChildren.Where((MenuElementCosmeticButton b) => (Object)(object)b != (Object)null && (Object)(object)b.cosmeticAsset != (Object)null).ToArray(); int num3 = 0; MenuElementCosmeticButton[] array2 = array; foreach (MenuElementCosmeticButton val3 in array2) { if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)null) { continue; } bool flag10; if (flag9 && HhhCosmeticLoader.IsWorldAsset(val3.cosmeticAsset)) { flag10 = false; } else { if (!flag5 && flag7) { if (BridgeFavoritesManager.IsHidden(val3.cosmeticAsset) && ((Component)val3).gameObject.activeSelf) { ((Component)val3).gameObject.SetActive(false); } if (((Component)val3).gameObject.activeSelf) { FavHideMarkerHelper.UpdateMarker(val3); } continue; } flag10 = Matches(val3.cosmeticAsset, flag, flag2, flag3, flag4, flag7, flag6, text, equippedSet, assetIndexMap, unlocksSet); } if (((Component)val3).gameObject.activeSelf != flag10) { ((Component)val3).gameObject.SetActive(flag10); } if (flag10) { FavHideMarkerHelper.UpdateMarker(val3); } else { num3++; } } int num4 = array.Length - num3; if (num4 == 0) { if (flag5) { __instance.sections.Remove(item2); Object.Destroy((Object)(object)((Component)item2).gameObject); } continue; } num2 += num4; if (flag5) { if (!((Component)item2).gameObject.activeSelf) { ((Component)item2).gameObject.SetActive(true); } if ((Object)(object)item2.highlightObj != (Object)null) { ((Component)item2.highlightObj).gameObject.SetActive(false); } GridLayoutGroup component2 = ((Component)item2.cosmeticListTransform).GetComponent(); ((LayoutGroup)component2).padding = new RectOffset(((LayoutGroup)component2).padding.left, ((LayoutGroup)component2).padding.right, ((LayoutGroup)component2).padding.top, 0); int num5 = Mathf.Max(1, component2.constraintCount); int num6 = Mathf.Max(1, Mathf.CeilToInt((float)(num4 + 1) / (float)num5)); float num7 = component2.cellSize.y * (float)num6 + component2.spacing.y * (float)(num6 - 1) + (float)((LayoutGroup)component2).padding.top + (float)((LayoutGroup)component2).padding.bottom; float num8 = 40f + num7; RectTransform component3 = ((Component)item2).GetComponent(); ((Transform)component3).localPosition = new Vector3(((Transform)component3).localPosition.x, num, ((Transform)component3).localPosition.z); component3.sizeDelta = new Vector2(component3.sizeDelta.x, num8); RectTransform component4 = ((Component)item2.cosmeticListTransform).GetComponent(); component4.sizeDelta = new Vector2(component4.sizeDelta.x, num7); LayoutRebuilder.ForceRebuildLayoutImmediate(component4); LayoutRebuilder.ForceRebuildLayoutImmediate(component3); val2 = item2; num -= num8 + 10f; } } if (!flag5 || flag2 || flag || flag3 || flag4) { SortFavoritesInCategory(__instance, flag); } int num9 = (flag8 ? InjectWorldSection(__instance, num, flag, flag2, flag3, flag4, flag7, flag6, text, equippedSet, assetIndexMap, unlocksSet) : 0); num2 += num9; if (flag5) { MenuElementCosmeticSection obj; if (num9 <= 0) { obj = val2; } else { List sections = __instance.sections; obj = sections[sections.Count - 1]; } MenuElementCosmeticSection section = obj; ApplyStickyPadding(__instance, section); } if (flag5 && num2 == 0) { string message = (flag3 ? "Add a favorite with Ctrl+click :)" : (flag4 ? "Hide cosmetics with Alt+click :P" : ((!flag2) ? "Equip a cosmetic to see it here :3" : (string.IsNullOrWhiteSpace(CosmeticsMenuState.SearchText) ? "Type to search cosmetics here :)" : "No cosmetics found :'(")))); ShowEmptyState(message); } else { HideEmptyState(); } if (flag5) { RebuildScroll(__instance); } } } private static void SortFavoritesInCategory(MenuPageCosmetics page, bool hiddenAtEnd = false) { bool flag = BridgeFavoritesManager.HasAnyFavorite(); bool hasHidden = hiddenAtEnd && BridgeFavoritesManager.HasAnyHidden(); foreach (MenuElementCosmeticSection section in page.sections) { if ((Object)(object)section == (Object)null || (Object)(object)section.cosmeticListTransform == (Object)null) { continue; } MenuElementCosmeticButton[] componentsInChildren = ((Component)section.cosmeticListTransform).GetComponentsInChildren(true); MenuElementCosmeticButton[] array = componentsInChildren; foreach (MenuElementCosmeticButton val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset != (Object)null && ((Component)val).gameObject.activeSelf) { FavHideMarkerHelper.UpdateMarker(val); } } MenuElementCosmeticButton val2 = ((IEnumerable)componentsInChildren).FirstOrDefault((Func)((MenuElementCosmeticButton b) => (Object)(object)b != (Object)null && (Object)(object)b.cosmeticAsset == (Object)null)); MenuElementCosmeticButton[] array2 = componentsInChildren.Where((MenuElementCosmeticButton b) => (Object)(object)b != (Object)null && (Object)(object)b.cosmeticAsset != (Object)null).ToArray(); if (array2.Length == 0) { continue; } bool flag2 = flag && array2.Any((MenuElementCosmeticButton b) => ((Component)b).gameObject.activeSelf && BridgeFavoritesManager.IsFavorite(b.cosmeticAsset)); bool flag3 = hasHidden && array2.Any((MenuElementCosmeticButton b) => ((Component)b).gameObject.activeSelf && BridgeFavoritesManager.IsHidden(b.cosmeticAsset)); bool flag4 = array2.Any((MenuElementCosmeticButton b) => ((Component)b).gameObject.activeSelf && PerCosmeticOverrides.IsModdedForAsset(b.cosmeticAsset)); if (!flag2 && !flag3 && !flag4) { continue; } MenuElementCosmeticButton[] array3 = array2.Where((MenuElementCosmeticButton b) => ((Component)b).gameObject.activeSelf).OrderBy(delegate(MenuElementCosmeticButton b) { if (BridgeFavoritesManager.IsFavorite(b.cosmeticAsset)) { return 0; } return (!hasHidden || !BridgeFavoritesManager.IsHidden(b.cosmeticAsset)) ? 1 : 3; }).ThenBy((MenuElementCosmeticButton b) => (!IsUnlocked(b)) ? 1 : 0) .ThenBy((MenuElementCosmeticButton b) => (!PerCosmeticOverrides.IsModdedForAsset(b.cosmeticAsset)) ? 1 : 0) .ThenBy((MenuElementCosmeticButton b) => ((Component)b).transform.GetSiblingIndex()) .ToArray(); MenuElementCosmeticButton[] array4 = (from b in array2 where !((Component)b).gameObject.activeSelf orderby ((Component)b).transform.GetSiblingIndex() select b).ToArray(); int num = 0; if ((Object)(object)val2 != (Object)null) { ((Component)val2).transform.SetSiblingIndex(num++); } MenuElementCosmeticButton[] array5 = array3; foreach (MenuElementCosmeticButton val3 in array5) { ((Component)val3).transform.SetSiblingIndex(num++); } MenuElementCosmeticButton[] array6 = array4; foreach (MenuElementCosmeticButton val4 in array6) { ((Component)val4).transform.SetSiblingIndex(num++); } Transform cosmeticListTransform = section.cosmeticListTransform; RectTransform val5 = ((cosmeticListTransform != null) ? ((Component)cosmeticListTransform).GetComponent() : null); if ((Object)(object)val5 != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val5); } } } private static bool IsUnlocked(MenuElementCosmeticButton btn) { if ((Object)(object)MetaManager.instance == (Object)null) { return true; } int assetIndex = CosmeticsMenuState.GetAssetIndex(btn.cosmeticAsset); if (assetIndex < 0) { return true; } return MetaManager.instance.cosmeticUnlocks.Contains(assetIndex); } private static void ApplyStickyPadding(MenuPageCosmetics page, MenuElementCosmeticSection? section) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: 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_0114: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)section == (Object)null) { return; } RectTransform val = page.stickyHeader?.viewport; if ((Object)(object)val == (Object)null) { return; } RectTransform component = ((Component)section).GetComponent(); Transform cosmeticListTransform = section.cosmeticListTransform; RectTransform val2 = ((cosmeticListTransform != null) ? ((Component)cosmeticListTransform).GetComponent() : null); Transform cosmeticListTransform2 = section.cosmeticListTransform; GridLayoutGroup val3 = ((cosmeticListTransform2 != null) ? ((Component)cosmeticListTransform2).GetComponent() : null); if (!((Object)(object)component == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val3 == (Object)null)) { Rect rect = val.rect; float num = Mathf.Max(0f, ((Rect)(ref rect)).height - component.sizeDelta.y - 10f); if (!(num <= 0f)) { ((LayoutGroup)val3).padding = new RectOffset(((LayoutGroup)val3).padding.left, ((LayoutGroup)val3).padding.right, ((LayoutGroup)val3).padding.top, (int)num); val2.sizeDelta = new Vector2(val2.sizeDelta.x, val2.sizeDelta.y + num); component.sizeDelta = new Vector2(component.sizeDelta.x, component.sizeDelta.y + num); LayoutRebuilder.ForceRebuildLayoutImmediate(val2); LayoutRebuilder.ForceRebuildLayoutImmediate(component); } } } private static int InjectWorldSection(MenuPageCosmetics page, float yPos, bool isSelected, bool isSearch, bool isFav, bool isHide, bool suppressHidden, bool applySearch, string search, HashSet equippedSet, Dictionary assetIndexMap, HashSet unlocksSet) { //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) string search2 = search; HashSet equippedSet2 = equippedSet; Dictionary assetIndexMap2 = assetIndexMap; HashSet unlocksSet2 = unlocksSet; for (int num = page.sections.Count - 1; num >= 0; num--) { if ((Object)(object)page.sections[num] != (Object)null && ((Object)((Component)page.sections[num]).gameObject).name == "MHB_WorldSection") { Object.Destroy((Object)(object)((Component)page.sections[num]).gameObject); page.sections.RemoveAt(num); } } int value; List list = (from a in MetaManager.instance.cosmeticAssets where (Object)(object)a != (Object)null && a.prefab.IsValid() && HhhCosmeticLoader.IsWorldAsset(a) && Matches(a, isSelected, isSearch, isFav, isHide, suppressHidden, applySearch, search2, equippedSet2, assetIndexMap2, unlocksSet2) orderby !unlocksSet2.Contains(assetIndexMap2.TryGetValue(a, out value) ? value : (-1)), a.rarity descending, a.assetName select a).ToList(); if (isSearch || isSelected) { bool hiddenAtEnd = isSelected; list = (from t in list.Select((CosmeticAsset a, int i) => (a, i)) orderby (!BridgeFavoritesManager.IsFavorite(t.a)) ? ((!hiddenAtEnd || !BridgeFavoritesManager.IsHidden(t.a)) ? 1 : 3) : 0, t.i select t.a).ToList(); } if (list.Count == 0) { return 0; } GameObject val = Object.Instantiate(page.sectionPrefab, page.sectionRootTransform); ((Object)val).name = "MHB_WorldSection"; MenuElementCosmeticSection component = val.GetComponent(); component.subCategory = (CosmeticType)999; if ((Object)(object)component.headerText != (Object)null) { ((TMP_Text)component.headerText).text = "WORLD"; ((TMP_Text)component.headerText).ForceMeshUpdate(false, false); } if ((Object)(object)component.highlightObj != (Object)null) { ((Component)component.highlightObj).gameObject.SetActive(false); } GridLayoutGroup component2 = ((Component)component.cosmeticListTransform).GetComponent(); foreach (CosmeticAsset item in list) { GameObject val2 = Object.Instantiate(page.sectionButtonPrefab, component.cosmeticListTransform); MenuElementCosmeticButton component3 = val2.GetComponent(); component3.cosmeticAsset = item; FavHideMarkerHelper.UpdateMarker(component3); } int count = list.Count; int num2 = Mathf.Max(1, component2.constraintCount); int num3 = Mathf.Max(1, Mathf.CeilToInt((float)(count + 1) / (float)num2)); float num4 = component2.cellSize.y * (float)num3 + component2.spacing.y * (float)(num3 - 1) + (float)((LayoutGroup)component2).padding.top + (float)((LayoutGroup)component2).padding.bottom; float num5 = 40f + num4; RectTransform component4 = val.GetComponent(); ((Transform)component4).localPosition = new Vector3(((Transform)component4).localPosition.x, yPos, ((Transform)component4).localPosition.z); component4.sizeDelta = new Vector2(component4.sizeDelta.x, num5); RectTransform component5 = ((Component)component.cosmeticListTransform).GetComponent(); component5.sizeDelta = new Vector2(component5.sizeDelta.x, num4); LayoutRebuilder.ForceRebuildLayoutImmediate(component5); LayoutRebuilder.ForceRebuildLayoutImmediate(component4); page.sections.Add(component); return count; } private static void UpdateSearchFieldVisibility(bool isSearch) { TMP_InputField searchField = CosmeticsMenuState.SearchField; if ((Object)(object)searchField == (Object)null) { return; } if (isSearch) { CosmeticsMenuState.SetSearchMode(v: true); ((Component)searchField).gameObject.SetActive(true); return; } if (CosmeticsMenuState.SearchMode) { CosmeticsMenuState.ClearSearch(); } ((Component)searchField).gameObject.SetActive(false); } private static bool Matches(CosmeticAsset asset, bool isSelected, bool isSearch, bool isFav, bool isHide, bool suppressHidden, bool applySearch, string search, HashSet equippedSet, Dictionary assetIndexMap, HashSet unlocksSet) { if (isSearch && !applySearch) { return false; } assetIndexMap.TryGetValue(asset, out var value); if (isHide) { return BridgeFavoritesManager.IsHidden(asset); } if (isFav) { return BridgeFavoritesManager.IsFavorite(asset); } if (suppressHidden && BridgeFavoritesManager.IsHidden(asset)) { return false; } if (isSelected && !equippedSet.Contains(value)) { return false; } if (isSearch && value >= 0 && !unlocksSet.Contains(value)) { return false; } if (applySearch) { string text = (asset.assetName ?? ((Object)asset).name ?? "").ToLowerInvariant(); if (!text.Contains(search)) { return false; } } return true; } private static Dictionary BuildAssetIndexMap(MetaManager meta) { Dictionary dictionary = new Dictionary(meta.cosmeticAssets.Count); for (int i = 0; i < meta.cosmeticAssets.Count; i++) { CosmeticAsset val = meta.cosmeticAssets[i]; if ((Object)(object)val != (Object)null) { dictionary[val] = i; } } return dictionary; } private static void ShowEmptyState(string message) { GameObject emptyStateLabel = CosmeticsMenuState.EmptyStateLabel; if (!((Object)(object)emptyStateLabel == (Object)null)) { TextMeshProUGUI component = emptyStateLabel.GetComponent(); if ((Object)(object)component != (Object)null) { ((TMP_Text)component).text = message; } if (!emptyStateLabel.activeSelf) { emptyStateLabel.SetActive(true); } } } private static void HideEmptyState() { GameObject emptyStateLabel = CosmeticsMenuState.EmptyStateLabel; if ((Object)(object)emptyStateLabel != (Object)null && emptyStateLabel.activeSelf) { emptyStateLabel.SetActive(false); } } private static void RebuildScroll(MenuPageCosmetics page) { ScrollRect componentInChildren = ((Component)page).GetComponentInChildren(true); if ((Object)(object)((componentInChildren != null) ? componentInChildren.content : null) != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(componentInChildren.content); } } } [HarmonyPatch(typeof(MenuPageCosmetics), "LateUpdate")] internal static class CosmeticsMenuLateUpdatePatch { private const string Hint = "Ctrl+click = Fav\nAlt+click = Hide"; private const float HintDelay = 2f; private const float HintFade = 0.5f; private const float HintAlpha = 0.4f; private static readonly Color NormalColor = Color.white; private static float _noHoverTime; internal static void OnMenuClosed() { _noHoverTime = 0f; } [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI statusLabel = CosmeticsMenuState.StatusLabel; if ((Object)(object)statusLabel == (Object)null) { return; } if ((int)__instance.selectedTab != 0) { _noHoverTime = 0f; ((Component)((TMP_Text)statusLabel).transform.parent).gameObject.SetActive(false); return; } ((Component)((TMP_Text)statusLabel).transform.parent).gameObject.SetActive(true); MenuElementCosmeticButton hoveredCosmeticButton = __instance.hoveredCosmeticButton; bool flag = false; if ((Object)(object)hoveredCosmeticButton != (Object)null) { flag = (Object)(object)hoveredCosmeticButton.cosmeticAsset != (Object)null || IsLocked(hoveredCosmeticButton); } if (flag) { _noHoverTime = 0f; string text; if (IsLocked(hoveredCosmeticButton)) { text = "Locked"; } else { CosmeticAsset cosmeticAsset = hoveredCosmeticButton.cosmeticAsset; string text2 = cosmeticAsset?.assetName ?? ((cosmeticAsset != null) ? ((Object)cosmeticAsset).name : null) ?? ""; BridgeFavoritesManager.EnsureLoaded(); bool flag2 = BridgeFavoritesManager.IsFavorite(cosmeticAsset); bool flag3 = BridgeFavoritesManager.IsHidden(cosmeticAsset); string text3 = ""; if (flag2 && flag3) { text3 = "[FAV] [HIDE] "; } else if (flag2) { text3 = "[FAV] "; } else if (flag3) { text3 = "[HIDE] "; } text = text3 + text2; } ((TMP_Text)statusLabel).text = text; ((Graphic)statusLabel).color = NormalColor; } else { _noHoverTime += Time.unscaledDeltaTime; float num = Mathf.Clamp01((_noHoverTime - 2f) / 0.5f); ((TMP_Text)statusLabel).text = "Ctrl+click = Fav\nAlt+click = Hide"; ((Graphic)statusLabel).color = new Color(1f, 1f, 1f, num * 0.4f); } } private static bool IsLocked(MenuElementCosmeticButton? btn) { CosmeticAsset val = btn?.cosmeticAsset; if ((Object)(object)val == (Object)null || (Object)(object)MetaManager.instance == (Object)null) { return false; } int assetIndex = CosmeticsMenuState.GetAssetIndex(val); if (assetIndex < 0) { return false; } return !MetaManager.instance.cosmeticUnlocks.Contains(assetIndex); } } [HarmonyPatch(typeof(MenuPageCosmetics), "Start")] internal static class CosmeticsMenuStartPatch { [CompilerGenerated] private sealed class d__12 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private MenuElementButtonCosmeticCategory btn; public MenuElementButtonCosmeticCategory <>3__btn; string IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__12(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { TextMeshProUGUI componentInChildren; switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (btn.category?.categoryName != null) { <>2__current = btn.category.categoryName; <>1__state = 1; return true; } goto IL_0061; case 1: <>1__state = -1; goto IL_0061; case 2: { <>1__state = -1; break; } IL_0061: componentInChildren = ((Component)btn).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null && !string.IsNullOrWhiteSpace(((TMP_Text)componentInChildren).text)) { <>2__current = ((TMP_Text)componentInChildren).text; <>1__state = 2; return true; } break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__12 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__12(0); } d__.btn = <>3__btn; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [HarmonyPostfix] [HarmonyPriority(600)] private static void Postfix(MenuPageCosmetics __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.selectedTab != 0) { return; } try { HhhCosmeticLoader.ReapplyDefaultRarityToAll(); CosmeticsMenuState.SetActivePage(__instance); if (Plugin.EnableMenuEnhancements.Value) { CosmeticsMenuState.EnsureCategories(); InjectVirtualCategoryButtons(__instance); ReorderCategoryStrip(__instance); InjectSecondDivider(__instance); BuildVirtualCategoryTypeList(__instance); InjectStatusLabel(__instance); InjectSearchField(__instance); InjectEmptyStateLabel(__instance); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Menu injection error: " + ex.Message + "\n" + ex.StackTrace)); } } private static void InjectVirtualCategoryButtons(MenuPageCosmetics page) { (CosmeticCategoryAsset, string)[] array = new(CosmeticCategoryAsset, string)[4] { (CosmeticsMenuState.SearchCategory, "SEARCH"), (CosmeticsMenuState.SelectedCategory, "SELECTED"), (CosmeticsMenuState.FavoritesCategory, "FAV"), (CosmeticsMenuState.HiddenCategory, "HIDE") }; (CosmeticCategoryAsset, string)[] array2 = array; for (int i = 0; i < array2.Length; i++) { var (cat, text) = array2[i]; if (!((Object)(object)cat == (Object)null) && !((Component)page.categoriesTransform).GetComponentsInChildren(true).Any((MenuElementButtonCosmeticCategory b) => (Object)(object)b.category == (Object)(object)cat)) { GameObject val = Object.Instantiate(page.categoryButtonPrefab, page.categoriesTransform); MenuElementButtonCosmeticCategory component = val.GetComponent(); component.category = cat; TextMeshProUGUI componentInChildren = val.GetComponentInChildren(); ((TMP_Text)componentInChildren).fontSize = 20f; ((TMP_Text)componentInChildren).text = text; } } page.categoriesHolder.UpdateButtons(); } private static void ReorderCategoryStrip(MenuPageCosmetics page) { (string[], bool)[] array = new(string[], bool)[11] { (new string[4] { "PRESETS", "PRESET", "OUTFITS", "OUTFIT" }, false), (new string[1] { "|" }, true), (new string[1] { "SEARCH" }, false), (new string[2] { "SELECTED", "EQUIPPED" }, false), (new string[1] { "FAV" }, false), (new string[1] { "HEAD" }, false), (new string[1] { "BODY" }, false), (new string[1] { "ARMS" }, false), (new string[1] { "LEGS" }, false), (new string[1] { "WORLD" }, false), (new string[1] { "HIDE" }, false) }; List source = ((Component)page.categoriesTransform).GetComponentsInChildren(true).ToList(); Transform val = FindDivider(page); int num = 0; (string[], bool)[] array2 = array; for (int i = 0; i < array2.Length; i++) { (string[], bool) tuple = array2[i]; string[] keys; (keys, _) = tuple; if (tuple.Item2) { if ((Object)(object)val != (Object)null) { val.SetSiblingIndex(num++); } continue; } MenuElementButtonCosmeticCategory val2 = ((IEnumerable)source).FirstOrDefault((Func)((MenuElementButtonCosmeticCategory b) => MatchesAnyLabel(b, keys))); if ((Object)(object)val2 != (Object)null) { ((Component)val2).transform.SetSiblingIndex(num++); } } } private static void InjectSecondDivider(MenuPageCosmetics page) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown Transform val = FindDivider(page); if ((Object)(object)val == (Object)null) { return; } MenuElementButtonCosmeticCategory val2 = ((IEnumerable)((Component)page.categoriesTransform).GetComponentsInChildren(true)).FirstOrDefault((Func)((MenuElementButtonCosmeticCategory b) => (Object)(object)b.category == (Object)(object)CosmeticsMenuState.SelectedCategory)); if ((Object)(object)val2 == (Object)null) { return; } foreach (Transform item in page.categoriesTransform) { Transform val3 = item; if ((Object)(object)val3 != (Object)(object)val && (Object)(object)((Component)val3).GetComponent() == (Object)null && ((Object)val3).name == ((Object)val).name + "_MHB") { return; } } Transform val4 = Object.Instantiate(val, page.categoriesTransform); ((Object)val4).name = ((Object)val).name + "_MHB"; val4.SetSiblingIndex(((Component)val2).transform.GetSiblingIndex() + 1); } internal static Transform? FindDivider(MenuPageCosmetics page) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown foreach (Transform item in page.categoriesTransform) { Transform val = item; if ((Object)(object)((Component)val).GetComponent() == (Object)null) { return val; } } return null; } private static void BuildVirtualCategoryTypeList(MenuPageCosmetics page) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) CosmeticCategoryAsset searchCategory = CosmeticsMenuState.SearchCategory; CosmeticCategoryAsset selectedCategory = CosmeticsMenuState.SelectedCategory; if ((Object)(object)searchCategory == (Object)null || (Object)(object)selectedCategory == (Object)null) { return; } HashSet hashSet = new HashSet(); List list = new List(); foreach (Transform item in page.categoriesTransform) { Transform val = item; MenuElementButtonCosmeticCategory component = ((Component)val).GetComponent(); if ((Object)(object)component?.category == (Object)null) { continue; } CosmeticCategoryAsset category = component.category; if (CosmeticsMenuState.IsVirtual(category) || WorldCosmeticsMenuState.IsWorldCategory(category) || CosmeticsMenuState.IsPresetsCategory(category) || category.typeList == null) { continue; } foreach (CosmeticType type in category.typeList) { if (hashSet.Add(type)) { list.Add(type); } } } searchCategory.typeList = new List(list); selectedCategory.typeList = new List(list); if ((Object)(object)CosmeticsMenuState.FavoritesCategory != (Object)null) { CosmeticsMenuState.FavoritesCategory.typeList = new List(list); } if ((Object)(object)CosmeticsMenuState.HiddenCategory != (Object)null) { CosmeticsMenuState.HiddenCategory.typeList = new List(list); } } private static void InjectStatusLabel(MenuPageCosmetics page) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_004e: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_00fc: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)CosmeticsMenuState.StatusLabel != (Object)null)) { TextMeshProUGUI componentInChildren = ((Component)page).GetComponentInChildren(); if (!((Object)(object)componentInChildren == (Object)null)) { GameObject val = new GameObject("MHB_StatusLabel"); val.transform.SetParent(((Component)page).transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0.5f, 1f); val2.anchoredPosition = new Vector2(-270f, -340f); val2.sizeDelta = new Vector2(-610f, 17f); Image val3 = val.AddComponent(); ((Graphic)val3).color = new Color(0f, 0f, 0f, 0.55f); GameObject val4 = new GameObject("Text"); val4.transform.SetParent(val.transform, false); RectTransform val5 = val4.AddComponent(); val5.anchorMin = Vector2.zero; val5.anchorMax = Vector2.one; val5.offsetMin = new Vector2(6f, 0f); val5.offsetMax = new Vector2(-6f, 0f); TextMeshProUGUI val6 = val4.AddComponent(); ((TMP_Text)val6).font = ((TMP_Text)componentInChildren).font; ((TMP_Text)val6).fontSize = 16f; ((Graphic)val6).color = Color.white; ((TMP_Text)val6).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val6).text = ""; ((Graphic)val6).raycastTarget = false; val.SetActive(false); CosmeticsMenuState.SetStatusLabel(val6); } } } private static void InjectSearchField(MenuPageCosmetics page) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_009a: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)CosmeticsMenuState.SearchField != (Object)null) { ApplySearchFieldLayout(CosmeticsMenuState.SearchField); return; } TextMeshProUGUI componentInChildren = ((Component)page).GetComponentInChildren(); if (!((Object)(object)componentInChildren == (Object)null)) { GameObject val = new GameObject("MHB_SearchField"); val.transform.SetParent(((Component)page).transform, false); val.AddComponent(); Image val2 = val.AddComponent(); ((Graphic)val2).color = new Color(0f, 0f, 0f, 0.65f); GameObject val3 = new GameObject("Text"); val3.transform.SetParent(val.transform, false); RectTransform val4 = val3.AddComponent(); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; val4.offsetMin = new Vector2(6f, 2f); val4.offsetMax = new Vector2(-6f, -2f); TextMeshProUGUI val5 = val3.AddComponent(); ((TMP_Text)val5).font = ((TMP_Text)componentInChildren).font; ((Graphic)val5).color = Color.white; ((TMP_Text)val5).alignment = (TextAlignmentOptions)4097; GameObject val6 = new GameObject("Placeholder"); val6.transform.SetParent(val.transform, false); RectTransform val7 = val6.AddComponent(); val7.anchorMin = Vector2.zero; val7.anchorMax = Vector2.one; val7.offsetMin = new Vector2(6f, 2f); val7.offsetMax = new Vector2(-6f, -2f); TextMeshProUGUI val8 = val6.AddComponent(); ((TMP_Text)val8).font = ((TMP_Text)componentInChildren).font; ((Graphic)val8).color = new Color(1f, 1f, 1f, 0.45f); ((TMP_Text)val8).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val8).text = "Type to Search..."; TMP_InputField val9 = val.AddComponent(); val9.textComponent = (TMP_Text)(object)val5; val9.placeholder = (Graphic)(object)val8; val9.characterLimit = 64; val9.lineType = (LineType)0; ((UnityEvent)(object)val9.onValueChanged).AddListener((UnityAction)delegate(string value) { CosmeticsMenuState.SetSearchText(value ?? ""); CosmeticsMenuState.ScheduleSearchRefresh(); }); ApplySearchFieldLayout(val9); val.SetActive(false); CosmeticsMenuState.SetSearchField(val9); } } private static void ApplySearchFieldLayout(TMP_InputField field) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0094: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) RectTransform component = ((Component)field).GetComponent(); if (!((Object)(object)component == (Object)null)) { component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); if (Plugin.SearchFieldPosition.Value == SearchBarPosition.Top) { component.anchoredPosition = new Vector2(60f, -60f); component.sizeDelta = new Vector2(-330f, 26f); } else { component.anchoredPosition = new Vector2(-255f, -305f); component.sizeDelta = new Vector2(-580f, 25f); } if ((Object)(object)field.textComponent != (Object)null) { field.textComponent.fontSize = 17f; } Graphic placeholder = field.placeholder; TextMeshProUGUI val = (TextMeshProUGUI)(object)((placeholder is TextMeshProUGUI) ? placeholder : null); if (val != null) { ((TMP_Text)val).fontSize = 17f; } } } private static void InjectEmptyStateLabel(MenuPageCosmetics page) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)CosmeticsMenuState.EmptyStateLabel != (Object)null) { ApplyEmptyStateLabelLayout(CosmeticsMenuState.EmptyStateLabel); return; } TextMeshProUGUI componentInChildren = ((Component)page).GetComponentInChildren(); if (!((Object)(object)componentInChildren == (Object)null)) { GameObject val = new GameObject("MHB_EmptyState"); val.transform.SetParent(((Component)page).transform, false); val.AddComponent(); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).font = ((TMP_Text)componentInChildren).font; ((Graphic)val2).color = new Color(1f, 1f, 1f, 0.45f); ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).text = "No items selected"; ((Graphic)val2).raycastTarget = false; ApplyEmptyStateLabelLayout(val); val.SetActive(false); CosmeticsMenuState.SetEmptyStateLabel(val); } } private static void ApplyEmptyStateLabelLayout(GameObject go) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent(); if (!((Object)(object)component == (Object)null)) { component.anchorMin = new Vector2(0f, 0.5f); component.anchorMax = new Vector2(1f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = new Vector2(40f, 75f); component.sizeDelta = new Vector2(0f, 30f); TextMeshProUGUI component2 = go.GetComponent(); if ((Object)(object)component2 != (Object)null) { ((TMP_Text)component2).fontSize = 24f; } } } private static bool MatchesAnyLabel(MenuElementButtonCosmeticCategory btn, string[] keys) { foreach (string buttonLabel in GetButtonLabels(btn)) { string i = Normalize(buttonLabel); if (i.Length != 0 && keys.Any((string k) => Normalize(k) == i)) { return true; } } return false; } [IteratorStateMachine(typeof(d__12))] private static IEnumerable GetButtonLabels(MenuElementButtonCosmeticCategory btn) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__12(-2) { <>3__btn = btn }; } private static string Normalize(string s) { return new string(s.ToUpperInvariant().Where(char.IsLetterOrDigit).ToArray()); } } [HarmonyPatch(typeof(MenuPageCosmetics), "Update")] internal static class CosmeticsMenuUpdatePatch { private static readonly List SearchAllowedKeys = new List { (InputKey)18, (InputKey)6, (InputKey)17, (InputKey)10 }; [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { if (!CosmeticsMenuState.SearchMode || (Object)(object)CosmeticsMenuState.ActivePage != (Object)(object)__instance) { return; } if ((Object)(object)CosmeticsMenuState.SearchField != (Object)null) { if (!CosmeticsMenuState.SearchField.isFocused) { CosmeticsMenuState.SearchField.ActivateInputField(); } if ((Object)(object)InputManager.instance != (Object)null) { InputManager.instance.DisableControlsExcept(0.1f, SearchAllowedKeys); } return; } if (Input.GetKeyDown((KeyCode)27)) { CosmeticsMenuState.ClearSearch(); __instance.RefreshScrollContent(); return; } bool flag = false; string inputString = Input.inputString; for (int i = 0; i < inputString.Length; i++) { char c = inputString[i]; if (c == '\b') { if (CosmeticsMenuState.SearchText.Length > 0) { string searchText = CosmeticsMenuState.SearchText; CosmeticsMenuState.SetSearchText(searchText.Substring(0, searchText.Length - 1)); flag = true; } continue; } if (c == '\r' || c == '\n') { CosmeticsMenuState.SetSearchMode(v: false); break; } if (c >= ' ') { CosmeticsMenuState.SetSearchText(CosmeticsMenuState.SearchText + c); flag = true; } } if (flag) { __instance.RefreshScrollContent(); } } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "ToggleCosmetic")] [HarmonyPriority(400)] internal static class FavHideTogglePatch { private static MethodInfo? _triggerClickAnimations; private static bool _triggerLookupDone; private static int _lastShiftFrame = int.MinValue; [HarmonyPrefix] private static bool Prefix(MenuElementCosmeticButton __instance) { //IL_0123: Unknown result type (might be due to invalid IL or missing references) bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); bool flag2 = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); bool flag3 = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); if (flag3) { _lastShiftFrame = Time.frameCount; } bool flag4 = flag3 || (_lastShiftFrame >= 0 && Time.frameCount - _lastShiftFrame <= 3); if (!flag && !flag2 && !flag4) { return true; } if ((flag || flag2) && !Plugin.EnableMenuEnhancements.Value) { return true; } if ((Object)(object)__instance.menuButton != (Object)null && __instance.menuButton.disabled) { return true; } CosmeticAsset cosmeticAsset = __instance.cosmeticAsset; if ((Object)(object)cosmeticAsset == (Object)null) { return true; } if (flag4 && !flag && !flag2) { if (Plugin.EnableCosmeticOverrideUI.Value && Plugin.MenuLibAvailable && BridgeIds.IsBridgeAsset(cosmeticAsset)) { CosmeticOverridePopup.Show(cosmeticAsset); return false; } return true; } BridgeFavoritesManager.EnsureLoaded(); if (flag) { BridgeFavoritesManager.ToggleFavorite(cosmeticAsset); } else { BridgeFavoritesManager.ToggleHidden(cosmeticAsset); } try { __instance.soundClick.Play(MenuManager.instance.soundPosition, 1f, 1f, 1f, 1f); } catch (Exception ex) { Plugin.Logger.LogDebug((object)("FavHideTogglePatch: sound skipped — " + ex.Message)); } if (!_triggerLookupDone) { _triggerClickAnimations = AccessTools.Method(typeof(MenuElementCosmeticButton), "TriggerClickAnimations", (Type[])null, (Type[])null); _triggerLookupDone = true; } try { _triggerClickAnimations?.Invoke(__instance, null); } catch (Exception ex2) { Plugin.Logger.LogDebug((object)("FavHideTogglePatch: animation skipped — " + ex2.Message)); } FavHideMarkerHelper.UpdateMarker(__instance); return false; } } [HarmonyPatch] internal static class RandomizeHiddenFilterPatch { [CompilerGenerated] private sealed class d__1 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "RandomizeAllButton", (Type[])null, (Type[])null); <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "RandomizeBodyButton", (Type[])null, (Type[])null); <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = AccessTools.Method(typeof(MenuPageCosmetics), "RandomizeCosmeticsButton", (Type[])null, (Type[])null); <>1__state = 3; return true; case 3: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__1(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private static readonly List _removed = new List(); [IteratorStateMachine(typeof(d__1))] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(-2); } [HarmonyPrefix] private static void Prefix() { _removed.Clear(); BridgeFavoritesManager.EnsureLoaded(); if (!BridgeFavoritesManager.HasAnyHidden()) { return; } MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } try { for (int num = instance.cosmeticUnlocks.Count - 1; num >= 0; num--) { int num2 = instance.cosmeticUnlocks[num]; if (num2 >= 0 && num2 < instance.cosmeticAssets.Count) { CosmeticAsset val = instance.cosmeticAssets[num2]; if ((Object)(object)val != (Object)null && BridgeFavoritesManager.IsHidden(val)) { _removed.Add(num2); instance.cosmeticUnlocks.RemoveAt(num); } } } } catch (Exception ex) { instance.cosmeticUnlocks.AddRange(_removed); _removed.Clear(); Plugin.Logger.LogWarning((object)("RandomizeHiddenFilterPatch: failed to filter hidden cosmetics, skipping: " + ex.Message)); } } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { if (_removed.Count != 0) { MetaManager instance = MetaManager.instance; if ((Object)(object)instance != (Object)null) { instance.cosmeticUnlocks.AddRange(_removed); instance.Save(false); } _removed.Clear(); } } [HarmonyFinalizer] private static Exception? Finalizer(Exception? __exception) { if (_removed.Count > 0) { MetaManager instance = MetaManager.instance; if ((Object)(object)instance != (Object)null) { instance.cosmeticUnlocks.AddRange(_removed); } _removed.Clear(); Plugin.Logger.LogWarning((object)"RandomizeHiddenFilterPatch: vanilla threw during Randomize — hidden cosmetics restored to unlock record via Finalizer."); } return __exception; } } [HarmonyPatch(typeof(MenuElementButtonCosmeticCategory), "UpdateHighlight")] internal static class VirtualCategoryHighlightPatch { [HarmonyPostfix] private static void Postfix(MenuElementButtonCosmeticCategory __instance) { if (CosmeticsMenuState.IsVirtual(__instance.category)) { MenuElementCosmeticHighlight componentInChildren = ((Component)__instance).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren.text).text = "0"; } } } } internal enum OverrideCosmeticType { Hat, HeadBottom, Ears, Eyewear, FaceTop, FaceBottom, BodyTop, BodyBottom, BodyTopOverlay, BodyBottomOverlay, ArmRight, ArmLeft, ArmRightOverlay, ArmLeftOverlay, LegRight, LegLeft, FootRight, FootLeft, LegRightOverlay, LegLeftOverlay, World } internal enum MainCosmeticCategory { Head, Body, Arms, Legs, World } internal static class PerCosmeticOverrides { private sealed class SaveData { [JsonProperty("overrides")] public Dictionary Overrides { get; set; } = new Dictionary(); } private static readonly string SavePath = Path.Combine(Paths.ConfigPath, "MoreHeadBridge_CosmeticOverrides.json"); private static Dictionary _overrides = new Dictionary(); private static Task _lastWrite = Task.CompletedTask; internal static void Load() { try { if (!File.Exists(SavePath)) { _overrides = new Dictionary(); return; } string text = File.ReadAllText(SavePath); _overrides = JsonConvert.DeserializeObject(text)?.Overrides ?? new Dictionary(); if (_overrides.Count > 0) { Plugin.Logger.LogDebug((object)$"PerCosmeticOverrides: loaded {_overrides.Count} override(s)."); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticOverrides: load failed — " + ex.Message)); _overrides = new Dictionary(); } } internal static bool TryGet(string assetId, out CosmeticOverrideData data) { return _overrides.TryGetValue(assetId, out data); } internal static void SetAndApply(CosmeticAsset asset, bool? isModded, Rarity pendingRarity, OverrideCosmeticType pendingType) { //IL_0022: 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) if (!_overrides.TryGetValue(asset.assetId, out CosmeticOverrideData value)) { value = new CosmeticOverrideData(); } value.IsModded = isModded; value.Rarity = pendingRarity; value.Type = pendingType; _overrides[asset.assetId] = value; ApplyToAsset(asset, value); Save(); Plugin.Logger.LogDebug((object)("CosmeticOverride: '" + asset.assetName + "' → isModded=" + (isModded?.ToString() ?? "Default") + ", " + $"rarity={pendingRarity}, type={pendingType}")); } internal static void ResetAll() { _overrides.Clear(); try { if (File.Exists(SavePath)) { File.Delete(SavePath); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticOverrides: could not delete save file — " + ex.Message)); } } internal static void Reset(CosmeticAsset asset) { if (_overrides.Remove(asset.assetId)) { HhhCosmeticLoader.ReapplyDefaults(asset); Save(); Plugin.Logger.LogDebug((object)("CosmeticOverride: '" + asset.assetName + "' reset to defaults.")); } } internal static void ApplyIfPresent(CosmeticAsset asset) { if (_overrides.TryGetValue(asset.assetId, out CosmeticOverrideData value)) { ApplyToAsset(asset, value); } } internal static bool HasOverride(CosmeticAsset asset) { return _overrides.ContainsKey(asset.assetId); } internal static bool IsModdedForAsset(CosmeticAsset? asset) { if ((Object)(object)asset == (Object)null || !BridgeIds.IsBridgeAsset(asset)) { return false; } if (_overrides.TryGetValue(asset.assetId, out CosmeticOverrideData value) && value.IsModded.HasValue) { return value.IsModded.Value; } return Plugin.HighlightModdedCosmetics.Value; } internal static OverrideCosmeticType GetCurrentType(CosmeticAsset asset) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected I4, but got Unknown if (HhhCosmeticLoader.IsWorldAsset(asset)) { return OverrideCosmeticType.World; } CosmeticType type = asset.type; return (type - 1) switch { 29 => OverrideCosmeticType.HeadBottom, 16 => OverrideCosmeticType.Ears, 17 => OverrideCosmeticType.Eyewear, 30 => OverrideCosmeticType.FaceTop, 31 => OverrideCosmeticType.FaceBottom, 19 => OverrideCosmeticType.BodyTop, 20 => OverrideCosmeticType.BodyBottom, 15 => OverrideCosmeticType.BodyTopOverlay, 22 => OverrideCosmeticType.BodyBottomOverlay, 0 => OverrideCosmeticType.ArmRight, 1 => OverrideCosmeticType.ArmLeft, 25 => OverrideCosmeticType.ArmRightOverlay, 26 => OverrideCosmeticType.ArmLeftOverlay, 2 => OverrideCosmeticType.LegRight, 3 => OverrideCosmeticType.LegLeft, 18 => OverrideCosmeticType.FootRight, 21 => OverrideCosmeticType.FootLeft, 27 => OverrideCosmeticType.LegRightOverlay, 28 => OverrideCosmeticType.LegLeftOverlay, _ => OverrideCosmeticType.Hat, }; } internal static MainCosmeticCategory GetCurrentMain(CosmeticAsset asset) { switch (GetCurrentType(asset)) { case OverrideCosmeticType.Hat: case OverrideCosmeticType.HeadBottom: case OverrideCosmeticType.Ears: case OverrideCosmeticType.Eyewear: case OverrideCosmeticType.FaceTop: case OverrideCosmeticType.FaceBottom: return MainCosmeticCategory.Head; case OverrideCosmeticType.BodyTop: case OverrideCosmeticType.BodyBottom: case OverrideCosmeticType.BodyTopOverlay: case OverrideCosmeticType.BodyBottomOverlay: return MainCosmeticCategory.Body; case OverrideCosmeticType.ArmRight: case OverrideCosmeticType.ArmLeft: case OverrideCosmeticType.ArmRightOverlay: case OverrideCosmeticType.ArmLeftOverlay: return MainCosmeticCategory.Arms; case OverrideCosmeticType.LegRight: case OverrideCosmeticType.LegLeft: case OverrideCosmeticType.FootRight: case OverrideCosmeticType.FootLeft: case OverrideCosmeticType.LegRightOverlay: case OverrideCosmeticType.LegLeftOverlay: return MainCosmeticCategory.Legs; default: return MainCosmeticCategory.World; } } internal static (CosmeticType cosmeticType, bool isWorld) ResolveType(OverrideCosmeticType t) { return t switch { OverrideCosmeticType.World => ((CosmeticType)0, true), OverrideCosmeticType.Hat => ((CosmeticType)0, false), OverrideCosmeticType.HeadBottom => ((CosmeticType)30, false), OverrideCosmeticType.Ears => ((CosmeticType)17, false), OverrideCosmeticType.Eyewear => ((CosmeticType)18, false), OverrideCosmeticType.FaceTop => ((CosmeticType)31, false), OverrideCosmeticType.FaceBottom => ((CosmeticType)32, false), OverrideCosmeticType.BodyTop => ((CosmeticType)20, false), OverrideCosmeticType.BodyBottom => ((CosmeticType)21, false), OverrideCosmeticType.BodyTopOverlay => ((CosmeticType)16, false), OverrideCosmeticType.BodyBottomOverlay => ((CosmeticType)23, false), OverrideCosmeticType.ArmRight => ((CosmeticType)1, false), OverrideCosmeticType.ArmLeft => ((CosmeticType)2, false), OverrideCosmeticType.ArmRightOverlay => ((CosmeticType)26, false), OverrideCosmeticType.ArmLeftOverlay => ((CosmeticType)27, false), OverrideCosmeticType.LegRight => ((CosmeticType)3, false), OverrideCosmeticType.LegLeft => ((CosmeticType)4, false), OverrideCosmeticType.FootRight => ((CosmeticType)19, false), OverrideCosmeticType.FootLeft => ((CosmeticType)22, false), OverrideCosmeticType.LegRightOverlay => ((CosmeticType)28, false), OverrideCosmeticType.LegLeftOverlay => ((CosmeticType)29, false), _ => ((CosmeticType)0, false), }; } internal static void ApplyToAsset(CosmeticAsset asset, CosmeticOverrideData data) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) if (data.Rarity.HasValue) { asset.rarity = data.Rarity.Value; } if (data.Type.HasValue) { (CosmeticType cosmeticType, bool isWorld) tuple = ResolveType(data.Type.Value); CosmeticType item = tuple.cosmeticType; bool item2 = tuple.isWorld; asset.type = item; if (item2) { HhhCosmeticLoader.WorldAssetIds.Add(asset.assetId); } else { HhhCosmeticLoader.WorldAssetIds.Remove(asset.assetId); } } } private static void Save() { string json = JsonConvert.SerializeObject((object)new SaveData { Overrides = _overrides }, (Formatting)1); _lastWrite = _lastWrite.ContinueWith(delegate { try { string text = SavePath + ".tmp"; File.WriteAllText(text, json, Encoding.UTF8); if (File.Exists(SavePath)) { File.Replace(text, SavePath, null); } else { File.Move(text, SavePath); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticOverrides: save failed — " + ex.Message)); } }, TaskScheduler.Default); } } internal sealed class CosmeticOverrideData { [JsonProperty("isModded")] public bool? IsModded { get; set; } [JsonProperty("rarity")] [JsonConverter(typeof(StringEnumConverter))] public Rarity? Rarity { get; set; } [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter))] public OverrideCosmeticType? Type { get; set; } } internal static class MultiEquipTypes { internal static readonly HashSet All = new HashSet { (CosmeticType)0, (CosmeticType)30, (CosmeticType)31, (CosmeticType)32, (CosmeticType)18, (CosmeticType)17, (CosmeticType)20, (CosmeticType)21, (CosmeticType)1, (CosmeticType)2, (CosmeticType)3, (CosmeticType)19, (CosmeticType)4, (CosmeticType)22 }; internal static bool IsEnabled(CosmeticAsset? asset) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.AllowMultipleCosmetics.Value) { return false; } if ((Object)(object)asset == (Object)null) { return false; } if (HhhCosmeticLoader.IsWorldAsset(asset)) { return true; } return All.Contains(asset.type); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupCosmeticsLogic")] internal static class MultiEquipSetupPatch { private sealed class PatchState { public List? PendingExtras; } private static readonly Dictionary> _extraInstances = new Dictionary>(); private static MethodInfo? _instantiateCosmetic; private static MethodInfo? _playerMaterialSetup; private static MethodInfo? _setupColorsLogic; private static FieldInfo? _iconCreationAvatarField; private static FieldInfo? _equipLerpField; private static FieldInfo? _meshParentsScaleField; private static FieldInfo? _colorsEquippedField; private static MethodInfo? GetInstantiateMethod() { return _instantiateCosmetic ?? (_instantiateCosmetic = AccessTools.Method(typeof(PlayerCosmetics), "InstantiateCosmetic", (Type[])null, (Type[])null)); } private static MethodInfo? GetPlayerMaterialSetup() { return _playerMaterialSetup ?? (_playerMaterialSetup = AccessTools.Method(typeof(PlayerCosmetics), "PlayerMaterialSetup", (Type[])null, (Type[])null)); } private static MethodInfo? GetSetupColorsLogic() { return _setupColorsLogic ?? (_setupColorsLogic = AccessTools.Method(typeof(PlayerCosmetics), "SetupColorsLogic", (Type[])null, (Type[])null)); } [HarmonyPriority(50)] [HarmonyPrefix] private static void Prefix(PlayerCosmetics __instance, ref int[] __0, ref PatchState __state) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.AllowMultipleCosmetics.Value) { DestroyAllTracked(__instance); return; } if ((Object)(object)MetaManager.instance == (Object)null) { return; } __state = new PatchState(); IEnumerable enumerable; IEnumerable cosmeticEquippedPreview; if (MetaManager.instance.cosmeticPreviewEnabled) { PlayerAvatarVisuals playerAvatarVisuals = __instance.playerAvatarVisuals; if (playerAvatarVisuals != null && playerAvatarVisuals.isMenuAvatar) { cosmeticEquippedPreview = MetaManager.instance.cosmeticEquippedPreview; enumerable = cosmeticEquippedPreview; goto IL_0063; } } cosmeticEquippedPreview = __0; enumerable = cosmeticEquippedPreview; goto IL_0063; IL_0063: IEnumerable enumerable2 = enumerable; HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (int item in enumerable2) { if (item >= 0 && item < MetaManager.instance.cosmeticAssets.Count) { CosmeticAsset val = MetaManager.instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && !HhhCosmeticLoader.IsWorldAsset(val) && MultiEquipTypes.All.Contains(val.type) && !hashSet.Add(val.type)) { hashSet2.Add(val); } } } TrimTracked(__instance, hashSet2); __state.PendingExtras = ((hashSet2.Count > 0) ? new List(hashSet2) : null); } [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, PatchState __state) { if (__state != null) { SpawnExtras(__instance, __state); } } [HarmonyFinalizer] private static Exception? Finalizer(PlayerCosmetics __instance, Exception? __exception, PatchState __state) { if (__exception != null && __state != null) { SpawnExtras(__instance, __state); } return __exception; } private static void SpawnExtras(PlayerCosmetics instance, PatchState state) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown List pendingExtras = state.PendingExtras; state.PendingExtras = null; if (pendingExtras == null || pendingExtras.Count == 0 || (Object)(object)instance == (Object)null) { return; } MethodInfo instantiateMethod = GetInstantiateMethod(); if (instantiateMethod == null) { return; } if (!_extraInstances.TryGetValue(instance, out List value)) { value = new List(); _extraInstances[instance] = value; } HashSet hashSet = new HashSet(); foreach (GameObject item in value) { if (!((Object)(object)item == (Object)null)) { Cosmetic component = item.GetComponent(); if ((Object)(object)component?.cosmeticAsset != (Object)null) { hashSet.Add(component.cosmeticAsset); } } } bool flag = MetaManager.instance?.cosmeticPreviewEnabled ?? false; PlayerAvatarVisuals playerAvatarVisuals = instance.playerAvatarVisuals; bool flag2 = playerAvatarVisuals != null && (playerAvatarVisuals.playerAvatarMenu?.iconMakerAvatar).GetValueOrDefault(); bool flag3 = flag || flag2; List list = new List(); foreach (CosmeticAsset item2 in pendingExtras) { if (hashSet.Contains(item2)) { continue; } try { GameObject val = (GameObject)instantiateMethod.Invoke(instance, new object[1] { item2 }); if ((Object)(object)val != (Object)null) { value.Add(val); list.Add(val); } } catch (Exception ex) { Plugin.Logger.LogDebug((object)("MultiEquipSetupPatch: failed to spawn extra '" + item2?.assetId + "': " + ex.Message)); } } if (list.Count == 0) { return; } try { GetPlayerMaterialSetup()?.Invoke(instance, null); if ((object)_colorsEquippedField == null) { _colorsEquippedField = AccessTools.Field(typeof(PlayerCosmetics), "colorsEquipped"); } if (_colorsEquippedField?.GetValue(instance) is int[] array) { GetSetupColorsLogic()?.Invoke(instance, new object[1] { array }); } } catch (Exception ex2) { Plugin.Logger.LogDebug((object)("MultiEquipSetupPatch: color reapply failed: " + ex2.Message)); } if (flag3) { foreach (GameObject item3 in list) { SkipEquipAnimation(item3); } } PlayerAvatarVisuals playerAvatarVisuals2 = instance.playerAvatarVisuals; if (!((Object)(object)playerAvatarVisuals2 != (Object)null) || playerAvatarVisuals2.isMenuAvatar) { return; } PlayerAvatar playerAvatar = playerAvatarVisuals2.playerAvatar; if (playerAvatar != null && playerAvatar.isLocal) { playerAvatarVisuals2.ApplyLocalVisibilityBody(); return; } int layer = LayerMask.NameToLayer("PlayerVisuals"); foreach (GameObject item4 in list) { PlayerMaterial[] componentsInChildren = item4.GetComponentsInChildren(true); foreach (PlayerMaterial val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.layer = layer; } } } } private static void SkipEquipAnimation(GameObject go) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) try { Cosmetic component = go.GetComponent(); if ((Object)(object)component == (Object)null) { return; } if ((object)_iconCreationAvatarField == null) { _iconCreationAvatarField = AccessTools.Field(typeof(Cosmetic), "iconCreationAvatar"); } _iconCreationAvatarField?.SetValue(component, true); if ((object)_equipLerpField == null) { _equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp"); } _equipLerpField?.SetValue(component, 1f); if ((object)_meshParentsScaleField == null) { _meshParentsScaleField = AccessTools.Field(typeof(Cosmetic), "meshParentsScale"); } if (!(_meshParentsScaleField?.GetValue(component) is List list)) { return; } List meshParents = component.meshParents; for (int i = 0; i < meshParents.Count && i < list.Count; i++) { if ((Object)(object)meshParents[i] != (Object)null) { meshParents[i].localScale = list[i]; } } } catch (Exception ex) { Plugin.Logger.LogDebug((object)("MultiEquipSetupPatch: SkipEquipAnimation failed: " + ex.Message)); } } private static void TrimTracked(PlayerCosmetics instance, HashSet desired) { if (!_extraInstances.TryGetValue(instance, out List value)) { return; } PlayerAvatarVisuals playerAvatarVisuals = instance.playerAvatarVisuals; for (int num = value.Count - 1; num >= 0; num--) { GameObject val = value[num]; if ((Object)(object)val == (Object)null) { value.RemoveAt(num); } else { Cosmetic component = val.GetComponent(); if (!((Object)(object)component?.cosmeticAsset != (Object)null) || !desired.Contains(component.cosmeticAsset)) { if ((Object)(object)playerAvatarVisuals != (Object)null) { PartShrinkerBridge.OnRemove(val, playerAvatarVisuals); } Object.Destroy((Object)(object)val); value.RemoveAt(num); } } } } private static void DestroyAllTracked(PlayerCosmetics instance) { if (!_extraInstances.TryGetValue(instance, out List value)) { return; } PlayerAvatarVisuals playerAvatarVisuals = instance.playerAvatarVisuals; foreach (GameObject item in value) { if (!((Object)(object)item == (Object)null)) { if ((Object)(object)playerAvatarVisuals != (Object)null) { PartShrinkerBridge.OnRemove(item, playerAvatarVisuals); } Object.Destroy((Object)(object)item); } } value.Clear(); } } [HarmonyPatch(typeof(MetaManager), "CosmeticEquip")] internal static class CosmeticEquipColorPreservePatch { [HarmonyPrefix] private static void Prefix(MetaManager __instance, CosmeticAsset _cosmeticAssetNew, bool _isPreview) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0137: 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) if (!Plugin.AllowMultipleCosmetics.Value || (Object)(object)_cosmeticAssetNew == (Object)null) { return; } int num = (int)_cosmeticAssetNew.type; int[] colorsEquipped = __instance.colorsEquipped; if (num < 0 || num >= colorsEquipped.Length) { return; } int num2 = colorsEquipped[num]; if (num2 < 0) { return; } if (_isPreview) { List cosmeticEquippedPreview = __instance.cosmeticEquippedPreview; { foreach (int item in cosmeticEquippedPreview) { if (item >= 0 && item < __instance.cosmeticAssets.Count) { CosmeticAsset val = __instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && val.type == _cosmeticAssetNew.type && !PerCosmeticColors.HasOverride(val.assetId) && !PerCosmeticColors.HasPreviewOverride(val.assetId)) { PerCosmeticColors.SetPreview(val.assetId, num2); } } } return; } } int realTypeColor = PerCosmeticColors.GetRealTypeColor(num, colorsEquipped); if (realTypeColor < 0) { return; } List cosmeticEquipped = __instance.cosmeticEquipped; bool flag = false; foreach (int item2 in cosmeticEquipped) { if (item2 >= 0 && item2 < __instance.cosmeticAssets.Count) { CosmeticAsset val2 = __instance.cosmeticAssets[item2]; if (!((Object)(object)val2 == (Object)null) && val2.type == _cosmeticAssetNew.type && !PerCosmeticColors.HasOverride(val2.assetId)) { PerCosmeticColors.SetNoSave(val2.assetId, realTypeColor); flag = true; } } } if (flag) { PerCosmeticColors.SaveNow(); } } } [HarmonyPatch(typeof(MetaManager), "CosmeticPreviewSet")] internal static class CosmeticPreviewSetClearPatch { [HarmonyPrefix] private static void Prefix(bool _state) { if (Plugin.AllowMultipleCosmetics.Value && !_state && (Object)(object)PerCosmeticColors.PendingAsset == (Object)null) { PerCosmeticColors.ClearPreviewOverrides(); } } } [HarmonyPatch(typeof(MenuPageCosmetics), "ResetAllButton")] internal static class ResetAllButtonClearPatch { [HarmonyPostfix] private static void Postfix() { PerCosmeticColors.ClearAll(); } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "ChangeColorButton")] internal static class PerCosmeticColorButtonPatch { [HarmonyPrefix] private static void Prefix(MenuElementCosmeticButton __instance) { if (Plugin.AllowMultipleCosmetics.Value) { PerCosmeticColors.PendingAsset = __instance.cosmeticAsset; if ((Object)(object)__instance.cosmeticAsset != (Object)null) { PerCosmeticColors.TemporarilyShowForColorPage(__instance.cosmeticAsset); } } } } [HarmonyPatch(typeof(MenuElementCosmeticSection), "ChangeColorButton")] internal static class SectionColorButtonPatch { [HarmonyPrefix] private static void Prefix(MenuElementCosmeticSection __instance) { if (Plugin.AllowMultipleCosmetics.Value) { PerCosmeticColors.PendingAsset = null; } } } [HarmonyPatch(typeof(MenuPageColor), "OnDestroy")] internal static class ColorPageClosePatch { [HarmonyPrefix] private static void Prefix() { PerCosmeticColors.RestoreTypeColor(); } [HarmonyPostfix] private static void Postfix() { PerCosmeticColors.PendingAsset = null; } } [HarmonyPatch(typeof(MetaManager), "CosmeticColorSet")] internal static class CosmeticColorSetPatch { [HarmonyPrefix] private static bool Prefix(int _index, int _colorID) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.AllowMultipleCosmetics.Value) { return true; } if ((Object)(object)PerCosmeticColors.PendingAsset != (Object)null) { CosmeticAsset pendingAsset = PerCosmeticColors.PendingAsset; if ((Object)(object)MetaManager.instance != (Object)null && _index >= 0 && _index < MetaManager.instance.colorsEquipped.Length) { int realTypeColor = PerCosmeticColors.GetRealTypeColor(_index, MetaManager.instance.colorsEquipped); if (realTypeColor >= 0) { foreach (int item in MetaManager.instance.cosmeticEquipped) { if (item >= 0 && item < MetaManager.instance.cosmeticAssets.Count) { CosmeticAsset val = MetaManager.instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && val.type == pendingAsset.type && !(val.assetId == pendingAsset.assetId) && !PerCosmeticColors.HasOverride(val.assetId)) { PerCosmeticColors.SetNoSave(val.assetId, realTypeColor); } } } } } PerCosmeticColors.Set(pendingAsset.assetId, _colorID); if ((Object)(object)MetaManager.instance != (Object)null && _index >= 0 && _index < MetaManager.instance.colorsEquipped.Length) { MetaManager.instance.colorsEquipped[_index] = _colorID; } return false; } return true; } [HarmonyPostfix] private static void Postfix(int _index, int _colorID) { if (Plugin.AllowMultipleCosmetics.Value && !((Object)(object)PerCosmeticColors.PendingAsset != (Object)null) && !((Object)(object)MetaManager.instance == (Object)null) && _index >= 0 && _index < MetaManager.instance.colorsEquipped.Length) { PerCosmeticColors.ClearForType((CosmeticType)_index, MetaManager.instance); } } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsLogic")] internal static class SetupColorsLogicOverridePatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance) { PerCosmeticColors.ApplyOverrides(__instance); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsAllLogic")] internal static class SetupColorsAllLogicOverridePatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance) { PerCosmeticColors.ApplyOverrides(__instance); } } [HarmonyPatch(typeof(MetaManager), "CosmeticPresetSet")] internal static class CosmeticPresetSetColorsPatch { [HarmonyPostfix] private static void Postfix(int _index, List _cosmeticEquipped) { if (Plugin.AllowMultipleCosmetics.Value) { if (_cosmeticEquipped.Count == 0) { PerCosmeticColors.DeletePresetColors(_index); } else { PerCosmeticColors.SavePreset(_index, _cosmeticEquipped); } } } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "Update")] internal static class PresetHoverIndexTrackPatch { [HarmonyPrefix] private static void Prefix(MenuElementCosmeticPreset __instance) { if (Plugin.AllowMultipleCosmetics.Value && (Object)(object)__instance.menuButton != (Object)null && __instance.menuButton.hovering) { PerCosmeticColors.NotifyPresetHoverStart(__instance.presetIndex); } } } [HarmonyPatch(typeof(MetaManager), "CosmeticPreviewSet")] internal static class PresetPreviewColorsPatch { [HarmonyPostfix] private static void Postfix(MetaManager __instance, bool _state) { if (!Plugin.AllowMultipleCosmetics.Value || !_state) { return; } int num = FindMatchingPreset(__instance); if (num < 0) { return; } Dictionary presetColors = PerCosmeticColors.GetPresetColors(num); if (presetColors == null || presetColors.Count == 0) { Plugin.Logger.LogDebug((object)$"Preset hover: preset {num} matched but has no per-cosmetic colors saved."); return; } foreach (KeyValuePair item in presetColors) { PerCosmeticColors.SetPreview(item.Key, item.Value); } PerCosmeticColors.SetPresetPreviewActive(value: true); } private static int FindMatchingPreset(MetaManager meta) { List cosmeticEquippedPreview = meta.cosmeticEquippedPreview; if (cosmeticEquippedPreview.Count == 0) { return -1; } HashSet hashSet = new HashSet(cosmeticEquippedPreview); int pendingHoverPresetIndex = PerCosmeticColors.GetPendingHoverPresetIndex(); if (pendingHoverPresetIndex >= 0 && pendingHoverPresetIndex < meta.cosmeticPresets.Count) { List list = meta.cosmeticPresets[pendingHoverPresetIndex]; if (list.Count == hashSet.Count) { bool flag = true; foreach (int item in list) { if (!hashSet.Contains(item)) { flag = false; break; } } if (flag) { return pendingHoverPresetIndex; } } } for (int i = 0; i < meta.cosmeticPresets.Count; i++) { if (i == pendingHoverPresetIndex) { continue; } List list2 = meta.cosmeticPresets[i]; if (list2.Count == 0 || list2.Count != hashSet.Count) { continue; } bool flag2 = true; foreach (int item2 in list2) { if (!hashSet.Contains(item2)) { flag2 = false; break; } } if (flag2) { return i; } } Plugin.Logger.LogDebug((object)string.Format("Preset hover: no preset matched preview [{0}] (hint={1}).", string.Join(", ", cosmeticEquippedPreview), pendingHoverPresetIndex)); return -1; } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "TogglePreset")] internal static class PresetLoadColorsPatch { private static bool _wasLoad; [HarmonyPrefix] private static void Prefix(MenuElementCosmeticPreset __instance) { MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { _wasLoad = false; } else { _wasLoad = instance.cosmeticPresets[__instance.presetIndex].Count > 0 || instance.colorPresets[__instance.presetIndex].Count > 0; } } [HarmonyPostfix] private static void Postfix(MenuElementCosmeticPreset __instance) { //IL_0095: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.AllowMultipleCosmetics.Value || !_wasLoad) { return; } MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } Dictionary presetColors = PerCosmeticColors.GetPresetColors(__instance.presetIndex); if (presetColors == null || presetColors.Count == 0) { return; } HashSet hashSet = new HashSet(); foreach (int item in instance.cosmeticPresets[__instance.presetIndex]) { if (item >= 0 && item < instance.cosmeticAssets.Count) { CosmeticAsset val = instance.cosmeticAssets[item]; if ((Object)(object)val != (Object)null) { hashSet.Add(val.type); } } } foreach (CosmeticType item2 in hashSet) { PerCosmeticColors.ClearForType(item2, instance); } foreach (KeyValuePair item3 in presetColors) { PerCosmeticColors.SetNoSave(item3.Key, item3.Value); } PerCosmeticColors.SaveNow(); instance.CosmeticPlayerUpdateLocal(false, false); } } [HarmonyPatch(typeof(PlayerCosmetics), "Awake")] internal static class PerCosmeticColorSyncAwakePatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance) { ((Component)__instance).gameObject.AddComponent(); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupCosmetics")] internal static class SendPerCosmeticColorsSyncPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, bool _synced) { if (_synced && Plugin.AllowMultipleCosmetics.Value && SemiFunc.IsMultiplayer() && !((Object)(object)__instance.photonView == (Object)null) && __instance.photonView.IsMine) { PerCosmeticColorSyncComponent component = ((Component)__instance).GetComponent(); if (!((Object)(object)((component != null) ? ((MonoBehaviourPun)component).photonView : null) == (Object)null)) { string text = PerCosmeticColorSerializer.Serialize(PerCosmeticColors.GetAll()); PhotonNetwork.RemoveBufferedRPCs(((MonoBehaviourPun)component).photonView.ViewID, "SyncPerCosmeticColorsRPC", (int[])null); ((MonoBehaviourPun)component).photonView.RPC("SyncPerCosmeticColorsRPC", (RpcTarget)4, new object[1] { text }); } } } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsLogic")] internal static class ApplyRemoteColorsAfterSetupLogicPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance) { RemoteColorSync.Apply(__instance); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsAllLogic")] internal static class ApplyRemoteColorsAfterSetupAllLogicPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance) { RemoteColorSync.Apply(__instance); } } internal static class RemoteColorSync { internal static void Apply(PlayerCosmetics pc) { PlayerAvatarVisuals playerAvatarVisuals = pc.playerAvatarVisuals; if (!((Object)(object)playerAvatarVisuals == (Object)null) && !playerAvatarVisuals.isMenuAvatar) { PlayerAvatar playerAvatar = playerAvatarVisuals.playerAvatar; if (playerAvatar == null || !playerAvatar.isLocal) { ((Component)pc).GetComponent()?.ApplyToCosmetics(pc); } } } } internal static class PerCosmeticColors { private static readonly string PresetSavePath = Path.Combine(Paths.ConfigPath, "MoreHeadBridge_PresetColors.json"); private static Dictionary> _presetColors = new Dictionary>(); private static bool _presetPreviewActive; private static int _pendingHoverPresetIndex = -1; private static Dictionary _previewOverrides = new Dictionary(); private static int _savedTypeColor = -1; private static int _savedTypeIndex = -1; private static readonly string SavePath = Path.Combine(Paths.ConfigPath, "MoreHeadBridge_PerCosmeticColors.json"); private static readonly string LegacyPath = Path.Combine(Application.persistentDataPath, "MoreHeadBridge_PerCosmeticColors.json"); internal static readonly int PropAlbedo = Shader.PropertyToID("_AlbedoColor"); internal static readonly int PropEmission = Shader.PropertyToID("_EmissionColor"); internal static readonly int PropFresnel = Shader.PropertyToID("_FresnelColor"); private static Dictionary _colors = new Dictionary(); internal static CosmeticAsset? PendingAsset { get; set; } internal static void SetPresetPreviewActive(bool value) { _presetPreviewActive = value; } internal static void NotifyPresetHoverStart(int presetIndex) { _pendingHoverPresetIndex = presetIndex; } internal static int GetPendingHoverPresetIndex() { return _pendingHoverPresetIndex; } internal static void SavePreset(int presetIndex, IList cosmeticIndices) { MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } Dictionary dictionary = new Dictionary(); foreach (int cosmeticIndex in cosmeticIndices) { if (cosmeticIndex >= 0 && cosmeticIndex < instance.cosmeticAssets.Count) { CosmeticAsset val = instance.cosmeticAssets[cosmeticIndex]; if (!((Object)(object)val == (Object)null) && _colors.TryGetValue(val.assetId, out var value)) { dictionary[val.assetId] = value; } } } if (dictionary.Count > 0) { _presetColors[presetIndex] = dictionary; } else { _presetColors.Remove(presetIndex); } SavePresets(); } internal static Dictionary? GetPresetColors(int presetIndex) { if (!_presetColors.TryGetValue(presetIndex, out Dictionary value)) { return null; } return value; } internal static void DeletePresetColors(int presetIndex) { if (_presetColors.Remove(presetIndex)) { SavePresets(); } } internal static void LoadPresets() { try { if (File.Exists(PresetSavePath)) { _presetColors = JsonConvert.DeserializeObject>>(File.ReadAllText(PresetSavePath)) ?? new Dictionary>(); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticColors: preset colors load failed: " + ex.Message)); _presetColors = new Dictionary>(); } } private static void SavePresets() { try { File.WriteAllText(PresetSavePath, JsonConvert.SerializeObject((object)_presetColors)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticColors: preset colors save failed: " + ex.Message)); } } internal static void TemporarilyShowForColorPage(CosmeticAsset asset) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected I4, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) _savedTypeIndex = -1; if ((Object)(object)MetaManager.instance == (Object)null || !_colors.TryGetValue(asset.assetId, out var value)) { return; } int num = (int)asset.type; if (num < 0 || num >= MetaManager.instance.colorsEquipped.Length) { return; } _savedTypeColor = MetaManager.instance.colorsEquipped[num]; _savedTypeIndex = num; MetaManager.instance.colorsEquipped[num] = value; foreach (int item in MetaManager.instance.cosmeticEquipped) { if (item >= 0 && item < MetaManager.instance.cosmeticAssets.Count) { CosmeticAsset val = MetaManager.instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && val.type == asset.type && !(val.assetId == asset.assetId) && !_colors.ContainsKey(val.assetId)) { _previewOverrides[val.assetId] = _savedTypeColor; } } } } internal static void RestoreTypeColor() { if (_savedTypeIndex >= 0 && !((Object)(object)MetaManager.instance == (Object)null)) { if (_savedTypeIndex < MetaManager.instance.colorsEquipped.Length) { MetaManager.instance.colorsEquipped[_savedTypeIndex] = _savedTypeColor; } _savedTypeIndex = -1; _savedTypeColor = -1; ClearPreviewOverrides(); } } internal static void SetPreview(string assetId, int colorIndex) { _previewOverrides[assetId] = colorIndex; } internal static bool HasPreviewOverride(string assetId) { return _previewOverrides.ContainsKey(assetId); } internal static void ClearPreviewOverrides() { _previewOverrides.Clear(); _presetPreviewActive = false; } internal static void Set(string assetId, int colorIndex) { _colors[assetId] = colorIndex; Save(); } internal static void SetNoSave(string assetId, int colorIndex) { _colors[assetId] = colorIndex; } internal static bool HasOverride(string assetId) { return _colors.ContainsKey(assetId); } internal static void SaveNow() { Save(); } internal static void ClearAll() { if (_colors.Count != 0) { _colors.Clear(); Save(); } } internal static IReadOnlyDictionary GetAll() { return _colors; } internal static int GetRealTypeColor(int typeIdx, int[] colorsEquipped) { if (_savedTypeIndex != typeIdx) { return colorsEquipped[typeIdx]; } return _savedTypeColor; } internal static void ClearForType(CosmeticType type, MetaManager meta) { //IL_0041: 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) bool flag = false; foreach (int item in meta.cosmeticEquipped) { if (item >= 0 && item < meta.cosmeticAssets.Count) { CosmeticAsset val = meta.cosmeticAssets[item]; if ((Object)(object)val != (Object)null && val.type == type) { flag |= _colors.Remove(val.assetId); } } } if (flag) { Save(); } } internal static void ApplyOverrides(PlayerCosmetics pc) { bool flag = _colors.Count > 0; bool flag2 = _previewOverrides.Count > 0; if ((!flag && !flag2) || pc?.playerMaterials == null) { return; } PlayerAvatarVisuals playerAvatarVisuals = pc.playerAvatarVisuals; if ((Object)(object)playerAvatarVisuals == (Object)null) { return; } if (!playerAvatarVisuals.isMenuAvatar) { PlayerAvatar playerAvatar = playerAvatarVisuals.playerAvatar; if (playerAvatar == null || !playerAvatar.isLocal) { return; } } foreach (PlayerMaterial playerMaterial in pc.playerMaterials) { if ((Object)(object)playerMaterial?.cosmetic?.cosmeticAsset == (Object)null) { continue; } string assetId = playerMaterial.cosmetic.cosmeticAsset.assetId; int value2; if (_presetPreviewActive) { if (_previewOverrides.TryGetValue(assetId, out var value)) { playerMaterial.ColorSet(PropAlbedo, PropEmission, PropFresnel, value); } } else if (_colors.TryGetValue(assetId, out value2)) { playerMaterial.ColorSet(PropAlbedo, PropEmission, PropFresnel, value2); } else if (_previewOverrides.TryGetValue(assetId, out value2)) { playerMaterial.ColorSet(PropAlbedo, PropEmission, PropFresnel, value2); } } } internal static void Load() { try { if (!File.Exists(SavePath) && File.Exists(LegacyPath)) { try { File.Move(LegacyPath, SavePath); Plugin.Logger.LogInfo((object)"PerCosmeticColors: migrated save file to BepInEx/config."); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticColors: migration failed, loading from old location: " + ex.Message)); } } string path = (File.Exists(SavePath) ? SavePath : LegacyPath); if (!File.Exists(path)) { return; } _colors = JsonConvert.DeserializeObject>(File.ReadAllText(path)) ?? new Dictionary(); } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("PerCosmeticColors: load failed: " + ex2.Message)); _colors = new Dictionary(); } LoadPresets(); } private static void Save() { try { File.WriteAllText(SavePath, JsonConvert.SerializeObject((object)_colors)); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PerCosmeticColors: save failed: " + ex.Message)); } } } internal static class PerCosmeticColorSerializer { private const char Sep = '\u001f'; internal static string Serialize(IReadOnlyDictionary colors) { if (colors.Count == 0) { return ""; } StringBuilder stringBuilder = new StringBuilder(colors.Count * 48); foreach (KeyValuePair color in colors) { stringBuilder.Append(color.Key); stringBuilder.Append('\u001f'); stringBuilder.Append(color.Value); stringBuilder.Append('\u001f'); } return stringBuilder.ToString(); } internal static Dictionary Deserialize(string data) { Dictionary dictionary = new Dictionary(); if (string.IsNullOrEmpty(data)) { return dictionary; } string[] array = data.Split('\u001f', StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i + 1 < array.Length; i += 2) { if (int.TryParse(array[i + 1], out var result)) { dictionary[array[i]] = result; } } return dictionary; } } internal sealed class PerCosmeticColorSyncComponent : MonoBehaviourPun { private Dictionary _remoteColors = new Dictionary(); [PunRPC] internal void SyncPerCosmeticColorsRPC(string colorData) { _remoteColors = PerCosmeticColorSerializer.Deserialize(colorData); PlayerCosmetics component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null) { ApplyToCosmetics(component); } } internal void ApplyToCosmetics(PlayerCosmetics pc) { if (_remoteColors.Count == 0 || pc?.playerMaterials == null) { return; } foreach (PlayerMaterial playerMaterial in pc.playerMaterials) { if (!((Object)(object)playerMaterial?.cosmetic?.cosmeticAsset == (Object)null) && _remoteColors.TryGetValue(playerMaterial.cosmetic.cosmeticAsset.assetId, out var value)) { playerMaterial.ColorSet(PerCosmeticColors.PropAlbedo, PerCosmeticColors.PropEmission, PerCosmeticColors.PropFresnel, value); } } } } internal static class MyPluginInfo { public const string PLUGIN_GUID = "Xuaun.MoreHeadBridge"; public const string PLUGIN_NAME = "MoreHead Bridge"; public const string PLUGIN_VERSION = "2.2.0"; } internal static class PartShrinkerBridge { private static bool _initialized; private static bool _available; private static Type? _shrinkerType; private static Type? _hiddenType; private static FieldInfo? _partField; private static FieldInfo? _hideChildrenField; private static MethodInfo? _addMethod; private static MethodInfo? _removeMethod; private static void EnsureInit() { if (_initialized) { return; } _initialized = true; try { _shrinkerType = AccessTools.TypeByName("MoreHeadUtilities.PartShrinker"); _hiddenType = AccessTools.TypeByName("MoreHeadUtilities.HiddenParts"); if (_shrinkerType == null || _hiddenType == null) { Plugin.Logger.LogDebug((object)"MoreHeadUtilities not loaded — PartShrinker bridge inactive."); return; } _partField = AccessTools.Field(_shrinkerType, "partToHide"); _hideChildrenField = AccessTools.Field(_shrinkerType, "hideChildren"); _addMethod = AccessTools.Method(_hiddenType, "AddHiddenPart", (Type[])null, (Type[])null); _removeMethod = AccessTools.Method(_hiddenType, "RemoveHiddenPart", (Type[])null, (Type[])null); _available = _partField != null && _hideChildrenField != null && _addMethod != null && _removeMethod != null; if (_available) { Plugin.Logger.LogInfo((object)"PartShrinker bridge loaded."); } else { Plugin.Logger.LogWarning((object)"PartShrinker types found but reflection failed — disabled."); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PartShrinker bridge init error: " + ex.Message)); } } internal static void OnSpawn(GameObject cosmetic, PlayerAvatarVisuals avatar) { Apply(cosmetic, avatar, isAdd: true); } internal static void OnRemove(GameObject cosmetic, PlayerAvatarVisuals avatar) { Apply(cosmetic, avatar, isAdd: false); } private static void Apply(GameObject cosmetic, PlayerAvatarVisuals avatar, bool isAdd) { EnsureInit(); if (!_available || (Object)(object)cosmetic == (Object)null || (Object)(object)avatar == (Object)null) { return; } Component[] componentsInChildren; try { componentsInChildren = cosmetic.GetComponentsInChildren(_shrinkerType, true); } catch { return; } if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } Component val = ((Component)avatar).GetComponent(_hiddenType); if ((Object)(object)val == (Object)null) { try { val = ((Component)avatar).gameObject.AddComponent(_hiddenType); } catch (Exception ex) { Plugin.Logger.LogDebug((object)("Could not add HiddenParts: " + ex.Message)); return; } } MethodInfo methodInfo = (isAdd ? _addMethod : _removeMethod); Component[] array = componentsInChildren; foreach (Component val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } try { object value = _partField.GetValue(val2); bool flag = (bool)_hideChildrenField.GetValue(val2); methodInfo.Invoke(val, new object[3] { value, flag, true }); if (isAdd) { MonoBehaviour val3 = (MonoBehaviour)(object)((val2 is MonoBehaviour) ? val2 : null); if (val3 != null) { ((Behaviour)val3).enabled = false; } } } catch (Exception ex2) { Plugin.Logger.LogDebug((object)("PartShrinker " + (isAdd ? "Add" : "Remove") + " failed: " + ex2.Message)); } } } } [HarmonyPatch(typeof(PlayerCosmetics), "InstantiateCosmetic")] internal static class PartShrinkerSpawnPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, CosmeticAsset _cosmeticAsset, GameObject __result) { if (!((Object)(object)__result == (Object)null) && BridgeIds.IsBridgeAsset(_cosmeticAsset) && !((Object)(object)__instance?.playerAvatarVisuals == (Object)null)) { PartShrinkerBridge.OnSpawn(__result, __instance.playerAvatarVisuals); } } } [HarmonyPatch(typeof(Cosmetic), "Remove")] internal static class PartShrinkerRemovePatch { [HarmonyPrefix] private static void Prefix(Cosmetic __instance) { if (!((Object)(object)__instance == (Object)null) && BridgeIds.IsBridgeAsset(__instance.cosmeticAsset) && !((Object)(object)__instance.playerCosmetics?.playerAvatarVisuals == (Object)null)) { PartShrinkerBridge.OnRemove(((Component)__instance).gameObject, __instance.playerCosmetics.playerAvatarVisuals); } } } internal static class PartShrinkerSuppressor { internal static void TryApply(Harmony harmony) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MoreHeadUtilities.PartShrinker"); if (type == null) { Plugin.Logger.LogDebug((object)"MoreHeadUtilities not loaded — PartShrinker suppressor skipped."); return; } MethodInfo methodInfo = AccessTools.Method(type, "OnDisable", (Type[])null, (Type[])null); if (!(methodInfo == null)) { MethodInfo method = typeof(PartShrinkerSuppressor).GetMethod("Finalizer", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null); Plugin.Logger.LogDebug((object)"PartShrinker.OnDisable NPE suppressor loaded."); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not install PartShrinker suppressor: " + ex.Message)); } } private static Exception? Finalizer(Exception? __exception) { if (__exception is NullReferenceException) { return null; } return __exception; } } internal sealed class AnimatorLooper : MonoBehaviour { private Animator? _animator; private void Awake() { _animator = ((Component)this).GetComponent(); } private void Update() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_animator == (Object)null) && ((Behaviour)_animator).isActiveAndEnabled && _animator.layerCount != 0) { AnimatorStateInfo currentAnimatorStateInfo = _animator.GetCurrentAnimatorStateInfo(0); if (!((AnimatorStateInfo)(ref currentAnimatorStateInfo)).loop && ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime >= 1f) { _animator.Play(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).shortNameHash, 0, 0f); } } } } [HarmonyPatch(typeof(MetaManager), "CosmeticEquip")] internal static class CosmeticHoverPatch { private enum AnimState { StillRunning, Done, Gone } [CompilerGenerated] private sealed class d__7 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CosmeticAsset asset; private float 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; 5__2 = 0f; goto IL_009f; case 2: <>1__state = -1; goto IL_009f; case 3: { <>1__state = -1; bool flag = false; try { flag = IconCapture.TryCapture(asset); } finally { if (!flag) { _scheduled.Remove(asset.assetId); } } return false; } IL_009f: if (5__2 < 3f) { switch (CheckEquipAnim(asset)) { case AnimState.Gone: _scheduled.Remove(asset.assetId); return false; default: 5__2 += Time.unscaledDeltaTime; <>2__current = null; <>1__state = 2; return true; case AnimState.Done: break; } } <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 3; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly HashSet _scheduled = new HashSet(); private static FieldInfo? _equipLerpField; private static bool _equipLerpWarned; private static AnimState CheckEquipAnim(CosmeticAsset asset) { if (_equipLerpField == null && !_equipLerpWarned) { _equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp"); if (_equipLerpField == null) { _equipLerpWarned = true; Plugin.Logger.LogWarning((object)"CosmeticHoverPatch: Cosmetic.equipLerp not found — will capture without waiting for animation."); } } if (_equipLerpField == null) { return AnimState.Done; } Cosmetic[] array = Object.FindObjectsOfType(); foreach (Cosmetic val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset) { if (!((float)(_equipLerpField.GetValue(val) ?? ((object)1f)) >= 1f)) { return AnimState.StillRunning; } return AnimState.Done; } } return AnimState.Gone; } internal static void OnMenuClosed() { _scheduled.Clear(); } [HarmonyPostfix] private static void Postfix(MetaManager __instance, CosmeticAsset __0, bool __1) { if (__1 && Plugin.AutoCaptureIcons.Value) { if (!((Object)(object)__0 == (Object)null) && BridgeIds.IsBridgeAsset(__0) && !IconCapture.HasCache(__0) && _scheduled.Add(__0.assetId)) { ((MonoBehaviour)__instance).StartCoroutine(CaptureAfterAnimation(__0)); } } } [IteratorStateMachine(typeof(d__7))] private static IEnumerator CaptureAfterAnimation(CosmeticAsset asset) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__7(0) { asset = asset }; } } [HarmonyPatch(typeof(Cosmetic), "Setup")] internal static class CosmeticSetupPatch { [HarmonyPrefix] private static void Prefix(Cosmetic __instance) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown if ((Object)(object)__instance == (Object)null || !BridgeIds.IsBridgeAsset(__instance.cosmeticAsset)) { return; } if (((int)__instance.type == 0 || (int)__instance.type == 5) && (Object)(object)((Component)__instance).GetComponentInChildren(true) == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } if (__instance.meshParents == null) { __instance.meshParents = new List(); } if (__instance.meshParents.Count != 0) { return; } foreach (Transform item2 in ((Component)__instance).transform) { Transform item = item2; __instance.meshParents.Add(item); } } } [HarmonyPatch(typeof(Cosmetic), "CustomTypesLogic")] internal static class CustomTypesLogicNpeGuardPatch { [HarmonyFinalizer] private static Exception? Finalizer(Cosmetic __instance, Exception? __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!BridgeIds.IsBridgeAsset(__instance?.cosmeticAsset)) { return __exception; } if (!Plugin.ShowBridgeDebugLogs.Value) { return null; } return __exception; } } [HarmonyPatch(typeof(MetaManager), "GetCosmeticsToUnequip")] internal static class GetCosmeticsToUnequipPatch { [HarmonyPrefix] private static bool Prefix(MetaManager __instance, List _cosmeticEquipped, CosmeticAsset _cosmeticAssetNew, ref List __result) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_0154: 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_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) __result = new List(); if (!Object.op_Implicit((Object)(object)_cosmeticAssetNew)) { return false; } CosmeticTypeAsset typeAsset = GetTypeAsset(__instance, _cosmeticAssetNew.type); foreach (int item in _cosmeticEquipped) { if (item < 0 || item >= __instance.cosmeticAssets.Count) { continue; } CosmeticAsset val = __instance.cosmeticAssets[item]; if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val == (Object)(object)_cosmeticAssetNew) { continue; } CosmeticTypeAsset typeAsset2 = GetTypeAsset(__instance, val.type); bool flag = HhhCosmeticLoader.IsWorldAsset(_cosmeticAssetNew); bool flag2 = HhhCosmeticLoader.IsWorldAsset(val); bool flag3 = flag || flag2; if (flag && flag2) { if (!Plugin.AllowMultipleCosmetics.Value) { __result.Add(val); } continue; } bool flag4 = !flag3 && val.type == _cosmeticAssetNew.type && (typeAsset == null || !typeAsset.canEquipMultiple) && !MultiEquipTypes.IsEnabled(_cosmeticAssetNew); bool flag5 = !flag3 && (TypeListContains(typeAsset?.disabledTypeList, typeAsset2) || TypeListContains(typeAsset2?.disabledTypeList, typeAsset)); if (flag4 || flag5) { __result.Add(val); } else { if (flag3) { continue; } bool flag6 = false; foreach (Type item2 in typeAsset?.disabledCustomTypeList ?? new List()) { if (ContainsCustomType(val.customTypeList, item2) || ContainsCustomType(typeAsset2?.customTypeList, item2)) { __result.Add(val); flag6 = true; break; } } if (flag6) { continue; } foreach (Type item3 in typeAsset2?.disabledCustomTypeList ?? new List()) { if (ContainsCustomType(_cosmeticAssetNew.customTypeList, item3) || ContainsCustomType(typeAsset?.customTypeList, item3)) { __result.Add(val); break; } } } } return false; } private static CosmeticTypeAsset? GetTypeAsset(MetaManager metaManager, CosmeticType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)type; if (num < 0 || num >= metaManager.cosmeticTypeAssets.Count) { return null; } return metaManager.cosmeticTypeAssets[num]; } private static bool TypeListContains(List? list, CosmeticTypeAsset? asset) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (list != null && (Object)(object)asset != (Object)null) { return list.Contains(asset.type); } return false; } private static bool ContainsCustomType(List? list, Type value) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return list?.Contains(value) ?? false; } } [HarmonyPatch(typeof(CosmeticAsset), "GetIcon")] internal static class GetIconPatch { [HarmonyPrefix] private static bool Prefix(CosmeticAsset __instance, ref Sprite? __result) { //IL_00be: 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) if ((Object)(object)__instance.icon != (Object)null) { __result = __instance.icon; return false; } PrefabRef prefab = __instance.prefab; if ((Object)(object)((prefab != null) ? prefab.Prefab : null) == (Object)null) { return true; } if ((Object)(object)__instance.prefab.Prefab.GetComponentInChildren(true) != (Object)null) { return true; } string text = IconCapture.CachePathFor(__instance); if (File.Exists(text)) { __result = SemiFunc.LoadSpriteFromFile(text); __instance.icon = __result; return false; } if (BridgeIds.IsBridgeAsset(__instance)) { if (Plugin.UseTextureAsPlaceholder.Value && HhhCosmeticLoader.BridgeIconTextures.TryGetValue(__instance.assetId, out Texture2D value) && (Object)(object)value != (Object)null) { __result = Sprite.Create(value, new Rect(0f, 0f, (float)((Texture)value).width, (float)((Texture)value).height), new Vector2(0.5f, 0.5f), 100f); ((Object)__result).name = "BridgeIcon_" + ((Object)__instance).name; } else { __result = PlaceholderIcon.Get(); } __instance.icon = __result; } else { __result = null; } return false; } } internal static class HideMoreHeadUIPatch { internal static bool SkipInitialize() { return false; } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "UpdateIcon")] internal static class MenuIconNpeGuardPatch { [HarmonyFinalizer] private static Exception? Finalizer(MenuElementCosmeticButton __instance, Exception? __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!BridgeIds.IsBridgeAsset(__instance?.cosmeticAsset)) { return __exception; } if (!Plugin.ShowBridgeDebugLogs.Value) { return null; } return __exception; } } [HarmonyPatch(typeof(CosmeticAsset), "GetRarityColor")] internal static class ModdedRarityBorderPatch { private static readonly Color OrangeColor = new Color(1f, 0.3f, 0f, 1f); [HarmonyPostfix] private static void Postfix(CosmeticAsset __instance, ref Color __result) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (PerCosmeticOverrides.IsModdedForAsset(__instance)) { __result = OrangeColor; } } } [HarmonyPatch(typeof(PlayerCosmetics), "InstantiateCosmetic")] internal static class MoreHeadCosmeticMountPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, CosmeticAsset _cosmeticAsset, GameObject __result) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Invalid comparison between Unknown and I4 if ((Object)(object)__result == (Object)null || !BridgeIds.IsBridgeAsset(_cosmeticAsset) || (Object)(object)__instance == (Object)null || (Object)(object)__instance.playerAvatarVisuals == (Object)null) { return; } PrefabRef prefab = _cosmeticAsset.prefab; GameObject val = ((prefab != null) ? prefab.Prefab : null); if ((Object)(object)val == (Object)null) { return; } if (HhhCosmeticLoader.IsWorldAsset(_cosmeticAsset)) { Transform val2 = EnsureWorldFollower(((Component)__instance.playerAvatarVisuals).transform); if (!((Object)(object)val2 == (Object)null)) { Mount(__result, val2, val); } return; } string name; if ((int)_cosmeticAsset.type == 1) { name = "ANIM ARM R SCALE"; } else { if ((int)_cosmeticAsset.type != 2) { return; } name = "code_arm_l"; } Transform val3 = FindByName(((Component)__instance.playerAvatarVisuals).transform, name); if (!((Object)(object)val3 == (Object)null)) { Mount(__result, val3, val); } } internal static void Mount(GameObject instance, Transform parent, GameObject sourcePrefab) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) instance.transform.SetParent(parent, false); instance.transform.localPosition = sourcePrefab.transform.localPosition; instance.transform.localRotation = sourcePrefab.transform.localRotation; instance.transform.localScale = sourcePrefab.transform.localScale; } private static Transform? FindByName(Transform root, string name) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown if (((Object)root).name == name) { return root; } foreach (Transform item in root) { Transform root2 = item; Transform val = FindByName(root2, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } internal static Transform? EnsureWorldFollower(Transform root) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Transform val = root.Find("WorldDecorationFollower"); if ((Object)(object)val != (Object)null) { return val; } GameObject val2 = new GameObject("WorldDecorationFollower"); val2.transform.SetParent(root, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; val2.AddComponent(); return val2.transform; } } internal static class SetupCosmeticsModdedRpcPatch { private static FieldInfo? _cosmeticEquippedField; internal static void TryApply(Harmony harmony) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("REPOLib.Objects.PlayerCosmeticsModded"); if (type == null) { Plugin.Logger.LogDebug((object)"PlayerCosmeticsModded not found — multiplayer bridge sync fix skipped."); return; } MethodInfo methodInfo = AccessTools.Method(type, "SetupCosmeticsModdedRPC", (Type[])null, (Type[])null); if (!(methodInfo == null)) { _cosmeticEquippedField = AccessTools.Field(type, "cosmeticEquipped"); if (!(_cosmeticEquippedField == null)) { MethodInfo method = typeof(SetupCosmeticsModdedRpcPatch).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Logger.LogDebug((object)"Multiplayer bridge sync fix applied."); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not apply multiplayer bridge sync fix: " + ex.Message)); } } private static void Postfix(MonoBehaviourPun __instance) { if (!((Object)(object)__instance.photonView == (Object)null) && !__instance.photonView.IsMine && _cosmeticEquippedField?.GetValue(__instance) is List) { PlayerCosmetics component = ((Component)__instance).GetComponent(); if (component != null) { component.SetupCosmeticsLogic(Array.Empty(), false); } } } } [HarmonyPatch(typeof(MetaManager), "Load")] internal static class UnlockPatch { private static readonly bool _resetRequestedAtStartup = Plugin.ResetUnlocks.Value; private static bool _resetDone; [HarmonyPostfix] private static void Postfix(MetaManager __instance) { if (_resetRequestedAtStartup && !_resetDone) { TryReset(__instance); BundleLoader.OnAllBundlesLoaded += OnBundlesLoaded; } else if (!Plugin.UnlockAll.Value) { Plugin.Logger.LogDebug((object)"UnlockAll=false, skipping auto-unlock."); } else { TryUnlock(__instance); BundleLoader.OnAllBundlesLoaded += OnBundlesLoaded; } } private static void OnBundlesLoaded() { BundleLoader.OnAllBundlesLoaded -= OnBundlesLoaded; if ((Object)(object)MetaManager.instance == (Object)null) { Plugin.Logger.LogWarning((object)"MetaManager.instance is null in deferred path — skipping."); return; } if (_resetRequestedAtStartup && !_resetDone) { TryReset(MetaManager.instance); if (!_resetDone) { return; } } if (Plugin.UnlockAll.Value) { TryUnlock(MetaManager.instance); } } private static void TryUnlock(MetaManager instance) { int num = 0; foreach (string assetId in HhhCosmeticLoader.RegisteredAssetIds) { int num2 = instance.cosmeticAssets.FindIndex((CosmeticAsset a) => (Object)(object)a != (Object)null && a.assetId == assetId); if (num2 >= 0 && !instance.cosmeticUnlocks.Contains(num2)) { instance.cosmeticUnlocks.Add(num2); num++; } } if (num > 0) { Plugin.Logger.LogInfo((object)$"Auto-unlocked {num} bridge cosmetic(s) (UnlockAll=true)."); } } private static void TryReset(MetaManager instance) { HashSet hashSet = new HashSet(HhhCosmeticLoader.RegisteredAssetIds); if (hashSet.Count == 0) { return; } HashSet hashSet2 = new HashSet(); for (int i = 0; i < instance.cosmeticAssets.Count; i++) { CosmeticAsset val = instance.cosmeticAssets[i]; if ((Object)(object)val != (Object)null && hashSet.Contains(val.assetId)) { hashSet2.Add(i); } } if (hashSet2.Count == 0) { Plugin.Logger.LogDebug((object)"ResetUnlocks: no bridge cosmetics in cosmeticAssets yet, deferring."); return; } int num = instance.cosmeticUnlocks.RemoveAll(hashSet2.Contains); int num2 = instance.cosmeticEquipped.RemoveAll(hashSet2.Contains); int num3 = instance.cosmeticHistory.RemoveAll(hashSet2.Contains); instance.Save(true); Plugin.ResetUnlocks.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); _resetDone = true; Plugin.Logger.LogInfo((object)($"ResetUnlocks: cleared {num} unlock(s), " + $"{num2} equipped, {num3} history entry. Flag reset to false.")); } } public enum SearchBarPosition { Bottom, Top } [BepInPlugin("Xuaun.MoreHeadBridge", "MoreHead Bridge", "2.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("Xuaun.MoreHeadBridge"); public static Plugin Instance { get; private set; } public static ManualLogSource Logger { get; private set; } public static ConfigEntry UnlockAll { get; private set; } public static ConfigEntry AllowMultipleCosmetics { get; private set; } public static ConfigEntry EnableMenuEnhancements { get; private set; } public static ConfigEntry HideMoreHeadButton { get; private set; } public static ConfigEntry SpecificFolders { get; private set; } public static ConfigEntry SearchFieldPosition { get; private set; } public static ConfigEntry HighlightModdedCosmetics { get; private set; } public static ConfigEntry DefaultRarity { get; private set; } public static ConfigEntry UseTextureAsPlaceholder { get; private set; } public static ConfigEntry AutoCaptureIcons { get; private set; } public static ConfigEntry GenerateAllIcons { get; private set; } public static ConfigEntry EnableCosmeticOverrideUI { get; private set; } public static ConfigEntry FixBridgedCosmetics { get; private set; } public static ConfigEntry ResetUnlocks { get; private set; } public static ConfigEntry ResetCosmeticCustomizer { get; private set; } public static ConfigEntry DeleteIconCache { get; private set; } public static ConfigEntry DeleteIconsMatching { get; private set; } public static ConfigEntry ShowBridgeDebugLogs { get; private set; } public static bool MenuLibAvailable { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; UnlockAll = ((BaseUnityPlugin)this).Config.Bind("General", "UnlockAll", true, "Auto-unlock NEW bridge cosmetics on every load.\n\nWhen TRUE — every bridge cosmetic gets added to your inventory\n on game start, so you never have to grind for them.\nWhen FALSE — bridge cosmetics behave like vanilla ones:\n you have to earn them in-game.\n\nIMPORTANT: this flag only controls what happens going FORWARD.\nCosmetics unlocked while UnlockAll was TRUE get saved permanently to\nthe REPOLib modded save file. Flipping this to FALSE later does NOT\nremove them — REPOLib re-reads the save on every launch.\nIf you want to wipe existing unlocks, see the [Reset] section below."); AllowMultipleCosmetics = ((BaseUnityPlugin)this).Config.Bind("General", "AllowMultipleCosmetics", true, "When true, you can equip multiple cosmetics of the same type at once\n(e.g. two hats, three body pieces, several worlds).\nApplies to: Hat, HeadBottom, FaceTop, FaceBottom, Eyewear, Ears,\n BodyTop, BodyBottom, ArmRight, ArmLeft,\n LegRight, FootRight, LegLeft, FootLeft, World."); EnableMenuEnhancements = ((BaseUnityPlugin)this).Config.Bind("General", "EnableMenuEnhancements", true, "When TRUE (default), enables the extended cosmetics menu:\n • Virtual tabs: SEARCH, SELECTED, FAV, HIDE\n • Ctrl+click to favorite, Alt+click to hide cosmetics\n • Live search bar and cosmetic name hover tooltip\nSet to FALSE if you prefer the unmodified vanilla cosmetics menu."); HideMoreHeadButton = ((BaseUnityPlugin)this).Config.Bind("General", "HideMoreHeadButton", false, "If true, removes the MoreHead button from all menus so you can use only the vanilla cosmetics UI. Requires restart."); SpecificFolders = ((BaseUnityPlugin)this).Config.Bind("General", "SpecificFolders", "", "Comma-separated subfolder names under BepInEx/plugins to scan for .hhh files. Empty = scan all. Example: 'Some-MoreHeadPack,Another-CosmeticsPack'. Matching is case-insensitive and uses path contains."); SearchFieldPosition = ((BaseUnityPlugin)this).Config.Bind("General", "SearchFieldPosition", SearchBarPosition.Top, "Where the search bar appears in the cosmetics menu.\nBottom = at the bottom of the Semibot.\nTop = above the category strip (default)."); HighlightModdedCosmetics = ((BaseUnityPlugin)this).Config.Bind("Appearance", "HighlightModdedCosmetics", true, "When TRUE (default), bridge cosmetics show an orange border in the cosmetics menu,\nmaking them visually distinct from vanilla cosmetics at a glance.\nThe sort position is unaffected — it is still controlled by DefaultRarity.\nWhen FALSE, bridge cosmetics use the standard rarity border color like any vanilla cosmetic.\n\nPer-cosmetic overrides set via the CosmeticCustomizer popup take priority over this setting."); DefaultRarity = ((BaseUnityPlugin)this).Config.Bind("Appearance", "DefaultRarity", (Rarity)0, "Rarity tier assigned to bridge cosmetics in the vanilla shop. Values: Common, Uncommon, Rare, UltraRare.\nControls sort position in the menu (UltraRare appears first, Common last).\nThe visual border color is controlled separately by HighlightModdedCosmetics.\n\nPer-cosmetic rarity overrides set via the CosmeticCustomizer popup take priority over this setting."); UseTextureAsPlaceholder = ((BaseUnityPlugin)this).Config.Bind("Icons", "UseTextureAsPlaceholder", true, "When TRUE (default) — the cosmetic's texture is used as the icon, overlaid on the placeholder background.\nWhen FALSE — the texture is NOT applied to the placeholder; the slot keeps the plain placeholder icon\n until a captured icon (AutoCaptureIcons / GenerateAllIcons) replaces it."); AutoCaptureIcons = ((BaseUnityPlugin)this).Config.Bind("Icons", "AutoCaptureIcons", true, "Reactively capture icons while you browse the cosmetics menu.\n\nWhen TRUE — every time you HOVER a bridge cosmetic in the menu,\n the game's existing avatar preview is snapshotted and\n saved as a PNG icon for that cosmetic. Next time the UI\n asks for that icon it loads the PNG (instant).\n Icons fill in gradually as you explore the menu.\nWhen FALSE — no captures. Bridge cosmetics keep the texture/placeholder\n fallback icons.\n\nPNG cache lives in:\n %userprofile%\\AppData\\LocalLow\\semiwork\\REPO\\Cache\\Icons\\CosmeticsModded\\MoreHeadBridge_CosmeticsIcons\\\nDelete that folder to wipe all generated icons.\n(Icons are stored in Cache\\Icons\\CosmeticsModded\\ — a sibling of the vanilla\n Cache\\Icons\\Cosmetics\\ that REPOLib wipes, so ours are never touched.)"); GenerateAllIcons = ((BaseUnityPlugin)this).Config.Bind("Icons", "GenerateAllIcons", false, "ONE-SHOT trigger. When TRUE, the next time you open the cosmetics menu\nthe mod will cycle through EVERY bridge cosmetic without a cached icon,\npreview-equipping each one, snapshotting the avatar, and saving the PNG.\n\nEffects while running:\n * The avatar will visibly rotate through cosmetics — that IS the progress.\n * Console logs progress every 50 items.\n * Expect ~1-3 minutes for 1600+ cosmetics.\n * Whatever you had previewing/equipped is restored at the end.\n * This flag auto-resets to FALSE so it doesn't fire again.\n\nUse this if you want all icons generated in one go instead of as you browse.\nRequires AutoCaptureIcons logic — keeps working even if AutoCaptureIcons=false."); EnableCosmeticOverrideUI = ((BaseUnityPlugin)this).Config.Bind("CosmeticCustomizer", "EnableCosmeticOverrideUI", false, "When TRUE, Shift+click on any bridge cosmetic in the menu opens a popup\nthat lets you override its rarity tier and category (Hat, BodyTop, World, …)\nindividually. Overrides are saved and applied on every launch.\n\nRequires MenuLib to be installed."); FixBridgedCosmetics = ((BaseUnityPlugin)this).Config.Bind("Compatibility", "FixBridgedCosmetics", true, "When TRUE (default), automatically fixes common asset issues on bridge (.hhh)\ncosmetics at load time (applied directly to the prefab, so every instance\n— including MoreHead's own rendering — inherits the fix):\n • Removes Collider and Rigidbody components (prevents physics interference\n and the character-rotation bug in the cosmetics preview menu)\n • Forces Animation and Animator clips to loop (prevents one-shot animations)\nTakes effect on the next game launch. Set to FALSE only if a specific\ncosmetic breaks due to these fixes."); ResetUnlocks = ((BaseUnityPlugin)this).Config.Bind("Reset", "ResetUnlocks", false, "⚠ DESTRUCTIVE ONE-SHOT TRIGGER ⚠\n\nSetting this to TRUE causes the NEXT game launch to:\n 1. Remove EVERY bridge cosmetic from your unlocks list\n 2. Remove them from any saved outfit/preset you have equipped\n 3. Remove them from your history\n 4. Rewrite the REPOLib modded save file\n 5. Auto-flip this flag back to FALSE so it doesn't fire again\n\nUse this if you want to start over with bridge cosmetics.\nIf UnlockAll=true, cosmetics are wiped and immediately re-unlocked on the same launch.\nSet UnlockAll=false FIRST if you want to keep them locked after the reset.\n\nThis does NOT touch vanilla cosmetics or cosmetics from other mods.\nThis does NOT delete the .hhh files — only the unlock state."); ResetCosmeticCustomizer = ((BaseUnityPlugin)this).Config.Bind("Reset", "ResetCosmeticCustomizer", false, "ONE-SHOT trigger. When TRUE on the next launch:\n 1. Clears ALL per-cosmetic overrides (rarity, category, modded flag)\n set via the Cosmetic Customizer popup\n 2. Deletes MoreHeadBridge_CosmeticOverrides.json from BepInEx/config\n 3. Auto-flips this flag back to FALSE\n\nBridge cosmetics will revert to the global DefaultRarity and their\noriginal .hhh file category on the same launch."); DeleteIconCache = ((BaseUnityPlugin)this).Config.Bind("Reset", "DeleteIconCache", false, "ONE-SHOT trigger. When TRUE on launch, delete cached bridge icon PNGs from:\n %userprofile%\\AppData\\LocalLow\\semiwork\\REPO\\Cache\\Icons\\CosmeticsModded\\MoreHeadBridge_CosmeticsIcons\\\nUse DeleteIconsMatching to filter which ones to delete.\nAuto-resets to FALSE after running."); DeleteIconsMatching = ((BaseUnityPlugin)this).Config.Bind("Reset", "DeleteIconsMatching", "", "Optional comma-separated filter for DeleteIconCache. Case-insensitive\nsubstring match against the icon filename (which is the cosmetic's internal name).\nEmpty = delete ALL bridge icons.\nExample: 'PirateHat,Waluigi' deletes only icons whose name contains either."); ShowBridgeDebugLogs = ((BaseUnityPlugin)this).Config.Bind("Debug", "ShowBridgeDebugLogs", false, "If true, do NOT suppress NullReferenceExceptions for bridge cosmetics.\nUse this to diagnose bridge-only issues (will spam logs if the base game is noisy)."); MenuLibAvailable = Chainloader.PluginInfos.ContainsKey("nickklmao.menulib"); if (MenuLibAvailable) { Logger.LogDebug((object)"MenuLib detected — CosmeticCustomizer UI enabled."); } PrintBanner(); PerCosmeticOverrides.Load(); if (ResetCosmeticCustomizer.Value) { PerCosmeticOverrides.ResetAll(); ResetCosmeticCustomizer.Value = false; Logger.LogInfo((object)"CosmeticCustomizer: all per-cosmetic overrides cleared."); } HhhCosmeticLoader.LoadAll(); PerCosmeticColors.Load(); IconCacheCleaner.Run(); _harmony.PatchAll(); AllowMultipleCosmetics.SettingChanged += OnAllowMultipleCosmeticsChanged; if (HideMoreHeadButton.Value) { TryHideMoreHeadUI(); } PartShrinkerSuppressor.TryApply(_harmony); SetupCosmeticsModdedRpcPatch.TryApply(_harmony); } private static void OnAllowMultipleCosmeticsChanged(object sender, EventArgs e) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } if (!AllowMultipleCosmetics.Value) { HashSet hashSet = new HashSet(); List list = new List(); foreach (int item in instance.cosmeticEquipped) { if (item >= 0 && item < instance.cosmeticAssets.Count) { CosmeticAsset val = instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && !hashSet.Add(val.type)) { list.Add(item); } } } foreach (int item2 in list) { instance.cosmeticEquipped.Remove(item2); } } instance.CosmeticPlayerUpdateLocal(SemiFunc.IsMultiplayer(), false); } private void TryHideMoreHeadUI() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MoreHead.MoreHeadUI"); if (type == null) { Logger.LogDebug((object)"HideMoreHeadButton=true but MoreHead is not loaded — skipping."); return; } MethodInfo methodInfo = AccessTools.Method(type, "Initialize", (Type[])null, (Type[])null); if (!(methodInfo == null)) { MethodInfo method = typeof(HideMoreHeadUIPatch).GetMethod("SkipInitialize", BindingFlags.Static | BindingFlags.NonPublic); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Logger.LogInfo((object)"MoreHead UI hidden (HideMoreHeadButton=true)."); } } catch (Exception ex) { Logger.LogWarning((object)("Could not hide MoreHead UI: " + ex.Message)); } } private static void PrintBanner() { if (BceConsole.IsAvailable) { BceConsole.WriteLine("══════════════════════════════════════════════════════════════════════════════════", ConsoleColor.DarkCyan); BceConsole.Write("[Info : MoreHead Bridge] ", ConsoleColor.Cyan); BceConsole.WriteLine("► MoreHead Bridge v2.2.0 by Xuaun", ConsoleColor.DarkCyan); BceConsole.Write("[Info : MoreHead Bridge] ", ConsoleColor.Cyan); BceConsole.WriteLine(" Translating .hhh cosmetics into vanilla REPO", ConsoleColor.DarkCyan); BceConsole.WriteLine("══════════════════════════════════════════════════════════════════════════════════", ConsoleColor.DarkCyan); } else { Logger.LogInfo((object)"MoreHead Bridge v2.2.0 by Xuaun"); } } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "ToggleCosmetic")] [HarmonyPriority(600)] internal static class WorldCosmeticsClearButtonPatch { private sealed class PatchState { public List? Backup; public bool UnequipAllWasActive; } private static readonly MethodInfo? _triggerClickAnimations = typeof(MenuElementCosmeticButton).GetMethod("TriggerClickAnimations", BindingFlags.Instance | BindingFlags.NonPublic); internal static bool IsUnequipAllRunning { get; private set; } [HarmonyPrefix] private static bool Prefix(MenuElementCosmeticButton __instance, ref PatchState __state) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_0068: Unknown result type (might be due to invalid IL or missing references) __state = new PatchState(); IsUnequipAllRunning = false; if (HhhCosmeticLoader.WorldAssetIds.Count == 0) { return true; } if ((Object)(object)__instance.cosmeticAsset != (Object)null) { return true; } if ((Object)(object)MetaManager.instance == (Object)null) { return true; } MenuElementCosmeticSection cosmeticSection = __instance.cosmeticSection; if ((Object)(object)cosmeticSection == (Object)null) { return true; } if ((int)cosmeticSection.subCategory == 999) { PlayClickFeedback(__instance); UnequipWorldsOnly(__instance); return false; } if ((int)cosmeticSection.subCategory != 0) { return true; } if (WorldCosmeticsMenuState.IsWorldCategory(CosmeticsMenuState.ActivePage?.selectedCategory) || ((Object)((Component)cosmeticSection).gameObject).name == "MHB_WorldSection") { PlayClickFeedback(__instance); UnequipWorldsOnly(__instance); return false; } WorldCosmeticsMenuState.PartitionHatCosmetics(MetaManager.instance, out List _, out List worlds); if (worlds.Count > 0) { __state.Backup = worlds; IsUnequipAllRunning = true; __state.UnequipAllWasActive = true; } return true; } [HarmonyPostfix] private static void Postfix(PatchState __state) { IsUnequipAllRunning = false; if (__state == null) { return; } List backup = __state.Backup; bool unequipAllWasActive = __state.UnequipAllWasActive; if ((Object)(object)MetaManager.instance == (Object)null) { return; } bool flag = false; if (backup != null) { foreach (int item in backup) { if (!MetaManager.instance.cosmeticEquipped.Contains(item)) { MetaManager.instance.cosmeticEquipped.Add(item); flag = true; } } } if (flag) { MetaManager.instance.Save(true); } if (flag || unequipAllWasActive) { MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); } } [HarmonyFinalizer] private static Exception? Finalizer(Exception? __exception) { IsUnequipAllRunning = false; return __exception; } private static void PlayClickFeedback(MenuElementCosmeticButton btn) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) btn.soundClick.Play(MenuManager.instance.soundPosition, 1f, 1f, 1f, 1f); _triggerClickAnimations?.Invoke(btn, null); } private static void UnequipWorldsOnly(MenuElementCosmeticButton btn) { WorldCosmeticsMenuState.PartitionHatCosmetics(MetaManager.instance, out List _, out List worlds); bool flag = false; foreach (int item in worlds) { MetaManager.instance.cosmeticEquipped.Remove(item); flag = true; } if (flag) { MetaManager.instance.Save(true); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); } MenuElementCosmeticSection cosmeticSection = btn.cosmeticSection; if (cosmeticSection != null) { cosmeticSection.UpdateColorButton((CosmeticAsset)null, false); } } } [HarmonyPatch(typeof(MenuElementButtonCosmeticCategory), "UpdateHighlight")] internal static class WorldCosmeticsHighlightPatch { [HarmonyPostfix] private static void Postfix(MenuElementButtonCosmeticCategory __instance) { //IL_0089: 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_014e: Invalid comparison between Unknown and I4 //IL_0156: Unknown result type (might be due to invalid IL or missing references) MenuElementButtonCosmeticCategory __instance2 = __instance; if ((Object)(object)__instance2 == (Object)null || (Object)(object)MetaManager.instance == (Object)null || HhhCosmeticLoader.WorldAssetIds.Count == 0) { return; } if (CosmeticsMenuState.IsVirtual(__instance2.category)) { if ((Object)(object)__instance2.highlightObj?.text != (Object)null) { ((TMP_Text)__instance2.highlightObj.text).text = "0"; } return; } int count; if ((int)__instance2.buttonType == 0) { if ((Object)(object)__instance2.category == (Object)null || (Object)(object)__instance2.highlightObj == (Object)null) { return; } bool isWorldCategory = WorldCosmeticsMenuState.IsWorldCategory(__instance2.category); bool flag = __instance2.category.typeList != null && __instance2.category.typeList.Contains((CosmeticType)0); if (!isWorldCategory && !flag) { return; } count = CountNewCosmetics((CosmeticAsset asset) => (!isWorldCategory) ? (MatchesCategory(asset, __instance2.category) && !HhhCosmeticLoader.IsWorldAsset(asset)) : HhhCosmeticLoader.IsWorldAsset(asset)); } else { if ((int)__instance2.buttonType != 1 || (int)__instance2.subCategory != 0 || (Object)(object)__instance2.highlightObj == (Object)null) { return; } count = CountNewCosmetics((CosmeticAsset asset) => asset.type == __instance2.subCategory && !HhhCosmeticLoader.IsWorldAsset(asset)); } SetHighlightCount(__instance2.highlightObj, count); } private static bool MatchesCategory(CosmeticAsset asset, CosmeticCategoryAsset category) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (category.typeList != null) { return category.typeList.Contains(asset.type); } return false; } private static int CountNewCosmetics(Func predicate) { MetaManager instance = MetaManager.instance; HashSet hashSet = new HashSet(instance.cosmeticHistory); int num = 0; foreach (int cosmeticUnlock in instance.cosmeticUnlocks) { if (cosmeticUnlock >= 0 && cosmeticUnlock < instance.cosmeticAssets.Count && !hashSet.Contains(cosmeticUnlock)) { CosmeticAsset val = instance.cosmeticAssets[cosmeticUnlock]; if ((Object)(object)val != (Object)null && predicate(val)) { num++; } } } return num; } private static void SetHighlightCount(MenuElementCosmeticHighlight highlight, int count) { if ((Object)(object)highlight.text != (Object)null) { ((TMP_Text)highlight.text).text = count.ToString(); } } } [HarmonyPatch(typeof(MenuElementCosmeticSection), "UpdateHighlight")] internal static class WorldCosmeticsSectionHighlightPatch { [HarmonyPostfix] private static void Postfix(MenuElementCosmeticSection __instance) { //IL_0026: 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_013c: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)MetaManager.instance == (Object)null || HhhCosmeticLoader.WorldAssetIds.Count == 0 || (int)__instance.subCategory != 0 || (Object)(object)__instance.highlightObj == (Object)null) { return; } bool flag = WorldCosmeticsMenuState.IsWorldCategory(__instance.menuPageCosmetics?.selectedCategory); HashSet hashSet = new HashSet(MetaManager.instance.cosmeticHistory); HashSet hashSet2 = new HashSet(MetaManager.instance.cosmeticUnlocks); int num = 0; for (int i = 0; i < MetaManager.instance.cosmeticAssets.Count; i++) { if (hashSet.Contains(i) || !hashSet2.Contains(i)) { continue; } CosmeticAsset val = MetaManager.instance.cosmeticAssets[i]; if (!((Object)(object)val == (Object)null) && (int)val.type == 0 && val.prefab.IsValid()) { bool flag2 = HhhCosmeticLoader.IsWorldAsset(val); if (flag ? flag2 : (!flag2)) { num++; } } } if ((Object)(object)__instance.highlightObj.text != (Object)null) { ((TMP_Text)__instance.highlightObj.text).text = num.ToString(); } if ((Object)(object)__instance.menuPageCosmetics != (Object)null && __instance.menuPageCosmetics.selectedSubCategory == __instance.subCategory && (Object)(object)__instance.menuPageCosmetics.stickyHeader?.highlightObj?.text != (Object)null) { ((TMP_Text)__instance.menuPageCosmetics.stickyHeader.highlightObj.text).text = num.ToString(); } } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "IsEquipped")] internal static class WorldCosmeticsIsEquippedPatch { [HarmonyPostfix] private static void Postfix(MenuElementCosmeticButton __instance, ref bool __result) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance.cosmeticAsset != (Object)null || (Object)(object)MetaManager.instance == (Object)null || HhhCosmeticLoader.WorldAssetIds.Count == 0) { return; } MenuElementCosmeticSection cosmeticSection = __instance.cosmeticSection; if ((Object)(object)cosmeticSection == (Object)null) { return; } if ((int)cosmeticSection.subCategory == 999 || ((Object)((Component)cosmeticSection).gameObject).name == "MHB_WorldSection" || ((int)cosmeticSection.subCategory == 0 && WorldCosmeticsMenuState.IsWorldCategory(WorldCosmeticsMenuState.CurrentPage?.selectedCategory))) { __result = !MetaManager.instance.cosmeticEquipped.Any((int idx) => idx >= 0 && idx < MetaManager.instance.cosmeticAssets.Count && HhhCosmeticLoader.IsWorldAsset(MetaManager.instance.cosmeticAssets[idx])); } else if ((int)cosmeticSection.subCategory == 0) { __result = !MetaManager.instance.cosmeticEquipped.Any((int idx) => idx >= 0 && idx < MetaManager.instance.cosmeticAssets.Count && (int)MetaManager.instance.cosmeticAssets[idx].type == 0 && !HhhCosmeticLoader.IsWorldAsset(MetaManager.instance.cosmeticAssets[idx])); } } } [HarmonyPatch(typeof(MenuPageCosmetics), "RefreshScrollContent")] internal static class WorldCosmeticsMenuFilterPatch { private const float SectionSpacing = 10f; private const float SectionHeader = 40f; [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || HhhCosmeticLoader.WorldAssetIds.Count == 0 || (int)__instance.selectedTab != 0 || CosmeticsMenuState.IsPresetsCategory(__instance.selectedCategory) || CosmeticsMenuState.IsVirtual(__instance.selectedCategory)) { return; } bool flag = WorldCosmeticsMenuState.IsWorldCategory(__instance.selectedCategory); bool flag2 = false; BridgeFavoritesManager.EnsureLoaded(); float yPos = 0f; foreach (MenuElementCosmeticSection section in __instance.sections) { MenuElementCosmeticButton[] componentsInChildren = ((Component)section.cosmeticListTransform).GetComponentsInChildren(true); MenuElementCosmeticButton[] array = componentsInChildren.Where((MenuElementCosmeticButton b) => (Object)(object)b != (Object)null && (Object)(object)b.cosmeticAsset != (Object)null).ToArray(); int num = 0; MenuElementCosmeticButton[] array2 = array; foreach (MenuElementCosmeticButton val in array2) { bool flag3 = HhhCosmeticLoader.IsWorldAsset(val.cosmeticAsset); bool flag4 = (flag ? flag3 : (!flag3)); if (flag4 && BridgeFavoritesManager.IsHidden(val.cosmeticAsset)) { flag4 = false; } if (((Component)val).gameObject.activeSelf != flag4) { ((Component)val).gameObject.SetActive(flag4); flag2 = true; } if (flag4) { num++; } } if (num == 0) { if (((Component)section).gameObject.activeSelf) { ((Component)section).gameObject.SetActive(false); flag2 = true; } continue; } if (!((Component)section).gameObject.activeSelf) { ((Component)section).gameObject.SetActive(true); } if (flag && (Object)(object)section.headerText != (Object)null) { ((TMP_Text)section.headerText).text = "WORLD"; ((TMP_Text)section.headerText).ForceMeshUpdate(false, false); MenuElementCosmeticHighlight highlightObj = section.highlightObj; RectTransform val2 = ((highlightObj != null) ? ((Component)highlightObj).GetComponent() : null); if ((Object)(object)val2 != (Object)null) { float x = ((TMP_Text)section.headerText).rectTransform.anchoredPosition.x; Bounds textBounds = ((TMP_Text)section.headerText).textBounds; val2.anchoredPosition = new Vector2(x + ((Bounds)(ref textBounds)).max.x + 15f, val2.anchoredPosition.y); } } ReflowSection(section, num, ref yPos); } if (flag) { HideSubCategoryButtons(__instance); } else { ShowAllSubCategoryButtons(__instance); } if (flag2) { RebuildScroll(__instance); } } private static void ReflowSection(MenuElementCosmeticSection section, int remaining, ref float yPos) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00ab: 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_00d2: 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) GridLayoutGroup component = ((Component)section.cosmeticListTransform).GetComponent(); if (!((Object)(object)component == (Object)null)) { int num = Mathf.Max(1, component.constraintCount); int num2 = Mathf.Max(1, Mathf.CeilToInt((float)(remaining + 1) / (float)num)); float num3 = component.cellSize.y * (float)num2 + component.spacing.y * (float)(num2 - 1) + (float)((LayoutGroup)component).padding.top + (float)((LayoutGroup)component).padding.bottom; float num4 = 40f + num3; RectTransform component2 = ((Component)section).GetComponent(); ((Transform)component2).localPosition = new Vector3(((Transform)component2).localPosition.x, yPos, ((Transform)component2).localPosition.z); component2.sizeDelta = new Vector2(component2.sizeDelta.x, num4); RectTransform component3 = ((Component)section.cosmeticListTransform).GetComponent(); component3.sizeDelta = new Vector2(component3.sizeDelta.x, num3); LayoutRebuilder.ForceRebuildLayoutImmediate(component3); LayoutRebuilder.ForceRebuildLayoutImmediate(component2); yPos -= num4 + 10f; } } private static void HideSubCategoryButtons(MenuPageCosmetics page) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 foreach (Transform item in page.subCategoriesTransform) { Transform val = item; MenuElementButtonCosmeticCategory component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && (int)component.buttonType == 1) { ((Component)val).gameObject.SetActive(false); } } } private static void ShowAllSubCategoryButtons(MenuPageCosmetics page) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 foreach (Transform item in page.subCategoriesTransform) { Transform val = item; MenuElementButtonCosmeticCategory component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && (int)component.buttonType == 1) { ((Component)val).gameObject.SetActive(true); } } } private static void RebuildScroll(MenuPageCosmetics page) { ScrollRect componentInChildren = ((Component)page).GetComponentInChildren(true); if ((Object)(object)((componentInChildren != null) ? componentInChildren.content : null) != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(componentInChildren.content); } } } [HarmonyPatch(typeof(MenuPageCosmetics), "Start")] internal static class WorldCosmeticsMenuStartPatch { [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || HhhCosmeticLoader.WorldAssetIds.Count == 0 || (int)__instance.selectedTab != 0) { return; } WorldCosmeticsMenuState.CurrentPage = __instance; try { WorldCosmeticsMenuState.EnsureCategory(); InjectWorldCategoryButton(__instance); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("WORLD menu injection failed: " + ex.Message)); } } private static void InjectWorldCategoryButton(MenuPageCosmetics page) { if ((Object)(object)WorldCosmeticsMenuState.Category == (Object)null) { return; } MenuElementButtonCosmeticCategory val = ((IEnumerable)((Component)page.categoriesTransform).GetComponentsInChildren(true)).FirstOrDefault((Func)((MenuElementButtonCosmeticCategory b) => (Object)(object)b.category == (Object)(object)WorldCosmeticsMenuState.Category || HasLabel(b, "WORLD"))); if ((Object)(object)val != (Object)null) { val.category = WorldCosmeticsMenuState.Category; return; } GameObject val2 = Object.Instantiate(page.categoryButtonPrefab, page.categoriesTransform); MenuElementButtonCosmeticCategory component = val2.GetComponent(); component.category = WorldCosmeticsMenuState.Category; TextMeshProUGUI componentInChildren = val2.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = 20f; ((TMP_Text)componentInChildren).text = "WORLD"; } MoveAfter(page, val2.transform, "LEGS"); page.categoriesHolder.UpdateButtons(); } private static bool HasLabel(MenuElementButtonCosmeticCategory btn, string label) { TextMeshProUGUI componentInChildren = ((Component)btn).GetComponentInChildren(); string text = btn.category?.categoryName ?? ((componentInChildren != null) ? ((TMP_Text)componentInChildren).text : null) ?? ""; return string.Equals(text.Trim(), label, StringComparison.OrdinalIgnoreCase); } private static void MoveAfter(MenuPageCosmetics page, Transform item, string label) { string label2 = label; List source = ((Component)page.categoriesTransform).GetComponentsInChildren(true).ToList(); MenuElementButtonCosmeticCategory val = ((IEnumerable)source).FirstOrDefault((Func)((MenuElementButtonCosmeticCategory b) => HasLabel(b, label2))); if ((Object)(object)val != (Object)null) { item.SetSiblingIndex(((Component)val).transform.GetSiblingIndex() + 1); } } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupCosmeticsLogic")] internal static class WorldCosmeticsSetupPatch { private sealed class PatchState { public List? PendingWorldAssets; public List? PreviewIndicesRemoved; } private static readonly Dictionary> _worldInstances = new Dictionary>(); [HarmonyPriority(100)] [HarmonyPrefix] private static bool Prefix(PlayerCosmetics __instance, ref int[] __0, ref PatchState __state) { __state = new PatchState(); PruneDestroyedInstances(); if ((Object)(object)MetaManager.instance == (Object)null) { return true; } if (WorldCosmeticsClearButtonPatch.IsUnequipAllRunning) { return false; } bool cosmeticPreviewEnabled = MetaManager.instance.cosmeticPreviewEnabled; List list = null; List list2 = new List(__0.Length); int[] array = __0; foreach (int num in array) { if (num < 0 || num >= MetaManager.instance.cosmeticAssets.Count) { list2.Add(num); continue; } CosmeticAsset val = MetaManager.instance.cosmeticAssets[num]; if (HhhCosmeticLoader.IsWorldAsset(val)) { if (!cosmeticPreviewEnabled) { if (list == null) { list = new List(); } list.Add(val); } } else { list2.Add(num); } } __0 = list2.ToArray(); if (list != null) { __state.PendingWorldAssets = list; } if (cosmeticPreviewEnabled) { List cosmeticEquippedPreview = MetaManager.instance.cosmeticEquippedPreview; List list3 = null; for (int num2 = cosmeticEquippedPreview.Count - 1; num2 >= 0; num2--) { int num3 = cosmeticEquippedPreview[num2]; if (num3 >= 0 && num3 < MetaManager.instance.cosmeticAssets.Count) { CosmeticAsset val2 = MetaManager.instance.cosmeticAssets[num3]; if (HhhCosmeticLoader.IsWorldAsset(val2)) { if (list3 == null) { list3 = new List(); } list3.Add(num3); cosmeticEquippedPreview.RemoveAt(num2); PatchState patchState = __state; if (patchState.PendingWorldAssets == null) { patchState.PendingWorldAssets = new List(); } if (!__state.PendingWorldAssets.Contains(val2)) { __state.PendingWorldAssets.Add(val2); } } } } __state.PreviewIndicesRemoved = list3; } SelectiveDestroyTracked(__instance, __state.PendingWorldAssets); return true; } [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, PatchState __state) { if (__state != null) { RestorePreviewIndices(__state); SpawnPendingWorldCosmetics(__instance, __state); } } [HarmonyFinalizer] private static Exception? Finalizer(PlayerCosmetics __instance, Exception? __exception, PatchState __state) { if (__state == null) { return __exception; } RestorePreviewIndices(__state); if (__exception != null) { SpawnPendingWorldCosmetics(__instance, __state); } return __exception; } private static void RestorePreviewIndices(PatchState state) { List previewIndicesRemoved = state.PreviewIndicesRemoved; state.PreviewIndicesRemoved = null; if (previewIndicesRemoved == null || (Object)(object)MetaManager.instance == (Object)null) { return; } List cosmeticEquippedPreview = MetaManager.instance.cosmeticEquippedPreview; foreach (int item in previewIndicesRemoved) { if (!cosmeticEquippedPreview.Contains(item)) { cosmeticEquippedPreview.Add(item); } } } private static void SpawnPendingWorldCosmetics(PlayerCosmetics instance, PatchState state) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected I4, but got Unknown List pendingWorldAssets = state.PendingWorldAssets; state.PendingWorldAssets = null; if (pendingWorldAssets == null || pendingWorldAssets.Count == 0 || (Object)(object)instance == (Object)null || (Object)(object)instance.playerAvatarVisuals == (Object)null) { return; } Transform val = MoreHeadCosmeticMountPatch.EnsureWorldFollower(((Component)instance.playerAvatarVisuals).transform); if ((Object)(object)val == (Object)null) { return; } if (!_worldInstances.TryGetValue(instance, out List value)) { value = new List(); _worldInstances[instance] = value; } foreach (CosmeticAsset item in pendingWorldAssets) { PrefabRef prefab = item.prefab; GameObject val2 = ((prefab != null) ? prefab.Prefab : null); if ((Object)(object)val2 == (Object)null) { continue; } try { GameObject val3 = Object.Instantiate(val2); Cosmetic val4 = val3.GetComponent() ?? val3.AddComponent(); val4.cosmeticAsset = item; val4.type = item.type; val4.rarity = item.rarity; val4.playerCosmetics = instance; val4.cosmeticParent = null; int num = (int)item.type; if ((Object)(object)MetaManager.instance != (Object)null && num >= 0 && num < MetaManager.instance.cosmeticTypeAssets.Count) { val4.cosmeticTypeAsset = MetaManager.instance.cosmeticTypeAssets[num]; } val4.Setup(); MoreHeadCosmeticMountPatch.Mount(val3, val, val2); if ((Object)(object)instance.playerAvatarVisuals != (Object)null) { PartShrinkerBridge.OnSpawn(val3, instance.playerAvatarVisuals); } value.Add(val3); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("WorldCosmeticsSetupPatch: failed to spawn '" + item?.assetId + "': " + ex.Message)); } } } internal static void SetAllWorldInstancesActive(bool active) { foreach (List value in _worldInstances.Values) { foreach (GameObject item in value) { if ((Object)(object)item != (Object)null) { item.SetActive(active); } } } } internal static void SetWorldAssetActive(CosmeticAsset asset, bool active) { foreach (List value in _worldInstances.Values) { foreach (GameObject item in value) { if (!((Object)(object)item == (Object)null)) { Cosmetic component = item.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.cosmeticAsset == (Object)(object)asset) { item.SetActive(active); } } } } } private static void PruneDestroyedInstances() { List list = null; foreach (PlayerCosmetics key in _worldInstances.Keys) { if ((Object)(object)key == (Object)null) { if (list == null) { list = new List(); } list.Add(key); } } if (list == null) { return; } foreach (PlayerCosmetics item in list) { _worldInstances.Remove(item); } } private static void SelectiveDestroyTracked(PlayerCosmetics instance, List? pending) { if (!_worldInstances.TryGetValue(instance, out List value)) { return; } HashSet hashSet = ((pending != null) ? new HashSet(pending) : null); PlayerAvatarVisuals playerAvatarVisuals = instance.playerAvatarVisuals; for (int num = value.Count - 1; num >= 0; num--) { GameObject val = value[num]; if ((Object)(object)val == (Object)null) { value.RemoveAt(num); } else { CosmeticAsset val2 = val.GetComponent()?.cosmeticAsset; if ((Object)(object)val2 != (Object)null && hashSet != null && hashSet.Remove(val2)) { pending.Remove(val2); } else { if ((Object)(object)playerAvatarVisuals != (Object)null) { PartShrinkerBridge.OnRemove(val, playerAvatarVisuals); } Object.Destroy((Object)(object)val); value.RemoveAt(num); } } } } } [HarmonyPatch(typeof(MetaManager), "CosmeticPreviewSet")] internal static class WorldCosmeticsUnequipHoverPatch { [HarmonyPrefix] private static void Prefix(bool _state) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Invalid comparison between Unknown and I4 if (!_state || HhhCosmeticLoader.WorldAssetIds.Count == 0) { return; } MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null) { return; } WorldCosmeticsMenuState.PartitionHatCosmetics(instance, out List realHats, out List worlds); if (realHats.Count == 0 && worlds.Count == 0) { return; } List cosmeticEquippedPreview = instance.cosmeticEquippedPreview; MenuElementCosmeticButton val = WorldCosmeticsMenuState.CurrentPage?.pendingHoveredCosmeticButton; MenuElementCosmeticSection val2 = val?.cosmeticSection; if ((Object)(object)val?.cosmeticAsset == (Object)null && (Object)(object)val2 != (Object)null && ((int)val2.subCategory == 999 || ((Object)((Component)val2).gameObject).name == "MHB_WorldSection")) { foreach (int item in worlds) { cosmeticEquippedPreview.Remove(item); } { foreach (int item2 in realHats) { if (!cosmeticEquippedPreview.Contains(item2)) { cosmeticEquippedPreview.Add(item2); } } return; } } bool flag = false; foreach (int item3 in cosmeticEquippedPreview) { if (item3 >= 0 && item3 < instance.cosmeticAssets.Count) { CosmeticAsset val3 = instance.cosmeticAssets[item3]; if (val3 != null && (int)val3.type == 0) { flag = true; break; } } } if (flag) { return; } bool flag2 = WorldCosmeticsMenuState.IsWorldCategory(WorldCosmeticsMenuState.CurrentPage?.selectedCategory); bool flag3 = false; if (!flag2 && (Object)(object)val2 != (Object)null) { flag3 = (int)val2.subCategory == 999 || ((Object)((Component)val2).gameObject).name == "MHB_WorldSection"; } List list = ((flag2 || flag3) ? realHats : worlds); foreach (int item4 in list) { if (!cosmeticEquippedPreview.Contains(item4)) { cosmeticEquippedPreview.Add(item4); } } } } [HarmonyPatch(typeof(MetaManager), "CosmeticUnequip")] internal static class WorldCosmeticsUnequipPatch { [HarmonyPrefix] private static void Prefix(MetaManager __instance, CosmeticAsset _cosmeticAsset, ref List? __state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) __state = null; if (HhhCosmeticLoader.WorldAssetIds.Count == 0 || _cosmeticAsset == null || (int)_cosmeticAsset.type > 0) { return; } bool flag = HhhCosmeticLoader.IsWorldAsset(_cosmeticAsset); List list = new List(); foreach (int item in __instance.cosmeticEquipped) { if (item < 0 || item >= __instance.cosmeticAssets.Count) { continue; } CosmeticAsset val = __instance.cosmeticAssets[item]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_cosmeticAsset) && (int)val.type == 0) { bool flag2 = HhhCosmeticLoader.IsWorldAsset(val); bool flag3 = flag2 != flag; bool flag4 = Plugin.AllowMultipleCosmetics.Value && flag2 == flag; if (flag3 || flag4) { list.Add(item); } } } if (list.Count > 0) { __state = list; } } [HarmonyPostfix] private static void Postfix(MetaManager __instance, List? __state) { if (__state == null) { return; } foreach (int item in __state) { if (!__instance.cosmeticEquipped.Contains(item)) { __instance.cosmeticEquipped.Add(item); } } } } internal static class WorldCosmeticsMenuState { internal static CosmeticCategoryAsset? Category { get; private set; } internal static MenuPageCosmetics? CurrentPage { get; set; } internal static bool IsWorldCategory(CosmeticCategoryAsset? category) { if ((Object)(object)category != (Object)null) { if (!((Object)(object)category == (Object)(object)Category)) { return ((Object)category).name == "MHB_World"; } return true; } return false; } internal static void EnsureCategory() { if (!((Object)(object)Category != (Object)null)) { Category = ScriptableObject.CreateInstance(); ((Object)Category).name = "MHB_World"; Category.categoryName = "WORLD"; Category.typeList = new List(1) { (CosmeticType)0 }; } } internal static void PartitionHatCosmetics(MetaManager meta, out List realHats, out List worlds) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 realHats = new List(); worlds = new List(); foreach (int item in meta.cosmeticEquipped) { if (item < 0 || item >= meta.cosmeticAssets.Count) { continue; } CosmeticAsset val = meta.cosmeticAssets[item]; if (val != null && (int)val.type <= 0) { if (HhhCosmeticLoader.IsWorldAsset(val)) { worlds.Add(item); } else { realHats.Add(item); } } } } } internal static class WorldCosmeticsRandomize { [HarmonyPatch(typeof(MenuPageCosmetics), "RandomizeAllButton")] internal static class RandomizeAllPostfix { [HarmonyPostfix] private static void Postfix() { ApplyIndependentRolls(); } } [HarmonyPatch(typeof(MenuPageCosmetics), "RandomizeCosmeticsButton")] internal static class RandomizeCosmeticsPostfix { [HarmonyPostfix] private static void Postfix() { ApplyIndependentRolls(); } } private static void ApplyIndependentRolls() { //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) MetaManager meta = MetaManager.instance; if ((Object)(object)meta == (Object)null) { return; } if (HhhCosmeticLoader.WorldAssetIds.Count > 0) { List list = new List(); List list2 = new List(); foreach (int cosmeticUnlock in meta.cosmeticUnlocks) { if (cosmeticUnlock < 0 || cosmeticUnlock >= meta.cosmeticAssets.Count) { continue; } CosmeticAsset val = meta.cosmeticAssets[cosmeticUnlock]; if (!((Object)(object)val == (Object)null) && (int)val.type == 0 && val.prefab.IsValid()) { if (HhhCosmeticLoader.IsWorldAsset(val)) { list2.Add(cosmeticUnlock); } else { list.Add(cosmeticUnlock); } } } if (list2.Count > 0) { meta.cosmeticEquipped.RemoveAll(delegate(int idx) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if (idx < 0 || idx >= meta.cosmeticAssets.Count) { return false; } CosmeticAsset val2 = meta.cosmeticAssets[idx]; return (Object)(object)val2 != (Object)null && (int)val2.type == 0; }); if (list.Count > 0 && Random.Range(0f, 1f) <= 0.75f) { meta.cosmeticEquipped.Add(list[Random.Range(0, list.Count)]); } if (Random.Range(0f, 1f) <= 0.75f) { meta.cosmeticEquipped.Add(list2[Random.Range(0, list2.Count)]); } meta.Save(true); meta.CosmeticPlayerUpdateLocal(false, false); } } MenuPageCosmetics activePage = CosmeticsMenuState.ActivePage; if ((Object)(object)activePage != (Object)null && (int)activePage.selectedTab == 0 && CosmeticsMenuState.IsSelected(activePage.selectedCategory)) { activePage.RefreshScrollContent(); } } } internal sealed class WorldSpaceFollower : MonoBehaviour { private Transform? _root; private Vector3 _initialOffset; private void Start() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) _root = ((Component)this).transform.parent; if (!((Object)(object)_root == (Object)null)) { _initialOffset = ((Component)this).transform.position - _root.position; ((Component)this).transform.rotation = Quaternion.identity; ((Component)this).transform.localScale = Vector3.one; } } private void LateUpdate() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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 (!((Object)(object)_root == (Object)null)) { ((Component)this).transform.position = _root.position + _initialOffset; ((Component)this).transform.rotation = Quaternion.Euler(0f, _root.eulerAngles.y, 0f); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }