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 Second Exterior Ship Ladder Fix v0.4.5
BepInEx/plugins/ShipLadderInteractionFix/ShipLadderInteractionFix.dll
Decompiled 10 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; [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("ShipLadderInteractionFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ShipLadderInteractionFix")] [assembly: AssemblyTitle("ShipLadderInteractionFix")] [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; } } } [BepInPlugin("bernard.shipladderinteractionfix", "Ship Ladder Interaction Fix", "0.4.5")] public sealed class ShipLadderInteractionFix : BaseUnityPlugin { public const string PluginGuid = "bernard.shipladderinteractionfix"; public const string PluginName = "Ship Ladder Interaction Fix"; public const string PluginVersion = "0.4.5"; private Harmony _harmony; internal static ManualLogSource LogSource; private bool _patchMapLogged; internal static ShipLadderInteractionFix Instance; internal static bool VerboseDiagnostics = false; private static readonly HashSet<int> QuietObservationPending = new HashSet<int>(); private static readonly Dictionary<int, float> QuietObservationLastScheduledAt = new Dictionary<int, float>(); private static readonly HashSet<int> ReportedCandidates = new HashSet<int>(); private static readonly HashSet<int> ReportedRepairs = new HashSet<int>(); private static readonly object ReportLock = new object(); private static readonly HashSet<int> DetailedGraphLogged = new HashSet<int>(); private static readonly object DetailLock = new object(); private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown LogSource = ((BaseUnityPlugin)this).Logger; Instance = this; _harmony = new Harmony("bernard.shipladderinteractionfix"); _harmony.PatchAll(typeof(ShipLadderInteractionFix).Assembly); SceneManager.sceneLoaded += OnSceneLoaded; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded v0.4.5 targeted upper exterior ladder room-gate repair. For the exact OutsideShipRoom0 ladder only, temporarily bypasses player.isInHangarShipRoom during vanilla InteractTrigger.Interact, restores the original value immediately afterward (including exceptions), leaves obstruction/coroutine/climb state to vanilla, and uses compact logs."); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; if (_harmony != null) { _harmony.UnpatchSelf(); } LogSource = null; Instance = null; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { try { GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { InspectAndRepair(rootGameObjects[i].transform, "scene-loaded:" + ((Scene)(ref scene)).name); } if (!_patchMapLogged && ((Scene)(ref scene)).name.IndexOf("SampleSceneRelay", StringComparison.OrdinalIgnoreCase) >= 0) { _patchMapLogged = true; LogHarmonyPatchOwners("InteractTrigger", "Interact", 1); LogHarmonyPatchOwners("GameNetcodeStuff.PlayerControllerB", "Interact_performed", -1); LogHarmonyPatchOwners("GameNetcodeStuff.PlayerControllerB", "InteractTriggerUseConditionsMet", -1); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Scene scan failed safely: " + ex.GetType().Name + ": " + ex.Message)); } } internal static void RunRepair(NetworkObject networkObject, MethodBase spawnMethod) { if ((Object)(object)networkObject == (Object)null) { return; } try { InspectAndRepair(((Component)networkObject).transform, "before-network-spawn:" + ((spawnMethod == null) ? "unknown" : spawnMethod.Name)); } catch (Exception ex) { ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogWarning((object)("Spawn-time ladder scan failed safely: " + ex.GetType().Name + ": " + ex.Message)); } } } private static void InspectAndRepair(Transform root, string context) { if ((Object)(object)root == (Object)null) { return; } InteractTrigger[] componentsInChildren = ((Component)root).GetComponentsInChildren<InteractTrigger>(true); foreach (InteractTrigger val in componentsInChildren) { if ((Object)(object)val == (Object)null || !LooksLikeLadderTrigger(((Component)val).transform) || !IsInShipHierarchy(((Component)val).transform)) { continue; } int instanceID = ((Object)val).GetInstanceID(); GetHierarchyPath(((Component)val).transform); bool enabled = ((Behaviour)val).enabled; bool activeInHierarchy = ((Component)val).gameObject.activeInHierarchy; if (!enabled) { ((Behaviour)val).enabled = true; lock (ReportLock) { if (ReportedRepairs.Add(instanceID)) { Debug.LogWarning((object)("[ShipLadderInteractionFix] LADDER_DISCOVERY_REPAIRED: " + DescribeLadderIdentity(((Component)val).transform) + "; ActiveInHierarchy=" + activeInHierarchy + "; Context=" + context)); } } continue; } lock (ReportLock) { if (ReportedCandidates.Add(instanceID)) { Debug.Log((object)("[ShipLadderInteractionFix] LADDER_DISCOVERY: " + DescribeLadderIdentity(((Component)val).transform) + "; ActiveInHierarchy=" + activeInHierarchy + "; Context=" + context)); } } } } internal static bool IsShipLadderTrigger(object triggerObject, out string path) { path = "<no-transform>"; if (triggerObject == null) { return false; } Component val = (Component)((triggerObject is Component) ? triggerObject : null); if ((Object)(object)val == (Object)null) { return false; } path = GetHierarchyPath(val.transform); if (LooksLikeLadderTrigger(val.transform)) { return IsInShipHierarchy(val.transform); } return false; } internal static int GetInstanceId(object obj) { Component val = (Component)((obj is Component) ? obj : null); if (!((Object)(object)val == (Object)null)) { return ((Object)val).GetInstanceID(); } return 0; } internal static string GetHierarchyPath(Transform transform) { List<string> list = new List<string>(); Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num++ < 40) { list.Add(((Object)val).name ?? "<unnamed>"); val = val.parent; } list.Reverse(); return string.Join("/", list); } internal static string ClassifyLadder(Transform transform) { if ((Object)(object)transform == (Object)null) { return "UNKNOWN_LADDER"; } string hierarchyPath = GetHierarchyPath(transform); if (hierarchyPath.IndexOf("/OutsideShipRoom0/", StringComparison.OrdinalIgnoreCase) >= 0) { return "SECOND_EXTERIOR_LADDER_CANDIDATE (OutsideShipRoom0 duplicate)"; } if (hierarchyPath.IndexOf("/OutsideShipRoom/", StringComparison.OrdinalIgnoreCase) >= 0) { return "PRIMARY_EXTERIOR_LADDER_REFERENCE (OutsideShipRoom)"; } if (hierarchyPath.IndexOf("/LadderShort (1)/", StringComparison.OrdinalIgnoreCase) >= 0) { return "SHORT_LADDER_CANDIDATE_2 (LadderShort (1))"; } if (hierarchyPath.IndexOf("/LadderShort/", StringComparison.OrdinalIgnoreCase) >= 0) { return "SHORT_LADDER_CANDIDATE_1 (LadderShort)"; } return "OTHER_SHIP_LADDER_CANDIDATE"; } internal static string DescribeLadderIdentity(Transform transform) { //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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)transform == (Object)null) { return "role=UNKNOWN_LADDER; path=<no-transform>"; } Vector3 position = transform.position; Vector3 localPosition = transform.localPosition; return "role=" + ClassifyLadder(transform) + "; instanceId=" + ((Object)((Component)transform).gameObject).GetInstanceID() + "; worldPos=(" + position.x.ToString("0.000", CultureInfo.InvariantCulture) + "," + position.y.ToString("0.000", CultureInfo.InvariantCulture) + "," + position.z.ToString("0.000", CultureInfo.InvariantCulture) + "); localPos=(" + localPosition.x.ToString("0.000", CultureInfo.InvariantCulture) + "," + localPosition.y.ToString("0.000", CultureInfo.InvariantCulture) + "," + localPosition.z.ToString("0.000", CultureInfo.InvariantCulture) + "); path=" + GetHierarchyPath(transform); } private void LogHarmonyPatchOwners(string typeName, string methodName, int parameterCount) { try { Type type = AccessTools.TypeByName(typeName); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("PATCH_MAP: target type not found: " + typeName + "::" + methodName)); return; } MethodBase methodBase = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == methodName && (parameterCount < 0 || m.GetParameters().Length == parameterCount) orderby m.GetParameters().Length select m).FirstOrDefault(); if (methodBase == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("PATCH_MAP: target method not found: " + typeName + "::" + methodName)); return; } object patchInfo = Harmony.GetPatchInfo(methodBase); if (patchInfo == null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("PATCH_MAP: no Harmony patch info: " + typeName + "::" + methodName)); return; } string[] array = new string[4] { "Prefixes", "Postfixes", "Transpilers", "Finalizers" }; foreach (string text in array) { IEnumerable enumerable = GetMember(patchInfo, text) as IEnumerable; int num2 = 0; if (enumerable != null) { foreach (object item in enumerable) { num2++; object obj = GetMember(item, "PatchMethod") ?? GetMember(item, "patchMethod"); string text2 = Convert.ToString(GetMember(item, "owner") ?? GetMember(item, "Owner")) ?? "<unknown-owner>"; string text3 = Convert.ToString(GetMember(item, "priority") ?? GetMember(item, "Priority")) ?? "?"; string text4 = ((obj is MethodBase methodBase2) ? (methodBase2.DeclaringType?.FullName + "." + methodBase2.Name) : "<unknown-method>"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("PATCH_MAP: target=" + typeName + "::" + methodName + "; kind=" + text + "; owner=" + text2 + "; priority=" + text3 + "; patch=" + text4)); } } if (num2 == 0) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("PATCH_MAP: target=" + typeName + "::" + methodName + "; kind=" + text + "; none")); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("PATCH_MAP failed safely for " + typeName + "::" + methodName + ": " + ex.GetType().Name + ": " + ex.Message)); } } private static bool LooksLikeLadderTrigger(Transform transform) { Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num++ < 8) { if ((((Object)val).name ?? string.Empty).IndexOf("ladder", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } val = val.parent; } return false; } private static bool IsInShipHierarchy(Transform transform) { Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num++ < 25) { string text = (((Object)val).name ?? string.Empty).ToLowerInvariant(); if (text.Contains("hangarship") || text.Contains("ship") || text.Contains("2story") || text.Contains("2-story") || text.Contains("melanie")) { return true; } val = val.parent; } return false; } internal static object GetMember(object instance, string name) { if (instance == null || string.IsNullOrEmpty(name)) { return null; } try { Type type = instance.GetType(); while (type != null) { FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(instance, null); } type = type.BaseType; } } catch { } return null; } internal static bool GetBool(object instance, string name, bool fallback = false) { object member = GetMember(instance, name); if (member is bool) { return (bool)member; } return fallback; } internal static float GetFloat(object instance, string name, float fallback = 0f) { object member = GetMember(instance, name); try { return (member == null) ? fallback : Convert.ToSingle(member); } catch { return fallback; } } internal static bool TrySetBool(object instance, string name, bool value) { if (instance == null || string.IsNullOrEmpty(name)) { return false; } try { Type type = instance.GetType(); while (type != null) { FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { if ((bool)field.GetValue(instance) != value) { field.SetValue(instance, value); } return true; } PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(bool) && property.CanWrite && property.GetIndexParameters().Length == 0) { if (!object.Equals(property.GetValue(instance, null), value)) { property.SetValue(instance, value, null); } return true; } type = type.BaseType; } } catch (Exception ex) { ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogDebug((object)("Could not set " + name + ": " + ex.GetType().Name)); } } return false; } internal static bool TrySetFloat(object instance, string name, float value) { if (instance == null || string.IsNullOrEmpty(name)) { return false; } try { Type type = instance.GetType(); while (type != null) { FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(float)) { field.SetValue(instance, value); return true; } PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(float) && property.CanWrite && property.GetIndexParameters().Length == 0) { property.SetValue(instance, value, null); return true; } type = type.BaseType; } } catch (Exception ex) { ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogDebug((object)("Could not set float " + name + ": " + ex.GetType().Name)); } } return false; } internal static string DescribeTrigger(object trigger, object player = null) { if (trigger == null) { return "trigger=null"; } Component val = (Component)((trigger is Component) ? trigger : null); string text = (((Object)(object)val == (Object)null) ? "role=UNKNOWN_LADDER; path=<no-transform>" : DescribeLadderIdentity(val.transform)); string[] obj = new string[17] { text, "; enabled=", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null }; Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null); obj[2] = ((val2 != null) ? val2.enabled.ToString() : "n/a"); obj[3] = "; active="; obj[4] = (((Object)(object)val != (Object)null) ? val.gameObject.activeInHierarchy.ToString() : "n/a"); obj[5] = "; interactable="; obj[6] = GetBool(trigger, "interactable").ToString(); obj[7] = "; isLadder="; obj[8] = GetBool(trigger, "isLadder").ToString(); obj[9] = "; usingLadder="; obj[10] = GetBool(trigger, "usingLadder").ToString(); obj[11] = "; holdInteraction="; obj[12] = GetBool(trigger, "holdInteraction").ToString(); obj[13] = "; oneHandedAllowed="; obj[14] = GetBool(trigger, "oneHandedItemAllowed").ToString(); obj[15] = "; twoHandedAllowed="; obj[16] = GetBool(trigger, "twoHandedItemAllowed").ToString(); string text2 = string.Concat(obj); if (player != null) { text2 = text2 + "; playerClimbing=" + GetBool(player, "isClimbingLadder") + "; playerInSpecialAnimation=" + GetBool(player, "inSpecialInteractAnimation") + "; playerDisableInteract=" + GetBool(player, "disableInteract") + "; playerTyping=" + GetBool(player, "isTypingChat") + "; playerTerminal=" + GetBool(player, "inTerminalMenu") + "; playerJetpack=" + GetBool(player, "jetpackControls") + "; timeSinceSlotSwitch=" + GetFloat(player, "timeSinceSwitchingSlots", -1f).ToString("0.000"); } return text2; } internal static object ResolvePlayerController(object argument) { Transform val = (Transform)((argument is Transform) ? argument : null); if ((Object)(object)val == (Object)null) { Component val2 = (Component)((argument is Component) ? argument : null); if (val2 != null) { val = val2.transform; } } if ((Object)(object)val == (Object)null) { return null; } try { Type type = AccessTools.TypeByName("GameNetcodeStuff.PlayerControllerB"); if (type == null) { return null; } Transform val3 = val; int num = 0; while ((Object)(object)val3 != (Object)null && num++ < 12) { Component component = ((Component)val3).GetComponent(type); if ((Object)(object)component != (Object)null) { return component; } val3 = val3.parent; } Object[] array = Object.FindObjectsOfType(type); if (array != null) { Object[] array2 = array; foreach (Object val4 in array2) { if (!(val4 == (Object)null)) { object member = GetMember(val4, "thisPlayerBody"); Transform val5 = (Transform)((member is Transform) ? member : null); if ((Object)(object)val5 != (Object)null && ((Object)(object)val5 == (Object)(object)val || val.IsChildOf(val5) || val5.IsChildOf(val))) { return val4; } } } } } catch (Exception ex) { ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogDebug((object)("Player resolution failed safely: " + ex.GetType().Name)); } } return null; } internal static string DescribeArgument(object argument) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (argument == null) { return "argument=<null>"; } Transform val = (Transform)((argument is Transform) ? argument : null); if (val != null) { Vector3 position = val.position; return "argumentType=Transform; argument=" + ((Object)val).name + "#" + ((Object)((Component)val).gameObject).GetInstanceID() + "; position=(" + position.x.ToString("0.000", CultureInfo.InvariantCulture) + "," + position.y.ToString("0.000", CultureInfo.InvariantCulture) + "," + position.z.ToString("0.000", CultureInfo.InvariantCulture) + "); path=" + GetHierarchyPath(val); } Component val2 = (Component)((argument is Component) ? argument : null); if (val2 != null) { return "argumentType=" + ((object)val2).GetType().FullName + "#" + ((Object)val2).GetInstanceID() + "; path=" + GetHierarchyPath(val2.transform); } return "argumentType=" + argument.GetType().FullName; } internal static string DescribePlayer(object player) { if (player == null) { return "playerController=<unresolved>"; } object member = GetMember(player, "thisPlayerBody"); Transform val = (Transform)((member is Transform) ? member : null); return "playerControllerType=" + player.GetType().FullName + "#" + GetInstanceId(player) + "; playerBody=" + (((Object)(object)val == (Object)null) ? "<unresolved>" : (((Object)val).name + "#" + ((Object)((Component)val).gameObject).GetInstanceID() + " path=" + GetHierarchyPath(val))) + "; isClimbingLadder=" + GetBool(player, "isClimbingLadder") + "; inSpecialInteractAnimation=" + GetBool(player, "inSpecialInteractAnimation") + "; disableInteract=" + GetBool(player, "disableInteract") + "; isPlayerDead=" + GetBool(player, "isPlayerDead") + "; isPlayerControlled=" + GetBool(player, "isPlayerControlled") + "; isTypingChat=" + GetBool(player, "isTypingChat") + "; inTerminalMenu=" + GetBool(player, "inTerminalMenu") + "; inSpecialMenu=" + GetBool(player, "inSpecialMenu") + "; jetpackControls=" + GetBool(player, "jetpackControls") + "; isHoldingObject=" + GetBool(player, "isHoldingObject") + "; twoHanded=" + GetBool(player, "twoHanded") + "; timeSinceSwitchingSlots=" + GetFloat(player, "timeSinceSwitchingSlots", -1f).ToString("0.000", CultureInfo.InvariantCulture); } private static bool IsRelevantFieldName(string name) { return new string[17] { "ladder", "interact", "anim", "player", "trigger", "position", "transform", "parent", "target", "reference", "body", "special", "climb", "cooldown", "hold", "handed", "active" }.Any((string term) => name.IndexOf(term, StringComparison.OrdinalIgnoreCase) >= 0); } private static string SummarizeFieldValue(object value) { //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) try { if (value == null) { return "<null>"; } Object val = (Object)((value is Object) ? value : null); if (val != null) { if (val == (Object)null) { return "<destroyed Unity object>"; } Component val2 = (Component)(object)((val is Component) ? val : null); if (val2 != null) { return ((object)val2).GetType().FullName + "#" + ((Object)val2).GetInstanceID() + " path=" + GetHierarchyPath(val2.transform); } GameObject val3 = (GameObject)(object)((val is GameObject) ? val : null); if (val3 != null) { return "GameObject#" + ((Object)val3).GetInstanceID() + " path=" + GetHierarchyPath(val3.transform); } return ((object)val).GetType().FullName + "#" + val.GetInstanceID() + " name=" + val.name; } if (value is Vector3 val4) { return "(" + val4.x.ToString("0.000", CultureInfo.InvariantCulture) + "," + val4.y.ToString("0.000", CultureInfo.InvariantCulture) + "," + val4.z.ToString("0.000", CultureInfo.InvariantCulture) + ")"; } if (value is Array array) { return value.GetType().GetElementType()?.Name + "[] length=" + array.Length; } string text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? "<no-string>"; return (text.Length > 120) ? (text.Substring(0, 117) + "...") : text.Replace("\r", " ").Replace("\n", " "); } catch { return "<format-failed>"; } } private static string RelevantFields(object target) { if (target == null) { return "<null>"; } List<string> list = new List<string>(); Type type = target.GetType(); int num = 0; while (type != null && type != typeof(object) && num < 100) { FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsStatic && IsRelevantFieldName(fieldInfo.Name)) { if (num++ >= 100) { break; } try { list.Add(fieldInfo.Name + ":" + fieldInfo.FieldType.Name + "=" + SummarizeFieldValue(fieldInfo.GetValue(target))); } catch (Exception ex) { list.Add(fieldInfo.Name + "=<read-failed:" + ex.GetType().Name + ">"); } } } type = type.BaseType; } if (list.Count != 0) { return string.Join(" | ", list); } return "<no matching fields>"; } internal static void LogLadderSnapshot(object trigger, object argument, string stage) { if (!VerboseDiagnostics) { return; } Component val = (Component)((trigger is Component) ? trigger : null); if ((Object)(object)val == (Object)null || !IsShipLadderTrigger(trigger, out var _)) { return; } object player = ResolvePlayerController(argument); ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogInfo((object)("LADDER_DIAG " + stage + ": " + DescribeTrigger(trigger) + "; " + DescribeArgument(argument) + "; " + DescribePlayer(player))); } bool flag; lock (DetailLock) { flag = DetailedGraphLogged.Add(((Object)val).GetInstanceID()); } if (!flag) { return; } ManualLogSource logSource2 = LogSource; if (logSource2 != null) { logSource2.LogInfo((object)("LADDER_TRIGGER_FIELDS: " + ((object)val).GetType().FullName + "#" + ((Object)val).GetInstanceID() + "; " + RelevantFields(val))); } List<Transform> list = new List<Transform>(); HashSet<int> hashSet = new HashSet<int>(); Transform val2 = val.transform; int num = 0; while ((Object)(object)val2 != (Object)null && num < 3) { if (hashSet.Add(((Object)val2).GetInstanceID())) { list.Add(val2); } num++; val2 = val2.parent; } Transform val3 = (((Object)(object)val.transform.parent != (Object)null) ? val.transform.parent : val.transform); Transform[] componentsInChildren = ((Component)val3).GetComponentsInChildren<Transform>(true); foreach (Transform val4 in componentsInChildren) { if (!((Object)(object)val4 == (Object)null)) { int num2 = 0; Transform val5 = val4; while ((Object)(object)val5 != (Object)null && (Object)(object)val5 != (Object)(object)val3 && num2 <= 4) { num2++; val5 = val5.parent; } if ((Object)(object)val5 == (Object)(object)val3 && num2 <= 4 && hashSet.Add(((Object)val4).GetInstanceID())) { list.Add(val4); } if (list.Count >= 40) { break; } } } foreach (Transform item2 in list) { if ((Object)(object)item2 == (Object)null) { continue; } Component[] components = ((Component)item2).GetComponents<Component>(); List<string> list2 = new List<string>(); Component[] array = components; foreach (Component val6 in array) { object item; if (!((Object)(object)val6 == (Object)null)) { string? fullName = ((object)val6).GetType().FullName; string text = ((Object)val6).GetInstanceID().ToString(); Behaviour val7 = (Behaviour)(object)((val6 is Behaviour) ? val6 : null); item = fullName + "#" + text + ((val7 != null) ? (";enabled=" + val7.enabled) : ""); } else { item = "<missing-script>"; } list2.Add((string)item); } ManualLogSource logSource3 = LogSource; if (logSource3 != null) { logSource3.LogInfo((object)("LADDER_COMPONENT_GRAPH: node=" + GetHierarchyPath(item2) + "#" + ((Object)((Component)item2).gameObject).GetInstanceID() + "; active=" + ((Component)item2).gameObject.activeInHierarchy + "; components=" + string.Join(", ", list2))); } array = components; foreach (Component val8 in array) { if ((Object)(object)val8 == (Object)null || val8 is Transform || val8 is Collider || val8 is Renderer) { continue; } string text2 = ((object)val8).GetType().FullName ?? ((object)val8).GetType().Name; if ((Object)(object)val8 == (Object)(object)val || text2.IndexOf("ladder", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("InteractTrigger", StringComparison.OrdinalIgnoreCase) >= 0) { ManualLogSource logSource4 = LogSource; if (logSource4 != null) { logSource4.LogInfo((object)("LADDER_COMPONENT_FIELDS: " + text2 + "#" + ((Object)val8).GetInstanceID() + "; path=" + GetHierarchyPath(val8.transform) + "; " + RelevantFields(val8))); } } } } } internal void StartDelayedLadderObservation(object trigger, object argument) { try { Component val = (Component)((trigger is Component) ? trigger : null); if ((Object)(object)val == (Object)null || !IsSecondExteriorLadderTrigger(val)) { return; } if (VerboseDiagnostics) { ((MonoBehaviour)this).StartCoroutine(ObserveLadderAfterInteraction(val, (Transform)((argument is Transform) ? argument : null))); return; } int instanceID = ((Object)val).GetInstanceID(); float unscaledTime = Time.unscaledTime; if ((!QuietObservationLastScheduledAt.TryGetValue(instanceID, out var value) || !(unscaledTime - value < 1.5f)) && QuietObservationPending.Add(instanceID)) { QuietObservationLastScheduledAt[instanceID] = unscaledTime; ((MonoBehaviour)this).StartCoroutine(ObserveLadderQuietly(val, (Transform)((argument is Transform) ? argument : null), instanceID)); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Could not schedule quiet upper-ladder observation: " + ex.GetType().Name)); } } private IEnumerator ObserveLadderQuietly(Component trigger, Transform argumentTransform, int instanceId) { yield return (object)new WaitForSeconds(1f); if ((Object)(object)trigger != (Object)null && IsShipLadderTrigger(trigger, out var _)) { object instance = ResolvePlayerController(argumentTransform); ((BaseUnityPlugin)this).Logger.LogInfo((object)("LADDER_RESULT after 1.0s: role=" + ClassifyLadder(trigger.transform) + "; usingLadder=" + GetBool(trigger, "usingLadder") + "; isClimbingLadder=" + GetBool(instance, "isClimbingLadder") + "; inSpecialInteractAnimation=" + GetBool(instance, "inSpecialInteractAnimation") + "; active=" + trigger.gameObject.activeInHierarchy + "; interactable=" + GetBool(trigger, "interactable") + "; isLadder=" + GetBool(trigger, "isLadder"))); } QuietObservationPending.Remove(instanceId); } private IEnumerator ObserveLadderAfterInteraction(Component trigger, Transform argumentTransform) { yield return null; if ((Object)(object)trigger == (Object)null) { yield break; } LogDelayedLadderState(trigger, argumentTransform, "next-frame"); yield return (object)new WaitForSeconds(0.25f); if (!((Object)(object)trigger == (Object)null)) { LogDelayedLadderState(trigger, argumentTransform, "plus-0.25s"); yield return (object)new WaitForSeconds(0.75f); if ((Object)(object)trigger != (Object)null) { LogDelayedLadderState(trigger, argumentTransform, "plus-1.0s"); } } } private void LogDelayedLadderState(Component trigger, Transform argumentTransform, string stage) { if (IsShipLadderTrigger(trigger, out var _)) { object player = ResolvePlayerController(argumentTransform); ((BaseUnityPlugin)this).Logger.LogInfo((object)("LADDER_DELAYED_STATE " + stage + ": " + DescribeTrigger(trigger) + "; " + DescribePlayer(player))); } } internal static bool IsSecondExteriorLadderTrigger(object trigger) { Component val = (Component)((trigger is Component) ? trigger : null); if ((Object)(object)val == (Object)null) { return false; } if (!IsShipLadderTrigger(trigger, out var _)) { return false; } return ClassifyLadder(val.transform).StartsWith("SECOND_EXTERIOR_LADDER_CANDIDATE", StringComparison.Ordinal); } internal static bool NormalizeShipLadderTrigger(object trigger, string context) { if (!IsShipLadderTrigger(trigger, out var _)) { return false; } Behaviour val = (Behaviour)((((trigger is Component) ? trigger : null) is Behaviour) ? ((trigger is Component) ? trigger : null) : null); if (val != null && !val.enabled) { val.enabled = true; } TrySetBool(trigger, "interactable", value: true); TrySetBool(trigger, "isLadder", value: true); TrySetBool(trigger, "holdInteraction", value: false); TrySetBool(trigger, "oneHandedItemAllowed", value: true); TrySetBool(trigger, "twoHandedItemAllowed", value: true); if (VerboseDiagnostics) { ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogDebug((object)("Normalized focused ship ladder flags (" + context + "): " + DescribeTrigger(trigger))); } } return true; } } internal static class ShipLadderGateDiagnostics { private static readonly HashSet<int> GeometryLogged = new HashSet<int>(); private static readonly object GeometryLock = new object(); private static readonly Dictionary<int, float> ObstructionLogLastAt = new Dictionary<int, float>(); private static readonly object ObstructionLogLock = new object(); internal static bool IsTarget(object trigger) { Component val = (Component)((trigger is Component) ? trigger : null); if ((Object)(object)val == (Object)null || !ShipLadderInteractionFix.IsShipLadderTrigger(trigger, out var _)) { return false; } string text = ShipLadderInteractionFix.ClassifyLadder(val.transform); if (!text.StartsWith("PRIMARY_EXTERIOR_LADDER_REFERENCE", StringComparison.Ordinal)) { return text.StartsWith("SECOND_EXTERIOR_LADDER_CANDIDATE", StringComparison.Ordinal); } return true; } internal static void LogGateState(object trigger, object playerArgument, string stage, bool includeGeometry) { try { Component val = (Component)((trigger is Component) ? trigger : null); if (!((Object)(object)val == (Object)null) && IsTarget(trigger)) { object instance = playerArgument; if (ShipLadderInteractionFix.GetMember(instance, "thisPlayerBody") == null) { instance = ShipLadderInteractionFix.ResolvePlayerController(playerArgument); } object member = ShipLadderInteractionFix.GetMember(instance, "thisPlayerBody"); Transform t = (Transform)((member is Transform) ? member : null); object member2 = ShipLadderInteractionFix.GetMember(instance, "gameplayCamera"); Component val2 = (Component)((member2 is Component) ? member2 : null); object member3 = ShipLadderInteractionFix.GetMember(trigger, "topOfLadderPosition"); Transform t2 = (Transform)((member3 is Transform) ? member3 : null); object member4 = ShipLadderInteractionFix.GetMember(trigger, "bottomOfLadderPosition"); Transform t3 = (Transform)((member4 is Transform) ? member4 : null); object member5 = ShipLadderInteractionFix.GetMember(trigger, "ladderHorizontalPosition"); Transform t4 = (Transform)((member5 is Transform) ? member5 : null); object member6 = ShipLadderInteractionFix.GetMember(trigger, "ladderPlayerPositionNode"); Transform t5 = (Transform)((member6 is Transform) ? member6 : null); object staticMember = GetStaticMember("StartOfRound", "Instance"); object member7 = ShipLadderInteractionFix.GetMember(instance, "IsOwner"); string text = "LADDER_VANILLA_GATE_STATE stage=" + stage + "; role=" + ShipLadderInteractionFix.ClassifyLadder(val.transform) + "; triggerPath=" + ShipLadderInteractionFix.GetHierarchyPath(val.transform) + "; triggerOnce=" + ShipLadderInteractionFix.GetBool(trigger, "triggerOnce") + "; hasTriggered=" + ShipLadderInteractionFix.GetBool(trigger, "hasTriggered") + "; firingPlayersCutsceneRunning=" + ShipLadderInteractionFix.GetBool(staticMember, "firingPlayersCutsceneRunning") + "; RandomChanceTrigger=" + ShipLadderInteractionFix.GetBool(trigger, "RandomChanceTrigger") + "; randomChancePercentage=" + F(ShipLadderInteractionFix.GetFloat(trigger, "randomChancePercentage", float.NaN)) + "; interactable=" + ShipLadderInteractionFix.GetBool(trigger, "interactable") + "; isPlayingSpecialAnimation=" + ShipLadderInteractionFix.GetBool(trigger, "isPlayingSpecialAnimation") + "; usingLadder=" + ShipLadderInteractionFix.GetBool(trigger, "usingLadder") + "; player.inSpecialInteractAnimation=" + ShipLadderInteractionFix.GetBool(instance, "inSpecialInteractAnimation") + "; player.isClimbingLadder=" + ShipLadderInteractionFix.GetBool(instance, "isClimbingLadder") + "; allowUseWhileInAnimation=" + ShipLadderInteractionFix.GetBool(trigger, "allowUseWhileInAnimation") + "; interactCooldown=" + ShipLadderInteractionFix.GetBool(trigger, "interactCooldown") + "; currentCooldownValue=" + F(ShipLadderInteractionFix.GetFloat(trigger, "currentCooldownValue", float.NaN)) + "; cooldownTime=" + F(ShipLadderInteractionFix.GetFloat(trigger, "cooldownTime", float.NaN)) + "; specialCharacterAnimation=" + ShipLadderInteractionFix.GetBool(trigger, "specialCharacterAnimation") + "; isLadder=" + ShipLadderInteractionFix.GetBool(trigger, "isLadder") + "; player.isInHangarShipRoom=" + ShipLadderInteractionFix.GetBool(instance, "isInHangarShipRoom") + "; player.isInElevator=" + ShipLadderInteractionFix.GetBool(instance, "isInElevator") + "; player.isTestingPlayer=" + ShipLadderInteractionFix.GetBool(instance, "isTestingPlayer") + "; player.isOwner=" + ((member7 == null) ? "<unavailable>" : Convert.ToString(member7)) + "; playerBody=" + Pose(t) + "; gameplayCamera=" + Pose(((Object)(object)val2 == (Object)null) ? null : val2.transform) + "; TopPos=" + Pose(t2) + "; BottomPos=" + Pose(t3) + "; HorizontalPosition=" + Pose(t4) + "; PlayerPositionNode=" + Pose(t5) + "; triggerTransform=" + Pose(val.transform) + "; triggerParent=" + Pose(val.transform.parent) + "; ladderParent=" + Pose(((Object)(object)val.transform.parent == (Object)null) ? null : val.transform.parent.parent); ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)text); } if (includeGeometry) { LogColliderGeometry(val); } } } catch (Exception ex) { ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)("LADDER_VANILLA_GATE_STATE diagnostic failed safely: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static void LogObstructionResult(object trigger, object[] args, bool result) { //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: 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) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) try { if (!IsTarget(trigger)) { return; } if (!ShipLadderInteractionFix.VerboseDiagnostics) { if (!ShipLadderInteractionFix.IsSecondExteriorLadderTrigger(trigger)) { return; } Component val = (Component)((trigger is Component) ? trigger : null); if ((Object)(object)val == (Object)null) { return; } float unscaledTime = Time.unscaledTime; lock (ObstructionLogLock) { if (ObstructionLogLastAt.TryGetValue(((Object)val).GetInstanceID(), out var value) && unscaledTime - value < 1f) { return; } ObstructionLogLastAt[((Object)val).GetInstanceID()] = unscaledTime; } ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)("LADDER_FIX_OBSTRUCTION_CHECK: result=" + result + "; triggerPath=" + ShipLadderInteractionFix.GetHierarchyPath(val.transform) + "; player room flag was temporarily bypassed only during vanilla Interact; no physics result was modified.")); } return; } object obj = ((args != null && args.Length != 0) ? args[0] : null); object member = ShipLadderInteractionFix.GetMember(trigger, "topOfLadderPosition"); Transform val2 = (Transform)((member is Transform) ? member : null); object member2 = ShipLadderInteractionFix.GetMember(trigger, "ladderPlayerPositionNode"); Transform val3 = (Transform)((member2 is Transform) ? member2 : null); object member3 = ShipLadderInteractionFix.GetMember(obj, "thisPlayerBody"); Transform val4 = (Transform)((member3 is Transform) ? member3 : null); object member4 = ShipLadderInteractionFix.GetMember(obj, "gameplayCamera"); Component val5 = (Component)((member4 is Component) ? member4 : null); bool flag = (Object)(object)val4 != (Object)null && (Object)(object)val2 != (Object)null && val4.position.y >= val2.position.y - 0.5f; string text = (flag ? "CAMERA_TO_NODE_PLUS_UP_2_8" : "CAMERA_TO_NODE"); string text2 = "not-run"; if (result && (Object)(object)val5 != (Object)null && (Object)(object)val3 != (Object)null) { Vector3 position = val5.transform.position; Vector3 val6 = (flag ? (val3.position + Vector3.up * 2.8f) : val3.position); object member5 = ShipLadderInteractionFix.GetMember(GetStaticMember("StartOfRound", "Instance"), "collidersAndRoomMaskAndDefault"); if (member5 != null) { int num = Convert.ToInt32(member5); RaycastHit val7 = default(RaycastHit); bool flag2 = Physics.Linecast(position, val6, ref val7, num, (QueryTriggerInteraction)1); string text3 = (((Object)(object)((RaycastHit)(ref val7)).collider == (Object)null) ? "<none>" : ((Object)((RaycastHit)(ref val7)).collider).name); string text4 = (((Object)(object)((RaycastHit)(ref val7)).collider == (Object)null) ? "<none>" : ShipLadderInteractionFix.GetHierarchyPath(((Component)((RaycastHit)(ref val7)).collider).transform)); text2 = "hit=" + flag2 + "; collider=" + text3 + "; colliderPath=" + text4 + "; from=" + V3(position) + "; to=" + V3(val6); } } ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)("LADDER_POSITION_OBSTRUCTION_RESULT role=" + ShipLadderInteractionFix.ClassifyLadder(((Component)trigger).transform) + "; result=" + result + "; vanillaRayBranch=" + text + "; repeatedReadOnlyLinecast=" + text2 + "; state is recorded after vanilla moved PlayerPositionNode to its proposed climb position.")); } LogGateState(trigger, obj, "AFTER_LadderPositionObstructed_result=" + result, includeGeometry: false); } catch (Exception ex) { ManualLogSource logSource3 = ShipLadderInteractionFix.LogSource; if (logSource3 != null) { logSource3.LogWarning((object)("LADDER_POSITION_OBSTRUCTION_RESULT diagnostic failed safely: " + ex.GetType().Name + ": " + ex.Message)); } } } private static void LogColliderGeometry(Component trigger) { //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)trigger).GetInstanceID(); lock (GeometryLock) { if (!GeometryLogged.Add(instanceID)) { return; } } Transform val = (((Object)(object)trigger.transform.parent != (Object)null) ? trigger.transform.parent : trigger.transform); Collider[] componentsInChildren = ((Component)val).GetComponentsInChildren<Collider>(true); ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)("LADDER_GEOMETRY_COLLIDERS role=" + ShipLadderInteractionFix.ClassifyLadder(trigger.transform) + "; root=" + ShipLadderInteractionFix.GetHierarchyPath(val) + "; count=" + ((componentsInChildren != null) ? componentsInChildren.Length : 0))); } if (componentsInChildren == null) { return; } int num = Math.Min(componentsInChildren.Length, 50); for (int i = 0; i < num; i++) { Collider val2 = componentsInChildren[i]; if ((Object)(object)val2 == (Object)null) { continue; } string text = "shape=" + ((object)val2).GetType().Name; BoxCollider val3 = (BoxCollider)(object)((val2 is BoxCollider) ? val2 : null); if (val3 != null) { text = text + "; center=" + V3(val3.center) + "; size=" + V3(val3.size); } else { SphereCollider val4 = (SphereCollider)(object)((val2 is SphereCollider) ? val2 : null); if (val4 != null) { text = text + "; center=" + V3(val4.center) + "; radius=" + F(val4.radius); } else { CapsuleCollider val5 = (CapsuleCollider)(object)((val2 is CapsuleCollider) ? val2 : null); if (val5 != null) { text = text + "; center=" + V3(val5.center) + "; radius=" + F(val5.radius) + "; height=" + F(val5.height) + "; direction=" + val5.direction; } } } string text3; try { Bounds bounds = val2.bounds; string text2 = V3(((Bounds)(ref bounds)).center); bounds = val2.bounds; text3 = "; boundsCenter=" + text2 + "; boundsSize=" + V3(((Bounds)(ref bounds)).size); } catch (Exception ex) { text3 = "; bounds=<error:" + ex.GetType().Name + ">"; } ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogInfo((object)("LADDER_GEOMETRY_COLLIDER role=" + ShipLadderInteractionFix.ClassifyLadder(trigger.transform) + "; path=" + ShipLadderInteractionFix.GetHierarchyPath(((Component)val2).transform) + "; enabled=" + val2.enabled + "; active=" + ((Component)val2).gameObject.activeInHierarchy + "; isTrigger=" + val2.isTrigger + "; layer=" + ((Component)val2).gameObject.layer + "; layerName=" + LayerMask.LayerToName(((Component)val2).gameObject.layer) + "; " + text + text3)); } } if (componentsInChildren.Length > num) { ManualLogSource logSource3 = ShipLadderInteractionFix.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)("LADDER_GEOMETRY_COLLIDER_LIMIT: omitted=" + (componentsInChildren.Length - num))); } } } private static object GetStaticMember(string typeName, string memberName) { Type type = AccessTools.TypeByName(typeName); if (type == null) { return null; } while (type != null) { FieldInfo field = type.GetField(memberName, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(null); } PropertyInfo property = type.GetProperty(memberName, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(null, null); } type = type.BaseType; } return null; } private static string Pose(Transform t) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)t == (Object)null) { return "<null>"; } return "path=" + ShipLadderInteractionFix.GetHierarchyPath(t) + "#" + ((Object)((Component)t).gameObject).GetInstanceID() + "{world=" + V3(t.position) + ";local=" + V3(t.localPosition) + ";worldEuler=" + V3(t.eulerAngles) + ";localEuler=" + V3(t.localEulerAngles) + ";localScale=" + V3(t.localScale) + ";lossyScale=" + V3(t.lossyScale) + "}"; } private static string V3(Vector3 v) { //IL_0010: 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_003c: Unknown result type (might be due to invalid IL or missing references) return "(" + F(v.x) + "," + F(v.y) + "," + F(v.z) + ")"; } private static string F(float v) { return v.ToString("0.000", CultureInfo.InvariantCulture); } } [HarmonyPatch] internal static class ShipLadderPositionObstructionDiagnosticPatch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("InteractTrigger"); if (!(type == null)) { return AccessTools.Method(type, "LadderPositionObstructed", (Type[])null, (Type[])null); } return null; } [HarmonyPostfix] private static void Postfix(object __instance, object[] __args, bool __result) { ShipLadderGateDiagnostics.LogObstructionResult(__instance, __args, __result); } } [HarmonyPatch] internal static class ShipLadderNetworkSpawnPatch { private static IEnumerable<MethodBase> TargetMethods() { return from method in typeof(NetworkObject).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.DeclaringType == typeof(NetworkObject) && (method.Name == "Spawn" || method.Name == "SpawnWithObservers" || method.Name == "SpawnAsPlayerObject" || method.Name == "SpawnWithOwnership") select method; } [HarmonyPrefix] private static void Prefix(NetworkObject __instance, MethodBase __originalMethod) { ShipLadderInteractionFix.RunRepair(__instance, __originalMethod); } } [HarmonyPatch] internal static class ShipLadderTriggerInteractPatch { private sealed class RoomFlagBypassState { internal object Player; internal bool OriginalValue; internal bool Restored; } private static int _lastFrame = -1; private static int _lastTriggerId; private static bool _lastOriginalRan; private static bool _lastCallObserved; internal static bool WasCalledThisFrame(object trigger) { if (_lastFrame == Time.frameCount && _lastTriggerId == ShipLadderInteractionFix.GetInstanceId(trigger)) { return _lastCallObserved; } return false; } internal static bool OriginalRanThisFrame(object trigger) { if (WasCalledThisFrame(trigger)) { return _lastOriginalRan; } return false; } private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("InteractTrigger"); if (type == null) { return null; } return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "Interact" && method.GetParameters().Length == 1); } [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(object __instance, object __0, out RoomFlagBypassState __state) { __state = null; if (!ShipLadderInteractionFix.IsShipLadderTrigger(__instance, out var _)) { return; } _lastFrame = Time.frameCount; _lastTriggerId = ShipLadderInteractionFix.GetInstanceId(__instance); _lastCallObserved = true; _lastOriginalRan = false; if (ShipLadderInteractionFix.VerboseDiagnostics) { ShipLadderInteractionFix.LogLadderSnapshot(__instance, __0, "INTERACT-PREFIX-RAW-BEFORE-NORMALIZE"); } if (ShipLadderInteractionFix.VerboseDiagnostics) { ShipLadderGateDiagnostics.LogGateState(__instance, __0, "INTERACT_ENTRY_BEFORE_NORMALIZE", includeGeometry: true); } ShipLadderInteractionFix.NormalizeShipLadderTrigger(__instance, "InteractTrigger.Interact prefix"); if (ShipLadderInteractionFix.VerboseDiagnostics) { ShipLadderGateDiagnostics.LogGateState(__instance, __0, "INTERACT_ENTRY_AFTER_NORMALIZE", includeGeometry: false); } if (!ShipLadderInteractionFix.IsSecondExteriorLadderTrigger(__instance)) { return; } object obj = ShipLadderInteractionFix.ResolvePlayerController(__0); if (obj == null) { ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogWarning((object)"LADDER_ROOM_GATE_BYPASS_SKIPPED: upper ladder matched but PlayerControllerB could not be resolved; vanilla behavior left unchanged."); } return; } object member = ShipLadderInteractionFix.GetMember(obj, "IsOwner"); if (!(member is bool) || !(bool)member) { return; } bool flag = ShipLadderInteractionFix.GetBool(obj, "isInHangarShipRoom"); if (!flag) { return; } if (!ShipLadderInteractionFix.TrySetBool(obj, "isInHangarShipRoom", value: false)) { ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)"LADDER_ROOM_GATE_BYPASS_FAILED: could not temporarily change the upper-ladder room gate; vanilla behavior left unchanged."); } return; } __state = new RoomFlagBypassState { Player = obj, OriginalValue = flag, Restored = false }; ManualLogSource logSource3 = ShipLadderInteractionFix.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)"LADDER_ROOM_GATE_BYPASS applied: exact upper exterior ladder only; player.isInHangarShipRoom=True->False for this vanilla Interact call; restoration scheduled immediately after."); } } private static bool RestoreRoomFlag(RoomFlagBypassState state) { if (state == null) { return true; } if (state.Restored) { return true; } bool num = ShipLadderInteractionFix.TrySetBool(state.Player, "isInHangarShipRoom", state.OriginalValue); if (num) { state.Restored = true; } return num; } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(object __instance, object __0, bool __runOriginal, RoomFlagBypassState __state) { bool flag = RestoreRoomFlag(__state); if (!ShipLadderInteractionFix.IsShipLadderTrigger(__instance, out var path)) { return; } _lastFrame = Time.frameCount; _lastTriggerId = ShipLadderInteractionFix.GetInstanceId(__instance); _lastCallObserved = true; _lastOriginalRan = __runOriginal; if (ShipLadderInteractionFix.IsSecondExteriorLadderTrigger(__instance)) { object instance = ((__state != null) ? __state.Player : ShipLadderInteractionFix.ResolvePlayerController(__0)); ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)("LADDER_FIX_INTERACT_RESULT: originalRan=" + __runOriginal + "; roomGateBypassApplied=" + (__state != null) + "; roomFlagRestored=" + ((__state == null) ? "not-needed" : flag.ToString()) + "; player.isInHangarShipRoom=" + ShipLadderInteractionFix.GetBool(instance, "isInHangarShipRoom") + "; usingLadder=" + ShipLadderInteractionFix.GetBool(__instance, "usingLadder") + "; isClimbingLadder=" + ShipLadderInteractionFix.GetBool(instance, "isClimbingLadder") + "; inSpecialInteractAnimation=" + ShipLadderInteractionFix.GetBool(instance, "inSpecialInteractAnimation") + "; path=" + path)); } ShipLadderInteractionFix.Instance?.StartDelayedLadderObservation(__instance, __0); } else if (ShipLadderInteractionFix.VerboseDiagnostics) { object player = ShipLadderInteractionFix.ResolvePlayerController(__0); ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogInfo((object)("LADDER_INTERACT_METHOD_RETURNED: originalRan=" + __runOriginal + ": " + ShipLadderInteractionFix.DescribeTrigger(__instance) + "; " + ShipLadderInteractionFix.DescribeArgument(__0) + "; " + ShipLadderInteractionFix.DescribePlayer(player))); } } } [HarmonyFinalizer] private static Exception Finalizer(RoomFlagBypassState __state, Exception __exception) { if (__state != null && !RestoreRoomFlag(__state)) { ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogWarning((object)"LADDER_ROOM_GATE_RESTORE_FAILED: couldn't restore the original isInHangarShipRoom value after Interact; inspect the next log before further testing."); } } return __exception; } } [HarmonyPatch] internal static class ShipLadderPlayerInputGuard { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("GameNetcodeStuff.PlayerControllerB"); if (!(type == null)) { return AccessTools.Method(type, "Interact_performed", (Type[])null, (Type[])null); } return null; } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(object __instance, object __0) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) try { if (!WasPerformed(__0)) { return; } object member = ShipLadderInteractionFix.GetMember(__instance, "hoveringOverTrigger"); if (!ShipLadderInteractionFix.IsShipLadderTrigger(member, out var path)) { return; } ShipLadderInteractionFix.NormalizeShipLadderTrigger(member, "PlayerControllerB.Interact_performed postfix"); if (ShipLadderInteractionFix.VerboseDiagnostics) { ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)("PLAYER PRESSED INTERACT ON " + ShipLadderInteractionFix.ClassifyLadder(((Component)member).transform) + ": " + ShipLadderInteractionFix.DescribeTrigger(member, __instance))); } } if (ShipLadderInteractionFix.GetBool(__instance, "isClimbingLadder")) { return; } if (ShipLadderInteractionFix.GetBool(__instance, "isPlayerDead") || (!ShipLadderInteractionFix.GetBool(__instance, "isPlayerControlled") && !ShipLadderInteractionFix.GetBool(__instance, "isTestingPlayer")) || ShipLadderInteractionFix.GetBool(__instance, "inSpecialMenu") || ShipLadderInteractionFix.GetBool(__instance, "inTerminalMenu") || ShipLadderInteractionFix.GetBool(__instance, "isTypingChat") || ShipLadderInteractionFix.GetBool(__instance, "jetpackControls") || ShipLadderInteractionFix.GetBool(__instance, "disablingJetpackControls") || ShipLadderInteractionFix.GetMember(__instance, "inAnimationWithEnemy") != null || ShipLadderInteractionFix.GetFloat(__instance, "timeSinceSwitchingSlots", 1f) < 0.2f) { if (ShipLadderInteractionFix.VerboseDiagnostics) { ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogInfo((object)"Ladder fallback not attempted because a vanilla safety gate is active."); } } return; } object member2 = ShipLadderInteractionFix.GetMember(__instance, "IsOwner"); if (member2 is bool && !(bool)member2) { return; } if (ShipLadderTriggerInteractPatch.WasCalledThisFrame(member)) { if (ShipLadderInteractionFix.IsSecondExteriorLadderTrigger(member) && !ShipLadderInteractionFix.GetBool(member, "usingLadder") && !ShipLadderInteractionFix.GetBool(__instance, "isClimbingLadder") && !ShipLadderInteractionFix.GetBool(__instance, "inSpecialInteractAnimation") && ShipLadderInteractionFix.VerboseDiagnostics) { ManualLogSource logSource3 = ShipLadderInteractionFix.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)"No extra upper-ladder interaction was issued; the room-gate repair relies on this original vanilla call only."); } } if (!ShipLadderInteractionFix.VerboseDiagnostics) { return; } if (!ShipLadderTriggerInteractPatch.OriginalRanThisFrame(member)) { ManualLogSource logSource4 = ShipLadderInteractionFix.LogSource; if (logSource4 != null) { logSource4.LogWarning((object)("The ladder Interact method was called, but its original body was skipped by a patch. No second call was made to avoid duplicate activation. Path=" + path)); } } else { ManualLogSource logSource5 = ShipLadderInteractionFix.LogSource; if (logSource5 != null) { logSource5.LogInfo((object)("The normal input path did call the ladder Interact method, but climbing was not confirmed in this frame. No duplicate call was made. Path=" + path)); } } } else if (ShipLadderInteractionFix.VerboseDiagnostics) { ManualLogSource logSource6 = ShipLadderInteractionFix.LogSource; if (logSource6 != null) { logSource6.LogWarning((object)("Normal input did not call the focused ladder's Interact method; this build does not issue a second call. Path=" + path)); } } } catch (Exception ex) { ManualLogSource logSource7 = ShipLadderInteractionFix.LogSource; if (logSource7 != null) { logSource7.LogWarning((object)("Input guard failed safely: " + ex.GetType().Name + ": " + ex.Message)); } } } private static bool WasPerformed(object context) { object member = ShipLadderInteractionFix.GetMember(context, "performed"); if (member is bool) { return (bool)member; } object member2 = ShipLadderInteractionFix.GetMember(context, "phase"); if (member2 != null) { return string.Equals(Convert.ToString(member2), "Performed", StringComparison.OrdinalIgnoreCase); } return false; } } [HarmonyPatch] internal static class ShipLadderUseConditionsGuard { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("GameNetcodeStuff.PlayerControllerB"); if (!(type == null)) { return AccessTools.Method(type, "InteractTriggerUseConditionsMet", (Type[])null, (Type[])null); } return null; } [HarmonyPrefix] private static void Prefix(object __instance) { try { object member = ShipLadderInteractionFix.GetMember(__instance, "hoveringOverTrigger"); if (ShipLadderInteractionFix.VerboseDiagnostics && ShipLadderInteractionFix.IsShipLadderTrigger(member, out var _)) { ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)("USE_CONDITIONS_METHOD_ENTERED: trigger=" + ShipLadderInteractionFix.DescribeTrigger(member) + "; " + ShipLadderInteractionFix.DescribePlayer(__instance))); } } } catch (Exception ex) { ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogDebug((object)("Use-condition prefix diagnostic failed safely: " + ex.GetType().Name)); } } } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(object __instance, ref bool __result) { try { object member = ShipLadderInteractionFix.GetMember(__instance, "hoveringOverTrigger"); if (!ShipLadderInteractionFix.IsShipLadderTrigger(member, out var path)) { return; } if (ShipLadderInteractionFix.VerboseDiagnostics) { ManualLogSource logSource = ShipLadderInteractionFix.LogSource; if (logSource != null) { logSource.LogInfo((object)("USE_CONDITIONS_RESULT_BEFORE_GUARD: result=" + __result + "; trigger=" + ShipLadderInteractionFix.DescribeTrigger(member) + "; " + ShipLadderInteractionFix.DescribePlayer(__instance))); } } if (ShipLadderInteractionFix.GetBool(__instance, "isPlayerDead") || ShipLadderInteractionFix.GetBool(__instance, "inTerminalMenu") || ShipLadderInteractionFix.GetBool(__instance, "inSpecialMenu") || ShipLadderInteractionFix.GetBool(__instance, "isTypingChat") || ShipLadderInteractionFix.GetBool(__instance, "jetpackControls") || ShipLadderInteractionFix.GetBool(__instance, "disablingJetpackControls")) { return; } if (!__result && ShipLadderInteractionFix.VerboseDiagnostics) { ManualLogSource logSource2 = ShipLadderInteractionFix.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)("Overriding false interaction-use result for focused ship ladder. Path=" + path)); } } __result = true; } catch (Exception ex) { ManualLogSource logSource3 = ShipLadderInteractionFix.LogSource; if (logSource3 != null) { logSource3.LogDebug((object)("Use-condition guard failed safely: " + ex.GetType().Name)); } } } }