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.1.0
MoreHeadBridge.dll
Decompiled 4 days 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.Reflection.Emit; 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.Audio; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.Localization; 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.1.0.0")] [assembly: AssemblyInformationalVersion("3.1.0+0c5d4add70b408fc5521df774f732d75ac7375fb")] [assembly: AssemblyProduct("MoreHeadBridge")] [assembly: AssemblyTitle("MoreHeadBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.1.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 _lastCustomizerFrame = int.MinValue; private static CosmeticClickAction _shortcut = CosmeticClickAction.Equip; internal static void RunShortcut(MenuElementCosmeticButton button, CosmeticClickAction action) { _shortcut = action; try { button.ToggleCosmetic(); } finally { _shortcut = CosmeticClickAction.Equip; } } [HarmonyPrefix] private static bool Prefix(MenuElementCosmeticButton __instance) { //IL_0224: 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; } (MenuClickModifier fav, MenuClickModifier hide, MenuClickModifier cust) tuple = MenuClickModifiers.Effective(); MenuClickModifier item = tuple.fav; MenuClickModifier item2 = tuple.hide; MenuClickModifier item3 = tuple.cust; bool flag = _shortcut == CosmeticClickAction.Favorite || MenuClickModifiers.Held(item); bool flag2 = _shortcut == CosmeticClickAction.Hide || MenuClickModifiers.Held(item2); bool flag3 = MenuClickModifiers.Held(item3); if (flag3) { _lastCustomizerFrame = Time.frameCount; } bool flag4 = _shortcut == CosmeticClickAction.Customize || flag3 || (_lastCustomizerFrame >= 0 && Time.frameCount - _lastCustomizerFrame <= 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); } SemiFunc.GamepadRumbleThockBigButton(); 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 text5 = new string(s.Where((char c) => !invalidChars.Contains(c)).ToArray()); if (text5 != s) { BceConsole.LogWarning("SpecificFolders: '" + s + "' contained invalid path characters — changed to '" + text5 + "'"); } return text5; }) 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 text2 = files[num4++]; queue.Enqueue((text2, ReadAhead(text2))); } 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 text3 = files[num4++]; queue.Enqueue((text3, ReadAhead(text3))); } } int num5 = files.Length; int num6 = num5 - num2; string text4 = ((MoreHeadPrefabs.Cloned > 0) ? $", {MoreHeadPrefabs.Cloned} reused from MoreHead" : ""); BceConsole.LogInfo($"Done — {num2}/{num5} registered, {num6} error(s){text4}"); if (_seeding) { BridgeBlacklist.Save(); } BridgeBlacklist.MirrorToMoreHead(); } private static Task<byte[]?> ReadAhead(string path) { if (!MoreHeadPrefabs.Has(path) && !HhhPreloader.Active) { return Task.Run(() => ReadBytesOrNull(path)); } return Task.FromResult<byte[]>(null); } 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_026b: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: 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; GameObject val = MoreHeadPrefabs.Clone(path) ?? OpenBundle(path, bytes, fileNameWithoutExtension); if ((Object)(object)val == (Object)null) { return false; } if (!val.activeSelf) { val.SetActive(true); } string name2 = ((Object)val).name; ((Object)val).name = EnsureUniqueId(name2, _usedPrefabIds); string text = name; name = EnsureUniqueId(name, _usedInternalNames); bool flag = ((Object)val).name != name2; bool flag2 = name != text; if (flag && flag2 && name2 == text) { if (((Object)val).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)val).name + "', internal id to '" + name + "'.", ConsoleColor.DarkYellow); } } else { if (flag) { BceConsole.LogWarning("Duplicate prefab name '" + name2 + "' → renamed to '" + ((Object)val).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)val); return false; } flag4 = true; } Cosmetic val2 = val.GetComponent<Cosmetic>(); if ((Object)(object)val2 == (Object)null) { val2 = val.AddComponent<Cosmetic>(); } val2.type = item; PrefabRef val3 = NetworkPrefabs.RegisterNetworkPrefab("Cosmetics/" + ((Object)val).name, val); if (val3 == null) { BceConsole.LogError("Failed to register network prefab: " + name); return false; } CosmeticAsset val4 = ScriptableObject.CreateInstance<CosmeticAsset>(); ((Object)val4).name = name; val4.assetName = name2; val4.type = item; val4.prefab = val3; val4.assetId = text2; val4.rarity = Plugin.BridgeDefaultRarity.Value; val4.customTypeList = new List<Type>(); bool flag5 = BridgeTintHelper.DetectTintable(val); _originalTintable[text2] = flag5; val4.tintable = Plugin.EnableBridgeTinting.Value && flag5; _originalTypes[text2] = value.Item2; CustomizerStore.ApplyIfPresent(val4); Cosmetics.RegisterCosmetic(val4); if (flag4 && flag3) { BridgeFavoritesManager.EnsureHidden(val4); } RegisteredAssetIds.Add(text2); _sourcePath[text2] = path; if (tag == "world") { WorldAssetIds.Add(text2); } Texture2D val5 = TryExtractIconTexture(val); if ((Object)(object)val5 != (Object)null) { BridgeIconTextures[text2] = val5; } return true; } private static GameObject? OpenBundle(string path, byte[]? bytes, string fileName) { AssetBundle val = ((bytes != null) ? AssetBundle.LoadFromMemory(bytes) : AssetBundle.LoadFromFile(path)); if ((Object)(object)val == (Object)null) { BceConsole.LogError("Failed to load bundle: " + fileName); return null; } GameObject val2 = LoadFirstPrefab(val); val.Unload(false); if ((Object)(object)val2 == (Object)null) { BceConsole.LogError("No GameObject in bundle: " + fileName); } return val2; } 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 HhhPreloader { internal const string OptionKey = "ParallelCosmeticLoading"; internal const bool EnabledByDefault = true; private const int MinFiles = 16; private const int MaxOpenAtOnce = 24; private const long MaxOpenBytes = 134217728L; private const int MaxStops = 3; private static readonly Dictionary<string, (AssetBundleCreateRequest Request, long Size)> _inFlight = new Dictionary<string, (AssetBundleCreateRequest, long)>(StringComparer.OrdinalIgnoreCase); private static readonly List<(string Path, long Size)> _files = new List<(string, long)>(); private static ManualLogSource? _log; private static Harmony? _harmony; private static int _next; private static int _openAtOnce; private static int _served; private static int _stops; private static long _openBytes; private static bool _started; internal static bool Active { get { if (_harmony != null) { if (_next >= _files.Count) { return _inFlight.Count > 0; } return true; } return false; } } internal static void Begin(ManualLogSource log) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown _log = log; try { int processorCount = Environment.ProcessorCount; if (processorCount >= 2 && OptionEnabled()) { List<(string, long)> list = FilesInOpeningOrder(); if (list.Count >= 16) { _files.AddRange(list); _openAtOnce = Mathf.Min(24, processorCount * 4); _harmony = new Harmony("0Xuaun.MoreHeadBridge.Preload"); _harmony.Patch((MethodBase)AccessTools.Method(typeof(AssetBundle), "LoadFromFile", new Type[1] { typeof(string) }, (Type[])null), new HarmonyMethod(typeof(HhhPreloader), "LoadFromFilePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } catch (Exception ex) { log.LogWarning((object)("Could not start: " + ex.Message)); Finish(); } } internal static void Finish() { if (_harmony == null) { return; } try { CloseInFlight(); _files.Clear(); _harmony.UnpatchSelf(); } catch (Exception ex) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("Could not finish: " + ex.Message)); } } _harmony = null; if (_served > 0) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)$"Opened {_served} .hhh file(s) in the background, up to {_openAtOnce} at a time"); } } } private static bool LoadFromFilePrefix(string path, ref AssetBundle? __result) { if (_files.Count == 0 || path == null || !path.EndsWith(".hhh", StringComparison.OrdinalIgnoreCase)) { return true; } try { if (!_started && !StartAt(path)) { return true; } if (_inFlight.TryGetValue(path, out (AssetBundleCreateRequest, long) value)) { _inFlight.Remove(path); _openBytes -= value.Item2; AssetBundle assetBundle = value.Item1.assetBundle; if ((Object)(object)assetBundle != (Object)null) { __result = assetBundle; _served++; TopUp(); return false; } } Stop(path); } catch (Exception ex) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("Error on '" + Path.GetFileName(path) + "': " + ex.Message)); } _files.Clear(); } return true; } private static bool StartAt(string path) { int num = _files.FindIndex(((string Path, long Size) file) => string.Equals(file.Path, path, StringComparison.OrdinalIgnoreCase)); if (num < 0) { return false; } _started = true; _next = num; TopUp(); return true; } private static void TopUp() { while (_inFlight.Count < _openAtOnce && _next < _files.Count) { var (text, num) = _files[_next]; if (_inFlight.Count > 0 && _openBytes + num > 134217728) { break; } _next++; _openBytes += num; _inFlight[text] = (AssetBundle.LoadFromFileAsync(text), num); } } private static void Stop(string path) { bool flag = _next >= _files.Count && _inFlight.Count == 0; int num = CloseInFlight(); _started = false; if (flag) { _files.Clear(); } else if (num > 0 || ++_stops > 3) { _files.Clear(); ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("Stopped at '" + Path.GetFileName(path) + "': the remaining .hhh files load one by one")); } } } private static int CloseInFlight() { (AssetBundleCreateRequest, long)[] array = _inFlight.Values.ToArray(); _inFlight.Clear(); _openBytes = 0L; int num = 0; (AssetBundleCreateRequest, long)[] array2 = array; for (int i = 0; i < array2.Length; i++) { AssetBundleCreateRequest item = array2[i].Item1; AssetBundle assetBundle = item.assetBundle; if ((Object)(object)assetBundle != (Object)null) { assetBundle.Unload(true); } else { num++; } } return num; } private static bool OptionEnabled() { string path = Path.Combine(Paths.ConfigPath, "Xuaun.MoreHeadBridge.cfg"); if (!File.Exists(path)) { return true; } string[] array = File.ReadAllLines(path); foreach (string text in array) { if (text.StartsWith("ParallelCosmeticLoading", StringComparison.Ordinal)) { int num = text.IndexOf('='); if (num >= 0 && bool.TryParse(text.Substring(num + 1).Trim(), out var result)) { return result; } } } return true; } private static List<(string Path, long Size)> FilesInOpeningOrder() { List<string> list = new List<string>(); string[] files = Directory.GetFiles(Paths.PluginPath, "MoreHead.dll", SearchOption.AllDirectories); foreach (string path in files) { string path2 = Path.Combine(Path.GetDirectoryName(path), "Decorations"); if (Directory.Exists(path2)) { list.AddRange(Directory.GetFiles(path2, "*.hhh")); } } list.AddRange(Directory.GetFiles(Paths.PluginPath, "*.hhh", SearchOption.AllDirectories)); return (from file in list.Distinct() select (Path: file, Size: new FileInfo(file).Length) into file where file.Size >= 1024 select file).ToList(); } } [BepInPlugin("0Xuaun.MoreHeadBridge.Preload", "MoreHead Bridge Preload", "3.1.0")] public sealed class HhhPreloadPlugin : BaseUnityPlugin { private void Awake() { HhhPreloader.Begin(((BaseUnityPlugin)this).Logger); } private void Start() { HhhPreloader.Finish(); } } 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++; LogFailedIcon(asset); 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 || IconCapture.HasCache(asset)) { _progressDone++; } else { _progressFailed++; LogFailedIcon(asset); } 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(); } } private static void LogFailedIcon(CosmeticAsset asset) { OverrideCosmeticType effectiveType = CustomizerStore.GetEffectiveType(asset); string text = ((effectiveType == OverrideCosmeticType.World) ? "WORLD" : $"{CustomizerStore.GetMainForType(effectiveType).ToString().ToUpperInvariant()} / {effectiveType}"); BceConsole.LogWarning("GenerateAllIcons: no icon for '" + (string.IsNullOrEmpty(asset.assetName) ? ((Object)asset).name : asset.assetName) + "' (" + text + ")"); } [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