using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalConfig;
using LethalConfig.ConfigItems;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
[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("StealthLadders")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Quiet ladders")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: AssemblyInformationalVersion("0.0.1+e5cbd81f0600a57dc495868fb6d22c52fdca8366")]
[assembly: AssemblyProduct("StealthLadders")]
[assembly: AssemblyTitle("StealthLadders")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
internal static class LethalConfigCompat
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
internal static void Register(ConfigEntry<bool> boolConfigEntry)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Expected O, but got Unknown
LethalConfigManager.SkipAutoGenFor((ConfigEntryBase)(object)boolConfigEntry);
LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(boolConfigEntry, false));
}
}
namespace StealthLadders
{
internal static class ConfigSettings
{
private static ConfigEntry<bool> _stealthLadders;
private static ConfigEntry<bool> _stealthOnLadders;
internal static bool StealthLadders = true;
internal static bool StealthOnLadders = true;
internal static void Init(BaseUnityPlugin plugin)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Expected O, but got Unknown
ConfigFile config = plugin.Config;
_stealthLadders = config.Bind<bool>("General", "StealthLadders", true, new ConfigDescription("When enabled, extension ladders can be deployed silently by crouching.", (AcceptableValueBase)null, Array.Empty<object>()));
_stealthOnLadders = config.Bind<bool>("General", "StealthOnLadders", true, new ConfigDescription("When enabled, ladders can be climbed silently by crouching.", (AcceptableValueBase)null, Array.Empty<object>()));
_stealthLadders.SettingChanged += OnStealthLaddersSettingChanged;
_stealthOnLadders.SettingChanged += OnStealthOnLaddersSettingChanged;
UpdateStealthLadders();
UpdateStealthOnLadders();
}
private static void OnStealthLaddersSettingChanged(object sender, EventArgs e)
{
UpdateStealthLadders();
}
private static void OnStealthOnLaddersSettingChanged(object sender, EventArgs e)
{
UpdateStealthOnLadders();
}
private static void UpdateStealthLadders()
{
StealthLadders = _stealthLadders?.Value ?? true;
}
private static void UpdateStealthOnLadders()
{
StealthOnLadders = _stealthOnLadders?.Value ?? true;
}
internal static void RegisterWithLethalConfigIfPresent()
{
if (Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig"))
{
LethalConfigCompat.Register(_stealthLadders);
LethalConfigCompat.Register(_stealthOnLadders);
}
}
}
internal class Debug
{
internal static readonly bool DebugEnabled;
internal static void Log(string message)
{
if (DebugEnabled)
{
Plugin.Log.LogInfo((object)message);
}
}
}
[Flags]
public enum LadderFlags : byte
{
None = 0,
Stealth = 1,
Falling = 2
}
public class LadderState : MonoBehaviour
{
private LadderFlags _flags;
public bool Is(LadderFlags f)
{
return (_flags & f) != 0;
}
public void Clear(LadderFlags f)
{
LadderFlags flags = _flags;
_flags &= (LadderFlags)(byte)(~(int)f);
Debug.Log($"[LadderState.Clear({f})] {flags} -> {_flags}");
}
public void ClearFlags()
{
LadderFlags flags = _flags;
_flags = LadderFlags.None;
Debug.Log($"[LadderState.ClearFlags] {flags} -> {_flags}");
}
public void SetFlag(LadderFlags f, bool value)
{
LadderFlags flags = _flags;
_flags = (value ? (_flags | f) : (_flags & (LadderFlags)(~(uint)f)));
Debug.Log($"[LadderState.SetFlag({f},{value})] {flags} -> {_flags}");
}
public void ApplyLocal(LadderFlags flags)
{
LadderFlags flags2 = _flags;
_flags = flags;
Debug.Log($"[LadderState.ApplyLocal({flags})] {flags2} -> {_flags}");
}
public override string ToString()
{
return $"LadderState({_flags})";
}
}
public class StealthLaddersHub : MonoBehaviour
{
public static StealthLaddersHub Instance;
private readonly Dictionary<ulong, LadderFlags> _pendingByLadderId = new Dictionary<ulong, LadderFlags>();
private bool _handlersRegistered;
private const string MSG_SET_NEXT = "StealthLadders.SetNext";
private const string MSG_APPLY = "StealthLadders.Apply";
private const string MSG_SET_CLIMB = "StealthLadders.SetClimb";
private const string MSG_APPLY_CLIMB = "StealthLadders.ApplyClimb";
public static StealthLaddersHub Ensure()
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Expected O, but got Unknown
if ((Object)(object)Instance != (Object)null)
{
return Instance;
}
GameObject val = new GameObject("StealthLaddersHub");
Object.DontDestroyOnLoad((Object)val);
Instance = val.AddComponent<StealthLaddersHub>();
Instance.RegisterHandlers();
Debug.Log("[Hub.Ensure] Using new Instance.");
return Instance;
}
private void RegisterHandlers()
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Expected O, but got Unknown
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Expected O, but got Unknown
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Expected O, but got Unknown
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
if (_handlersRegistered)
{
Debug.Log("[Hub.Register] Already registered; skipping.");
return;
}
if ((Object)(object)NetworkManager.Singleton == (Object)null)
{
Debug.Log("[Hub.Register] NetworkManager missing; will register later.");
return;
}
CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager;
if (customMessagingManager == null)
{
Debug.Log("[Hub.Register] CustomMessagingManager missing; cannot register.");
return;
}
customMessagingManager.RegisterNamedMessageHandler("StealthLadders.SetNext", new HandleNamedMessageDelegate(OnSetNextActivationFlagsServer));
customMessagingManager.RegisterNamedMessageHandler("StealthLadders.Apply", new HandleNamedMessageDelegate(OnApplyNextActivationFlagsClient));
customMessagingManager.RegisterNamedMessageHandler("StealthLadders.SetClimb", new HandleNamedMessageDelegate(OnSetClimbStealthServer));
customMessagingManager.RegisterNamedMessageHandler("StealthLadders.ApplyClimb", new HandleNamedMessageDelegate(OnApplyClimbStealthClient));
Debug.Log("[Hub.Register] Handlers registered.");
_handlersRegistered = true;
}
private void Update()
{
if (!_handlersRegistered && (Object)(object)NetworkManager.Singleton != (Object)null)
{
RegisterHandlers();
}
}
public void SendNextActivationFlagsToServer(ulong ladderNetId, LadderFlags flags)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsClient)
{
Debug.Log("[Hub.SendNext] Not a client or NetworkManager missing; skipping.");
return;
}
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(9, (Allocator)2, -1);
try
{
((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref ladderNetId, default(ForPrimitives));
byte b = (byte)flags;
((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
Debug.Log($"[Hub.SendNext] Sending flags={flags} for ladder NetId={ladderNetId} to server.");
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("StealthLadders.SetNext", 0uL, val, (NetworkDelivery)3);
}
finally
{
((IDisposable)(FastBufferWriter)(ref val)).Dispose();
}
}
private void OnSetNextActivationFlagsServer(ulong senderClientId, FastBufferReader reader)
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
{
Debug.Log("[Hub.SetNext:ServerHandler] Not server; ignoring.");
return;
}
if (!((FastBufferReader)(ref reader)).TryBeginRead(9))
{
Debug.Log("[Hub.SetNext:ServerHandler] Reader underflow.");
return;
}
ulong num = default(ulong);
((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
byte b = default(byte);
((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
LadderFlags ladderFlags = (LadderFlags)b;
bool flag = _pendingByLadderId.ContainsKey(num);
_pendingByLadderId[num] = ladderFlags;
Debug.Log($"[Hub.SetNext:ServerHandler] From {senderClientId}: flags={ladderFlags} for ladder NetId={num} (replacing={flag}).");
}
public bool TryConsumePending(ulong ladderId, out LadderFlags flags)
{
if (_pendingByLadderId.TryGetValue(ladderId, out flags))
{
_pendingByLadderId.Remove(ladderId);
Debug.Log($"[Hub.TryConsumePending] Consumed flags={flags} for ladder NetId={ladderId}.");
return true;
}
flags = LadderFlags.None;
Debug.Log($"[Hub.TryConsumePending] No pending flags for ladder NetId={ladderId}.");
return false;
}
public void BroadcastApplyNextActivationFlags(ulong ladderId, LadderFlags flags)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
{
Debug.Log("[Hub.BroadcastApply] Not server or NetworkManager missing; skipping.");
return;
}
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(9, (Allocator)2, -1);
try
{
((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref ladderId, default(ForPrimitives));
byte b = (byte)flags;
((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
Debug.Log($"[Hub.BroadcastApply] Broadcasting ladder NetId={ladderId} flags={flags} to all clients.");
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessageToAll("StealthLadders.Apply", val, (NetworkDelivery)3);
}
finally
{
((IDisposable)(FastBufferWriter)(ref val)).Dispose();
}
}
private void OnApplyNextActivationFlagsClient(ulong _senderClientId, FastBufferReader reader)
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
if (!((FastBufferReader)(ref reader)).TryBeginRead(9))
{
Debug.Log("[Hub.Apply:ClientHandler] Reader underflow.");
return;
}
ulong num = default(ulong);
((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
byte b = default(byte);
((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
LadderFlags ladderFlags = (LadderFlags)b;
NetworkManager singleton = NetworkManager.Singleton;
if ((Object)(object)singleton == (Object)null)
{
Debug.Log("[Hub.Apply:ClientHandler] NetworkManager missing; cannot resolve ladder.");
return;
}
if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(num, out var value))
{
Debug.Log($"[Hub.Apply:ClientHandler] Could not find ladder NetId={num}; not applying.");
return;
}
ExtensionLadderItem component = ((Component)value).GetComponent<ExtensionLadderItem>();
if ((Object)(object)component == (Object)null)
{
Debug.Log($"[Hub.Apply:ClientHandler] NetId={num} is not a ladder; skipping.");
return;
}
LadderState ladderState = ((Component)component).GetComponent<LadderState>() ?? ((Component)component).gameObject.AddComponent<LadderState>();
if ((Object)(object)ladderState != (Object)null)
{
((object)ladderState).ToString();
}
ladderState.ApplyLocal(ladderFlags);
Debug.Log($"[Hub.Apply:ClientHandler] Applied flags={ladderFlags} to ladder NetId={num}. NewState={ladderState}");
}
public void SendClimbStealthToServer(ulong playerNetObjId, bool muted)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsClient)
{
Debug.Log("[Hub.SendClimb] Not a client or NetworkManager missing; skipping.");
return;
}
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(9, (Allocator)2, -1);
try
{
((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerNetObjId, default(ForPrimitives));
byte b = (muted ? ((byte)1) : ((byte)0));
((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
Debug.Log($"[Hub.SendClimb] Sending climb-stealth muted={muted} for player NetId={playerNetObjId} to server.");
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("StealthLadders.SetClimb", 0uL, val, (NetworkDelivery)3);
}
finally
{
((IDisposable)(FastBufferWriter)(ref val)).Dispose();
}
}
private void OnSetClimbStealthServer(ulong senderClientId, FastBufferReader reader)
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
{
Debug.Log("[Hub.SetClimb:ServerHandler] Not server; ignoring.");
return;
}
if (!((FastBufferReader)(ref reader)).TryBeginRead(9))
{
Debug.Log("[Hub.SetClimb:ServerHandler] Reader underflow.");
return;
}
ulong num = default(ulong);
((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
byte b = default(byte);
((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
bool flag = b != 0;
Debug.Log($"[Hub.SetClimb:ServerHandler] From {senderClientId}: player NetId={num} muted={flag}. Broadcasting...");
BroadcastApplyClimbStealth(num, flag);
}
public void BroadcastApplyClimbStealth(ulong playerNetObjId, bool muted)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
{
Debug.Log("[Hub.BroadcastApplyClimb] Not server or NetworkManager missing; skipping.");
return;
}
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(9, (Allocator)2, -1);
try
{
((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerNetObjId, default(ForPrimitives));
byte b = (muted ? ((byte)1) : ((byte)0));
((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessageToAll("StealthLadders.ApplyClimb", val, (NetworkDelivery)3);
}
finally
{
((IDisposable)(FastBufferWriter)(ref val)).Dispose();
}
}
private void OnApplyClimbStealthClient(ulong _senderClientId, FastBufferReader reader)
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
if (!((FastBufferReader)(ref reader)).TryBeginRead(9))
{
Debug.Log("[Hub.ApplyClimb:ClientHandler] Reader underflow.");
return;
}
ulong num = default(ulong);
((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
byte b = default(byte);
((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
bool flag = b != 0;
NetworkManager singleton = NetworkManager.Singleton;
if ((Object)(object)singleton == (Object)null)
{
Debug.Log("[Hub.ApplyClimb:ClientHandler] NetworkManager missing; cannot resolve player.");
return;
}
if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(num, out var value))
{
Debug.Log($"[Hub.ApplyClimb:ClientHandler] Could not find player NetId={num}; not applying.");
return;
}
PlayerControllerB component = ((Component)value).GetComponent<PlayerControllerB>();
if ((Object)(object)component == (Object)null)
{
Debug.Log($"[Hub.ApplyClimb:ClientHandler] NetId={num} is not a PlayerControllerB; skipping.");
return;
}
(((Component)component).GetComponent<ClimbSilenceState>() ?? ((Component)component).gameObject.AddComponent<ClimbSilenceState>()).SetMuted(flag);
Debug.Log("[Hub.ApplyClimb:ClientHandler] " + (flag ? "Muted" : "Unmuted") + " movementAudio on player '" + component.playerUsername + "'.");
}
}
internal static class LadderPatch
{
[HarmonyPatch(typeof(GrabbableObject), "Start")]
private static class Patch_GrabbableObject_Start
{
private static void Postfix(GrabbableObject __instance)
{
ExtensionLadderItem val = (ExtensionLadderItem)(object)((__instance is ExtensionLadderItem) ? __instance : null);
if (val != null)
{
if ((Object)(object)((Component)val).GetComponent<LadderState>() == (Object)null)
{
((Component)val).gameObject.AddComponent<LadderState>();
NetworkObject networkObject = ((NetworkBehaviour)val).NetworkObject;
Debug.Log($"[GrabbableObject.Start:Postfix] Added LadderState to ladder NetId={((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null)}");
}
else
{
NetworkObject networkObject2 = ((NetworkBehaviour)val).NetworkObject;
Debug.Log($"[GrabbableObject.Start:Postfix] LadderState already present on ladder NetId={((networkObject2 != null) ? new ulong?(networkObject2.NetworkObjectId) : null)}");
}
}
}
}
[HarmonyPatch(typeof(StartOfRound), "Awake")]
private static class Patch_StartOfRound_Awake
{
private static void Postfix()
{
bool flag = Object.op_Implicit((Object)(object)NetworkManager.Singleton) && NetworkManager.Singleton.IsServer;
bool flag2 = Object.op_Implicit((Object)(object)NetworkManager.Singleton) && NetworkManager.Singleton.IsClient;
Debug.Log($"[StartOfRound.Awake:Postfix] IsServer={flag} IsClient={flag2}");
StealthLaddersHub stealthLaddersHub = StealthLaddersHub.Ensure();
Debug.Log("[StartOfRound.Awake:Postfix] Hub ensured (custom-messaging relay; no NetworkObject). hubInstance=" + (((stealthLaddersHub != null) ? ((Object)stealthLaddersHub).GetInstanceID().ToString() : null) ?? "null"));
}
}
[HarmonyPatch(typeof(ExtensionLadderItem), "ItemActivate")]
private static class Patch_ItemActivate
{
private static void Prefix(ExtensionLadderItem __instance)
{
ulong? obj;
if (__instance == null)
{
obj = null;
}
else
{
NetworkObject networkObject = ((NetworkBehaviour)__instance).NetworkObject;
obj = ((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null);
}
Debug.Log($"[ItemActivate:Prefix] Ladder NetId={obj}");
}
}
[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
internal static class Patch_AudioSource_Play_NoArgs
{
private static bool Prefix(AudioSource __instance)
{
try
{
if (!Object.op_Implicit((Object)(object)__instance))
{
return true;
}
ExtensionLadderItem componentInParent = ((Component)__instance).GetComponentInParent<ExtensionLadderItem>();
if (!Object.op_Implicit((Object)(object)componentInParent))
{
return true;
}
LadderState component = ((Component)componentInParent).GetComponent<LadderState>();
bool flag = Object.op_Implicit((Object)(object)component) && component.Is(LadderFlags.Stealth);
if ((Object)(object)__instance.clip == (Object)(object)componentInParent.ladderFallSFX)
{
component?.SetFlag(LadderFlags.Falling, value: true);
NetworkObject networkObject = ((NetworkBehaviour)componentInParent).NetworkObject;
object arg = ((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null);
AudioClip clip = __instance.clip;
Debug.Log($"[Audio.Play()] Ladder NetId={arg} clip={((clip != null) ? ((Object)clip).name : null)} -> Set Falling=true");
}
bool flag2 = flag && ((Object)(object)__instance.clip == (Object)(object)componentInParent.ladderExtendSFX || (Object)(object)__instance.clip == (Object)(object)componentInParent.ladderFallSFX);
object[] array = new object[4];
NetworkObject networkObject2 = ((NetworkBehaviour)componentInParent).NetworkObject;
array[0] = ((networkObject2 != null) ? new ulong?(networkObject2.NetworkObjectId) : null);
AudioClip clip2 = __instance.clip;
array[1] = ((clip2 != null) ? ((Object)clip2).name : null);
array[2] = flag;
array[3] = flag2;
Debug.Log(string.Format("[Audio.Play()] Ladder NetId={0} clip={1} isStealth={2} block={3}", array));
return !flag2;
}
catch (Exception arg2)
{
Debug.Log($"[Audio.Play()] Exception: {arg2}");
return true;
}
}
}
[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { typeof(ulong) })]
internal static class Patch_AudioSource_Play_WithDelay
{
private static bool Prefix(AudioSource __instance, ulong delay)
{
try
{
if (!Object.op_Implicit((Object)(object)__instance))
{
return true;
}
ExtensionLadderItem componentInParent = ((Component)__instance).GetComponentInParent<ExtensionLadderItem>();
if (!Object.op_Implicit((Object)(object)componentInParent))
{
return true;
}
LadderState component = ((Component)componentInParent).GetComponent<LadderState>();
bool flag = Object.op_Implicit((Object)(object)component) && component.Is(LadderFlags.Stealth);
if ((Object)(object)__instance.clip == (Object)(object)componentInParent.ladderFallSFX)
{
component?.SetFlag(LadderFlags.Falling, value: true);
object arg = delay;
NetworkObject networkObject = ((NetworkBehaviour)componentInParent).NetworkObject;
object arg2 = ((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null);
AudioClip clip = __instance.clip;
Debug.Log($"[Audio.Play(delay={arg})] Ladder NetId={arg2} clip={((clip != null) ? ((Object)clip).name : null)} -> Set Falling=true");
}
bool flag2 = flag && ((Object)(object)__instance.clip == (Object)(object)componentInParent.ladderExtendSFX || (Object)(object)__instance.clip == (Object)(object)componentInParent.ladderFallSFX);
object[] obj = new object[5] { delay, null, null, null, null };
NetworkObject networkObject2 = ((NetworkBehaviour)componentInParent).NetworkObject;
obj[1] = ((networkObject2 != null) ? new ulong?(networkObject2.NetworkObjectId) : null);
AudioClip clip2 = __instance.clip;
obj[2] = ((clip2 != null) ? ((Object)clip2).name : null);
obj[3] = flag;
obj[4] = flag2;
Debug.Log(string.Format("[Audio.Play(delay={0})] Ladder NetId={1} clip={2} isStealth={3} block={4}", obj));
return !flag2;
}
catch (Exception arg3)
{
Debug.Log($"[Audio.Play(delay)] Exception: {arg3}");
return true;
}
}
}
[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
{
typeof(AudioClip),
typeof(float)
})]
internal static class Patch_AudioSource_PlayOneShot_WithVol
{
private static bool Prefix(AudioSource __instance, AudioClip clip, float volumeScale)
{
try
{
if (!Object.op_Implicit((Object)(object)__instance) || !Object.op_Implicit((Object)(object)clip))
{
return true;
}
ExtensionLadderItem componentInParent = ((Component)__instance).GetComponentInParent<ExtensionLadderItem>();
if (!Object.op_Implicit((Object)(object)componentInParent))
{
return true;
}
LadderState component = ((Component)componentInParent).GetComponent<LadderState>();
bool flag = Object.op_Implicit((Object)(object)component) && component.Is(LadderFlags.Stealth);
bool flag2 = Object.op_Implicit((Object)(object)component) && component.Is(LadderFlags.Falling);
if ((Object)(object)clip == (Object)(object)componentInParent.ladderShrinkSFX)
{
NetworkObject networkObject = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot(vol)] Ladder NetId={((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null)} clip={((Object)clip).name} -> ClearFlags()");
component?.ClearFlags();
return true;
}
if (flag && (Object)(object)clip == (Object)(object)componentInParent.hitWall)
{
if (flag2)
{
NetworkObject networkObject2 = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot(vol)] Ladder NetId={((networkObject2 != null) ? new ulong?(networkObject2.NetworkObjectId) : null)} clip=hitWall during Falling -> ClearFlags(); allow");
component?.ClearFlags();
return true;
}
NetworkObject networkObject3 = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot(vol)] Ladder NetId={((networkObject3 != null) ? new ulong?(networkObject3.NetworkObjectId) : null)} clip=hitWall stealth=true -> BLOCK");
return false;
}
if (flag && ((Object)(object)clip == (Object)(object)componentInParent.lidOpenSFX || (Object)(object)clip == (Object)(object)componentInParent.fullExtend || (Object)(object)clip == (Object)(object)((GrabbableObject)componentInParent).itemProperties?.dropSFX))
{
NetworkObject networkObject4 = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot(vol)] Ladder NetId={((networkObject4 != null) ? new ulong?(networkObject4.NetworkObjectId) : null)} clip={((Object)clip).name} (lid/full/drop) stealth=true -> BLOCK");
return false;
}
object[] array = new object[4];
NetworkObject networkObject5 = ((NetworkBehaviour)componentInParent).NetworkObject;
array[0] = ((networkObject5 != null) ? new ulong?(networkObject5.NetworkObjectId) : null);
array[1] = ((Object)clip).name;
array[2] = flag;
array[3] = flag2;
Debug.Log(string.Format("[Audio.PlayOneShot(vol)] Ladder NetId={0} clip={1} isStealth={2} isFalling={3} -> allow", array));
return true;
}
catch (Exception arg)
{
Debug.Log($"[Audio.PlayOneShot(vol)] Exception: {arg}");
return true;
}
}
}
[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[] { typeof(AudioClip) })]
internal static class Patch_AudioSource_PlayOneShot_NoVol
{
private static bool Prefix(AudioSource __instance, AudioClip clip)
{
try
{
if (!Object.op_Implicit((Object)(object)__instance) || !Object.op_Implicit((Object)(object)clip))
{
return true;
}
ExtensionLadderItem componentInParent = ((Component)__instance).GetComponentInParent<ExtensionLadderItem>();
if (!Object.op_Implicit((Object)(object)componentInParent))
{
return true;
}
LadderState component = ((Component)componentInParent).GetComponent<LadderState>();
bool flag = Object.op_Implicit((Object)(object)component) && component.Is(LadderFlags.Stealth);
bool flag2 = Object.op_Implicit((Object)(object)component) && component.Is(LadderFlags.Falling);
if ((Object)(object)clip == (Object)(object)componentInParent.ladderShrinkSFX)
{
NetworkObject networkObject = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot] Ladder NetId={((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null)} clip={((Object)clip).name} -> ClearFlags()");
component?.ClearFlags();
return true;
}
if (flag && (Object)(object)clip == (Object)(object)componentInParent.hitWall)
{
if (flag2)
{
NetworkObject networkObject2 = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot] Ladder NetId={((networkObject2 != null) ? new ulong?(networkObject2.NetworkObjectId) : null)} clip=hitWall during Falling -> Set Stealth=false,Falling=false; allow");
component?.SetFlag(LadderFlags.Stealth, value: false);
component?.SetFlag(LadderFlags.Falling, value: false);
return true;
}
NetworkObject networkObject3 = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot] Ladder NetId={((networkObject3 != null) ? new ulong?(networkObject3.NetworkObjectId) : null)} clip=hitWall stealth=true -> BLOCK");
return false;
}
if (flag && ((Object)(object)clip == (Object)(object)componentInParent.lidOpenSFX || (Object)(object)clip == (Object)(object)componentInParent.fullExtend || (Object)(object)clip == (Object)(object)((GrabbableObject)componentInParent).itemProperties?.dropSFX))
{
NetworkObject networkObject4 = ((NetworkBehaviour)componentInParent).NetworkObject;
Debug.Log($"[Audio.PlayOneShot] Ladder NetId={((networkObject4 != null) ? new ulong?(networkObject4.NetworkObjectId) : null)} clip={((Object)clip).name} (lid/full/drop) stealth=true -> BLOCK");
return false;
}
object[] array = new object[4];
NetworkObject networkObject5 = ((NetworkBehaviour)componentInParent).NetworkObject;
array[0] = ((networkObject5 != null) ? new ulong?(networkObject5.NetworkObjectId) : null);
array[1] = ((Object)clip).name;
array[2] = flag;
array[3] = flag2;
Debug.Log(string.Format("[Audio.PlayOneShot] Ladder NetId={0} clip={1} isStealth={2} isFalling={3} -> allow", array));
return true;
}
catch (Exception arg)
{
Debug.Log($"[Audio.PlayOneShot] Exception: {arg}");
return true;
}
}
}
}
[BepInPlugin("Azx.StealthLadders", "Stealth Ladders", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "Azx.StealthLadders";
public const string PluginName = "Stealth Ladders";
public const string PluginVersion = "1.0.0";
internal static ManualLogSource Log;
internal static Harmony Harmony;
private GameObject _controllerGO;
internal static Plugin Instance { get; private set; }
private void Awake()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
Harmony = new Harmony("Azx.StealthLadders");
ConfigSettings.Init((BaseUnityPlugin)(object)this);
ConfigSettings.RegisterWithLethalConfigIfPresent();
Harmony.PatchAll();
StealthLaddersHub.Ensure();
_controllerGO = new GameObject("StealthLaddersController");
_controllerGO.AddComponent<ClimbSilenceMonitor>();
Object.DontDestroyOnLoad((Object)(object)_controllerGO);
Log.LogInfo((object)"Stealth Ladders 1.0.0 loaded.");
}
private void OnDestroy()
{
if ((Object)(object)_controllerGO != (Object)null)
{
Object.Destroy((Object)(object)_controllerGO);
}
}
internal static bool IsHost()
{
if (Object.op_Implicit((Object)(object)NetworkManager.Singleton))
{
if (!NetworkManager.Singleton.IsServer)
{
return NetworkManager.Singleton.IsHost;
}
return true;
}
return false;
}
}
[HarmonyPatch(typeof(AudioSource))]
internal static class ClimbAudioPatch
{
private const string TAG = "[Stealth Ladders] [ClimbAudioPatch]";
private const bool Verbose = true;
private const int MaxPerSourceNoisy = 8;
private static readonly Dictionary<int, int> _noisyCount = new Dictionary<int, int>();
private static bool EvaluateBlock(AudioSource src, out string reason, out PlayerControllerB player, out bool isMovementAudio, out bool mutedFlag)
{
reason = "";
player = null;
isMovementAudio = false;
mutedFlag = false;
try
{
if ((Object)(object)src == (Object)null)
{
reason = "src=null";
return false;
}
player = ((Component)src).GetComponentInParent<PlayerControllerB>();
if ((Object)(object)player == (Object)null)
{
reason = "no-PlayerControllerB-in-parent";
return false;
}
isMovementAudio = (Object)(object)player.movementAudio == (Object)(object)src;
if (!isMovementAudio)
{
string[] obj = new string[5]
{
"not-movementAudio src='",
((Object)src).name,
"' vs player.movementAudio='",
null,
null
};
AudioSource movementAudio = player.movementAudio;
obj[3] = ((movementAudio != null) ? ((Object)movementAudio).name : null) ?? "null";
obj[4] = "'";
reason = string.Concat(obj);
return false;
}
mutedFlag = ClimbSilenceState.IsClimbMuted(player);
reason = (mutedFlag ? "muted=true" : "muted=false");
return mutedFlag;
}
catch (Exception ex)
{
reason = "exception:" + ex.GetType().Name;
Debug.Log(string.Format("{0} EvaluateBlock exception:\n{1}", "[Stealth Ladders] [ClimbAudioPatch]", ex));
return false;
}
}
private static bool GetAnimatorBool(Animator anim, string name)
{
try
{
return (Object)(object)anim != (Object)null && anim.GetBool(name);
}
catch
{
return false;
}
}
private static bool GetBoolFieldOrProp(object obj, params string[] names)
{
if (obj == null)
{
return false;
}
Type type = obj.GetType();
foreach (string name in names)
{
try
{
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null && field.FieldType == typeof(bool))
{
return (bool)field.GetValue(obj);
}
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.PropertyType == typeof(bool))
{
return (bool)property.GetValue(obj);
}
}
catch
{
}
}
return false;
}
private static void TraceDecision(string where, AudioSource src, string clipName, string reason, PlayerControllerB player, bool isMovementAudio, bool block, bool mutedFlag)
{
int num = (Object.op_Implicit((Object)(object)src) ? ((Object)src).GetInstanceID() : 0);
bool flag = !block && !isMovementAudio;
if (!flag || !_noisyCount.TryGetValue(num, out var value) || value < 8)
{
if (flag)
{
_noisyCount[num] = ((!_noisyCount.TryGetValue(num, out value)) ? 1 : (value + 1));
}
string text = (((Object)(object)player != (Object)null) ? $"{player.playerUsername} ({player.playerClientId})" : "null");
string text2 = (((Object)(object)src != (Object)null) ? $"'{((Object)src).name}'#{num}" : "null");
bool flag2 = GetAnimatorBool(player?.playerBodyAnimator, "ClimbingLadder") || GetAnimatorBool(player?.playerBodyAnimator, "climbingLadder");
bool flag3 = player?.isClimbingLadder ?? flag2;
bool boolFieldOrProp = GetBoolFieldOrProp(player, "isCrouching", "crouching", "playerIsCrouching", "IsCrouching");
bool boolFieldOrProp2 = GetBoolFieldOrProp(player, "isGrounded", "grounded", "playerGrounded");
bool boolFieldOrProp3 = GetBoolFieldOrProp(player, "isSprinting", "sprinting");
string text3 = $"ctx[climb={flag3}, animClimb={flag2}, crouch?={boolFieldOrProp}, grounded?={boolFieldOrProp2}, sprint?={boolFieldOrProp3}, src.mute={((src != null) ? new bool?(src.mute) : null)}, src.vol={((src != null) ? new float?(src.volume) : null):0.00}]";
Debug.Log(string.Format("{0} {1}: clip='{2}', src={3}, player={4}, isMovementAudio={5}, muted={6}, block={7}, reason={8}; {9}", "[Stealth Ladders] [ClimbAudioPatch]", where, clipName, text2, text, isMovementAudio, mutedFlag, block, reason, text3));
}
}
[HarmonyPatch("Play", new Type[] { })]
[HarmonyPrefix]
private static bool Play_NoArgs(AudioSource __instance)
{
string reason;
PlayerControllerB player;
bool isMovementAudio;
bool mutedFlag;
bool flag = EvaluateBlock(__instance, out reason, out player, out isMovementAudio, out mutedFlag);
TraceDecision("Play()", __instance, "<current-clip>", reason, player, isMovementAudio, flag, mutedFlag);
if (flag)
{
Debug.Log("[Stealth Ladders] [ClimbAudioPatch] Blocked: AudioSource.Play()");
}
return !flag;
}
[HarmonyPatch("Play", new Type[] { typeof(ulong) })]
[HarmonyPrefix]
private static bool Play_WithDelay(AudioSource __instance, ulong delay)
{
string reason;
PlayerControllerB player;
bool isMovementAudio;
bool mutedFlag;
bool flag = EvaluateBlock(__instance, out reason, out player, out isMovementAudio, out mutedFlag);
TraceDecision($"Play(delay={delay})", __instance, "<current-clip>", reason, player, isMovementAudio, flag, mutedFlag);
if (flag)
{
Debug.Log(string.Format("{0} Blocked: AudioSource.Play({1})", "[Stealth Ladders] [ClimbAudioPatch]", delay));
}
return !flag;
}
[HarmonyPatch("PlayOneShot", new Type[] { typeof(AudioClip) })]
[HarmonyPrefix]
private static bool PlayOneShot_Clip(AudioSource __instance, AudioClip clip)
{
string text = (Object.op_Implicit((Object)(object)clip) ? ((Object)clip).name : "null");
string reason;
PlayerControllerB player;
bool isMovementAudio;
bool mutedFlag;
bool flag = EvaluateBlock(__instance, out reason, out player, out isMovementAudio, out mutedFlag);
TraceDecision("PlayOneShot(clip)", __instance, text, reason, player, isMovementAudio, flag, mutedFlag);
if (flag)
{
Debug.Log("[Stealth Ladders] [ClimbAudioPatch] Blocked: PlayOneShot('" + text + "')");
}
return !flag;
}
[HarmonyPatch("PlayOneShot", new Type[]
{
typeof(AudioClip),
typeof(float)
})]
[HarmonyPrefix]
private static bool PlayOneShot_ClipFloat(AudioSource __instance, AudioClip clip, float volumeScale)
{
string text = (Object.op_Implicit((Object)(object)clip) ? ((Object)clip).name : "null");
string reason;
PlayerControllerB player;
bool isMovementAudio;
bool mutedFlag;
bool flag = EvaluateBlock(__instance, out reason, out player, out isMovementAudio, out mutedFlag);
TraceDecision($"PlayOneShot(clip,vol={volumeScale:0.00})", __instance, text, reason, player, isMovementAudio, flag, mutedFlag);
if (flag)
{
Debug.Log(string.Format("{0} Blocked: PlayOneShot('{1}', {2:0.00})", "[Stealth Ladders] [ClimbAudioPatch]", text, volumeScale));
}
return !flag;
}
}
public class ClimbSilenceMonitor : MonoBehaviour
{
private const string TAG = "[Stealth Ladders] [ClimbSilenceMonitor]";
private static ClimbSilenceMonitor _singleton;
private PlayerControllerB _local;
private bool _lastMuted;
private InputAction _crouchAction;
private double _lastStatusAt;
private string _lastStatus;
public static ClimbSilenceMonitor Ensure(string cause)
{
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Expected O, but got Unknown
if ((Object)(object)_singleton != (Object)null)
{
if (!((Behaviour)_singleton).enabled)
{
((Behaviour)_singleton).enabled = true;
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Ensure: re-enabled (cause=" + cause + ")");
}
if (!((Component)_singleton).gameObject.activeSelf)
{
((Component)_singleton).gameObject.SetActive(true);
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Ensure: re-activated GO (cause=" + cause + ")");
}
return _singleton;
}
ClimbSilenceMonitor climbSilenceMonitor = Object.FindObjectsOfType<ClimbSilenceMonitor>(true).FirstOrDefault();
if ((Object)(object)climbSilenceMonitor != (Object)null)
{
_singleton = climbSilenceMonitor;
((Object)((Component)climbSilenceMonitor).gameObject).hideFlags = (HideFlags)61;
((Behaviour)climbSilenceMonitor).enabled = true;
((Component)climbSilenceMonitor).gameObject.SetActive(true);
Object.DontDestroyOnLoad((Object)(object)((Component)climbSilenceMonitor).gameObject);
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Ensure: found existing; revived (cause=" + cause + ")");
return climbSilenceMonitor;
}
GameObject val = new GameObject("StealthLadders::Monitor")
{
hideFlags = (HideFlags)61
};
Object.DontDestroyOnLoad((Object)val);
_singleton = val.AddComponent<ClimbSilenceMonitor>();
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Ensure: created new (cause=" + cause + ")");
return _singleton;
}
[RuntimeInitializeOnLoadMethod(/*Could not decode attribute arguments.*/)]
private static void AutoInstall()
{
Ensure("AutoInstall");
}
private void Awake()
{
if ((Object)(object)_singleton != (Object)null && (Object)(object)_singleton != (Object)(object)this)
{
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Duplicate monitor destroyed.");
Object.Destroy((Object)(object)((Component)this).gameObject);
}
else
{
_singleton = this;
((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
}
}
private void OnEnable()
{
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] OnEnable");
TryBindCrouchAction();
}
private void OnDisable()
{
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] OnDisable -> forcing unmute");
ForceMute(mute: false, "OnDisable");
}
private void TryBindCrouchAction()
{
_crouchAction = null;
try
{
IngamePlayerSettings instance = IngamePlayerSettings.Instance;
object obj;
if (instance == null)
{
obj = null;
}
else
{
PlayerInput playerInput = instance.playerInput;
obj = ((playerInput != null) ? playerInput.actions : null);
}
InputActionAsset val = (InputActionAsset)obj;
if ((Object)(object)val == (Object)null)
{
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] PlayerInput actions null; will use PCB fallback.");
return;
}
_crouchAction = val.FindAction("Crouch", true);
if (_crouchAction == null)
{
_crouchAction = ((IEnumerable<InputAction>)val).Where((InputAction a) => a != null && !string.IsNullOrEmpty(a.name)).FirstOrDefault((Func<InputAction, bool>)((InputAction a) => a.name.ToLowerInvariant().Contains("crouch")));
}
if (_crouchAction != null)
{
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Bound InputAction '" + _crouchAction.name + "'.");
}
else
{
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] Could not find any InputAction matching 'crouch' (will fall back to PCB fields).");
}
}
catch (Exception arg)
{
Debug.Log(string.Format("{0} Exception binding InputAction: {1}", "[Stealth Ladders] [ClimbSilenceMonitor]", arg));
}
}
private static bool GetBoolFieldOrProp(object obj, params string[] names)
{
if (obj == null)
{
return false;
}
Type type = obj.GetType();
foreach (string name in names)
{
try
{
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null && field.FieldType == typeof(bool))
{
return (bool)field.GetValue(obj);
}
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.PropertyType == typeof(bool))
{
return (bool)property.GetValue(obj);
}
}
catch
{
}
}
return false;
}
private (bool value, string source, float rawAction) GetCrouchState(PlayerControllerB p)
{
float num = 0f;
if (_crouchAction != null)
{
try
{
num = _crouchAction.ReadValue<float>();
}
catch
{
num = 0f;
}
if (num > 0.5f)
{
return (true, "InputAction('" + _crouchAction.name + "')>0.5", num);
}
}
return (GetBoolFieldOrProp(p, "isCrouching", "crouching", "playerIsCrouching", "IsCrouching"), "ReflectedPCBBool", num);
}
private bool GetClimbState(PlayerControllerB p, out string source, out bool animFlag)
{
animFlag = false;
try
{
animFlag = (Object)(object)p?.playerBodyAnimator != (Object)null && (p.playerBodyAnimator.GetBool("ClimbingLadder") || p.playerBodyAnimator.GetBool("climbingLadder"));
}
catch
{
}
bool result = (p?.isClimbingLadder ?? false) | animFlag;
source = ((p != null && p.isClimbingLadder) ? "PCB.isClimbingLadder" : (animFlag ? "Animator.ClimbingLadder" : "None"));
return result;
}
private void Status(string msg, double minInterval = 0.33)
{
double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
if (!(msg == _lastStatus) || !(realtimeSinceStartupAsDouble - _lastStatusAt < minInterval))
{
_lastStatus = msg;
_lastStatusAt = realtimeSinceStartupAsDouble;
Debug.Log("[Stealth Ladders] [ClimbSilenceMonitor] " + msg);
}
}
private void Update()
{
if (!ConfigSettings.StealthOnLadders)
{
Status("ConfigOff -> unmuting");
ForceMute(mute: false, "ConfigOff");
return;
}
if (_crouchAction == null)
{
TryBindCrouchAction();
}
_local = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)_local == (Object)null)
{
Status("NoLocalPlayer -> unmuting");
ForceMute(mute: false, "NoLocalPlayer");
return;
}
string source;
bool animFlag;
bool climbState = GetClimbState(_local, out source, out animFlag);
var (flag, text, num) = GetCrouchState(_local);
if (!climbState)
{
Status($"NotClimbing(src={source}, anim={animFlag}) -> unmuting");
ForceMute(mute: false, "NotClimbing");
return;
}
bool flag2 = flag;
Status($"Climbing({source}, anim={animFlag}) Crouch={flag}({text}, raw={num:0.00}) => wantMute={flag2}");
ForceMute(flag2, "Update");
}
private void ForceMute(bool mute, string cause)
{
if (mute == _lastMuted)
{
Debug.Log(string.Format("{0} ForceMute no-change (mute={1}) cause={2}", "[Stealth Ladders] [ClimbSilenceMonitor]", mute, cause));
}
else
{
Debug.Log(string.Format("{0} ForceMute change {1} -> {2} cause={3}", "[Stealth Ladders] [ClimbSilenceMonitor]", _lastMuted, mute, cause));
_lastMuted = mute;
}
if ((Object)(object)_local != (Object)null)
{
ClimbSilenceState.EnsureOn(((Component)_local).gameObject).SetMuted(mute, "local-fallback:" + cause);
}
NetworkManager singleton = NetworkManager.Singleton;
PlayerControllerB local = _local;
NetworkObject val = ((local != null) ? ((NetworkBehaviour)local).NetworkObject : null);
StealthLaddersHub stealthLaddersHub = StealthLaddersHub.Ensure();
if ((Object)(object)singleton == (Object)null || !singleton.IsListening || (Object)(object)val == (Object)null || (Object)(object)stealthLaddersHub == (Object)null)
{
Debug.Log(string.Format("{0} HubSend skipped (NMListening={1}, NetObj={2}, Hub={3})", "[Stealth Ladders] [ClimbSilenceMonitor]", singleton != null && singleton.IsListening, (Object)(object)val != (Object)null, (Object)(object)stealthLaddersHub != (Object)null));
}
else
{
Debug.Log(string.Format("{0} HubSend mute={1} for netId={2}", "[Stealth Ladders] [ClimbSilenceMonitor]", mute, val.NetworkObjectId));
stealthLaddersHub.SendClimbStealthToServer(val.NetworkObjectId, mute);
}
}
}
public class ClimbSilenceState : MonoBehaviour
{
private const string TAG = "[Stealth Ladders] [ClimbSilenceState]";
private AudioSource _movementAudioCached;
private PlayerControllerB _player;
public bool Muted { get; private set; }
private void Awake()
{
_player = ((Component)this).GetComponent<PlayerControllerB>();
_movementAudioCached = _player?.movementAudio;
string[] obj = new string[5]
{
"[Stealth Ladders] [ClimbSilenceState] Awake: player='",
_player?.playerUsername ?? "null",
"' audio='",
null,
null
};
AudioSource movementAudioCached = _movementAudioCached;
obj[3] = ((movementAudioCached != null) ? ((Object)movementAudioCached).name : null) ?? "null";
obj[4] = "'";
Debug.Log(string.Concat(obj));
}
private void OnEnable()
{
Debug.Log(string.Format("{0} OnEnable: Muted={1}", "[Stealth Ladders] [ClimbSilenceState]", Muted));
}
private void OnDisable()
{
ApplyLocalMute(mute: false, "OnDisable");
}
private void OnDestroy()
{
ApplyLocalMute(mute: false, "OnDestroy");
}
public void SetMuted(bool muted, string cause = null)
{
if (Muted == muted)
{
Debug.Log(string.Format("{0} SetMuted no-change ({1}) cause={2}", "[Stealth Ladders] [ClimbSilenceState]", muted, cause));
return;
}
bool muted2 = Muted;
Muted = muted;
Debug.Log(string.Format("{0} SetMuted {1} -> {2} for '{3}' cause={4}", "[Stealth Ladders] [ClimbSilenceState]", muted2, Muted, _player?.playerUsername ?? "null", cause));
ApplyLocalMute(Muted, "SetMuted:" + cause);
}
private void ApplyLocalMute(bool mute, string cause)
{
try
{
if ((Object)(object)_movementAudioCached == (Object)null)
{
_movementAudioCached = _player?.movementAudio;
}
if ((Object)(object)_movementAudioCached == (Object)null)
{
Debug.Log("[Stealth Ladders] [ClimbSilenceState] ApplyLocalMute skipped (no movementAudio) cause=" + cause);
}
else if (_movementAudioCached.mute != mute)
{
_movementAudioCached.mute = mute;
Debug.Log(string.Format("{0} Local AudioSource.mute -> {1} ({2}) cause={3}", "[Stealth Ladders] [ClimbSilenceState]", mute, ((Object)_movementAudioCached).name, cause));
}
else
{
Debug.Log(string.Format("{0} Local AudioSource.mute already {1} ({2}) cause={3}", "[Stealth Ladders] [ClimbSilenceState]", mute, ((Object)_movementAudioCached).name, cause));
}
}
catch (Exception arg)
{
Debug.Log(string.Format("{0} ApplyLocalMute exception: {1}", "[Stealth Ladders] [ClimbSilenceState]", arg));
}
}
public static bool IsClimbMuted(PlayerControllerB player)
{
if ((Object)(object)player == (Object)null)
{
return false;
}
ClimbSilenceState climbSilenceState = default(ClimbSilenceState);
if (((Component)player).TryGetComponent<ClimbSilenceState>(ref climbSilenceState))
{
return climbSilenceState.Muted;
}
return false;
}
public static ClimbSilenceState EnsureOn(GameObject go)
{
if ((Object)(object)go == (Object)null)
{
return null;
}
ClimbSilenceState result = default(ClimbSilenceState);
if (!go.TryGetComponent<ClimbSilenceState>(ref result))
{
result = go.AddComponent<ClimbSilenceState>();
Debug.Log("[Stealth Ladders] [ClimbSilenceState] EnsureOn: added new state component.");
}
return result;
}
}
[HarmonyPatch]
internal static class StealthLaddersBootstrap
{
[HarmonyPatch(typeof(GameNetworkManager), "Start")]
[HarmonyPostfix]
private static void GN_Start_Postfix()
{
ClimbSilenceMonitor.Ensure("GameNetworkManager.Start");
}
[HarmonyPatch(typeof(PlayerControllerB), "Start")]
[HarmonyPostfix]
private static void PCB_Start_Postfix(PlayerControllerB __instance)
{
ClimbSilenceMonitor.Ensure("PlayerControllerB.Start");
ClimbSilenceState.EnsureOn(((Component)__instance).gameObject);
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "StealthLadders";
public const string PLUGIN_NAME = "StealthLadders";
public const string PLUGIN_VERSION = "0.0.1";
}
}
namespace StealthLadders.Patches
{
internal class ItemUsePatch
{
[HarmonyPatch(typeof(GrabbableObject), "UseItemOnClient")]
private static class Patch_UseItemOnClient
{
private static void Prefix(GrabbableObject __instance, bool buttonDown)
{
if (!ConfigSettings.StealthLadders)
{
Debug.Log("[UseItemOnClient:Prefix] Disabled in config; skipping.");
return;
}
if (!buttonDown)
{
Debug.Log("[UseItemOnClient:Prefix] buttonDown=false; skipping.");
return;
}
ExtensionLadderItem val = (ExtensionLadderItem)(object)((__instance is ExtensionLadderItem) ? __instance : null);
if (val == null)
{
Debug.Log("[UseItemOnClient:Prefix] Not a ladder (" + ((object)__instance)?.GetType().Name + "); skipping.");
return;
}
if ((Object)(object)__instance.playerHeldBy == (Object)null)
{
Debug.Log("[UseItemOnClient:Prefix] playerHeldBy is null; skipping.");
return;
}
bool isCrouching = __instance.playerHeldBy.isCrouching;
object[] array = new object[5];
NetworkObject networkObject = ((NetworkBehaviour)val).NetworkObject;
array[0] = ((networkObject != null) ? new ulong?(networkObject.NetworkObjectId) : null);
array[1] = isCrouching;
array[2] = ((NetworkBehaviour)__instance).IsOwner;
array[3] = ((NetworkBehaviour)__instance).IsClient;
array[4] = ((NetworkBehaviour)__instance).IsServer;
Debug.Log(string.Format("[UseItemOnClient:Prefix] ladder NetId={0} crouching={1} isOwner={2} isClient={3} isServer={4}", array));
LadderState ladderState = ((Component)val).GetComponent<LadderState>();
if ((Object)(object)ladderState == (Object)null)
{
ladderState = ((Component)val).gameObject.AddComponent<LadderState>();
NetworkObject networkObject2 = ((NetworkBehaviour)val).NetworkObject;
Debug.Log($"[UseItemOnClient:Prefix] Added LadderState to ladder NetId={((networkObject2 != null) ? new ulong?(networkObject2.NetworkObjectId) : null)}");
}
ladderState.ApplyLocal(isCrouching ? LadderFlags.Stealth : LadderFlags.None);
Debug.Log($"[UseItemOnClient:Prefix] Local ApplyLocal -> {ladderState}");
StealthLaddersHub stealthLaddersHub = StealthLaddersHub.Ensure();
if ((Object)(object)NetworkManager.Singleton != (Object)null && (Object)(object)((NetworkBehaviour)val).NetworkObject != (Object)null)
{
ulong networkObjectId = ((NetworkBehaviour)val).NetworkObject.NetworkObjectId;
Debug.Log($"[UseItemOnClient:Prefix] Sending SetNextActivationFlags (custom msg) flags={(isCrouching ? LadderFlags.Stealth : LadderFlags.None)} for ladder NetId={networkObjectId}.");
stealthLaddersHub.SendNextActivationFlagsToServer(networkObjectId, isCrouching ? LadderFlags.Stealth : LadderFlags.None);
}
else
{
Debug.Log("[UseItemOnClient:Prefix] NetworkManager or Ladder.NetworkObject is null; cannot send.");
}
}
}
[HarmonyPatch(typeof(GrabbableObject), "ActivateItemClientRpc")]
private static class Patch_SendActivateItemClientRpc
{
private static void Prefix(GrabbableObject __instance)
{
if (!((NetworkBehaviour)__instance).IsServer)
{
Debug.Log("[ActivateItemClientRpc:Prefix] Not server; skipping.");
return;
}
ExtensionLadderItem val = (ExtensionLadderItem)(object)((__instance is ExtensionLadderItem) ? __instance : null);
if (val == null)
{
Debug.Log("[ActivateItemClientRpc:Prefix] Non-ladder object (" + ((object)__instance)?.GetType().Name + "); skipping.");
return;
}
StealthLaddersHub stealthLaddersHub = StealthLaddersHub.Ensure();
if ((Object)(object)stealthLaddersHub == (Object)null)
{
Debug.Log("[ActivateItemClientRpc:Prefix] Hub Ensure() returned null; skipping.");
return;
}
if ((Object)(object)((NetworkBehaviour)val).NetworkObject == (Object)null)
{
Debug.Log("[ActivateItemClientRpc:Prefix] Ladder NetworkObject is null; skipping.");
return;
}
ulong networkObjectId = ((NetworkBehaviour)val).NetworkObject.NetworkObjectId;
if (stealthLaddersHub.TryConsumePending(networkObjectId, out var flags))
{
Debug.Log($"[ActivateItemClientRpc:Prefix] Consumed pending flags={flags} for ladder NetId={networkObjectId}; broadcasting APPLY (custom msg) BEFORE base RPC.");
stealthLaddersHub.BroadcastApplyNextActivationFlags(networkObjectId, flags);
}
else
{
Debug.Log($"[ActivateItemClientRpc:Prefix] No pending flags for ladder NetId={networkObjectId}; nothing to broadcast.");
}
}
}
}
}