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 DrawableSuits v0.5.77
BepInEx/plugins/DrawableSuits/DrawableSuits.dll
Decompiled 17 hours 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.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.UI; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DrawableSuits")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+4aec450a9737f33a67a55ab1962cf6ed017afa14")] [assembly: AssemblyProduct("DrawableSuits")] [assembly: AssemblyTitle("DrawableSuits")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DrawableSuits { internal static class DrawableSuitDesignCode { [Serializable] internal sealed class Payload { public int formatVersion; public string modVersion; public string designName; public string baseSuitName; public int sourceSuitId; public int width; public int height; public string exportedUtc; public string pngBase64; } internal struct CodeInfo { internal int FormatVersion; internal string DesignName; internal string BaseSuitName; internal int SourceSuitId; internal int Width; internal int Height; internal int PngBytes; internal int JsonBytes; internal int PayloadBytes; internal int CompressedBytes; internal int CodeLength; } internal const string PrefixV1 = "DSUIT1:"; internal const string PrefixV2 = "DSUIT2:"; internal const string Prefix = "DSUIT2:"; private const int FormatVersionV1 = 1; private const int FormatVersionV2 = 2; private const int MaxDecodedJsonBytes = 25165824; private const int MaxDecodedBinaryBytes = 18874368; private const int MaxPngBytes = 16777216; internal static bool HasSupportedPrefix(string code) { if (string.IsNullOrWhiteSpace(code)) { return false; } string text = code.TrimStart(); if (!text.StartsWith("DSUIT2:", StringComparison.Ordinal)) { return text.StartsWith("DSUIT1:", StringComparison.Ordinal); } return true; } internal static bool TryExport(SuitTextureState state, string designName, out string code, out CodeInfo info, out string failureReason) { code = string.Empty; info = default(CodeInfo); failureReason = string.Empty; if ((Object)(object)state?.EditableTexture == (Object)null) { failureReason = "No editable suit texture is available."; return false; } string text = TextureTools.SanitizeFileName(designName); if (string.IsNullOrWhiteSpace(text)) { failureReason = "Design name is empty."; return false; } byte[] array = ImageConversion.EncodeToPNG(state.EditableTexture); if (array == null || array.Length == 0) { failureReason = "Failed to encode the editable texture."; return false; } if (array.Length > 16777216) { failureReason = $"Encoded texture is too large ({array.Length} bytes)."; return false; } byte[] array2 = BuildV2PayloadBytes(text, state.SuitName ?? string.Empty, state.SuitId, ((Texture)state.EditableTexture).width, ((Texture)state.EditableTexture).height, DateTime.UtcNow.ToString("o"), array); byte[] array3 = Compress(array2); code = "DSUIT2:" + ToBase64Url(array3); info = new CodeInfo { FormatVersion = 2, DesignName = text, BaseSuitName = (state.SuitName ?? string.Empty), SourceSuitId = state.SuitId, Width = ((Texture)state.EditableTexture).width, Height = ((Texture)state.EditableTexture).height, PngBytes = array.Length, JsonBytes = 0, PayloadBytes = array2.Length, CompressedBytes = array3.Length, CodeLength = code.Length }; return true; } internal static bool TryDecode(string code, int maxTextureSize, out Payload payload, out Texture2D texture, out CodeInfo info, out string failureReason) { payload = null; texture = null; info = default(CodeInfo); failureReason = string.Empty; if (string.IsNullOrWhiteSpace(code)) { failureReason = "Design code is empty."; return false; } code = code.Trim(); if (code.StartsWith("DSUIT2:", StringComparison.Ordinal)) { return TryDecodeV2(code, maxTextureSize, out payload, out texture, out info, out failureReason); } if (code.StartsWith("DSUIT1:", StringComparison.Ordinal)) { return TryDecodeV1(code, maxTextureSize, out payload, out texture, out info, out failureReason); } failureReason = "Design code must start with DSUIT2: or DSUIT1:"; return false; } private static byte[] BuildV2PayloadBytes(string designName, string baseSuitName, int sourceSuitId, int width, int height, string exportedUtc, byte[] pngBytes) { using MemoryStream memoryStream = new MemoryStream(); using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true)) { binaryWriter.Write(2); binaryWriter.Write("0.5.77"); binaryWriter.Write(designName ?? string.Empty); binaryWriter.Write(baseSuitName ?? string.Empty); binaryWriter.Write(sourceSuitId); binaryWriter.Write(width); binaryWriter.Write(height); binaryWriter.Write(exportedUtc ?? string.Empty); binaryWriter.Write((pngBytes != null) ? pngBytes.Length : 0); if (pngBytes != null && pngBytes.Length != 0) { binaryWriter.Write(pngBytes); } } return memoryStream.ToArray(); } private static bool TryDecodeV2(string code, int maxTextureSize, out Payload payload, out Texture2D texture, out CodeInfo info, out string failureReason) { payload = null; texture = null; info = default(CodeInfo); failureReason = string.Empty; byte[] array; try { array = FromBase64Url(RemoveWhitespace(code.Substring("DSUIT2:".Length))); } catch (Exception ex) { failureReason = "Design code is not valid Base64Url data (" + ex.GetType().Name + ")."; return false; } byte[] array2; try { array2 = Decompress(array, 18874368); } catch (Exception ex2) { failureReason = "Design code payload could not be decompressed (" + ex2.GetType().Name + ")."; return false; } byte[] array3; try { using MemoryStream memoryStream = new MemoryStream(array2); using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8); int num = binaryReader.ReadInt32(); if (num != 2) { failureReason = $"Unsupported design code format {num}."; return false; } payload = new Payload { formatVersion = num, modVersion = binaryReader.ReadString(), designName = binaryReader.ReadString(), baseSuitName = binaryReader.ReadString(), sourceSuitId = binaryReader.ReadInt32(), width = binaryReader.ReadInt32(), height = binaryReader.ReadInt32(), exportedUtc = binaryReader.ReadString(), pngBase64 = string.Empty }; int num2 = binaryReader.ReadInt32(); if (num2 <= 0) { failureReason = "Design code PNG data is empty."; return false; } if (num2 > 16777216) { failureReason = $"Design code PNG data is too large ({num2} bytes)."; return false; } if (memoryStream.Length - memoryStream.Position < num2) { failureReason = "Design code PNG data is truncated."; return false; } array3 = binaryReader.ReadBytes(num2); if (array3.Length != num2) { failureReason = "Design code PNG data is truncated."; return false; } } catch (Exception ex3) { payload = null; failureReason = "Design code binary payload could not be parsed (" + ex3.GetType().Name + ")."; return false; } return TryLoadDecodedTexture(code, maxTextureSize, payload, array3, array2.Length, 0, array.Length, out texture, out info, out failureReason); } private static bool TryDecodeV1(string code, int maxTextureSize, out Payload payload, out Texture2D texture, out CodeInfo info, out string failureReason) { payload = null; texture = null; info = default(CodeInfo); failureReason = string.Empty; byte[] array; try { array = FromBase64Url(RemoveWhitespace(code.Substring("DSUIT1:".Length))); } catch (Exception ex) { failureReason = "Design code is not valid Base64Url data (" + ex.GetType().Name + ")."; return false; } byte[] array2; try { array2 = Decompress(array, 25165824); } catch (Exception ex2) { failureReason = "Design code payload could not be decompressed (" + ex2.GetType().Name + ")."; return false; } try { payload = JsonUtility.FromJson<Payload>(Encoding.UTF8.GetString(array2)); } catch (Exception ex3) { failureReason = "Design code JSON could not be parsed (" + ex3.GetType().Name + ")."; return false; } if (payload == null) { failureReason = "Design code payload is missing."; return false; } if (payload.formatVersion != 1) { failureReason = $"Unsupported design code format {payload.formatVersion}."; return false; } if (string.IsNullOrWhiteSpace(payload.pngBase64)) { failureReason = "Design code is missing PNG data."; return false; } byte[] pngBytes; try { pngBytes = Convert.FromBase64String(payload.pngBase64); } catch (Exception ex4) { failureReason = "Design code PNG data is invalid (" + ex4.GetType().Name + ")."; return false; } return TryLoadDecodedTexture(code, maxTextureSize, payload, pngBytes, 0, array2.Length, array.Length, out texture, out info, out failureReason); } private static bool TryLoadDecodedTexture(string code, int maxTextureSize, Payload payload, byte[] pngBytes, int payloadBytes, int jsonBytes, int compressedBytes, out Texture2D texture, out CodeInfo info, out string failureReason) { texture = null; info = default(CodeInfo); failureReason = string.Empty; if (payload == null) { failureReason = "Design code payload is missing."; return false; } if (string.IsNullOrWhiteSpace(payload.designName)) { failureReason = "Design code is missing a design name."; return false; } if (payload.width <= 0 || payload.height <= 0) { failureReason = "Design code has invalid texture dimensions."; return false; } if (payload.width > maxTextureSize || payload.height > maxTextureSize) { failureReason = $"Design code texture is too large ({payload.width}x{payload.height}, max {maxTextureSize})."; return false; } if (pngBytes == null || pngBytes.Length == 0) { failureReason = "Design code PNG data is empty."; return false; } if (pngBytes.Length > 16777216) { failureReason = $"Design code PNG data is too large ({pngBytes.Length} bytes)."; return false; } texture = TextureTools.LoadImageBytes(pngBytes, "DrawableSuitsDesignCodeTexture", maxTextureSize, resizeOversized: false); if ((Object)(object)texture == (Object)null) { failureReason = $"Design code PNG data could not be loaded as an image or exceeds max size {maxTextureSize}."; return false; } if (((Texture)texture).width > maxTextureSize || ((Texture)texture).height > maxTextureSize) { int width = ((Texture)texture).width; int height = ((Texture)texture).height; Object.Destroy((Object)(object)texture); texture = null; failureReason = $"Decoded texture is too large ({width}x{height}, max {maxTextureSize})."; return false; } info = new CodeInfo { FormatVersion = payload.formatVersion, DesignName = payload.designName, BaseSuitName = payload.baseSuitName, SourceSuitId = payload.sourceSuitId, Width = ((Texture)texture).width, Height = ((Texture)texture).height, PngBytes = pngBytes.Length, JsonBytes = jsonBytes, PayloadBytes = payloadBytes, CompressedBytes = compressedBytes, CodeLength = code.Length }; return true; } private static byte[] Compress(byte[] bytes) { using MemoryStream memoryStream = new MemoryStream(); using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, leaveOpen: true)) { gZipStream.Write(bytes, 0, bytes.Length); } return memoryStream.ToArray(); } private static byte[] Decompress(byte[] bytes, int maxBytes) { using MemoryStream stream = new MemoryStream(bytes); using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); byte[] array = new byte[8192]; int num; while ((num = gZipStream.Read(array, 0, array.Length)) > 0) { if (memoryStream.Length + num > maxBytes) { throw new InvalidDataException("Decoded payload exceeds the maximum allowed size."); } memoryStream.Write(array, 0, num); } return memoryStream.ToArray(); } private static string ToBase64Url(byte[] bytes) { return Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', '-') .Replace('/', '_'); } private static byte[] FromBase64Url(string value) { value = value.Replace('-', '+').Replace('_', '/'); switch (value.Length % 4) { case 2: value += "=="; break; case 3: value += "="; break; default: throw new FormatException("Invalid Base64Url padding."); case 0: break; } return Convert.FromBase64String(value); } private static string RemoveWhitespace(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { if (!char.IsWhiteSpace(value[i])) { stringBuilder.Append(value[i]); } } return stringBuilder.ToString(); } } internal sealed class DrawableSuitsConfig { public ConfigEntry<KeyCode> OpenEditorKey { get; } public ConfigEntry<KeyCode> EmergencyOpenKey { get; } public ConfigEntry<int> MaxTextureSize { get; } public ConfigEntry<int> MaxUndoStates { get; } public ConfigEntry<int> MaxSyncBytes { get; } public ConfigEntry<int> SyncChunkBytes { get; } public ConfigEntry<float> ControllerCursorSpeed { get; } public ConfigEntry<bool> EnableNetworkSync { get; } public ConfigEntry<bool> EnableExperimentalModelPreview { get; } public ConfigEntry<bool> StartInUvFallbackMode { get; } public ConfigEntry<float> ThirdPersonCameraDistance { get; } public ConfigEntry<string> RecentColors { get; } public ConfigEntry<bool> ApplyLocalFirstPersonArms { get; } public DrawableSuitsConfig(ConfigFile config) { OpenEditorKey = config.Bind<KeyCode>("Input", "OpenEditorKey", (KeyCode)289, "Keyboard key that opens or closes the suit editor."); EmergencyOpenKey = config.Bind<KeyCode>("Input", "EmergencyOpenKey", (KeyCode)291, "Keyboard key that opens the editor even when suit/player detection is incomplete."); ControllerCursorSpeed = config.Bind<float>("Input", "ControllerCursorSpeed", 900f, "Virtual cursor speed in pixels per second."); MaxTextureSize = config.Bind<int>("Textures", "MaxTextureSize", 1024, "Maximum width and height for editable and synced suit textures."); MaxUndoStates = config.Bind<int>("Editor", "MaxUndoStates", 12, "Maximum undo snapshots kept while editing."); EnableNetworkSync = config.Bind<bool>("Multiplayer", "EnableNetworkSync", true, "Sync applied and saved suit designs to other DrawableSuits users."); MaxSyncBytes = config.Bind<int>("Multiplayer", "MaxSyncBytes", 4194304, "Maximum PNG payload size allowed for multiplayer sync."); SyncChunkBytes = config.Bind<int>("Multiplayer", "SyncChunkBytes", 48000, "Byte size for each Netcode custom-message texture chunk."); EnableExperimentalModelPreview = config.Bind<bool>("Editor", "EnableExperimentalModelPreview", false, "Disabled by default. Uses the old camera/RenderTexture 3D model preview only for diagnostics; third-person world painting is the default."); StartInUvFallbackMode = config.Bind<bool>("Editor", "StartInUvFallbackMode", false, "Open directly into the old UV texture fallback instead of third-person world painting. Useful for diagnostics."); ThirdPersonCameraDistance = config.Bind<float>("Editor", "ThirdPersonCameraDistance", 3.4f, "Default third-person editor camera orbit distance."); RecentColors = config.Bind<string>("Editor", "RecentColors", string.Empty, "Recent brush colors as comma-separated #RRGGBB values. Colors are added only after Paint, Fill, or Text writes to the suit."); ApplyLocalFirstPersonArms = config.Bind<bool>("Compatibility", "ApplyLocalFirstPersonArms", false, "Experimental. When false, DrawableSuits does not apply edited materials to the local first-person arms/body outside the editor."); } } internal static class DrawableSuitsDiagnostics { private static readonly object SyncRoot = new object(); private static string _logPath; private static bool _initialized; public static string LogPath => _logPath ?? Path.Combine(DrawableSuitsPaths.Logs, "diagnostics.log"); public static void Initialize() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) try { DrawableSuitsPaths.EnsureCreated(); _logPath = Path.Combine(DrawableSuitsPaths.Logs, "diagnostics.log"); Directory.CreateDirectory(Path.GetDirectoryName(_logPath)); File.WriteAllText(_logPath, string.Empty); _initialized = true; Info("Diagnostics initialized."); Info("DrawableSuits 0.5.77 (com.toxcgang.drawablesuits) starting."); Info($"UnityVersion={Application.unityVersion}; ApplicationVersion={Application.version}; Platform={Application.platform}; ProductName={Application.productName}"); Info("DataPath=" + Application.dataPath + "; PersistentDataPath=" + Application.persistentDataPath + "; ConfigRoot=" + DrawableSuitsPaths.Root); Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; activeScene = SceneManager.GetActiveScene(); Info($"ActiveScene={name}; SceneBuildIndex={((Scene)(ref activeScene)).buildIndex}"); AssemblyName[] referencedAssemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); foreach (AssemblyName assemblyName in referencedAssemblies) { Info($"Reference={assemblyName.Name}, Version={assemblyName.Version}"); } } catch (Exception arg) { _initialized = false; LogToBepInEx((LogLevel)2, $"DrawableSuits diagnostics failed to initialize: {arg}"); } } public static void Info(string message) { Write((LogLevel)16, message); } public static void Warn(string message) { Write((LogLevel)4, message); } public static void Error(string message) { Write((LogLevel)2, message); } public static void Exception(string context, Exception exception) { if (exception == null) { Error(context + ": unknown exception."); return; } Write((LogLevel)2, context + ": " + exception.GetType().FullName + ": " + exception.Message + Environment.NewLine + exception.StackTrace); } private static void Write(LogLevel level, string message) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) string text = $"[{DateTime.Now:O}] [{level}] {message ?? string.Empty}"; LogToBepInEx(level, message ?? string.Empty); if (!_initialized) { return; } try { lock (SyncRoot) { File.AppendAllText(LogPath, text + Environment.NewLine); } } catch { } } private static void LogToBepInEx(LogLevel level, string message) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 ManualLogSource modLogger = DrawableSuitsPlugin.ModLogger; if (modLogger == null) { Debug.Log((object)("[DrawableSuits] " + message)); } else if ((int)level != 2) { if ((int)level != 4) { if ((int)level == 32) { modLogger.LogDebug((object)message); } else { modLogger.LogInfo((object)message); } } else { modLogger.LogWarning((object)message); } } else { modLogger.LogError((object)message); } } } internal static class DrawableSuitsInput { private static bool _legacyUnavailable; private static bool _legacyUnavailableLogged; internal static bool WasKeyPressed(Key defaultKey, KeyCode configuredKey) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) Key key = MapKeyCode(configuredKey, defaultKey); if (Keyboard.current != null) { return WasInputSystemKeyPressed(key); } return WasLegacyKeyPressedOnceGuarded(configuredKey); } internal static bool WasKeyPressed(Key key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return WasInputSystemKeyPressed(key); } internal static bool IsKeyPressed(Key key) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) try { Keyboard current = Keyboard.current; return current != null && ((ButtonControl)current[key]).isPressed; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception($"Input System key held polling failed for {key}", exception); return false; } } internal static bool TryGetMousePosition(out Vector2 position) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) try { Mouse current = Mouse.current; if (current != null) { position = ((InputControl<Vector2>)(object)((Pointer)current).position).ReadValue(); return true; } } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Input System mouse position polling failed", exception); } position = default(Vector2); return false; } internal static bool IsLeftMousePressed() { return IsMouseButtonPressed((Mouse mouse) => mouse.leftButton.isPressed, "left"); } internal static bool IsRightMousePressed() { return IsMouseButtonPressed((Mouse mouse) => mouse.rightButton.isPressed, "right"); } internal static float MouseDeltaX() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) try { Mouse current = Mouse.current; return (current != null) ? ((InputControl<Vector2>)(object)((Pointer)current).delta).ReadValue().x : 0f; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Input System mouse delta polling failed", exception); return 0f; } } internal static float MouseDeltaY() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) try { Mouse current = Mouse.current; return (current != null) ? ((InputControl<Vector2>)(object)((Pointer)current).delta).ReadValue().y : 0f; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Input System mouse delta polling failed", exception); return 0f; } } internal static bool WasMouseUsedThisFrame() { //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_0047: Unknown result type (might be due to invalid IL or missing references) try { Mouse current = Mouse.current; if (current == null) { return false; } Vector2 val = ((InputControl<Vector2>)(object)((Pointer)current).delta).ReadValue(); return ((Vector2)(ref val)).sqrMagnitude > 0.01f || current.leftButton.isPressed || current.rightButton.isPressed || Mathf.Abs(((InputControl<Vector2>)(object)current.scroll).ReadValue().y) > 0.01f; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Input System mouse activity polling failed", exception); return false; } } internal static float MouseScrollY() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) try { Mouse current = Mouse.current; if (current == null) { return 0f; } float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y; return (Mathf.Abs(y) > 10f) ? (y / 120f) : y; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Input System mouse scroll polling failed", exception); return 0f; } } private static bool IsMouseButtonPressed(Func<Mouse, bool> accessor, string buttonName) { try { Mouse current = Mouse.current; return current != null && accessor(current); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Input System " + buttonName + " mouse button polling failed", exception); return false; } } private static bool WasInputSystemKeyPressed(Key key) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) try { Keyboard current = Keyboard.current; return current != null && ((ButtonControl)current[key]).wasPressedThisFrame; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception($"Input System key polling failed for {key}", exception); return false; } } private static bool WasLegacyKeyPressedOnceGuarded(KeyCode key) { //IL_004c: 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_000a: Unknown result type (might be due to invalid IL or missing references) if (_legacyUnavailable) { return false; } try { return Input.GetKeyDown(key); } catch (InvalidOperationException ex) { _legacyUnavailable = true; if (!_legacyUnavailableLogged) { _legacyUnavailableLogged = true; DrawableSuitsDiagnostics.Warn($"Legacy UnityEngine.Input is disabled; using Unity Input System only. First legacy failure for {key}: {ex.Message}"); } return false; } catch (Exception exception) { DrawableSuitsDiagnostics.Exception($"Legacy key polling failed once for {key}", exception); return false; } } private unsafe static Key MapKeyCode(KeyCode keyCode, Key fallback) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (TryMapSpecialKey(keyCode, out var key)) { return key; } if (Enum.TryParse<Key>(((object)(*(KeyCode*)(&keyCode))/*cast due to .constrained prefix*/).ToString(), ignoreCase: true, out key)) { return key; } return fallback; } private static bool TryMapSpecialKey(KeyCode keyCode, out Key key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_007e: 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_00ae: Expected I4, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected I4, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_00ae: 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_00d2: Expected I4, but got Unknown if ((int)keyCode <= 27) { if ((int)keyCode <= 9) { if ((int)keyCode == 8) { key = (Key)65; return true; } if ((int)keyCode == 9) { key = (Key)3; return true; } } else { if ((int)keyCode == 13) { key = (Key)2; return true; } if ((int)keyCode == 27) { key = (Key)60; return true; } } } else if ((int)keyCode <= 57) { if ((int)keyCode == 32) { key = (Key)1; return true; } switch (keyCode - 48) { case 0: key = (Key)50; return true; case 1: key = (Key)41; return true; case 2: key = (Key)42; return true; case 3: key = (Key)43; return true; case 4: key = (Key)44; return true; case 5: key = (Key)45; return true; case 6: key = (Key)46; return true; case 7: key = (Key)47; return true; case 8: key = (Key)48; return true; case 9: key = (Key)49; return true; } } else { if ((int)keyCode == 127) { key = (Key)71; return true; } switch (keyCode - 273) { case 0: key = (Key)63; return true; case 1: key = (Key)64; return true; case 3: key = (Key)61; return true; case 2: key = (Key)62; return true; case 4: key = (Key)70; return true; case 5: key = (Key)68; return true; case 6: key = (Key)69; return true; case 7: key = (Key)67; return true; case 8: key = (Key)66; return true; } switch (keyCode - 303) { case 1: key = (Key)51; return true; case 0: key = (Key)52; return true; case 3: key = (Key)55; return true; case 2: key = (Key)56; return true; case 5: key = (Key)53; return true; case 4: key = (Key)54; return true; } } key = (Key)0; return false; } } internal static class DrawableSuitsPaths { public static string Root => Path.Combine(Paths.ConfigPath, "DrawableSuits"); public static string Saves => Path.Combine(Root, "Saves"); public static string Textures => Path.Combine(Root, "Textures"); public static string Decals => Path.Combine(Root, "Decals"); public static string Logs => Path.Combine(Root, "Logs"); public static void EnsureCreated() { Directory.CreateDirectory(Root); Directory.CreateDirectory(Saves); Directory.CreateDirectory(Textures); Directory.CreateDirectory(Decals); Directory.CreateDirectory(Logs); } } [BepInPlugin("com.toxcgang.drawablesuits", "DrawableSuits", "0.5.77")] [BepInProcess("Lethal Company.exe")] public sealed class DrawableSuitsPlugin : BaseUnityPlugin { private static GameObject _runtimeRoot; private Harmony _harmony; internal static DrawableSuitsPlugin Instance { get; private set; } internal static ManualLogSource ModLogger { get; private set; } internal static DrawableSuitsConfig ModConfig { get; private set; } internal static SuitTextureRegistry Registry { get; private set; } internal static SuitEditorController Editor { get; private set; } internal static DrawableSuitsRuntimeHost RuntimeHost { get; private set; } internal static SuitSyncManager Sync { get; private set; } internal static bool IsEditorOpen { get { if ((Object)(object)Editor != (Object)null) { return Editor.IsOpenForDiagnostics; } return false; } } private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown Instance = this; ModLogger = ((BaseUnityPlugin)this).Logger; ModConfig = new DrawableSuitsConfig(((BaseUnityPlugin)this).Config); DrawableSuitsPaths.EnsureCreated(); DrawableSuitsDiagnostics.Initialize(); string[] obj = new string[6] { "Plugin Awake. pluginInstance=", DescribeUnityObject((Object)(object)this), "; pluginGameObject=", DescribeUnityObject((Object)(object)((Component)this).gameObject), "; scene=", null }; Scene activeScene = SceneManager.GetActiveScene(); obj[5] = ((Scene)(ref activeScene)).name; DrawableSuitsDiagnostics.Info(string.Concat(obj)); DrawableSuitsDiagnostics.Info($"Config OpenEditorKey={ModConfig.OpenEditorKey.Value}; EmergencyOpenKey={ModConfig.EmergencyOpenKey.Value}; ControllerCursorSpeed={ModConfig.ControllerCursorSpeed.Value}; MaxTextureSize={ModConfig.MaxTextureSize.Value}; MaxUndoStates={ModConfig.MaxUndoStates.Value}; NetworkSync={ModConfig.EnableNetworkSync.Value}; MaxSyncBytes={ModConfig.MaxSyncBytes.Value}; SyncChunkBytes={ModConfig.SyncChunkBytes.Value}; ExperimentalModelPreview={ModConfig.EnableExperimentalModelPreview.Value}; StartInUvFallbackMode={ModConfig.StartInUvFallbackMode.Value}; ThirdPersonCameraDistance={ModConfig.ThirdPersonCameraDistance.Value}; ApplyLocalFirstPersonArms={ModConfig.ApplyLocalFirstPersonArms.Value}"); _harmony = new Harmony("com.toxcgang.drawablesuits"); try { DrawableSuitsDiagnostics.Info("Applying Harmony patches."); _harmony.PatchAll(); PlayerControllerBPatches.ApplyOptionalGameplayInputPatches(_harmony); DrawableSuitsDiagnostics.Info("Harmony patches applied."); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Harmony PatchAll failed", exception); throw; } SceneManager.sceneLoaded += OnSceneLoaded; EnsureRuntimeReady("Plugin.Awake"); DrawableSuitsDiagnostics.Info("DrawableSuits 0.5.77 loaded with GUID com.toxcgang.drawablesuits."); } private void Start() { DrawableSuitsDiagnostics.Info("Plugin Start. plugin=" + DescribeUnityObject((Object)(object)this) + "; host=" + DescribeUnityObject((Object)(object)RuntimeHost) + "; editor=" + DescribeUnityObject((Object)(object)Editor)); EnsureRuntimeReady("Plugin.Start"); SessionSafetyGuard.Run("Plugin.Start", forceLog: true); } private void Update() { EnsureRuntimeReady("Plugin.Update"); } private void OnDestroy() { DrawableSuitsDiagnostics.Info("DrawableSuitsPlugin.OnDestroy called."); if ((Object)(object)Editor != (Object)null && Editor.IsOpenForDiagnostics) { Editor.CloseEditorForPluginDestroy(); } SceneManager.sceneLoaded -= OnSceneLoaded; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Registry = null; Sync = null; Editor = null; RuntimeHost = null; if ((Object)(object)_runtimeRoot != (Object)null) { Object.Destroy((Object)(object)_runtimeRoot); _runtimeRoot = null; } if (Instance == this) { Instance = null; } } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) DrawableSuitsDiagnostics.Info($"Scene loaded. name={((Scene)(ref scene)).name}; buildIndex={((Scene)(ref scene)).buildIndex}; mode={mode}"); bool num = ShouldCloseEditorForScene(scene, mode); EnsureRuntimeReady("SceneLoaded:" + ((Scene)(ref scene)).name); if (num && (Object)(object)Editor != (Object)null && Editor.IsOpenForDiagnostics) { DrawableSuitsDiagnostics.Info($"Closing editor because scene changed to '{((Scene)(ref scene)).name}' with mode={mode}."); Editor.CloseEditorForSceneChange(); } Registry?.ReapplyAllIfReady("SceneLoaded:" + ((Scene)(ref scene)).name); SessionSafetyGuard.Run("SceneLoaded:" + ((Scene)(ref scene)).name, forceLog: true); } internal static bool HasSessionSafetyContext() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!((Object)(object)StartOfRound.Instance != (Object)null) && !((Object)(object)Object.FindObjectOfType<GameNetworkManager>()?.localPlayerController != (Object)null)) { return string.Equals(name, "SampleSceneRelay", StringComparison.OrdinalIgnoreCase); } return true; } internal static bool HasGameplayEditorContext() { if ((Object)(object)StartOfRound.Instance?.localPlayerController != (Object)null) { return (Object)(object)Camera.main != (Object)null; } return false; } private static bool ShouldCloseEditorForScene(Scene scene, LoadSceneMode mode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)mode != 1) { return true; } if (!string.Equals(((Scene)(ref scene)).name, "MainMenu", StringComparison.OrdinalIgnoreCase) && !string.Equals(((Scene)(ref scene)).name, "InitScene", StringComparison.OrdinalIgnoreCase)) { return string.Equals(((Scene)(ref scene)).name, "InitSceneLaunchOptions", StringComparison.OrdinalIgnoreCase); } return true; } internal static bool EnsureRuntimeReady(string reason) { if ((Object)(object)Instance == (Object)null) { DrawableSuitsDiagnostics.Warn("EnsureRuntimeReady(" + reason + ") failed: plugin instance is null."); return false; } GameObject orCreateRuntimeRoot = GetOrCreateRuntimeRoot(reason); if ((Object)(object)orCreateRuntimeRoot == (Object)null) { DrawableSuitsDiagnostics.Warn("EnsureRuntimeReady(" + reason + ") failed: runtime root is null."); return false; } Registry = EnsureComponent<SuitTextureRegistry>(Registry, orCreateRuntimeRoot, reason, "registry"); Sync = EnsureComponent<SuitSyncManager>(Sync, orCreateRuntimeRoot, reason, "sync"); Editor = EnsureComponent<SuitEditorController>(Editor, orCreateRuntimeRoot, reason, "editor"); RuntimeHost = EnsureComponent<DrawableSuitsRuntimeHost>(RuntimeHost, orCreateRuntimeRoot, reason, "runtime host"); bool flag = (Object)(object)Registry != (Object)null && (Object)(object)Sync != (Object)null && (Object)(object)Editor != (Object)null && (Object)(object)RuntimeHost != (Object)null; if (!flag) { DrawableSuitsDiagnostics.Warn("EnsureRuntimeReady(" + reason + ") incomplete. registry=" + DescribeUnityObject((Object)(object)Registry) + "; sync=" + DescribeUnityObject((Object)(object)Sync) + "; editor=" + DescribeUnityObject((Object)(object)Editor) + "; host=" + DescribeUnityObject((Object)(object)RuntimeHost)); } return flag; } private static GameObject GetOrCreateRuntimeRoot(string reason) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if ((Object)(object)_runtimeRoot != (Object)null) { return _runtimeRoot; } _runtimeRoot = new GameObject("DrawableSuitsRuntimeRoot"); ((Object)_runtimeRoot).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)_runtimeRoot); DrawableSuitsDiagnostics.Info("Runtime root created. reason=" + reason + "; root=" + DescribeUnityObject((Object)(object)_runtimeRoot)); return _runtimeRoot; } internal static bool RequestOpenEditor(string source) { bool flag = EnsureRuntimeReady("RequestOpenEditor:" + source); DrawableSuitsDiagnostics.Info($"RequestOpenEditor source={source}; runtimeReady={flag}; editor={DescribeUnityObject((Object)(object)Editor)}"); if (!flag || (Object)(object)Editor == (Object)null) { return false; } bool flag2 = Editor.OpenEditor(source); DrawableSuitsDiagnostics.Info($"RequestOpenEditor result source={source}; opened={flag2}; editorOpen={Editor.IsOpenForDiagnostics}; canvasActive={Editor.CanvasActiveForDiagnostics}"); return flag2; } internal static bool RequestToggleEditor(string source) { bool flag = EnsureRuntimeReady("RequestToggleEditor:" + source); DrawableSuitsDiagnostics.Info($"RequestToggleEditor source={source}; runtimeReady={flag}; editor={DescribeUnityObject((Object)(object)Editor)}"); if (!flag || (Object)(object)Editor == (Object)null) { return false; } if (!Editor.IsOpenForDiagnostics) { return RequestOpenEditor(source); } return CloseEditor(source); } internal static bool CloseEditor(string source) { EnsureRuntimeReady("CloseEditor:" + source); if ((Object)(object)Editor == (Object)null) { DrawableSuitsDiagnostics.Warn("CloseEditor source=" + source + " ignored because editor is null."); return false; } DrawableSuitsDiagnostics.Info("CloseEditor source=" + source + "; editor=" + DescribeUnityObject((Object)(object)Editor)); Editor.CloseEditor(source); return true; } internal static string DescribeUnityObject(Object unityObject) { if (unityObject == (Object)null) { return "null"; } return $"{((object)unityObject).GetType().Name}(id={unityObject.GetInstanceID()}, name={unityObject.name})"; } private static T EnsureComponent<T>(T current, GameObject hostObject, string reason, string label) where T : Component { if ((Object)(object)current != (Object)null) { return current; } T component = hostObject.GetComponent<T>(); if ((Object)(object)component != (Object)null) { DrawableSuitsDiagnostics.Info("EnsureRuntimeReady(" + reason + ") recovered existing " + label + ": " + DescribeUnityObject((Object)(object)component)); return component; } T val = hostObject.AddComponent<T>(); DrawableSuitsDiagnostics.Info("EnsureRuntimeReady(" + reason + ") created " + label + ": " + DescribeUnityObject((Object)(object)val) + " on " + DescribeUnityObject((Object)(object)hostObject)); return val; } } internal sealed class DrawableSuitsRuntimeHost : MonoBehaviour { private bool _firstUpdateLogged; private bool _controllerOpenChordWasHeld; private void Awake() { DrawableSuitsDiagnostics.Info("RuntimeHost Awake. host=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)this) + "; gameObject=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)((Component)this).gameObject)); } private void Start() { DrawableSuitsDiagnostics.Info("RuntimeHost Start. editor=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)DrawableSuitsPlugin.Editor)); } private void Update() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!_firstUpdateLogged) { _firstUpdateLogged = true; object arg = Time.frameCount; Scene activeScene = SceneManager.GetActiveScene(); DrawableSuitsDiagnostics.Info($"RuntimeHost first Update. frame={arg}; scene={((Scene)(ref activeScene)).name}; editor={DrawableSuitsPlugin.DescribeUnityObject((Object)(object)DrawableSuitsPlugin.Editor)}"); } DrawableSuitsPlugin.EnsureRuntimeReady("RuntimeHost.Update"); HandleInput(); } private void HandleInput() { //IL_000c: 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) if (DrawableSuitsInput.WasKeyPressed((Key)103, DrawableSuitsPlugin.ModConfig.EmergencyOpenKey.Value)) { DrawableSuitsDiagnostics.Info("RuntimeHost F10 emergency open. editor=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)DrawableSuitsPlugin.Editor)); DrawableSuitsPlugin.RequestOpenEditor("F10Emergency"); } if (DrawableSuitsInput.WasKeyPressed((Key)101, DrawableSuitsPlugin.ModConfig.OpenEditorKey.Value)) { DrawableSuitsDiagnostics.Info("RuntimeHost F8 toggle. editor=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)DrawableSuitsPlugin.Editor)); RequestGameplayToggle("F8Keyboard"); } if (WasControllerOpenChordPressed()) { DrawableSuitsDiagnostics.Info("RuntimeHost controller open chord. editor=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)DrawableSuitsPlugin.Editor)); RequestGameplayToggle("ControllerViewBackY"); } } private static void RequestGameplayToggle(string source) { SuitEditorController editor = DrawableSuitsPlugin.Editor; if (editor != null && editor.IsOpenForDiagnostics) { DrawableSuitsPlugin.RequestToggleEditor(source); } else if (!DrawableSuitsPlugin.HasGameplayEditorContext()) { DrawableSuitsDiagnostics.Info("RuntimeHost ignored " + source + " open because no gameplay player/camera context is available. Use F10 for diagnostics."); } else { DrawableSuitsPlugin.RequestToggleEditor(source); } } private bool WasControllerOpenChordPressed() { Gamepad current = Gamepad.current; if (current == null) { _controllerOpenChordWasHeld = false; return false; } ButtonControl val = current.selectButton ?? ((InputControl)current).TryGetChildControl<ButtonControl>("backButton"); bool flag = val != null && val.isPressed && current.buttonNorth.isPressed; bool result = flag && !_controllerOpenChordWasHeld; _controllerOpenChordWasHeld = flag; return result; } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatches { [HarmonyPostfix] [HarmonyPatch("LoadUnlockables")] private static void LoadUnlockablesPostfix() { RunPatch("StartOfRound.LoadUnlockables", delegate { DrawableSuitsPlugin.Registry?.ReapplyAllIfReady("StartOfRound.LoadUnlockables"); SessionSafetyGuard.Run("StartOfRound.LoadUnlockables", forceLog: true); }); } [HarmonyPostfix] [HarmonyPatch("PositionSuitsOnRack")] private static void PositionSuitsOnRackPostfix() { RunPatch("StartOfRound.PositionSuitsOnRack", delegate { DrawableSuitsPlugin.Registry?.ReapplyAllIfReady("StartOfRound.PositionSuitsOnRack"); }); } [HarmonyPostfix] [HarmonyPatch("SyncSuitsClientRpc")] private static void SyncSuitsClientRpcPostfix() { RunPatch("StartOfRound.SyncSuitsClientRpc", delegate { DrawableSuitsPlugin.Registry?.ReapplyAllIfReady("StartOfRound.SyncSuitsClientRpc"); }); } [HarmonyPostfix] [HarmonyPatch("OnPlayerConnectedClientRpc")] private static void OnPlayerConnectedClientRpcPostfix() { RunPatch("StartOfRound.OnPlayerConnectedClientRpc", delegate { DrawableSuitsPlugin.Registry?.ReapplyAllIfReady("StartOfRound.OnPlayerConnectedClientRpc"); SessionSafetyGuard.Run("StartOfRound.OnPlayerConnectedClientRpc", forceLog: true); DrawableSuitsPlugin.Sync?.RequestActiveDesigns(); }); } private static void RunPatch(string context, Action action) { try { DrawableSuitsPlugin.EnsureRuntimeReady(context); action(); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Patch failed: " + context, exception); } } } [HarmonyPatch(typeof(UnlockableSuit))] internal static class UnlockableSuitPatches { [HarmonyPostfix] [HarmonyPatch("SwitchSuitForPlayer")] private static void SwitchSuitForPlayerPostfix(PlayerControllerB player) { RunPatch("UnlockableSuit.SwitchSuitForPlayer", delegate { DrawableSuitsPlugin.Registry?.RecordPlayerSuitSnapshot(player, "UnlockableSuit.SwitchSuitForPlayer"); DrawableSuitsPlugin.Registry?.ApplyToPlayer(player); DrawableSuitsPlugin.Registry?.ScheduleReapplyAll("UnlockableSuit.SwitchSuitForPlayer"); }); } [HarmonyPostfix] [HarmonyPatch("SwitchSuitForAllPlayers")] private static void SwitchSuitForAllPlayersPostfix() { RunPatch("UnlockableSuit.SwitchSuitForAllPlayers", delegate { DrawableSuitsPlugin.Registry?.ReapplyAllIfReady("UnlockableSuit.SwitchSuitForAllPlayers"); }); } private static void RunPatch(string context, Action action) { try { DrawableSuitsPlugin.EnsureRuntimeReady(context); action(); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Patch failed: " + context, exception); } } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class PlayerControllerBPatches { private static readonly string[] OptionalBlockedInputMethods = new string[25] { "ScrollMouse_performed", "ScrollMouse_canceled", "SwitchItem_performed", "SwitchItem_canceled", "NextItem_performed", "PreviousItem_performed", "Scan_performed", "PingScan_performed", "ItemPrimaryUse_performed", "ItemInteract_performed", "ItemSecondaryUse_performed", "ItemTertiaryUse_performed", "ActivateItem_performed", "ActivateItem_canceled", "Discard_performed", "Crouch_performed", "InspectItem_performed", "UseUtilitySlot_performed", "Emote1_performed", "Emote2_performed", "SetFreeCamera_performed", "SpeedCheat_performed", "BuildMode_performed", "ConfirmBuildMode_performed", "Delete_performed" }; private static readonly HashSet<string> OptionalPatchedMethods = new HashSet<string>(StringComparer.Ordinal); private static float _lastBlockedInputLogTime; private static string _lastBlockedInputMethod; internal static void ApplyOptionalGameplayInputPatches(Harmony harmony) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown if (harmony == null) { return; } MethodInfo methodInfo = AccessTools.Method(typeof(PlayerControllerBPatches), "OptionalGameplayInputPrefix", (Type[])null, (Type[])null); if (methodInfo == null) { DrawableSuitsDiagnostics.Warn("Optional gameplay input prefix was not found; scan/inventory fallback patches were not applied."); return; } List<string> list = new List<string>(); List<string> list2 = new List<string>(); for (int i = 0; i < OptionalBlockedInputMethods.Length; i++) { string text = OptionalBlockedInputMethods[i]; if (OptionalPatchedMethods.Contains(text)) { continue; } MethodInfo methodInfo2 = null; try { methodInfo2 = AccessTools.Method(typeof(PlayerControllerB), text, (Type[])null, (Type[])null); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Optional PlayerControllerB input method lookup failed for " + text, exception); list2.Add(text); continue; } if (methodInfo2 == null) { list2.Add(text); continue; } try { harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); OptionalPatchedMethods.Add(text); list.Add(text); } catch (Exception exception2) { DrawableSuitsDiagnostics.Exception("Failed to apply optional PlayerControllerB input block for " + text, exception2); } } DrawableSuitsDiagnostics.Info("Optional gameplay input patches applied. patched=[" + string.Join(", ", list) + "]; missing=[" + string.Join(", ", list2) + "]"); } [HarmonyPostfix] [HarmonyPatch("ConnectClientToPlayerObject")] private static void ConnectClientToPlayerObjectPostfix(PlayerControllerB __instance) { RunPatch("PlayerControllerB.ConnectClientToPlayerObject", delegate { DrawableSuitsPlugin.Registry?.RecordPlayerSuitSnapshot(__instance, "PlayerControllerB.ConnectClientToPlayerObject"); DrawableSuitsPlugin.Registry?.ApplyToPlayer(__instance); DrawableSuitsPlugin.Registry?.ScheduleReapplyAll("PlayerControllerB.ConnectClientToPlayerObject"); SessionSafetyGuard.Run("PlayerControllerB.ConnectClientToPlayerObject", forceLog: true); DrawableSuitsPlugin.Sync?.RequestActiveDesigns(); }); } [HarmonyPostfix] [HarmonyPatch("SpawnPlayerAnimation")] private static void SpawnPlayerAnimationPostfix(PlayerControllerB __instance) { RunPatch("PlayerControllerB.SpawnPlayerAnimation", delegate { DrawableSuitsPlugin.Registry?.RecordPlayerSuitSnapshot(__instance, "PlayerControllerB.SpawnPlayerAnimation"); DrawableSuitsPlugin.Registry?.ApplyToPlayer(__instance); DrawableSuitsPlugin.Registry?.ScheduleReapplyAll("PlayerControllerB.SpawnPlayerAnimation"); SessionSafetyGuard.Run("PlayerControllerB.SpawnPlayerAnimation", forceLog: true); }); } [HarmonyPostfix] [HarmonyPatch("SpawnDeadBody", new Type[] { typeof(int), typeof(Vector3), typeof(int), typeof(PlayerControllerB), typeof(int), typeof(Transform), typeof(Transform), typeof(Vector3) })] private static void SpawnDeadBodyPostfix(int playerId, PlayerControllerB deadPlayerController) { RunPatch("PlayerControllerB.SpawnDeadBody", delegate { DrawableSuitsPlugin.Registry?.RecordPlayerSuitSnapshot(deadPlayerController, "PlayerControllerB.SpawnDeadBody"); DeadBodyInfo val = deadPlayerController?.deadBody; if ((Object)(object)val != (Object)null) { DrawableSuitsPlugin.Registry?.ApplyToDeadBody(val, "PlayerControllerB.SpawnDeadBody:deadPlayerController.deadBody"); DrawableSuitsPlugin.Registry?.ScheduleDeadBodyReapply(val, "PlayerControllerB.SpawnDeadBody:deadPlayerController.deadBody"); DrawableSuitsDiagnostics.Info($"DeadBodySuitSpawnPatch: playerId={playerId}; exactBody={((Object)val).name}; deadPlayer={DrawableSuitsPlugin.DescribeUnityObject((Object)(object)deadPlayerController)}"); } else { DeadBodyInfo[] array = Object.FindObjectsOfType<DeadBodyInfo>(); int num = 0; if (array != null) { foreach (DeadBodyInfo val2 in array) { if (!((Object)(object)val2 == (Object)null) && val2.playerObjectId == playerId) { DrawableSuitsPlugin.Registry?.ApplyToDeadBody(val2, "PlayerControllerB.SpawnDeadBody"); DrawableSuitsPlugin.Registry?.ScheduleDeadBodyReapply(val2, "PlayerControllerB.SpawnDeadBody"); num++; } } } DrawableSuitsDiagnostics.Info($"DeadBodySuitSpawnPatch: playerId={playerId}; matchedBodies={num}; deadPlayer={DrawableSuitsPlugin.DescribeUnityObject((Object)(object)deadPlayerController)}"); } }); } [HarmonyPrefix] [HarmonyPatch("Jump_performed")] private static bool JumpPerformedPrefix(PlayerControllerB __instance) { return AllowGameplayInput("Jump_performed", __instance); } [HarmonyPrefix] [HarmonyPatch("Interact_performed")] private static bool InteractPerformedPrefix(PlayerControllerB __instance) { return AllowGameplayInput("Interact_performed", __instance); } [HarmonyPrefix] [HarmonyPatch("QEItemInteract_performed")] private static bool QeItemInteractPerformedPrefix(PlayerControllerB __instance) { return AllowGameplayInput("QEItemInteract_performed", __instance); } [HarmonyPrefix] [HarmonyPatch("Look_performed")] private static bool LookPerformedPrefix(PlayerControllerB __instance) { return AllowGameplayInput("Look_performed", __instance); } private static bool OptionalGameplayInputPrefix(PlayerControllerB __instance, MethodBase __originalMethod) { return AllowGameplayInput(__originalMethod?.Name ?? "OptionalGameplayInput", __instance); } private static void RunPatch(string context, Action action) { try { DrawableSuitsPlugin.EnsureRuntimeReady(context); action(); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Patch failed: " + context, exception); } } private static bool AllowGameplayInput(string method, PlayerControllerB player) { if (!DrawableSuitsPlugin.IsEditorOpen) { return true; } if (Time.unscaledTime - _lastBlockedInputLogTime > 0.75f || !string.Equals(_lastBlockedInputMethod, method, StringComparison.Ordinal)) { _lastBlockedInputLogTime = Time.unscaledTime; _lastBlockedInputMethod = method; DrawableSuitsDiagnostics.Info("Blocked PlayerControllerB." + method + " while DrawableSuits editor is open. player=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)player)); } return false; } } [HarmonyPatch(typeof(DeadBodyInfo))] internal static class DeadBodyInfoPatches { [HarmonyPrefix] [HarmonyPatch("ChangeMesh")] private static void ChangeMeshPrefix(DeadBodyInfo __instance, ref Material changeMaterial) { try { DrawableSuitsPlugin.EnsureRuntimeReady("DeadBodyInfo.ChangeMesh.prefix"); if ((Object)(object)DrawableSuitsPlugin.Registry != (Object)null && DrawableSuitsPlugin.Registry.TryGetDeadBodyRuntimeMaterial(__instance, "DeadBodyInfo.ChangeMesh.prefix", out var material, out var reason)) { string text = (((Object)(object)changeMaterial != (Object)null) ? ((Object)changeMaterial).name : "null"); changeMaterial = material; DrawableSuitsDiagnostics.Info("DeadBodySuitMaterialSubstituted: context=DeadBodyInfo.ChangeMesh.prefix; previousMaterial=" + text + "; newMaterial=" + ((Object)material).name + "; reason=" + reason + "; body=" + DrawableSuitsPlugin.DescribeUnityObject((Object)(object)__instance)); } } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Patch failed: DeadBodyInfo.ChangeMesh.prefix", exception); } } [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix(DeadBodyInfo __instance) { RunPatch("DeadBodyInfo.Start", __instance); } [HarmonyPostfix] [HarmonyPatch("ChangeMesh")] private static void ChangeMeshPostfix(DeadBodyInfo __instance) { RunPatch("DeadBodyInfo.ChangeMesh", __instance); } private static void RunPatch(string context, DeadBodyInfo body) { try { DrawableSuitsPlugin.EnsureRuntimeReady(context); DrawableSuitsPlugin.Registry?.ApplyToDeadBody(body, context); DrawableSuitsPlugin.Registry?.ScheduleDeadBodyReapply(body, context); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Patch failed: " + context, exception); } } } [HarmonyPatch(typeof(QuickMenuManager))] internal static class QuickMenuManagerPatches { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix(QuickMenuManager __instance) { RunPatch("QuickMenuManager.Start", delegate { PauseMenuButtonInjector.EnsureButton(__instance); }); } [HarmonyPostfix] [HarmonyPatch("OpenQuickMenu")] private static void OpenQuickMenuPostfix(QuickMenuManager __instance) { RunPatch("QuickMenuManager.OpenQuickMenu", delegate { PauseMenuButtonInjector.EnsureButton(__instance); PauseMenuButtonInjector.SelectIfNeeded(__instance); }); } private static void RunPatch(string context, Action action) { try { DrawableSuitsPlugin.EnsureRuntimeReady(context); action(); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Patch failed: " + context, exception); } } } internal static class PauseMenuButtonInjector { private sealed class MenuRow { public GameObject GameObject { get; } public RectTransform RectTransform { get; } public Button Button { get; } public string LabelText { get; } public bool IsDrawable { get { if (!(((Object)GameObject).name == "DrawableSuitsButton")) { return Normalize(LabelText).Contains("drawablesuits"); } return true; } } public MenuRow(GameObject gameObject, RectTransform rectTransform, Button button, string labelText) { GameObject = gameObject; RectTransform = rectTransform; Button = button; LabelText = labelText ?? string.Empty; } } private const string ButtonName = "DrawableSuitsButton"; private const string ButtonText = "DrawableSuits"; private const string ButtonTextWithInlinePrefix = "> DrawableSuits"; private const string PrefixText = ">"; private const float MinimumRowSpacing = 36f; private const float FallbackRowSpacing = 92f; public static void EnsureButton(QuickMenuManager quickMenu) { //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)quickMenu == (Object)null || (Object)(object)quickMenu.mainButtonsPanel == (Object)null) { DrawableSuitsDiagnostics.Warn($"EnsureButton skipped. quickMenuNull={(Object)(object)quickMenu == (Object)null}; mainButtonsPanelNull={(Object)(object)quickMenu?.mainButtonsPanel == (Object)null}"); return; } RectTransform component = quickMenu.mainButtonsPanel.GetComponent<RectTransform>(); if ((Object)(object)component == (Object)null) { DrawableSuitsDiagnostics.Warn("EnsureButton skipped because QuickMenuManager.mainButtonsPanel has no RectTransform."); return; } List<MenuRow> list = CollectRows((Transform)(object)component); DrawableSuitsDiagnostics.Info($"EnsureButton started. menuOpen={quickMenu.isMenuOpen}; panel={((Object)component).name}; rowsFound={list.Count}; labels=[{DescribeRows(list)}]"); MenuRow menuRow = ChooseTemplateRow(list); DrawableSuitsDiagnostics.Info("Pause-menu DrawableSuits template selected: label=" + (menuRow?.LabelText ?? "none") + "; object=" + (((menuRow != null) ? ((Object)menuRow.GameObject).name : null) ?? "none") + "; sibling=" + (((menuRow != null) ? ((Transform)menuRow.RectTransform).GetSiblingIndex().ToString() : null) ?? "none")); GameObject val = FindExistingButton((Transform)(object)component); if ((Object)(object)val != (Object)null && menuRow != null) { DrawableSuitsDiagnostics.Info("Replacing existing DrawableSuits pause button with a fresh clone so native prefix/arrow children match the current menu template."); val.SetActive(false); Object.Destroy((Object)(object)val); val = null; } if ((Object)(object)val == (Object)null) { DrawableSuitsDiagnostics.Info("No existing DrawableSuits pause button. template=" + ((menuRow != null) ? (menuRow.LabelText + "/" + ((Object)menuRow.GameObject).name) : "none") + "."); val = ((menuRow != null) ? CloneTemplate(menuRow) : CreateFallbackButton((Transform)(object)component)); } else { DrawableSuitsDiagnostics.Info($"Reusing existing DrawableSuits pause button. active={val.activeSelf}; siblingIndex={val.transform.GetSiblingIndex()}"); } if ((Object)(object)val == (Object)null) { DrawableSuitsDiagnostics.Warn("Could not create pause-menu DrawableSuits button."); return; } RemoveDuplicateButtons((Transform)(object)component, val); ConfigureButton(val, quickMenu, menuRow); PlaceButtonAndRows(component, val); RebuildNavigation(component); RectTransform component2 = val.GetComponent<RectTransform>(); Button component3 = val.GetComponent<Button>(); DrawableSuitsDiagnostics.Info(string.Format("EnsureButton complete. exists={0}; active={1}; anchoredPosition={2}; siblingIndex={3}; nav={4}; colors={5}; rowsAfter={6}", (Object)(object)val != (Object)null, val.activeSelf, ((component2 != null) ? ((object)component2.anchoredPosition/*cast due to .constrained prefix*/).ToString() : null) ?? "null", val.transform.GetSiblingIndex(), DescribeNavigation(component3), DescribeButtonColors(component3), CollectRows((Transform)(object)component).Count)); } public static void SelectIfNeeded(QuickMenuManager quickMenu) { if (!((Object)(object)quickMenu == (Object)null) && quickMenu.isMenuOpen && !((Object)(object)quickMenu.mainButtonsPanel == (Object)null) && !((Object)(object)EventSystem.current == (Object)null) && !((Object)(object)EventSystem.current.currentSelectedGameObject != (Object)null)) { List<MenuRow> rowsInVisualOrder = GetRowsInVisualOrder(quickMenu.mainButtonsPanel.GetComponent<RectTransform>()); if (rowsInVisualOrder.Count > 0) { EventSystem.current.SetSelectedGameObject(rowsInVisualOrder[0].GameObject); } } } private static GameObject FindExistingButton(Transform panel) { Button[] componentsInChildren = ((Component)panel).GetComponentsInChildren<Button>(true); foreach (Button val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "DrawableSuitsButton") { return ((Component)val).gameObject; } } return null; } private static void RemoveDuplicateButtons(Transform panel, GameObject keep) { Button[] componentsInChildren = ((Component)panel).GetComponentsInChildren<Button>(true); foreach (Button val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)(object)keep) && !(((Object)((Component)val).gameObject).name != "DrawableSuitsButton")) { ((Component)val).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val).gameObject); } } } private static GameObject CloneTemplate(MenuRow template) { //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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) GameObject obj = Object.Instantiate<GameObject>(template.GameObject, ((Transform)template.RectTransform).parent, false); ((Object)obj).name = "DrawableSuitsButton"; obj.transform.SetAsLastSibling(); obj.SetActive(true); DrawableSuitsDiagnostics.Info("Cloned pause-menu template '" + template.LabelText + "' into DrawableSuitsButton."); RectTransform component = obj.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null) { component.anchorMin = template.RectTransform.anchorMin; component.anchorMax = template.RectTransform.anchorMax; component.pivot = template.RectTransform.pivot; component.sizeDelta = template.RectTransform.sizeDelta; ((Transform)component).localScale = ((Transform)template.RectTransform).localScale; } return obj; } private static GameObject CreateFallbackButton(Transform panel) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011b: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("DrawableSuitsButton", new Type[5] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button), typeof(LayoutElement) }); val.transform.SetParent(panel, false); val.transform.SetAsLastSibling(); DrawableSuitsDiagnostics.Warn("Created fallback pause-menu DrawableSuits button because no cloneable row template was found."); val.GetComponent<RectTransform>().sizeDelta = new Vector2(260f, 54f); ((Graphic)val.GetComponent<Image>()).color = new Color(0.08f, 0.08f, 0.08f, 0.86f); LayoutElement component = val.GetComponent<LayoutElement>(); component.preferredWidth = 260f; component.preferredHeight = 54f; component.minHeight = 42f; GameObject val2 = new GameObject("Label", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent<RectTransform>(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = new Vector2(8f, 2f); component2.offsetMax = new Vector2(-8f, -2f); TextMeshProUGUI component3 = val2.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component3).alignment = (TextAlignmentOptions)514; ((TMP_Text)component3).fontSize = 24f; ((Graphic)component3).color = Color.white; TextMeshProUGUI componentInChildren = ((Component)panel).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)component3).font = ((TMP_Text)componentInChildren).font; ((TMP_Text)component3).fontSharedMaterial = ((TMP_Text)componentInChildren).fontSharedMaterial; ((TMP_Text)component3).fontSize = ((TMP_Text)componentInChildren).fontSize; ((Graphic)component3).color = ((Graphic)componentInChildren).color; } return val; } private static void ConfigureButton(GameObject buttonObject, QuickMenuManager quickMenu, MenuRow template) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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) ((Object)buttonObject).name = "DrawableSuitsButton"; buttonObject.SetActive(true); if (template != null) { ApplyTemplateStyle(template, buttonObject); } SetButtonText(buttonObject, template?.LabelText); EnsureNativePrefix(buttonObject, template); Button val = buttonObject.GetComponent<Button>() ?? buttonObject.AddComponent<Button>(); ((Selectable)val).interactable = true; val.onClick = new ButtonClickedEvent(); ((UnityEvent)val.onClick).AddListener((UnityAction)delegate { OpenEditorFromPauseMenu(quickMenu); }); DrawableSuitsDiagnostics.Info($"Configured pause-menu button listener. button={((Object)buttonObject).name}; interactable={((Selectable)val).interactable}; targetQuickMenuNull={(Object)(object)quickMenu == (Object)null}"); Navigation navigation = ((Selectable)val).navigation; ((Navigation)(ref navigation)).mode = (Mode)3; ((Selectable)val).navigation = navigation; } private static void ApplyTemplateStyle(MenuRow template, GameObject buttonObject) { //IL_0036: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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_0140: Unknown result type (might be due to invalid IL or missing references) RectTransform component = buttonObject.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null) { CopyRectTemplate(template.RectTransform, component); } Button button = template.Button; Button val = buttonObject.GetComponent<Button>() ?? buttonObject.AddComponent<Button>(); ((Selectable)val).transition = ((Selectable)button).transition; ((Selectable)val).colors = ((Selectable)button).colors; ((Selectable)val).spriteState = ((Selectable)button).spriteState; ((Selectable)val).animationTriggers = ((Selectable)button).animationTriggers; ((Selectable)val).targetGraphic = FindMatchingGraphic(((Selectable)button).targetGraphic, template.GameObject.transform, buttonObject.transform) ?? buttonObject.GetComponent<Graphic>() ?? buttonObject.GetComponentInChildren<Graphic>(true); Image component2 = template.GameObject.GetComponent<Image>(); Image component3 = buttonObject.GetComponent<Image>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component3 != (Object)null) { component3.sprite = component2.sprite; component3.overrideSprite = component2.overrideSprite; component3.type = component2.type; component3.preserveAspect = component2.preserveAspect; component3.fillCenter = component2.fillCenter; component3.fillMethod = component2.fillMethod; component3.fillAmount = component2.fillAmount; component3.fillClockwise = component2.fillClockwise; component3.fillOrigin = component2.fillOrigin; ((Graphic)component3).color = ((Graphic)component2).color; ((Graphic)component3).material = ((Graphic)component2).material; ((Graphic)component3).raycastTarget = ((Graphic)component2).raycastTarget; } LayoutElement component4 = template.GameObject.GetComponent<LayoutElement>(); LayoutElement val2 = buttonObject.GetComponent<LayoutElement>(); if ((Object)(object)component4 != (Object)null) { if (val2 == null) { val2 = buttonObject.AddComponent<LayoutElement>(); } val2.ignoreLayout = component4.ignoreLayout; val2.minWidth = component4.minWidth; val2.minHeight = component4.minHeight; val2.preferredWidth = component4.preferredWidth; val2.preferredHeight = component4.preferredHeight; val2.flexibleWidth = component4.flexibleWidth; val2.flexibleHeight = component4.flexibleHeight; val2.layoutPriority = component4.layoutPriority; } DrawableSuitsDiagnostics.Info("Applied pause-menu template style. template=" + template.LabelText + "/" + ((Object)template.GameObject).name + "; target=" + ((Object)buttonObject).name + "; colors=" + DescribeButtonColors(val)); } private static void SetButtonText(GameObject buttonObject, string templateLabelText) { TextMeshProUGUI[] componentsInChildren = buttonObject.GetComponentsInChildren<TextMeshProUGUI>(true); TextMeshProUGUI val = FindPrimaryTmpLabel(componentsInChildren, templateLabelText); List<string> list = new List<string>(); TextMeshProUGUI[] array = componentsInChildren; foreach (TextMeshProUGUI val2 in array) { if (!((Object)(object)val2 == (Object)null)) { if ((Object)(object)val2 == (Object)(object)val) { ((TMP_Text)val2).text = "DrawableSuits"; ((TMP_Text)val2).enableWordWrapping = false; } else { list.Add(((TMP_Text)val2).text ?? string.Empty); } } } Text[] componentsInChildren2 = buttonObject.GetComponentsInChildren<Text>(true); Text val3 = (((Object)(object)val == (Object)null) ? FindPrimaryLegacyLabel(componentsInChildren2, templateLabelText) : null); Text[] array2 = componentsInChildren2; foreach (Text val4 in array2) { if (!((Object)(object)val4 == (Object)null)) { if ((Object)(object)val4 == (Object)(object)val3) { val4.text = "DrawableSuits"; } else { list.Add(val4.text ?? string.Empty); } } } DrawableSuitsDiagnostics.Info("Pause-menu DrawableSuits text configured. templateLabel=" + (templateLabelText ?? "none") + "; primaryTmp=" + DescribeTmpLabel(val) + "; primaryLegacy=" + DescribeLegacyLabel(val3) + "; preserved=[" + string.Join(", ", list) + "]"); } private static void EnsureNativePrefix(GameObject buttonObject, MenuRow template) { TextMeshProUGUI val = FindPrefixText(buttonObject); if ((Object)(object)val != (Object)null) { TextMeshProUGUI label = FindPrimaryTmpLabel(buttonObject.GetComponentsInChildren<TextMeshProUGUI>(true), "DrawableSuits"); DrawableSuitsDiagnostics.Info($"PauseMenuPrefixMode mode=PreservedChild; prefix={((Object)((Component)val).gameObject).name}; prefixText='{((TMP_Text)val).text}'; primary={DescribeTmpLabel(label)}; rowRect={DescribeRect(buttonObject.GetComponent<RectTransform>())}; sibling={buttonObject.transform.GetSiblingIndex()}"); return; } Text val2 = FindPrefixLegacyText(buttonObject); if ((Object)(object)val2 != (Object)null) { Text label2 = FindPrimaryLegacyLabel(buttonObject.GetComponentsInChildren<Text>(true), "DrawableSuits"); DrawableSuitsDiagnostics.Info($"PauseMenuPrefixMode mode=PreservedChild; prefix={((Object)((Component)val2).gameObject).name}; prefixText='{val2.text}'; primary={DescribeLegacyLabel(label2)}; rowRect={DescribeRect(buttonObject.GetComponent<RectTransform>())}; sibling={buttonObject.transform.GetSiblingIndex()}"); return; } TextMeshProUGUI val3 = FindPrimaryTmpLabel(buttonObject.GetComponentsInChildren<TextMeshProUGUI>(true), "DrawableSuits"); if ((Object)(object)val3 != (Object)null) { ((TMP_Text)val3).text = "> DrawableSuits"; ((TMP_Text)val3).enableWordWrapping = false; DrawableSuitsDiagnostics.Info($"PauseMenuPrefixMode mode=InlineLabel; displayed='{((TMP_Text)val3).text}'; primary={DescribeTmpLabel(val3)}; rowRect={DescribeRect(buttonObject.GetComponent<RectTransform>())}; sibling={buttonObject.transform.GetSiblingIndex()}"); return; } Text val4 = FindPrimaryLegacyLabel(buttonObject.GetComponentsInChildren<Text>(true), "DrawableSuits"); if ((Object)(object)val4 != (Object)null) { val4.text = "> DrawableSuits"; DrawableSuitsDiagnostics.Info($"PauseMenuPrefixMode mode=InlineLabel; displayed='{val4.text}'; primary={DescribeLegacyLabel(val4)}; rowRect={DescribeRect(buttonObject.GetComponent<RectTransform>())}; sibling={buttonObject.transform.GetSiblingIndex()}"); } else { DrawableSuitsDiagnostics.Warn(string.Format("PauseMenuPrefixMode mode=SkippedNoPrimaryLabel; template={0}; button={1}; rowRect={2}; sibling={3}", template?.LabelText ?? "none", ((Object)buttonObject).name, DescribeRect(buttonObject.GetComponent<RectTransform>()), buttonObject.transform.GetSiblingIndex())); } } private static TextMeshProUGUI FindPrefixText(GameObject buttonObject) { TextMeshProUGUI[] componentsInChildren = buttonObject.GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val in componentsInChildren) { if ((Object)(object)val != (Object)null && Normalize(((TMP_Text)val).text) == ">") { return val; } } return null; } private static Text FindPrefixLegacyText(GameObject buttonObject) { Text[] componentsInChildren = buttonObject.GetComponentsInChildren<Text>(true); foreach (Text val in componentsInChildren) { if ((Object)(object)val != (Object)null && Normalize(val.text) == ">") { return val; } } return null; } private static void PlaceButtonAndRows(RectTransform panel, GameObject buttonObject) { List<MenuRow> rows = CollectRows((Transform)(object)panel); MenuRow menuRow = FindRowFor(buttonObject, rows); if (menuRow == null) { DrawableSuitsDiagnostics.Warn("PlaceButtonAndRows could not find the DrawableSuits row after collection."); return; } LayoutGroup component = ((Component)panel).GetComponent<LayoutGroup>(); if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled) { DrawableSuitsDiagnostics.Info("PlaceButtonAndRows using layout group " + ((object)component).GetType().Name + "."); InsertAfterResumeBySibling(rows, menuRow); LayoutRebuilder.ForceRebuildLayoutImmediate(panel); } else { DrawableSuitsDiagnostics.Info("PlaceButtonAndRows using explicit anchored positions."); PlaceRowsExplicitly(rows, menuRow); } } private static void InsertAfterResumeBySibling(List<MenuRow> rows, MenuRow drawable) { MenuRow menuRow = FindResumeRow(rows) ?? FirstNonDrawableRow(rows); if (menuRow == null) { ((Transform)drawable.RectTransform).SetAsLastSibling(); } else { ((Transform)drawable.RectTransform).SetSiblingIndex(((Transform)menuRow.RectTransform).GetSiblingIndex() + 1); } } private static void PlaceRowsExplicitly(List<MenuRow> rows, MenuRow drawable) { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_017b: 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_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) List<MenuRow> list = new List<MenuRow>(); foreach (MenuRow row in rows) { if ((Object)(object)((Transform)row.RectTransform).parent == (Object)(object)((Transform)drawable.RectTransform).parent) { list.Add(row); } } if (list.Count == 0) { DrawableSuitsDiagnostics.Warn("PlaceRowsExplicitly found no same-parent rows."); return; } MenuRow menuRow = FindResumeRow(list) ?? FirstNonDrawableRow(list); if (menuRow == null) { DrawableSuitsDiagnostics.Warn("PlaceRowsExplicitly found no resume or non-Drawable row."); return; } CopyRectTemplate(menuRow.RectTransform, drawable.RectTransform); list.Sort(CompareTopToBottom); List<MenuRow> list2 = new List<MenuRow>(); foreach (MenuRow item in list) { if (!item.IsDrawable) { list2.Add(item); } } int num = list2.IndexOf(menuRow); if (num < 0) { num = 0; } float num2 = DetectRowSpacing(list2); MenuRow menuRow2 = ((num + 1 < list2.Count) ? list2[num + 1] : null); float num3 = ((menuRow2 != null) ? Mathf.Abs(menuRow.RectTransform.anchoredPosition.y - menuRow2.RectTransform.anchoredPosition.y) : float.PositiveInfinity); bool num4 = menuRow2 != null && num3 < num2 * 1.45f; float y = menuRow.RectTransform.anchoredPosition.y - num2; int num5 = ((Transform)menuRow.RectTransform).GetSiblingIndex() + 1; Vector2 anchoredPosition = menuRow.RectTransform.anchoredPosition; anchoredPosition.y = y; drawable.RectTransform.anchoredPosition = anchoredPosition; ((Transform)drawable.RectTransform).SetSiblingIndex(num5); int num6 = 0; if (num4) { for (int i = num + 1; i < list2.Count; i++) { MenuRow menuRow3 = list2[i]; Vector2 anchoredPosition2 = menuRow3.RectTransform.anchoredPosition; anchoredPosition2.y -= num2; menuRow3.RectTransform.anchoredPosition = anchoredPosition2; ((Transform)menuRow3.RectTransform).SetSiblingIndex(num5 + 1 + (i - num - 1)); num6++; } } DrawableSuitsDiagnostics.Info(string.Format("PlaceRowsExplicitly nativeCount={0}; resume={1}; next={2}; spacing={3}; gapToNext={4}; shiftedLowerRows={5}; drawablePosition={6}; drawableSibling={7}", list2.Count, menuRow.LabelText, menuRow2?.LabelText ?? "none", num2, num3, num6, drawable.RectTransform.anchoredPosition, ((Transform)drawable.RectTransform).GetSiblingIndex())); } private static void CopyRectTemplate(RectTransform source, RectTransform target) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) target.anchorMin = source.anchorMin; target.anchorMax = source.anchorMax; target.pivot = source.pivot; target.sizeDelta = source.sizeDelta; ((Transform)target).localScale = ((Transform)source).localScale; } private static float DetectRowSpacing(List<MenuRow> rows) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) List<float> list = new List<float>(); foreach (MenuRow row in rows) { list.Add(row.RectTransform.anchoredPosition.y); } list.Sort((float a, float b) => b.CompareTo(a)); List<float> list2 = new List<float>(); for (int num = 0; num < list.Count - 1; num++) { float num2 = Mathf.Abs(list[num] - list[num + 1]); if (num2 >= 36f && num2 <= 240f) { list2.Add(num2); } } if (list2.Count > 0) { list2.Sort(); return Mathf.Max(36f, list2[list2.Count / 2]); } float num3 = 0f; foreach (MenuRow row2 in rows) { float[] obj = new float[3] { num3, 0f, 0f }; Rect rect = row2.RectTransform.rect; obj[1] = ((Rect)(ref rect)).height; obj[2] = row2.RectTransform.sizeDelta.y; num3 = Mathf.Max(obj); } return Mathf.Max(92f, num3 + 24f); } private static Graphic FindMatchingGraphic(Graphic templateGraphic, Transform templateRoot, Transform targetRoot) { if ((Object)(object)templateGraphic == (Object)null || (Object)(object)templateRoot == (Object)null || (Object)(object)targetRoot == (Object)null) { return null; } string relativePath = GetRelativePath(templateRoot, ((Component)templateGraphic).transform); Transform val = (string.IsNullOrEmpty(relativePath) ? targetRoot : targetRoot.Find(relativePath)); Graphic val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Graphic>() : null); DrawableSuitsDiagnostics.Info("Pause-menu targetGraphic mapping. templateGraphic=" + ((Object)templateGraphic).name + "; path=" + relativePath + "; mapped=" + (((val2 != null) ? ((Object)val2).name : null) ?? "none")); return val2; } private static string GetRelativePath(Transform root, Transform child) { if ((Object)(object)root == (Object)null || (Object)(object)child == (Object)null || (Object)(object)child == (Object)(object)root) { return string.Empty; } List<string> list = new List<string>(); Transform val = child; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Insert(0, ((Object)val).name); val = val.parent; } if (!((Object)(object)val == (Object)(object)root)) { return string.Empty; } return string.Join("/", list); } private static void RebuildNavigation(RectTransform panel) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) List<MenuRow> rowsInVisualOrder = GetRowsInVisualOrder(panel); if (rowsInVisualOrder.Count == 0) { DrawableSuitsDiagnostics.Warn("RebuildNavigation skipped because no rows were collected."); return; } for (int i = 0; i < rowsInVisualOrder.Count; i++) { Button button = rowsInVisualOrder[i].Button; Navigation navigation = ((Selectable)button).navigation; ((Navigation)(ref navigation)).mode = (Mode)4; ((Navigation)(ref navigation)).selectOnUp = (Selectable)(object)((i > 0) ? rowsInVisualOrder[i - 1].Button : null); ((Navigation)(ref navigation)).selectOnDown = (Selectable)(object)((i < rowsInVisualOrder.Count - 1) ? rowsInVisualOrder[i + 1].Button : null); ((Navigation)(ref navigation)).selectOnLeft = null; ((Navigation)(ref navigation)).selectOnRight = null; ((Selectable)button).navigation = navigation; } DrawableSuitsDiagnostics.Info($"RebuildNavigation complete. rowCount={rowsInVisualOrder.Count}; rows=[{DescribeRows(rowsInVisualOrder)}]"); } private static List<MenuRow> GetRowsInVisualOrder(RectTransform panel) { List<MenuRow> list = CollectRows((Transform)(object)panel); list.Sort(CompareTopToBottom); return list; } private static List<MenuRow> CollectRows(Transform root) { List<MenuRow> list = new List<MenuRow>(); if ((Object)(object)root == (Object)null) { return list; } Button[] componentsInChildren = ((Component)root).GetComponentsInChildren<Button>(true); foreach (Button val in componentsInChildren) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf) { continue; } RectTransform component = ((Component)val).GetComponent<RectTransform>(); if (!((Object)(object)component == (Object)null)) { string labelText = GetLabelText(((Component)val).gameObject); if (!string.IsNullOrWhiteSpace(labelText) || !(((Object)((Component)val).gameObject).name != "DrawableSuitsButton")) { list.Add(new MenuRow(((Component)val).gameObject, component, val, labelText)); } } } return list; } private static string GetLabelText(GameObject gameObject) { TextMeshProUGUI val = FindPrimaryTmpLabel(gameObject.GetComponentsInChildren<TextMeshProUGUI>(true), null); if ((Object)(object)val != (Object)null) { return ((TMP_Text)val).text ?? string.Empty; } Text val2 = FindPrimaryLegacyLabel(gameObject.GetComponentsInChildren<Text>(true), null); if (!((Object)(object)val2 != (Object)null)) { return string.Empty; } return val2.text ?? string.Empty; } private static MenuRow ChooseTemplateRow(List<MenuRow> rows) { return FindResumeRow(rows) ?? FindRowContaining(rows, "invite") ?? FindRowContaining(rows, "settings") ?? FindRowContaining(rows, "lethalconfig") ?? FirstNonDrawableRow(rows); } private static MenuRow FindResumeRow(List<MenuRow> rows) { return FindRowContaining(rows, "resume"); } private static MenuRow FindRowContaining(List<MenuRow> rows, string token) { foreach (MenuRow row in rows) { if (!row.IsDrawable && Normalize(row.LabelText).Contains(token)) { return row; } } return null; } private static MenuRow FirstNonDrawableRow(List<MenuRow> rows) { foreach (MenuRow row in rows) { if (!row.IsDrawable) { return row; } } return null; } private static MenuRow FindRowFor(GameObject gameObject, List<MenuRow> rows) { foreach (MenuRow row in rows) { if ((Object)(object)row.GameObject == (Object)(object)gameObject) { return row; } } return null; } private static int CompareTopToBottom(MenuRow a, MenuRow b) { //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_0019: Unknown result type (might be due to invalid IL or missing references) int num = b.RectTransform.anchoredPosition.y.CompareTo(a.RectTransform.anchoredPosition.y); if (num == 0) { return ((Transform)a.RectTransform).GetSiblingIndex().CompareTo(((Transform)b.RectTransform).GetSiblingIndex()); } return num; } private static string Normalize(string value) { return (value ?? string.Empty).Replace(" ", string.Empty).Replace("\n", string.Empty).ToLowerInvariant(); } private static TextMeshProUGUI FindPrimaryTmpLabel(TextMeshProUGUI[] labels, string preferredText) { TextMeshProUGUI result = null; float num = float.MinValue; string preferred = Normalize(preferredText); foreach (TextMeshProUGUI val in labels) { if (!((Object)(object)val == (Object)null)) { float num2 = ScoreLabelCandidate(((TMP_Text)val).text, ((TMP_Text)val).rectTransform, ((TMP_Text)val).fontSize, preferred); if (num2 > num) { num = num2; result = val; } } } return result; } private static Text FindPrimaryLegacyLabel(Text[] labels, string preferredText) { Text result = null; float num = float.MinValue; string preferred = Normalize(preferredText); foreach (Text val in labels) { if (!((Object)(object)val == (Object)null)) { float num2 = ScoreLabelCandidate(val.text, ((Graphic)val).rectTransform, val.fontSize, preferred); if (num2 > num) { num = num2; result = val; } } } return result; } private static float ScoreLabelCandidate(string text, RectTransform rect, float fontSize, string preferred) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) string text2 = Normalize(text); if (string.IsNullOrEmpty(text2)) { return -100000f; } float num = 0f; if (!string.IsNullOrEmpty(preferred) && text2.Contains(preferred)) { num += 100000f; } num = ((!HasLetterOrDigit(text2)) ? (num - 1000f) : (num + (1000f + (float)text2.Length * 25f))); if ((Object)(object)rect != (Object)null) { float num2 = num; Rect rect2 = rect.rect; num = num2 + Mathf.Max(0f, ((Rect)(ref rect2)).width) * 0.2f; float num3 = num; rect2 = rect.rect; num = num3 + Mathf.Max(0f, ((Rect)(ref rect2)).height) * 0.1f; } return num + fontSize; } private static bool HasLetterOrDigit(string value) { for (int i = 0; i < value.Length; i++) { if (char.IsLetterOrDigit(value[i])) { return true; } } return false; } private static string DescribeTmpLabel(TextMeshProUGUI label) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)label == (Object)null)) { return $"{((Object)((Component)label).gameObject).name}='{((TMP_Text)label).text}' rect={((TMP_Text)label).rectTransform.rect}"; } return "none"; } private static string DescribeLegacyLabel(Text label) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)label == (Object)null)) { return $"{((Object)((Component)label).gameObject).name}='{label.text}' rect={((Graphic)label).rectTransform.rect}"; } return "none"; } private static string DescribeRect(RectTransform rect) { //IL_000f: 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_0025: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rect == (Object)null)) { return $"rect={rect.rect}; anchored={rect.anchoredPosition}; sizeDelta={rect.sizeDelta}"; } return "rect=null"; } private static string DescribeButtonColors(Button button) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0051: 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_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)button == (Object)null) { return "button=null"; } ColorBlock colors = ((Selectable)button).colors; object[] obj = new object[7] { ((Selectable)button).transition, ((ColorBlock)(ref colors)).normalColor, ((ColorBlock)(ref colors)).highlightedColor, ((ColorBlock)(ref colors)).selectedColor, ((ColorBlock)(ref colors)).pressedColor, ((ColorBlock)(ref colors)).disabledColor, null }; Graphic targetGraphic = ((Selectable)button).targetGraphic; obj[6] = ((targetGraphic != null) ? ((Object)targetGraphic).name : null) ?? "null"; return string.Format("transition={0}; normal={1}; highlighted={2}; selected={3}; pressed={4}; disabled={5}; targetGraphic={6}", obj); } private static string DescribeNavigation(Button button) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)button == (Object)null) { return "button=null"; } Navigation navigation = ((Selectable)button).navigation; object arg = ((Navigation)(ref navigation)).mode; Selectable selectOnUp = ((Navigation)(ref navigation)).selectOnUp; string arg2 = ((selectOnUp != null) ? ((Object)((Component)selectOnUp).gameObject).name : null) ?? "null"; Selectable selectOnDown = ((Navigation)(ref navigation)).selectOnDown; return string.Format("mode={0}; up={1}; down={2}", arg, arg2, ((selectOnDown != null) ? ((Object)((Component)selectOnDown).gameObject).name : null) ?? "null"); } private static void OpenEditorFromPauseMenu(QuickMenuManager quickMenu) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) DrawableSuitsDiagnostics.Info($"Pause-menu DrawableSuits button clicked. quickMenuNull={(Object)(object)quickMenu == (Object)null}; menuOpenBeforeClose={quickMenu?.isMenuOpen}; cursorVisible={Cursor.visible}; cursorLock={Cursor.lockState}"); try { if (quickMenu != null) { quickMenu.CloseQuickMenu(); } DrawableSuitsDiagnostics.Info($"Quick menu close requested. menuOpenAfterClose={quickMenu?.isMenuOpen}"); } catch (Exception exception) { DrawableSuitsDiagnostics.Exception("Failed to close quick menu before opening editor", exception); } bool flag = DrawableSuitsPlugin.EnsureRuntimeRe