Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MoreHeadBridge v3.0.0
MoreHeadBridge.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Photon.Pun; using Photon.Realtime; using REPOLib; using REPOLib.Modules; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; 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("3.0.0.0")] [assembly: AssemblyInformationalVersion("3.0.0+ce7a067d8229aec8619b63065777d71107459760")] [assembly: AssemblyProduct("MoreHeadBridge")] [assembly: AssemblyTitle("MoreHeadBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 AtomicJson { internal static void Write(string path, string json) { string text = path + ".tmp"; File.WriteAllText(text, json, Encoding.UTF8); try { if (File.Exists(path)) { File.Replace(text, path, null); } else { File.Move(text, path); } } catch { try { if (File.Exists(text)) { File.Delete(text); } } catch { } throw; } } internal static Task QueueWrite(Task lastWrite, string path, string json, string errLabel) { return lastWrite.ContinueWith(delegate { try { Write(path, json); } catch (Exception ex) { BceConsole.LogWarning(errLabel + ": " + ex.Message); } }, TaskScheduler.Default); } } internal enum AvatarKind { Local, Menu, RemoteMini, Remote } internal readonly struct AvatarIdentity { public readonly AvatarKind Kind; public readonly int Actor; public bool IsRemote { get { if (Kind != AvatarKind.RemoteMini) { return Kind == AvatarKind.Remote; } return true; } } public AvatarIdentity(AvatarKind kind, int actor) { Kind = kind; Actor = actor; } public static AvatarIdentity Of(PlayerCosmetics instance) { if (!SemiFunc.IsMultiplayer()) { return new AvatarIdentity(AvatarKind.Local, -1); } int num = MiniSemibotSpawner.RemoteMiniActorOf(instance); if (num > 0) { return new AvatarIdentity(AvatarKind.RemoteMini, num); } if (MiniSemibotSpawner.IsMenuOrPreviewWearer(instance.playerAvatarVisuals)) { return new AvatarIdentity(AvatarKind.Menu, -1); } PhotonView val = ((Object.op_Implicit((Object)(object)instance.deathHead) && instance.deathHead.setup && Object.op_Implicit((Object)(object)instance.deathHead.playerAvatar)) ? instance.deathHead.playerAvatar.photonView : instance.photonView); if ((Object)(object)val == (Object)null || val.IsMine) { return new AvatarIdentity(AvatarKind.Local, -1); } Player owner = val.Owner; int num2 = ((owner != null) ? owner.ActorNumber : (-1)); if (num2 <= 0) { return new AvatarIdentity(AvatarKind.Local, -1); } return new AvatarIdentity(AvatarKind.Remote, num2); } internal static bool TryGetRemoteActor(PlayerCosmetics instance, out int actorNumber) { AvatarIdentity avatarIdentity = Of(instance); actorNumber = (avatarIdentity.IsRemote ? avatarIdentity.Actor : (-1)); return avatarIdentity.IsRemote; } internal static bool IsLocalOrMenu(PlayerCosmetics pc) { if ((Object)(object)pc == (Object)null) { return false; } PlayerAvatarVisuals playerAvatarVisuals = pc.playerAvatarVisuals; if (playerAvatarVisuals != null && playerAvatarVisuals.isMenuAvatar) { return true; } if ((Object)(object)pc.photonView == (Object)null) { return true; } if (pc.photonView.IsMine) { return true; } if ((Object)(object)pc.deathHead != (Object)null && pc.deathHead.setup) { PlayerAvatar playerAvatar = pc.deathHead.playerAvatar; if (playerAvatar == null) { return false; } PhotonView photonView = playerAvatar.photonView; return ((photonView != null) ? new bool?(photonView.IsMine) : ((bool?)null)) == true; } return false; } internal static bool IsRemoteMini(PlayerCosmetics? pc) { return MiniSemibotSpawner.IsRemoteMiniCosmetics(pc); } internal static bool IsLocalStyleTarget(PlayerCosmetics pc) { if (IsLocalOrMenu(pc)) { return !IsRemoteMini(pc); } return false; } } internal static class BceConsole { private const string InfoPrefix = "[Info : MoreHead Bridge] "; private const string WarnPrefix = "[Warning: MoreHead Bridge] "; private const string ErrorPrefix = "[Error : MoreHead Bridge] "; private const string DebugPrefix = "[Debug : MoreHead Bridge] "; private static readonly Action<string, ConsoleColor>? _writeLineDelegate; private static readonly Action<string, ConsoleColor>? _writeDelegate; internal static bool IsAvailable => _writeLineDelegate != null; static BceConsole() { Type type = Type.GetType("BCE.console, BCE"); if (type == null) { return; } try { MethodInfo method = type.GetMethod("WriteLine", new Type[2] { typeof(string), typeof(ConsoleColor) }); MethodInfo method2 = type.GetMethod("Write", new Type[2] { typeof(string), typeof(ConsoleColor) }); if (method != null) { _writeLineDelegate = (Action<string, ConsoleColor>)Delegate.CreateDelegate(typeof(Action<string, ConsoleColor>), null, method); } if (method2 != null) { _writeDelegate = (Action<string, ConsoleColor>)Delegate.CreateDelegate(typeof(Action<string, ConsoleColor>), null, method2); } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("BceConsole: delegate creation failed (" + ex.Message + "). BCE output disabled — falling back to BepInEx logger.")); } } } internal static void WriteLine(string msg, ConsoleColor color) { _writeLineDelegate?.Invoke(msg, color); } internal static void Write(string msg, ConsoleColor color) { _writeDelegate?.Invoke(msg, color); } internal static void LogInfo(string msg) { LogInfo(msg, ConsoleColor.Cyan); } internal static void LogInfo(string msg, ConsoleColor color) { if (IsAvailable) { WriteLine("[Info : MoreHead Bridge] " + msg, color); } else { Plugin.Logger.LogInfo((object)msg); } } internal static void LogWarning(string msg) { LogWarning(msg, ConsoleColor.Yellow); } internal static void LogWarning(string msg, ConsoleColor color) { if (IsAvailable) { WriteLine("[Warning: MoreHead Bridge] " + msg, color); } else { Plugin.Logger.LogWarning((object)msg); } } internal static void LogError(string msg) { if (IsAvailable) { WriteLine("[Error : MoreHead Bridge] " + msg, ConsoleColor.Red); } else { Plugin.Logger.LogError((object)msg); } } internal static void LogDebug(string msg) { if (IsAvailable) { WriteLine("[Debug : MoreHead Bridge] " + msg, ConsoleColor.DarkGray); } else { Plugin.Logger.LogDebug((object)msg); } } } public enum BlacklistLoadMode { NotLoadIngame, LoadOnHiddenMenu } internal static class BridgeBlacklist { private sealed class Entry { public string AssetId { get; set; } = ""; public string DisplayName { get; set; } = ""; } private sealed class SaveData { public List<Entry> Entries { get; set; } = new List<Entry>(); public List<string> MirroredNames { get; set; } = new List<string>(); } private sealed class MoreHeadBlacklistData { public List<string> DecorationNames { get; set; } = new List<string>(); } private static readonly Dictionary<string, string> _byAssetId = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly HashSet<string> _mirrored = new HashSet<string>(StringComparer.Ordinal); private static readonly string SavePath = BridgePaths.Of("Blacklist.json"); private static readonly string MoreHeadPath = Path.Combine(Paths.ConfigPath, "MoreHeadBlacklist.json"); private static bool _loaded; internal static bool ExistedAtStartup { get; private set; } internal static void EnsureLoaded() { if (_loaded) { return; } _loaded = true; ExistedAtStartup = File.Exists(SavePath); if (!ExistedAtStartup) { return; } try { SaveData saveData = JsonConvert.DeserializeObject<SaveData>(File.ReadAllText(SavePath)); if (saveData == null) { return; } foreach (Entry item in saveData.Entries ?? new List<Entry>()) { if (!string.IsNullOrEmpty(item.AssetId)) { _byAssetId[item.AssetId] = item.DisplayName ?? ""; } } foreach (string item2 in saveData.MirroredNames ?? new List<string>()) { _mirrored.Add(item2); } } catch (Exception ex) { BceConsole.LogWarning("BridgeBlacklist: load failed: " + ex.Message); } } internal static bool Contains(string assetId) { return _byAssetId.ContainsKey(assetId); } internal static void Add(string assetId, string displayName) { if (!string.IsNullOrEmpty(assetId)) { _byAssetId[assetId] = displayName ?? ""; } } internal static void SetBlacklisted(string assetId, string? displayName, bool blacklisted) { EnsureLoaded(); if (string.IsNullOrEmpty(assetId)) { return; } string value; if (blacklisted) { if (!_byAssetId.ContainsKey(assetId)) { _byAssetId[assetId] = displayName ?? ""; Save(); if (Plugin.MirrorBlacklistToMoreHead.Value) { MirrorAdd(displayName); } } } else if (_byAssetId.TryGetValue(assetId, out value)) { _byAssetId.Remove(assetId); Save(); if (Plugin.MirrorBlacklistToMoreHead.Value) { MirrorRemove(value); } } } internal static HashSet<string> ReadMoreHeadNames() { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); try { if (!File.Exists(MoreHeadPath)) { return hashSet; } foreach (string item in JsonConvert.DeserializeObject<MoreHeadBlacklistData>(File.ReadAllText(MoreHeadPath))?.DecorationNames ?? new List<string>()) { hashSet.Add(item); } } catch (Exception ex) { BceConsole.LogWarning("BridgeBlacklist: reading MoreHead blacklist failed: " + ex.Message); } return hashSet; } internal static void Save() { try { Directory.CreateDirectory(BridgePaths.DataDir); SaveData saveData = new SaveData { Entries = _byAssetId.Select<KeyValuePair<string, string>, Entry>((KeyValuePair<string, string> kv) => new Entry { AssetId = kv.Key, DisplayName = kv.Value }).ToList(), MirroredNames = _mirrored.ToList() }; AtomicJson.Write(SavePath, JsonConvert.SerializeObject((object)saveData, (Formatting)1)); } catch (Exception ex) { BceConsole.LogWarning("BridgeBlacklist: save failed: " + ex.Message); } } internal static void MirrorToMoreHead() { if (!Plugin.MirrorBlacklistToMoreHead.Value) { return; } try { HashSet<string> hashSet = ReadMoreHeadNames(); bool flag = false; foreach (string item in _byAssetId.Values.Where((string n) => !string.IsNullOrEmpty(n))) { if (hashSet.Add(item)) { _mirrored.Add(item); flag = true; } } if (flag) { WriteMoreHead(hashSet); Save(); } } catch (Exception ex) { BceConsole.LogWarning("BridgeBlacklist: mirror failed: " + ex.Message); } } private static void MirrorAdd(string? name) { if (string.IsNullOrEmpty(name)) { return; } try { HashSet<string> hashSet = ReadMoreHeadNames(); if (hashSet.Add(name)) { _mirrored.Add(name); WriteMoreHead(hashSet); Save(); } } catch (Exception ex) { BceConsole.LogWarning("BridgeBlacklist: mirror add failed: " + ex.Message); } } private static void MirrorRemove(string? name) { if (string.IsNullOrEmpty(name) || !_mirrored.Contains(name)) { return; } try { HashSet<string> hashSet = ReadMoreHeadNames(); if (hashSet.Remove(name)) { WriteMoreHead(hashSet); } _mirrored.Remove(name); Save(); } catch (Exception ex) { BceConsole.LogWarning("BridgeBlacklist: mirror remove failed: " + ex.Message); } } private static void WriteMoreHead(HashSet<string> names) { Directory.CreateDirectory(Path.GetDirectoryName(MoreHeadPath)); File.WriteAllText(MoreHeadPath, JsonConvert.SerializeObject((object)new MoreHeadBlacklistData { DecorationNames = names.ToList() }, (Formatting)1)); } } internal static class BridgeIds { internal const string Prefix = "morehead-bridge:"; private static HashSet<CosmeticAsset>? _registeredSet; private static int _registeredCount = -1; private static HashSet<CosmeticAsset> RegisteredSet() { IReadOnlyList<CosmeticAsset> registeredCosmetics = Cosmetics.RegisteredCosmetics; if (_registeredSet == null || _registeredCount != registeredCosmetics.Count) { _registeredSet = new HashSet<CosmeticAsset>(registeredCosmetics); _registeredCount = registeredCosmetics.Count; } return _registeredSet; } 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 bool IsModdedCosmetic(CosmeticAsset? asset) { if ((Object)(object)asset != (Object)null && !IsBridgeAsset(asset)) { return RegisteredSet().Contains(asset); } return false; } internal static bool HasAnyNonBridgeModded() { return Cosmetics.RegisteredCosmetics.Count > 0; } internal static bool IsCustomizable(CosmeticAsset? asset) { if (!IsBridgeAsset(asset)) { if (Plugin.AllowModdedOverrides.Value) { return IsModdedCosmetic(asset); } return false; } return true; } } internal static class BridgeLog { internal static void UserInfo(string msg) { BceConsole.LogInfo(msg); } internal static void UserInfo(string msg, ConsoleColor color) { BceConsole.LogInfo(msg, color); } internal static void UserWarning(string msg) { BceConsole.LogWarning(msg); } internal static void UserWarning(string msg, ConsoleColor color) { BceConsole.LogWarning(msg, color); } internal static void UserError(string msg) { BceConsole.LogError(msg); } internal static void Debug(string msg) { if (Plugin.ShowBridgeDebugLogs.Value) { BceConsole.LogDebug(msg); } } internal static void Trace(string msg) { Plugin.Logger.LogDebug((object)msg); } } internal static class BridgePaths { internal static readonly string DataDir = Path.Combine(Paths.ConfigPath, "MoreHeadBridge"); private static bool _migrated; internal static string Of(string fileName) { return Path.Combine(DataDir, fileName); } internal static void Init() { if (_migrated) { return; } _migrated = true; try { Directory.CreateDirectory(DataDir); string[] files = Directory.GetFiles(Paths.ConfigPath, "MoreHeadBridge_*.json"); foreach (string text in files) { string text2 = Path.Combine(DataDir, Path.GetFileName(text).Substring("MoreHeadBridge_".Length)); if (!File.Exists(text2)) { File.Move(text, text2); } } } catch (Exception ex) { BceConsole.LogWarning("BridgePaths: could not move legacy save files into MoreHeadBridge/ — " + ex.Message); } } } internal sealed class BridgeHideCondition : MonoBehaviour { private sealed class Target { internal Transform Transform; internal Vector3 BaseScale; internal Renderer[] Renderers = Array.Empty<Renderer>(); } private const float CheckInterval = 0.1f; private const float HideSpeed = 6f; private const float ShowSpeed = 4f; private Cosmetic _cosmetic; private PlayerCosmetics? _pc; private CosmeticHideConfig _config; private readonly List<Target> _targets = new List<Target>(); private float _checkTimer; private bool _hidden; private float _shownFactor = 1f; private bool _renderersDisabled; internal void Init(Cosmetic cosmetic, CosmeticHideConfig config) { _cosmetic = cosmetic; _pc = cosmetic.playerCosmetics; _config = config; CosmeticHideCondition[] componentsInChildren = ((Component)cosmetic).GetComponentsInChildren<CosmeticHideCondition>(true); foreach (CosmeticHideCondition val in componentsInChildren) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } _targets.Clear(); List<Transform> meshParents = cosmetic.meshParents; if (meshParents != null && meshParents.Count > 0) { foreach (Transform meshParent in cosmetic.meshParents) { AddTarget(meshParent); } } if (_targets.Count == 0) { Renderer[] componentsInChildren2 = ((Component)cosmetic).GetComponentsInChildren<Renderer>(true); foreach (Renderer val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null) { AddTarget(((Component)val2).transform); } } } ((Behaviour)this).enabled = _targets.Count > 0 && config.HasAny; } private void AddTarget(Transform? t) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)t == (Object)null) { return; } foreach (Target target in _targets) { if ((Object)(object)target.Transform == (Object)(object)t) { return; } } _targets.Add(new Target { Transform = t, BaseScale = t.localScale, Renderers = ((Component)t).GetComponentsInChildren<Renderer>(true) }); } private void LateUpdate() { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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) if (_targets.Count == 0) { return; } _checkTimer -= Time.deltaTime; if (_checkTimer <= 0f) { _checkTimer = 0.1f; _hidden = ShouldHide(); } float num = (_hidden ? 0f : 1f); float num2 = (_hidden ? 6f : 4f); _shownFactor = Mathf.MoveTowards(_shownFactor, num, num2 * Time.deltaTime); if (_shownFactor >= 0.999f && !_hidden) { EnsureRenderers(on: true); for (int i = 0; i < _targets.Count; i++) { Target target = _targets[i]; if ((Object)(object)target.Transform != (Object)null) { target.BaseScale = target.Transform.localScale; } } return; } float num3 = Mathf.SmoothStep(0f, 1f, _shownFactor); for (int num4 = _targets.Count - 1; num4 >= 0; num4--) { Target target2 = _targets[num4]; if ((Object)(object)target2.Transform == (Object)null) { _targets.RemoveAt(num4); } else { target2.Transform.localScale = target2.BaseScale * num3; } } EnsureRenderers(_shownFactor > 0.001f); } private void OnDestroy() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) foreach (Target target in _targets) { if ((Object)(object)target.Transform != (Object)null) { target.Transform.localScale = target.BaseScale; } Renderer[] renderers = target.Renderers; foreach (Renderer val in renderers) { if ((Object)(object)val != (Object)null) { val.enabled = true; } } } } private void EnsureRenderers(bool on) { if (_renderersDisabled == !on) { return; } _renderersDisabled = !on; foreach (Target target in _targets) { Renderer[] renderers = target.Renderers; foreach (Renderer val in renderers) { if ((Object)(object)val != (Object)null) { val.enabled = on; } } } } private bool ShouldHide() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_pc == (Object)null) { return false; } List<Type> whenConditions = _config.WhenConditions; if (whenConditions != null && whenConditions.Count > 0) { foreach (Type whenCondition in _config.WhenConditions) { if (_pc.ConditionCustomCheck(whenCondition)) { return true; } } } List<Pose> whenPoses = _config.WhenPoses; if (whenPoses != null && whenPoses.Count > 0) { PlayerAvatarVisuals playerAvatarVisuals = _pc.playerAvatarVisuals; if ((Object)(object)playerAvatarVisuals != (Object)null && _config.WhenPoses.Contains(playerAvatarVisuals.currentPose)) { return true; } } List<CosmeticType> whenTypes = _config.WhenTypes; bool flag = whenTypes != null && whenTypes.Count > 0; List<string> whenCosmetics = _config.WhenCosmetics; bool flag2 = whenCosmetics != null && whenCosmetics.Count > 0; if (flag || flag2) { PlayerAvatarVisuals playerAvatarVisuals2 = _pc.playerAvatarVisuals; if ((Object)(object)playerAvatarVisuals2 != (Object)null) { Cosmetic[] componentsInChildren = ((Component)playerAvatarVisuals2).GetComponentsInChildren<Cosmetic>(false); foreach (Cosmetic val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_cosmetic)) { if (flag && _config.WhenTypes.Contains(val.type)) { return true; } if (flag2 && (Object)(object)val.cosmeticAsset != (Object)null && _config.WhenCosmetics.Contains(((Object)val.cosmeticAsset).name)) { return true; } } } } } return false; } } internal sealed class BridgeLiveBlocked : MonoBehaviour { private const float CheckInterval = 0.1f; private const float BlockedCooldown = 0.25f; private const float SwitchDebounce = 0.1f; private const float ProbeRadiusMin = 0.03f; private const float ProbeRadiusMax = 0.1f; private const float SpringStiffness = 120f; private const float SpringDamping = 14f; private const float SpringKickVelocity = 6f; private const float MaxBlockedDuration = 6f; private const string GrabHandleColliderName = "Health Grab"; private PlayerCosmetics? _pc; private Transform _target; private Transform _anchor; private DeathHeadFloorPose _pose; private MiniSemibotFollow? _miniFollow; private Vector3 _anchorLocalCenter; private float _localMaxExtent; private bool _valid; private LayerMask _layerMask; private float _springPos; private float _springVel; private bool _blocked; private float _checkTimer; private float _cooldownTimer; private float _switchTimer; private float _blockedDuration; private Vector3 _refPos; private Vector3 _refEuler; private Vector3 _refScale; internal void Init(Cosmetic cosmetic, DeathHeadFloorPose pose) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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_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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) _pc = cosmetic.playerCosmetics; _target = ((Component)cosmetic).transform; _anchor = (Transform)(((Object)(object)_target.parent != (Object)null) ? ((object)_target.parent) : ((object)_target)); _pose = pose; _pose.MigrateLegacy(); _refPos = _target.localPosition; _refEuler = _target.localEulerAngles; _refScale = _target.localScale; Transform val = _target; while ((Object)(object)val != (Object)null && (Object)(object)_miniFollow == (Object)null) { _miniFollow = ((Component)val).GetComponent<MiniSemibotFollow>(); val = val.parent; } if (TryGetWorldBounds(out var bounds)) { Vector3 val2 = ((Bounds)(ref bounds)).center + Vector3.up * ((Bounds)(ref bounds)).extents.y; _anchorLocalCenter = _anchor.InverseTransformPoint(val2); float num = Mathf.Max(new float[3] { ((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.y, ((Bounds)(ref bounds)).extents.z }); _localMaxExtent = num / Mathf.Max(0.0001f, Mathf.Abs(_anchor.lossyScale.y)); _valid = num > 0.0001f; } _layerMask = LayerMask.op_Implicit(LayerMask.op_Implicit(SemiFunc.LayerMaskGetPhysGrabObject()) + LayerMask.GetMask(new string[1] { "Default" }) + LayerMask.GetMask(new string[1] { "Enemy" })); } private bool IsLiveBody() { if ((Object)(object)_pc == (Object)null) { return false; } PlayerAvatarVisuals playerAvatarVisuals = _pc.playerAvatarVisuals; if ((Object)(object)playerAvatarVisuals == (Object)null) { return false; } if (!playerAvatarVisuals.isMenuAvatar) { return true; } if ((Object)(object)_miniFollow != (Object)null && !_miniFollow.ExpressionPreview && (Object)(object)_miniFollow.WearerVisuals != (Object)null) { return !MiniSemibotSpawner.IsMenuOrPreviewWearer(_miniFollow.WearerVisuals); } return false; } private void LateUpdate() { //IL_004f: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0337: 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_0363: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) if (!_valid || _pose == null) { return; } if (!_pose.ReactWhenAlive || !IsLiveBody()) { if (_springPos > 0.001f || Mathf.Abs(_springVel) > 0.001f) { _target.localPosition = _refPos; _target.localRotation = Quaternion.Euler(_refEuler); _target.localScale = _refScale; } _springPos = 0f; _springVel = 0f; _blocked = false; _blockedDuration = 0f; return; } if (_cooldownTimer > 0f) { _cooldownTimer -= Time.deltaTime; } if (_switchTimer > 0f) { _switchTimer -= Time.deltaTime; } bool blocked = _blocked; if (_switchTimer <= 0f) { _checkTimer -= Time.deltaTime; if (_checkTimer <= 0f) { _checkTimer = 0.1f; if (CheckBlocked()) { _blocked = true; _cooldownTimer = 0.25f; } else if (_cooldownTimer <= 0f) { _blocked = false; } } } if (_blocked) { _blockedDuration += Time.deltaTime; if (_blockedDuration >= 6f) { _blocked = false; _cooldownTimer = 0f; _blockedDuration = 0f; } } else { _blockedDuration = 0f; } if (_blocked != blocked) { _switchTimer = 0.1f; _springVel += (_blocked ? 6f : (-6f)); } float num = (_blocked ? 1f : 0f); float num2 = (num - _springPos) * 120f - _springVel * 14f; _springVel += num2 * Time.deltaTime; _springPos += _springVel * Time.deltaTime; float num3 = Mathf.Clamp01(_springPos); if (num3 <= 0f) { _refPos = _target.localPosition; _refEuler = _target.localEulerAngles; _refScale = _target.localScale; if (!_blocked && Mathf.Abs(_springVel) < 0.05f) { _springPos = 0f; _springVel = 0f; } } else { _target.localPosition = Vector3.Lerp(_refPos, new Vector3(_pose.PosX, _pose.PosY, _pose.PosZ), num3); _target.localRotation = Quaternion.Slerp(Quaternion.Euler(_refEuler), Quaternion.Euler(_pose.RotX, _pose.RotY, _pose.RotZ), num3); _target.localScale = Vector3.Lerp(_refScale, new Vector3(_pose.ScaleX, _pose.ScaleY, _pose.ScaleZ), num3); } } private void OnDestroy() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_target == (Object)null) && (_springPos > 0.001f || Mathf.Abs(_springVel) > 0.001f)) { _target.localPosition = _refPos; _target.localRotation = Quaternion.Euler(_refEuler); _target.localScale = _refScale; } } private bool CheckBlocked() { //IL_0007: 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) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Vector3 val = _anchor.TransformPoint(_anchorLocalCenter); float num = Mathf.Max(0.0001f, Mathf.Abs(_anchor.lossyScale.y)); float num2 = ((num < 1f) ? Mathf.Sqrt(num) : num); float num3 = Mathf.Clamp(_localMaxExtent * num2 * 0.25f, 0.03f * num2, 0.1f * num2); Collider[] array = Physics.OverlapSphere(val, num3, LayerMask.op_Implicit(_layerMask), (QueryTriggerInteraction)2); Collider[] array2 = array; foreach (Collider val2 in array2) { if (!((Object)(object)val2 == (Object)null) && !(((Object)val2).name == "Health Grab") && !((Object)(object)((Component)val2).GetComponentInParent<PlayerDeathHead>() != (Object)null) && !((Object)(object)((Component)val2).GetComponentInParent<PlayerTumble>() != (Object)null)) { return true; } } return false; } private bool TryGetWorldBounds(out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); Renderer[] componentsInChildren = ((Component)_target).GetComponentsInChildren<Renderer>(true); bool flag = false; Renderer[] array = componentsInChildren; foreach (Renderer val in array) { if (!((Object)(object)val == (Object)null) && val.enabled && ((Component)val).gameObject.activeInHierarchy) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } if (flag) { return true; } Renderer[] array2 = componentsInChildren; foreach (Renderer val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { if (!flag) { bounds = val2.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val2.bounds); } } } return flag; } } internal static class CosmeticEquipAnimation { private static FieldInfo? _equipLerpField; private static FieldInfo? _meshParentsScaleField; internal static void Finish(GameObject go) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) try { Cosmetic val = (((Object)(object)go != (Object)null) ? go.GetComponentInChildren<Cosmetic>(true) : null); if ((Object)(object)val == (Object)null) { return; } if ((object)_equipLerpField == null) { _equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp"); } _equipLerpField?.SetValue(val, 1f); if ((object)_meshParentsScaleField == null) { _meshParentsScaleField = AccessTools.Field(typeof(Cosmetic), "meshParentsScale"); } if (!(_meshParentsScaleField?.GetValue(val) is List<Vector3> list)) { return; } List<Transform> meshParents = val.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) { BridgeLog.Trace("CosmeticEquipAnimation.Finish failed: " + ex.Message); } } } internal static class MultiEquipTypeFlags { private static readonly Dictionary<CosmeticTypeAsset, bool> _originals = new Dictionary<CosmeticTypeAsset, bool>(); internal static void Sync() { if (!Plugin.AllowMultipleCosmetics.Value) { Restore(); } else { Apply(); } } private static void Apply() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) MetaManager instance = MetaManager.instance; if (instance?.cosmeticTypeAssets == null) { return; } foreach (CosmeticTypeAsset cosmeticTypeAsset in instance.cosmeticTypeAssets) { if (!((Object)(object)cosmeticTypeAsset == (Object)null) && MultiEquipTypes.All.Contains(cosmeticTypeAsset.type)) { if (!_originals.ContainsKey(cosmeticTypeAsset)) { _originals[cosmeticTypeAsset] = cosmeticTypeAsset.canEquipMultiple; } cosmeticTypeAsset.canEquipMultiple = true; } } } internal static void Restore() { if (_originals.Count == 0) { return; } foreach (KeyValuePair<CosmeticTypeAsset, bool> original in _originals) { if ((Object)(object)original.Key != (Object)null) { original.Key.canEquipMultiple = original.Value; } } _originals.Clear(); } } internal static class MultiEquipTypes { internal static readonly HashSet<CosmeticType> All = new HashSet<CosmeticType> { (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 }; } [HarmonyPatch(typeof(MetaManager), "Awake")] internal static class MultiEquipFlagsApplyPatch { [HarmonyPostfix] private static void Postfix() { MultiEquipTypeFlags.Sync(); } } internal static class CustomizerIO { internal static string ExportFolder => Path.Combine(Paths.ConfigPath, "MoreHeadBridge"); internal static string ExportFilePath => Path.Combine(ExportFolder, "overrides_export.json"); internal static void ExportAll() { Dictionary<string, CosmeticOverrideData> allData = CustomizerStore.GetAllData(); if (allData.Count == 0) { BceConsole.LogInfo("CustomizerIO: no overrides to export."); } else { MergeWrite(allData); } } internal static void ExportSingle(string assetId) { if (!CustomizerStore.TryGet(assetId, out CosmeticOverrideData data)) { BceConsole.LogWarning("CustomizerIO: no saved override for '" + assetId + "' — save first."); return; } MergeWrite(new Dictionary<string, CosmeticOverrideData> { [assetId] = data }); } internal static void ImportMerge() { try { if (!File.Exists(ExportFilePath)) { BceConsole.LogWarning("CustomizerIO: export file not found — nothing to import."); return; } string text = File.ReadAllText(ExportFilePath); Dictionary<string, CosmeticOverrideData> dictionary = JsonConvert.DeserializeObject<Dictionary<string, CosmeticOverrideData>>(text); if (dictionary == null || dictionary.Count == 0) { BceConsole.LogWarning("CustomizerIO: export file is empty — nothing to import."); return; } CustomizerStore.ImportBatch(dictionary); BceConsole.LogInfo($"CustomizerIO: imported {dictionary.Count} override(s) from {ExportFilePath}"); } catch (Exception ex) { BceConsole.LogWarning("CustomizerIO: import failed — " + ex.Message); } } private static void MergeWrite(Dictionary<string, CosmeticOverrideData> incoming) { try { Directory.CreateDirectory(ExportFolder); Dictionary<string, CosmeticOverrideData> dictionary = new Dictionary<string, CosmeticOverrideData>(); if (File.Exists(ExportFilePath)) { string text = File.ReadAllText(ExportFilePath); Dictionary<string, CosmeticOverrideData> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, CosmeticOverrideData>>(text); if (dictionary2 != null) { dictionary = dictionary2; } } foreach (KeyValuePair<string, CosmeticOverrideData> item in incoming) { dictionary[item.Key] = item.Value; } string json = JsonConvert.SerializeObject((object)dictionary, (Formatting)1); AtomicJson.Write(ExportFilePath, json); BceConsole.LogInfo($"CustomizerIO: exported {incoming.Count} override(s) to {ExportFilePath}"); } catch (Exception ex) { BceConsole.LogWarning("CustomizerIO: export failed — " + ex.Message); } } } internal static class BridgeFavoritesManager { private sealed class SaveData { public List<string> Favorites { get; set; } = new List<string>(); public List<string> Hidden { get; set; } = new List<string>(); } private static readonly HashSet<string> _favorites = new HashSet<string>(); private static readonly HashSet<string> _hidden = new HashSet<string>(); private static bool _loaded; private static readonly string SavePath = BridgePaths.Of("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; } internal static void EnsureHidden(CosmeticAsset asset) { EnsureLoaded(); string item = KeyFor(asset); _favorites.Remove(item); if (_hidden.Add(item)) { Save(); } } private static void Load() { try { if (!File.Exists(SavePath)) { return; } SaveData saveData = JsonConvert.DeserializeObject<SaveData>(File.ReadAllText(SavePath)); if (saveData == null) { return; } _favorites.Clear(); _hidden.Clear(); foreach (string item in saveData.Favorites ?? new List<string>()) { _favorites.Add(item); } foreach (string item2 in saveData.Hidden ?? new List<string>()) { _hidden.Add(item2); } BceConsole.LogInfo($"BridgeFavoritesManager: loaded {_favorites.Count} favorite(s), {_hidden.Count} hidden.", ConsoleColor.DarkBlue); } catch (Exception ex) { BceConsole.LogWarning("BridgeFavoritesManager: load failed: " + ex.Message); } } private static void Save() { string json = JsonConvert.SerializeObject((object)new SaveData { Favorites = new List<string>(_favorites), Hidden = new List<string>(_hidden) }, (Formatting)1); _lastWrite = AtomicJson.QueueWrite(_lastWrite, SavePath, json, "BridgeFavoritesManager: save failed"); } internal static void FlushPendingWrites() { try { _lastWrite.Wait(TimeSpan.FromSeconds(2.0)); } catch { } } 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 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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) { BceConsole.LogWarning("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)) { BceConsole.LogWarning("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) { BceConsole.LogWarning("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 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) { if (!((Object)(object)btn == (Object)null) && !((Object)(object)btn.cosmeticAsset == (Object)null)) { bool isFav = BridgeFavoritesManager.IsFavorite(btn.cosmeticAsset); bool isHide = BridgeFavoritesManager.IsHidden(btn.cosmeticAsset); UpdateMarker(btn, isFav, isHide); } } internal static void UpdateMarker(MenuElementCosmeticButton btn, bool isFav, bool isHide) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_010c: 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; } Transform val = ((Component)btn).transform.Find("MHB_FavHideMarker"); if (!isFav && !isHide) { 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<RectTransform>(); 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<Image>(); ((Graphic)val4).raycastTarget = false; val4.preserveAspect = true; } else { val.SetAsLastSibling(); ApplyRect(((Component)val).GetComponent<RectTransform>()); val4 = ((Component)val).GetComponent<Image>(); } if (!((Object)(object)val4 == (Object)null)) { if (isFav) { 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(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_0214: Unknown result type (might be due to invalid IL or missing references) VariantCell component = ((Component)__instance).GetComponent<VariantCell>(); if ((Object)(object)component != (Object)null) { component.OnClick?.Invoke(); return false; } 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) { CosmeticGroupButton group = ((Component)__instance).GetComponent<CosmeticGroupButton>(); if ((Object)(object)group != (Object)null && group.IsActive && Plugin.MenuLibAvailable) { PopupUI.AfterMouseRelease((MonoBehaviour)(object)Plugin.Instance, delegate { CosmeticVariantPopup.Show(__instance, group); }); return false; } } 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 asset = __instance.cosmeticAsset; if ((Object)(object)asset == (Object)null) { return true; } if (flag4 && !flag && !flag2) { if (Plugin.EnableCosmeticCustomizer.Value && Plugin.MenuLibAvailable && BridgeIds.IsCustomizable(asset)) { bool flag5 = __instance.IsEquipped(); PopupUI.AfterMouseRelease((MonoBehaviour)(object)Plugin.Instance, delegate { CosmeticOverridePopup.Show(asset); }); return !flag5; } return true; } BridgeFavoritesManager.EnsureLoaded(); if (flag) { BridgeFavoritesManager.ToggleFavorite(asset); } else { BridgeFavoritesManager.ToggleHidden(asset); } try { __instance.soundClick.Play(MenuManager.instance.soundPosition, 1f, 1f, 1f, 1f); } catch (Exception ex) { BridgeLog.Trace("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) { BridgeLog.Trace("FavHideTogglePatch: animation skipped — " + ex2.Message); } FavHideMarkerHelper.UpdateMarker(__instance); return false; } } internal static class HhhCosmeticLoader { internal static readonly List<string> RegisteredAssetIds = new List<string>(); internal static readonly HashSet<string> WorldAssetIds = new HashSet<string>(); private static readonly Dictionary<string, string> _sourcePath = new Dictionary<string, string>(StringComparer.Ordinal); internal static readonly Dictionary<string, Texture2D> BridgeIconTextures = new Dictionary<string, Texture2D>(); private static readonly Dictionary<string, (CosmeticType VanillaType, OverrideCosmeticType OverrideType)> TagMap = new Dictionary<string, (CosmeticType, OverrideCosmeticType)> { ["head"] = ((CosmeticType)0, OverrideCosmeticType.Hat), ["neck"] = ((CosmeticType)30, OverrideCosmeticType.HeadBottom), ["body"] = ((CosmeticType)20, OverrideCosmeticType.BodyTop), ["hip"] = ((CosmeticType)21, OverrideCosmeticType.BodyBottom), ["rightarm"] = ((CosmeticType)1, OverrideCosmeticType.ArmRight), ["leftarm"] = ((CosmeticType)2, OverrideCosmeticType.ArmLeft), ["rightleg"] = ((CosmeticType)3, OverrideCosmeticType.LegRight), ["leftleg"] = ((CosmeticType)4, OverrideCosmeticType.LegLeft), ["world"] = ((CosmeticType)0, OverrideCosmeticType.World) }; private static readonly HashSet<string> ValidTags; private static readonly HashSet<string> _usedPrefabIds; private static readonly HashSet<string> _usedInternalNames; private static bool _seeding; private static HashSet<string>? _seedNames; private static bool _moreHeadFixDone; private static Rarity _lastAppliedRarity; private static bool? _lastAppliedTinting; private static readonly Dictionary<string, OverrideCosmeticType> _originalTypes; private static readonly Dictionary<string, bool> _originalTintable; internal static bool IsFromFolder(CosmeticAsset? asset, string folderName) { if ((Object)(object)asset == (Object)null || string.IsNullOrEmpty(folderName)) { return false; } if (_sourcePath.TryGetValue(asset.assetId, out string value)) { return value.IndexOf(folderName, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } internal static string? SourceModFolder(CosmeticAsset? asset) { if ((Object)(object)asset == (Object)null) { return null; } if (!_sourcePath.TryGetValue(asset.assetId, out string value) || string.IsNullOrEmpty(value)) { return null; } string[] array = value.Replace('\\', '/').Split('/'); for (int i = 0; i < array.Length - 1; i++) { if (string.Equals(array[i], "plugins", StringComparison.OrdinalIgnoreCase)) { if (array[i + 1].Length <= 0) { return null; } return array[i + 1]; } } return null; } public static void LoadAll() { BridgeBlacklist.EnsureLoaded(); _seeding = !BridgeBlacklist.ExistedAtStartup; _seedNames = (_seeding ? BridgeBlacklist.ReadMoreHeadNames() : null); 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) { BceConsole.LogWarning("SpecificFolders: '" + s + "' contained invalid path characters — changed to '" + text2 + "'"); } return text2; }) where s.Length > 0 select s).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); string[] matched = array.Where((string a) => files.Any((string f) => f.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray(); string[] array2 = array.Except<string>(matched, StringComparer.OrdinalIgnoreCase).ToArray(); if (matched.Length == 0) { BceConsole.LogWarning("SpecificFolders: none of the specified folders were found (" + string.Join(", ", array) + "). Loading all .hhh files instead"); } else { if (array2.Length != 0) { BceConsole.LogWarning("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(); BceConsole.LogInfo(string.Format("SpecificFolders: loaded from {0} — kept {1}/{2} files", string.Join(", ", matched), files.Length, num)); } } BceConsole.LogInfo($"Found {files.Length} .hhh file(s). Translating cosmetics from MoreHead to Vanilla REPO..."); int num2 = 0; int num3 = Math.Max(2, Environment.ProcessorCount); Queue<(string, Task<byte[]>)> queue = new Queue<(string, Task<byte[]>)>(num3); int num4 = 0; while (num4 < files.Length && queue.Count < num3) { string p = files[num4++]; queue.Enqueue((p, Task.Run(() => ReadBytesOrNull(p)))); } while (queue.Count > 0) { (string, Task<byte[]>) tuple = queue.Dequeue(); string item = tuple.Item1; Task<byte[]> item2 = tuple.Item2; byte[] result = item2.GetAwaiter().GetResult(); if (TryRegister(item, result)) { num2++; } if (num4 < files.Length) { string p2 = files[num4++]; queue.Enqueue((p2, Task.Run(() => ReadBytesOrNull(p2)))); } } int num5 = files.Length; int num6 = num5 - num2; BceConsole.LogInfo($"Done — {num2}/{num5} registered, {num6} error(s)"); if (_seeding) { BridgeBlacklist.Save(); } BridgeBlacklist.MirrorToMoreHead(); } private static byte[]? ReadBytesOrNull(string path) { try { FileInfo fileInfo = new FileInfo(path); return (fileInfo.Exists && fileInfo.Length >= 1024) ? File.ReadAllBytes(path) : null; } catch { return null; } } private static bool TryRegister(string path, byte[]? bytes = null) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: 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_031d: Unknown result type (might be due to invalid IL or missing references) FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists || fileInfo.Length < 1024) { BceConsole.LogWarning("Skipped (too small/missing): " + Path.GetFileName(path)); return false; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); ParseFileName(fileNameWithoutExtension, out string name, out string tag); if (!TagMap.TryGetValue(tag, out (CosmeticType, OverrideCosmeticType) value)) { return false; } CosmeticType item = value.Item1; AssetBundle val = ((bytes != null) ? AssetBundle.LoadFromMemory(bytes) : AssetBundle.LoadFromFile(path)); if ((Object)(object)val == (Object)null) { BceConsole.LogError("Failed to load bundle: " + fileNameWithoutExtension); return false; } GameObject val2 = LoadFirstPrefab(val); val.Unload(false); if ((Object)(object)val2 == (Object)null) { BceConsole.LogError("No GameObject in bundle: " + fileNameWithoutExtension); return false; } if (!val2.activeSelf) { val2.SetActive(true); } string name2 = ((Object)val2).name; ((Object)val2).name = EnsureUniqueId(name2, _usedPrefabIds); string text = name; name = EnsureUniqueId(name, _usedInternalNames); bool flag = ((Object)val2).name != name2; bool flag2 = name != text; if (flag && flag2 && name2 == text) { if (((Object)val2).name == name) { BceConsole.LogWarning("Duplicate name '" + name2 + "' — renamed internal and prefab to '" + name + "'.", ConsoleColor.DarkYellow); } else { BceConsole.LogWarning("Duplicate name '" + name2 + "' — prefab renamed to '" + ((Object)val2).name + "', internal id to '" + name + "'.", ConsoleColor.DarkYellow); } } else { if (flag) { BceConsole.LogWarning("Duplicate prefab name '" + name2 + "' → renamed to '" + ((Object)val2).name + "'", ConsoleColor.DarkYellow); } if (flag2) { BceConsole.LogWarning("Duplicate internal id '" + text + "' → renamed to '" + name + "'", ConsoleColor.DarkYellow); } } string text2 = "morehead-bridge:" + name.ToLowerInvariant(); bool flag3 = _seeding && _seedNames != null && _seedNames.Contains(name2); if (flag3) { BridgeBlacklist.Add(text2, name2); } bool flag4 = false; if (BridgeBlacklist.Contains(text2)) { if (Plugin.BridgeBlacklistMode.Value == BlacklistLoadMode.NotLoadIngame) { Object.Destroy((Object)(object)val2); return false; } flag4 = true; } Cosmetic val3 = val2.GetComponent<Cosmetic>(); if ((Object)(object)val3 == (Object)null) { val3 = val2.AddComponent<Cosmetic>(); } val3.type = item; PrefabRef val4 = NetworkPrefabs.RegisterNetworkPrefab("Cosmetics/" + ((Object)val2).name, val2); if (val4 == null) { BceConsole.LogError("Failed to register network prefab: " + name); return false; } CosmeticAsset val5 = ScriptableObject.CreateInstance<CosmeticAsset>(); ((Object)val5).name = name; val5.assetName = name2; val5.type = item; val5.prefab = val4; val5.assetId = text2; val5.rarity = Plugin.BridgeDefaultRarity.Value; val5.customTypeList = new List<Type>(); bool flag5 = BridgeTintHelper.DetectTintable(val2); _originalTintable[text2] = flag5; val5.tintable = Plugin.EnableBridgeTinting.Value && flag5; _originalTypes[text2] = value.Item2; CustomizerStore.ApplyIfPresent(val5); Cosmetics.RegisterCosmetic(val5); if (flag4 && flag3) { BridgeFavoritesManager.EnsureHidden(val5); } RegisteredAssetIds.Add(text2); _sourcePath[text2] = path; if (tag == "world") { WorldAssetIds.Add(text2); } Texture2D val6 = TryExtractIconTexture(val2); if ((Object)(object)val6 != (Object)null) { BridgeIconTextures[text2] = val6; } return true; } private static GameObject? LoadFirstPrefab(AssetBundle bundle) { string[] allAssetNames = bundle.GetAllAssetNames(); foreach (string text in allAssetNames) { GameObject val = bundle.LoadAsset<GameObject>(text); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static Texture2D? TryExtractIconTexture(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(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<string> used) { string text = baseName; int num = 1; while (!used.Add(text)) { text = $"{baseName}({num})"; num++; } return text; } internal static bool IsWorldAsset(CosmeticAsset? asset) { if ((Object)(object)asset != (Object)null && BridgeIds.IsBridgeAsset(asset)) { return WorldAssetIds.Contains(asset.assetId); } return false; } internal static bool TryGetOriginalType(string assetId, out OverrideCosmeticType type) { return _originalTypes.TryGetValue(assetId, out type); } internal static void RefreshTintableFlags() { if ((Object)(object)MetaManager.instance == (Object)null) { return; } bool value = Plugin.EnableBridgeTinting.Value; _lastAppliedTinting = value; foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets) { if (!((Object)(object)cosmeticAsset == (Object)null) && BridgeIds.IsBridgeAsset(cosmeticAsset) && (!CustomizerStore.TryGet(cosmeticAsset.assetId, out CosmeticOverrideData data) || !data.Tintable.HasValue) && _originalTintable.TryGetValue(cosmeticAsset.assetId, out var value2)) { cosmeticAsset.tintable = value && value2; } } } internal static void RefreshDefaultRarity() { //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)MetaManager.instance == (Object)null) { return; } Rarity rarity = (_lastAppliedRarity = Plugin.BridgeDefaultRarity.Value); foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets) { if (!((Object)(object)cosmeticAsset == (Object)null) && BridgeIds.IsBridgeAsset(cosmeticAsset) && (!CustomizerStore.TryGet(cosmeticAsset.assetId, out CosmeticOverrideData data) || !data.Rarity.HasValue)) { cosmeticAsset.rarity = rarity; } } } internal static bool TryGetDefaultTintable(CosmeticAsset asset, out bool tintable) { tintable = false; if ((Object)(object)asset == (Object)null || !BridgeIds.IsBridgeAsset(asset)) { return false; } if (!_originalTintable.TryGetValue(asset.assetId, out var value)) { return false; } tintable = Plugin.EnableBridgeTinting.Value && value; return true; } internal static void OnMenuOpen(MenuPageCosmetics page) { //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)MetaManager.instance == (Object)null)) { Rarity value = Plugin.BridgeDefaultRarity.Value; if (value != _lastAppliedRarity) { RefreshDefaultRarity(); } bool value2 = Plugin.EnableBridgeTinting.Value; if (_lastAppliedTinting != value2) { RefreshTintableFlags(); } if (!_moreHeadFixDone && (Plugin.RemoveBridgePhysics.Value || Plugin.LoopBridgeAnimation.Value)) { _moreHeadFixDone = true; ((MonoBehaviour)page).StartCoroutine(CosmeticPrefabFixer.TryFixMoreHeadPrefabsAsync()); } } } 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.BridgeDefaultRarity.Value; if (_originalTypes.TryGetValue(asset.assetId, out var value)) { (CosmeticType cosmeticType, bool isWorld) tuple = CustomizerStore.MapOverrideToVanilla(value); CosmeticType item = tuple.cosmeticType; bool item2 = tuple.isWorld; asset.type = item; if (item2) { WorldAssetIds.Add(asset.assetId); } else { WorldAssetIds.Remove(asset.assetId); } if (_originalTintable.TryGetValue(asset.assetId, out var value2)) { asset.tintable = Plugin.EnableBridgeTinting.Value && value2; } } } static HhhCosmeticLoader() { //IL_013f: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = new HashSet<string>(); foreach (string key in TagMap.Keys) { hashSet.Add(key); } ValidTags = hashSet; _usedPrefabIds = new HashSet<string>(); _usedInternalNames = new HashSet<string>(); _lastAppliedRarity = (Rarity)(-1); _lastAppliedTinting = null; _originalTypes = new Dictionary<string, OverrideCosmeticType>(); _originalTintable = new Dictionary<string, bool>(); } } internal static class BatchIconGenerator { private static bool _isRunning; private static bool _didStartOnce; private static int _progressDone; private static int _progressFailed; private static int _progressTotal; private static RawImage? _avatarRawImage; internal static Action? OnBatchCompleted; private static FieldInfo? _menuPageStateField; private static FieldInfo? _iconCreationAvatarField; private static readonly string[] FaceMeshNames = new string[5] { "mesh_eye_l", "mesh_eye_r", "mesh_pupil_l", "mesh_pupil_r", "mesh_head_top" }; private static FieldInfo? _equipLerpField; internal static bool IsGenerating => _isRunning; internal static string ProgressText { get; private set; } = ""; internal static int ProgressDone => _progressDone; internal static int ProgressFailed => _progressFailed; internal static int ProgressTotal => _progressTotal; internal static void TryStart(MonoBehaviour host) { if (!_isRunning && Plugin.GenerateAllIcons.Value) { if (_didStartOnce) { BceConsole.LogWarning("GenerateAllIcons: previous batch was interrupted. Resuming — only icons still missing will be generated"); } _isRunning = true; MenuPage component = ((Component)host).GetComponent<MenuPage>(); host.StartCoroutine(Run(component)); } } internal static void NotifyMenuClosed() { if (_isRunning) { _isRunning = false; if (Plugin.MenuLibAvailable) { PopupDestroy(); } if ((Object)(object)_avatarRawImage != (Object)null) { ((Behaviour)_avatarRawImage).enabled = true; _avatarRawImage = null; } if ((Object)(object)MetaManager.instance != (Object)null) { MetaManager.instance.CosmeticPreviewSet(false); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); } WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true); ProgressText = ""; int num = _progressTotal - _progressDone - _progressFailed; BceConsole.LogWarning("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num} still to go). " + "Reopen the menu to continue. (Your equipped cosmetics were not modified.)"); } } private static IEnumerator Run(MenuPage? cosmeticsMenuPage) { _didStartOnce = true; if ((object)_menuPageStateField == null) { _menuPageStateField = AccessTools.Field(typeof(MenuPage), "currentPageState"); } if ((Object)(object)cosmeticsMenuPage != (Object)null && _menuPageStateField != null) { float elapsed = 0f; while (elapsed < 3f) { PageState val = (PageState)(_menuPageStateField.GetValue(cosmeticsMenuPage) ?? ((object)(PageState)1)); if ((int)val == 1) { break; } elapsed += Time.unscaledDeltaTime; yield return null; } } else { yield return (object)new WaitForSecondsRealtime(0.5f); } if ((Object)(object)MetaManager.instance == (Object)null) { _isRunning = false; yield break; } List<CosmeticAsset> work = new List<CosmeticAsset>(); foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets) { if (!((Object)(object)cosmeticAsset == (Object)null) && cosmeticAsset.assetId != null && BridgeIds.IsBridgeAsset(cosmeticAsset) && !(cosmeticAsset.assetId == MiniSemibotCosmetic.AssetId) && !IconCapture.HasCache(cosmeticAsset)) { work.Add(cosmeticAsset); } } BceConsole.LogInfo($"GenerateAllIcons: {work.Count} icon(s) to generate.", ConsoleColor.DarkGreen); if (work.Count == 0) { _isRunning = false; Plugin.GenerateAllIcons.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); yield break; } _progressDone = 0; _progressFailed = 0; _progressTotal = work.Count; _avatarRawImage = FindAvatarRawImage(); if ((Object)(object)_avatarRawImage != (Object)null && Plugin.HideAvatarWhileGenerating.Value) { ((Behaviour)_avatarRawImage).enabled = false; } if (Plugin.MenuLibAvailable) { PopupOpen(); } bool interrupted = true; try { foreach (CosmeticAsset asset in work) { if (!_isRunning || (Object)(object)MetaManager.instance == (Object)null) { break; } int num = MetaManager.instance.cosmeticAssets.IndexOf(asset); if (num < 0) { _progressFailed++; continue; } MetaManager.instance.cosmeticEquippedPreview.Clear(); if (!Plugin.HideClothesWhileGenerating.Value && MetaManager.instance.cosmeticEquipped != null) { foreach (int item in MetaManager.instance.cosmeticEquipped) { MetaManager.instance.cosmeticEquippedPreview.Add(item); } } if (!MetaManager.instance.cosmeticEquippedPreview.Contains(num)) { MetaManager.instance.cosmeticEquippedPreview.Add(num); } if (MetaManager.instance.colorsEquipped != null) { MetaManager.instance.colorsEquippedPreview = (Plugin.ResetBodyColorWhileGenerating.Value ? new int[MetaManager.instance.colorsEquipped.Length] : ((int[])MetaManager.instance.colorsEquipped.Clone())); } MetaManager.instance.CosmeticPreviewSet(true); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: false); if (HhhCosmeticLoader.IsWorldAsset(asset)) { WorldCosmeticsSetupPatch.SetWorldAssetActive(asset, active: true); } Cosmetic[] snapshot = Object.FindObjectsOfType<Cosmetic>(); SkipEquipAnimationFor(asset, snapshot); yield return null; for (int guard = 0; guard < 3; guard++) { if (IsAnimComplete(asset)) { break; } snapshot = Object.FindObjectsOfType<Cosmetic>(); if (IsAnimComplete(asset, snapshot)) { break; } yield return null; } PlayerAvatarVisuals iconVisuals = Object.FindObjectOfType<PlayerAvatarMenuHover>()?.playerAvatarMenu?.playerVisuals ?? PlayerAvatarMenu.instance?.playerVisuals; PartShrinkerBridge.ResyncFromMountedCosmetics(iconVisuals); PartShrinkerBridge.SetAllHiddenPartsEnabled(enabled: false); MirrorFaceMeshesToAllAvatars(iconVisuals); ResetCustomConditionsForCapture(); yield return null; MirrorFaceMeshesToAllAvatars(iconVisuals); SnapHideConditions(); yield return (object)new WaitForEndOfFrame(); MirrorFaceMeshesToAllAvatars(iconVisuals); SnapHideConditions(); yield return (object)new WaitForEndOfFrame(); if ((Object)(object)MetaManager.instance == (Object)null) { PartShrinkerBridge.SetAllHiddenPartsEnabled(enabled: true); break; } bool flag = IconCapture.TryCapture(asset); PartShrinkerBridge.SetAllHiddenPartsEnabled(enabled: true); if (flag) { _progressDone++; } else { _progressFailed++; } int num2 = _progressDone + _progressFailed; int num3 = ((_progressTotal > 0) ? (num2 * 100 / _progressTotal) : 0); ProgressText = ((_progressFailed > 0) ? $"Generating icons: {num2}/{_progressTotal} ({num3}%) | {_progressFailed} failed" : $"Generating icons: {num2}/{_progressTotal} ({num3}%)"); if (Plugin.MenuLibAvailable) { PopupUpdate(); } MetaManager.instance.CosmeticPreviewSet(false); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true); int num4 = _progressDone + _progressFailed; if (num4 % 50 == 0) { BceConsole.LogInfo($"Batch progress: {num4}/{work.Count} " + $"({_progressDone} ok, {_progressFailed} failed)", ConsoleColor.DarkGreen); } } if (_isRunning) { interrupted = false; } } finally { if (Plugin.MenuLibAvailable) { PopupClose(); } if (_isRunning) { _isRunning = false; if (interrupted) { int num5 = _progressTotal - _progressDone - _progressFailed; BceConsole.LogWarning("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num5} 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); if ((Object)(object)_avatarRawImage != (Object)null) { ((Behaviour)_avatarRawImage).enabled = true; _avatarRawImage = null; } ProgressText = ""; } if (!interrupted) { Plugin.GenerateAllIcons.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); _didStartOnce = false; BceConsole.LogInfo($"GenerateAllIcons done — {_progressDone} captured, " + $"{_progressFailed} failed.", ConsoleColor.DarkGreen); Action onBatchCompleted = OnBatchCompleted; OnBatchCompleted = null; onBatchCompleted?.Invoke(); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void PopupOpen() { BatchIconPopup.Open(); } [MethodImpl(MethodImplOptions.NoInlining)] private static void PopupUpdate() { BatchIconPopup.UpdateProgress(); } [MethodImpl(MethodImplOptions.NoInlining)] private static void PopupClose() { BatchIconPopup.Close(); } [MethodImpl(MethodImplOptions.NoInlining)] private static void PopupDestroy() { BatchIconPopup.Destroy(); } internal static bool OnPopupEscape() { if ((Object)(object)_avatarRawImage != (Object)null) { ((Behaviour)_avatarRawImage).enabled = true; _avatarRawImage = null; } _isRunning = false; int num = _progressTotal - _progressDone - _progressFailed; BceConsole.LogWarning("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num} still to go). " + "Reopen the menu to continue. (Your equipped cosmetics were not modified.)"); return true; } private static RawImage? FindAvatarRawImage() { PlayerAvatarMenuHover val = Object.FindObjectOfType<PlayerAvatarMenuHover>(); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).GetComponent<RawImage>(); } private static void SkipEquipAnimationFor(CosmeticAsset asset, Cosmetic[]? snapshot = null) { if ((object)_iconCreationAvatarField == null) { _iconCreationAvatarField = AccessTools.Field(typeof(Cosmetic), "iconCreationAvatar"); } if (_iconCreationAvatarField == null) { return; } Cosmetic[] array = snapshot ?? Object.FindObjectsOfType<Cosmetic>(); Cosmetic[] array2 = array; foreach (Cosmetic val in array2) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset) { _iconCreationAvatarField.SetValue(val, true); } } } private static void ResetCustomConditionsForCapture() { PlayerCosmetics[] array = Object.FindObjectsOfType<PlayerCosmetics>(true); foreach (PlayerCosmetics val in array) { try { val.conditionsCustom.Clear(); val.ConditionUpdateAll(); } catch (Exception ex) { BridgeLog.Debug("BatchIconGenerator: condition reset failed — " + ex.Message); } } } private static void SnapHideConditions() { CosmeticHideCondition[] array = Object.FindObjectsOfType<CosmeticHideCondition>(true); foreach (CosmeticHideCondition val in array) { try { val.AnimateInstant(); } catch { } } } private static void MirrorFaceMeshesToAllAvatars(PlayerAvatarVisuals? reference) { if ((Object)(object)reference == (Object)null) { return; } Dictionary<string, bool> dictionary = new Dictionary<string, bool>(); MeshRenderer[] componentsInChildren = ((Component)reference).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val in componentsInChildren) { string name = ((Object)((Component)val).gameObject).name; if (Array.IndexOf(FaceMeshNames, name) >= 0 && !dictionary.ContainsKey(name)) { dictionary[name] = ((Renderer)val).enabled; } } if (dictionary.Count == 0) { return; } PlayerAvatarVisuals[] array = Object.FindObjectsOfType<PlayerAvatarVisuals>(true); foreach (PlayerAvatarVisuals val2 in array) { MeshRenderer[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val3 in componentsInChildren2) { if (dictionary.TryGetValue(((Object)((Component)val3).gameObject).name, out var value) && ((Renderer)val3).enabled != value) { ((Renderer)val3).enabled = value; } } } } private static bool IsAnimComplete(CosmeticAsset asset, Cosmetic[]? snapshot = null) { if ((object)_equipLerpField == null) { _equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp"); } if (_equipLerpField == null) { return true; } Cosmetic[] array = snapshot ?? Object.FindObjectsOfType<Cosmetic>(); Cosmetic[] array2 = array; foreach (Cosmetic val in array2) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset && (float)(_equipLerpField.GetValue(val) ?? ((object)1f)) < 1f) { return false; } } return true; } } internal static class BatchIconPopup { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ShouldCloseMenuDelegate <>9__12_1; internal bool <Create>b__12_1() { Clear(); return BatchIconGenerator.OnPopupEscape(); } } private static REPOPopupPage? _page; private static TextMeshProUGUI? _progressLabel; private static TextMeshProUGUI? _hintLabel; private const float GeneratingLabelH = 140f; private const float NumbersLabelH = 40f; private static string[] _tips = Array.Empty<string>(); private static int _currentTipIndex; internal static void Open() { _page = Create(); _page.OpenPage(false); UpdateProgress(); } internal static void Close() { if (!((Object)(object)_page == (Object)null)) { _page.ClosePage(false); Clear(); } } internal static void Destroy() { if (!((Object)(object)_page == (Object)null)) { Object.Destroy((Object)(object)((Component)_page).gameObject); Clear(); } } private static void Clear() { _page = null; _progressLabel = null; _hintLabel = null; } internal static void UpdateProgress() { if (!((Object)(object)_progressLabel == (Object)null)) { int num = BatchIconGenerator.ProgressDone + BatchIconGenerator.ProgressFailed; int progressTotal = BatchIconGenerator.ProgressTotal; int num2 = ((progressTotal > 0) ? (num * 100 / progressTotal) : 0); string text = $"{num} / {progressTotal} ({num2}%)"; if (BatchIconGenerator.ProgressFailed > 0) { text += $"\n{BatchIconGenerator.ProgressFailed} failed"; } ((TMP_Text)_progressLabel).text = text; } } private static REPOPopupPage Create() { //IL_001c: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown REPOPopupPage val = MenuAPI.CreateREPOPopupPage("Generating Icons", false, true, 0f, (Vector2?)new Vector2(-120f, 0f)); PopupScrollGuard popupScrollGuard = ((Component)val).gameObject.AddComponent<PopupScrollGuard>(); popupScrollGuard.Init(((Component)val).transform); _tips = BuildTipList(); _currentTipIndex = 0; TextMeshProUGUI capturedLabel = null; TextMeshProUGUI capturedHint = null; REPOLabel capturedParent = null; float maskW = val.maskRectTransform.sizeDelta.x; val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) capturedParent = MenuAPI.CreateREPOLabel("Starting...", sv, default(Vector2)); capturedLabel = capturedParent.labelTMP; if ((Object)(object)capturedLabel != (Object)null) { ((TMP_Text)capturedLabel).fontSize = 16f; ((TMP_Text)capturedLabel).alignment = (TextAlignmentOptions)514; ((TMP_Text)capturedLabel).enableWordWrapping = true; float num = ((maskW > 0f) ? maskW : 200f) - 10f; ((Component)capturedParent).GetComponent<RectTransform>().sizeDelta = new Vector2(num, 140f); GameObject val3 = Object.Instantiate<GameObject>(((Component)capturedLabel).gameObject, ((TMP_Text)capturedLabel).transform.parent); ((Object)val3).name = "HintLabel"; capturedHint = val3.GetComponent<TextMeshProUGUI>(); ((TMP_Text)capturedLabel).rectTransform.sizeDelta = new Vector2(num, 40f); ((Transform)((TMP_Text)capturedLabel).rectTransform).localPosition = new Vector3(0f, 100f); ((TMP_Text)capturedHint).rectTransform.sizeDelta = new Vector2(num, 100f); ((Transform)((TMP_Text)capturedHint).rectTransform).localPosition = Vector3.zero; } return ((Component)capturedParent).GetComponent<RectTransform>(); }, 0f, 0f); if ((Object)(object)capturedParent != (Object)null) { REPOScrollViewElement component = ((Component)capturedParent).GetComponent<REPOScrollViewElement>(); if ((Object)(object)component != (Object)null) { ((MonoBehaviour)val).StartCoroutine(CenterScrollElement(val, component)); } } _progressLabel = capturedLabel; _hintLabel = capturedHint; UpdateHintLabel(); if (_tips.Length > 1) { ((MonoBehaviour)val).StartCoroutine(CycleTips()); } object obj = <>c.<>9__12_1; if (obj == null) { ShouldCloseMenuDelegate val2 = delegate { Clear(); return BatchIconGenerator.OnPopupEscape(); }; <>c.<>9__12_1 = val2; obj = (object)val2; } val.onEscapePressed = (ShouldCloseMenuDelegate)obj; return val; } private static void UpdateHintLabel() { if (!((Object)(object)_hintLabel == (Object)null)) { string text = "Press ESC to stop early"; if (_tips.Length != 0) { text = text + "\n\n" + _tips[_currentTipIndex]; } ((TMP_Text)_hintLabel).text = text; } } private static IEnumerator CycleTips() { while (BatchIconGenerator.IsGenerating && (Object)(object)_progressLabel != (Object)null) { yield return (object)new WaitForSecondsRealtime(10f); if (!BatchIconGenerator.IsGenerating || (Object)(object)_progressLabel == (Object)null) { break; } _currentTipIndex = (_currentTipIndex + 1) % _tips.Length; UpdateHintLabel(); } } private static string[] BuildTipList() { List<string> list = new List<string> { "Tip: close the menu anytime to pause — reopen it to resume where you left off" }; if (Plugin.AutoCaptureIcons.Value) { list.Add("Tip: hovering a cosmetic with no icon captures it automatically"); } return list.ToArray(); } private static IEnumerator CenterScrollElement(REPOPopupPage popup, REPOScrollViewElement element) { yield return null; if ((Object)(object)_progressLabel != (Object)null) { ((Transform)((TMP_Text)_progressLabel).rectTransform).localPosition = new Vector3(0f, 100f); } float y = popup.maskRectTransform.sizeDelta.y; Rect rect = element.rectTransform.rect; float height = ((Rect)(ref rect)).height; if (height > 0f) { element.topPadding = Mathf.Max(0f, (y - height) / 2f); } } } internal static class IconCacheCleaner { internal static void Run() { if (!Plugin.DeleteIconCache.Value) { return; } try { string cacheDir = IconCapture.CacheDir; if (!Directory.Exists(cacheDir)) { BceConsole.LogInfo("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<string> hashSet = new HashSet<string>(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 text3 = "MHB_MiniMe".ToLowerInvariant(); string[] files = Directory.GetFiles(cacheDir, "*.png"); foreach (string text4 in files) { string name = Path.GetFileNameWithoutExtension(text4).ToLowerInvariant(); if (name == text3) { num4++; continue; } if (!hashSet.Contains(name)) { num4++; continue; } if (array.Length != 0 && !array.Any((string f) => name.Contains(f))) { num4++; continue; } try { File.Delete(text4); num3++; } catch (Exception ex) { BceConsole.LogWarning("Failed to delete '" + text4 + "': " + ex.Message); } } BceConsole.LogInfo($"DeleteIconCache: removed {num3} bridge icon(s), kept {num4}. " + "Filter: " + ((array.Length == 0) ? "(all bridge icons)" : string.Join(",", array))); if (num3 > 0) { IconCapture.InvalidateAll(); } } catch (Exception ex2) { BceConsole.LogError("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 HashSet<string>? _knownCached; 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; } BceConsole.LogInfo("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++; BceConsole.LogWarning("IconCapture: could not migrate '" + Path.GetFileName(text) + "': " + ex.Message); } } try { if (Directory.GetFiles(path).Length == 0) { Directory.Delete(path, recursive: false); } } catch { } BceConsole.LogInfo($"IconCapture: cache migration done — {num} moved, {num2} failed"); } catch (Exception ex2) { BceConsole.LogWarning("IconCapture: cache migration failed: " + ex2.Message); } } internal static string CachePathFor(CosmeticAsset asset) { string name = ((Object)asset).name.Replace("(Clone)", "").Trim().ToLowerInvariant(); return Path.Combine(CacheDir, MakeSafeFileName(name) + ".png"); } private static string MakeSafeFileName(string name) { name = name.Replace('/', '_').Replace('\\', '_').Replace("..", "__"); if (name.Length != 0) { return name; } return "_unnamed"; } private static void EnsureCacheSeeded() { if (_knownCached != null) { return; } _knownCached = new HashSet<string>(StringComparer.OrdinalIgnoreCase); try { if (Directory.Exists(CacheDir)) { string[] files = Directory.GetFiles(CacheDir, "*.png"); foreach (string item in files) { _knownCached.Add(item); } } } catch { } } internal static void MarkCached(string path) { EnsureCacheSeeded(); _knownCached.Add(path); } internal static bool HasCache(CosmeticAsset asset) { EnsureCacheSeeded(); return _knownCached.Contains(CachePathFor(asset)); } internal static void MarkCached(CosmeticAsset asset) { EnsureCacheSeeded(); _knownCached.Add(CachePathFor(asset)); } internal static void DeleteCache(CosmeticAsset asset) { EnsureCacheSeeded(); string text = CachePathFor(asset); _knownCached.Remove(text); try { if (File.Exists(text)) { File.Delete(text); } } catch (Exception ex) { BceConsole.LogWarning("IconCapture: could not delete '" + Path.GetFileName(text) + "': " + ex.Message); } if ((Object)(object)asset.icon != (Object)null) { Object.Destroy((Object)(object)asset.icon); asset.icon = null; } CosmeticHoverPatch.Invalidate(asset); CosmeticsMenuStartPatch.RefreshToolsButtons?.Invoke(); BridgeLog.Trace("IconCapture: deleted cached icon for '" + ((Object)asset).name + "'"); } private static RenderTexture? FindActiveAvatarRT() { PlayerAvatarMenuHover val = Object.FindObjectOfType<PlayerAvatarMenuHover>(); if ((Object)(object)val == (Object)null) { return null; } if ((object)_renderTextureInstanceField == null) { _renderTextureInstanceField = AccessTools.Field(typeof(PlayerAvatarMenuHover), "renderTextureInstance"); } if (_renderTextureInstanceField == null) { BceConsole.LogWarning("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<RawImage>(); 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Expected O, but got Unknown if ((Object)(object)asset == (Object)null) { return false; } if (HasCache(asset)) { return false; } if (BridgeIds.IsBridgeAsset(asset) && asset.assetId != MiniSemibotCosmet