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 PhantomWhisper v1.1.0
PhantomWhisper.dll
Decompiled 23 minutes agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PhantomWhisper { internal static class GhostVoice { private static GameObject _voiceObject; private static AudioDistortionFilter _distortion; private static AudioEchoFilter _echo; private static AudioChorusFilter _chorus; public static bool Applied { get; private set; } public static int PitchPlayerId { get; private set; } = -1; public static void Apply(PlayerControllerB whisperer) { AudioSource currentVoiceChatAudioSource = whisperer.currentVoiceChatAudioSource; if (!((Object)(object)currentVoiceChatAudioSource == (Object)null)) { if (!Applied || (Object)(object)_voiceObject != (Object)(object)((Component)currentVoiceChatAudioSource).gameObject) { Remove(); _voiceObject = ((Component)currentVoiceChatAudioSource).gameObject; _distortion = _voiceObject.AddComponent<AudioDistortionFilter>(); _echo = _voiceObject.AddComponent<AudioEchoFilter>(); _chorus = _voiceObject.AddComponent<AudioChorusFilter>(); PitchPlayerId = (int)whisperer.playerClientId; Applied = true; Plugin.Log.LogDebug((object)$"Ghost voice attached to player {PitchPlayerId}."); } VoiceStyle currentStyle = PhantomConfig.CurrentStyle; ((Behaviour)_distortion).enabled = currentStyle.Distortion > 0.001f; _distortion.distortionLevel = currentStyle.Distortion; ((Behaviour)_echo).enabled = currentStyle.EchoDecay > 0.001f; _echo.delay = currentStyle.EchoDelayMs; _echo.decayRatio = currentStyle.EchoDecay; _echo.dryMix = 1f; _echo.wetMix = 0.6f; ((Behaviour)_chorus).enabled = currentStyle.Chorus > 0.001f; _chorus.dryMix = 0.5f; _chorus.wetMix1 = 0.5f * currentStyle.Chorus; _chorus.wetMix2 = 0.5f * currentStyle.Chorus; _chorus.wetMix3 = 0.5f * currentStyle.Chorus; _chorus.delay = 40f; _chorus.rate = 0.8f; _chorus.depth = 0.03f; SoundManager instance = SoundManager.Instance; if ((Object)(object)instance != (Object)null) { instance.SetPlayerPitch(currentStyle.Pitch, PitchPlayerId); } } } public static void Remove() { if (Applied) { int pitchPlayerId = PitchPlayerId; Applied = false; PitchPlayerId = -1; if ((Object)(object)_distortion != (Object)null) { Object.Destroy((Object)(object)_distortion); } if ((Object)(object)_echo != (Object)null) { Object.Destroy((Object)(object)_echo); } if ((Object)(object)_chorus != (Object)null) { Object.Destroy((Object)(object)_chorus); } _distortion = null; _echo = null; _chorus = null; _voiceObject = null; SoundManager instance = SoundManager.Instance; if ((Object)(object)instance != (Object)null && instance.playerVoicePitches != null && pitchPlayerId >= 0 && pitchPlayerId < instance.playerVoicePitches.Length) { instance.SetPlayerPitch(instance.playerVoicePitches[pitchPlayerId], pitchPlayerId); } RefreshVanillaVoiceState(); } } public static void RefreshVanillaVoiceState() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { instance.UpdatePlayerVoiceEffects(); } } } internal static class ModInfo { public const string Guid = "phantomwhisper.lethalcompany"; public const string Name = "PhantomWhisper"; public const string Version = "1.1.0"; } [HarmonyPatch] internal static class Patches { private static PlayerControllerB LocalPlayer { get { GameNetworkManager instance = GameNetworkManager.Instance; if (!((Object)(object)instance != (Object)null)) { return null; } return instance.localPlayerController; } } [HarmonyPatch(typeof(PlayerVoiceIngameSettings), "LateUpdate")] [HarmonyPostfix] private static void ApplyWhisperOverrides(PlayerVoiceIngameSettings __instance) { if (!WhisperNetwork.Active) { return; } PlayerControllerB localPlayer = LocalPlayer; PlayerControllerB whisperer = WhisperNetwork.GetWhisperer(); if ((Object)(object)localPlayer == (Object)null || (Object)(object)whisperer == (Object)null || (Object)(object)whisperer == (Object)(object)localPlayer || (Object)(object)__instance != (Object)(object)whisperer.currentVoiceChatIngameSettings) { return; } AudioSource currentVoiceChatAudioSource = whisperer.currentVoiceChatAudioSource; if ((Object)(object)currentVoiceChatAudioSource == (Object)null || whisperer.voicePlayerState == null) { return; } if (WhisperNetwork.IsTarget(localPlayer)) { __instance.set2D = true; currentVoiceChatAudioSource.spatialBlend = 0f; whisperer.voicePlayerState.Volume = PhantomConfig.WhisperVolume.Value; AudioLowPassFilter component = ((Component)currentVoiceChatAudioSource).GetComponent<AudioLowPassFilter>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } AudioHighPassFilter component2 = ((Component)currentVoiceChatAudioSource).GetComponent<AudioHighPassFilter>(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = false; } } else { whisperer.voicePlayerState.Volume = 0f; } } [HarmonyPatch(typeof(SoundManager), "SetPlayerPitch")] [HarmonyPrefix] private static void OverridePlayerPitch(ref float pitch, int playerObjNum) { if (GhostVoice.Applied && playerObjNum == GhostVoice.PitchPlayerId) { pitch = PhantomConfig.CurrentStyle.Pitch; } } } internal enum VoicePreset { Ghost, Demon, Banshee, Custom } internal struct VoiceStyle { public float Pitch; public float Distortion; public float EchoDelayMs; public float EchoDecay; public float Chorus; } internal static class PhantomConfig { public static ConfigEntry<Key> WhisperKey; public static ConfigEntry<bool> ToggleMode; public static ConfigEntry<float> WhisperVolume; public static ConfigEntry<VoicePreset> Preset; public static ConfigEntry<float> CustomPitch; public static ConfigEntry<float> CustomDistortion; public static ConfigEntry<float> CustomEchoDelayMs; public static ConfigEntry<float> CustomEchoDecay; public static ConfigEntry<float> CustomChorus; public static VoiceStyle CurrentStyle => Preset.Value switch { VoicePreset.Demon => new VoiceStyle { Pitch = 0.5f, Distortion = 0.55f, EchoDelayMs = 180f, EchoDecay = 0.35f, Chorus = 0f }, VoicePreset.Banshee => new VoiceStyle { Pitch = 1.6f, Distortion = 0.25f, EchoDelayMs = 400f, EchoDecay = 0.65f, Chorus = 0.8f }, VoicePreset.Custom => new VoiceStyle { Pitch = CustomPitch.Value, Distortion = CustomDistortion.Value, EchoDelayMs = CustomEchoDelayMs.Value, EchoDecay = CustomEchoDecay.Value, Chorus = CustomChorus.Value }, _ => new VoiceStyle { Pitch = 0.7f, Distortion = 0.15f, EchoDelayMs = 300f, EchoDecay = 0.55f, Chorus = 0.5f }, }; public static void Bind(ConfigFile cfg) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown WhisperKey = cfg.Bind<Key>("Host", "WhisperKey", (Key)22, "Key the HOST holds to whisper to the player they are spectating. Any UnityEngine.InputSystem.Key name works (H, B, F, Backquote, ...). Only the host's key matters."); ToggleMode = cfg.Bind<bool>("Host", "ToggleMode", false, "false = hold the key to whisper. true = press once to start, press again to stop (handy if you use voice activation)."); WhisperVolume = cfg.Bind<float>("Listeners", "WhisperVolume", 1f, new ConfigDescription("How loud the whisper is for the player it is aimed at (applies on that player's side).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); Preset = cfg.Bind<VoicePreset>("Voice", "Preset", VoicePreset.Ghost, "Voice effect preset. Use Custom to take the values below instead."); CustomPitch = cfg.Bind<float>("Voice", "CustomPitch", 0.7f, new ConfigDescription("Custom preset: pitch (0.5 = one octave down, 2 = one octave up).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); CustomDistortion = cfg.Bind<float>("Voice", "CustomDistortion", 0.15f, new ConfigDescription("Custom preset: distortion amount.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); CustomEchoDelayMs = cfg.Bind<float>("Voice", "CustomEchoDelayMs", 300f, new ConfigDescription("Custom preset: echo delay in milliseconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 1000f), Array.Empty<object>())); CustomEchoDecay = cfg.Bind<float>("Voice", "CustomEchoDecay", 0.55f, new ConfigDescription("Custom preset: echo decay (0 disables the echo).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.95f), Array.Empty<object>())); CustomChorus = cfg.Bind<float>("Voice", "CustomChorus", 0.5f, new ConfigDescription("Custom preset: chorus / shimmer amount (0 disables it).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); } } [BepInPlugin("phantomwhisper.lethalcompany", "PhantomWhisper", "1.1.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; private Harmony _harmony; private bool _hostWhispering; private bool _toggled; private ulong _currentTarget; private float _lastHeartbeat; private float _lastHint = -100f; private bool _mutedHost; private static PlayerControllerB LocalPlayer { get { GameNetworkManager instance = GameNetworkManager.Instance; if (!((Object)(object)instance != (Object)null)) { return null; } return instance.localPlayerController; } } private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; PhantomConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("phantomwhisper.lethalcompany"); _harmony.PatchAll(typeof(Patches)); Log.LogInfo((object)string.Format("{0} {1} loaded. Host whisper key: {2}", "PhantomWhisper", "1.1.0", PhantomConfig.WhisperKey.Value)); } private void OnDestroy() { GhostVoice.Remove(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { WhisperNetwork.EnsureRegistered(); WhisperNetwork.ExpireIfStale(); if ((Object)(object)StartOfRound.Instance == (Object)null) { WhisperNetwork.ResetState(); } HostTick(); ListenerTick(); } private void HostTick() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; PlayerControllerB localPlayer = LocalPlayer; Keyboard current = Keyboard.current; if (!((Object)(object)singleton != (Object)null) || !singleton.IsHost || !((Object)(object)localPlayer != (Object)null) || current == null || (int)PhantomConfig.WhisperKey.Value == 0) { _hostWhispering = false; _toggled = false; return; } bool flag = localPlayer.isTypingChat || localPlayer.inTerminalMenu || ((Object)(object)localPlayer.quickMenuManager != (Object)null && localPlayer.quickMenuManager.isMenuOpen); KeyControl val = current[PhantomConfig.WhisperKey.Value]; bool flag2 = !flag && ((ButtonControl)val).wasPressedThisFrame; bool flag3; if (PhantomConfig.ToggleMode.Value) { if (flag) { _toggled = false; } else if (flag2) { _toggled = !_toggled; } flag3 = _toggled; } else { flag3 = !flag && ((ButtonControl)val).isPressed; } ulong targetId; bool flag4 = TryGetSpectatedTarget(localPlayer, out targetId); if (flag3 && !flag4) { _toggled = false; if (flag2) { Hint("You need to be dead and spectating a player to whisper to them."); } } if (flag3 && flag4) { if (!_hostWhispering || targetId != _currentTarget || Time.unscaledTime - _lastHeartbeat >= 1f) { _currentTarget = targetId; _hostWhispering = true; _lastHeartbeat = Time.unscaledTime; WhisperNetwork.Broadcast(active: true, localPlayer.playerClientId, targetId); } } else if (_hostWhispering) { _hostWhispering = false; WhisperNetwork.Broadcast(active: false, localPlayer.playerClientId, _currentTarget); } } private static bool TryGetSpectatedTarget(PlayerControllerB local, out ulong targetId) { targetId = 0uL; if (!local.isPlayerDead) { return false; } PlayerControllerB spectatedPlayerScript = local.spectatedPlayerScript; if ((Object)(object)spectatedPlayerScript == (Object)null || (Object)(object)spectatedPlayerScript == (Object)(object)local) { return false; } targetId = spectatedPlayerScript.playerClientId; return true; } private void Hint(string text) { if (!(Time.unscaledTime - _lastHint < 8f) && !((Object)(object)HUDManager.Instance == (Object)null)) { _lastHint = Time.unscaledTime; HUDManager.Instance.DisplayTip("Phantom Whisper", text, false, false, "PhantomWhisper_Hint"); } } private void ListenerTick() { PlayerControllerB localPlayer = LocalPlayer; PlayerControllerB whisperer = WhisperNetwork.GetWhisperer(); bool flag = (Object)(object)localPlayer != (Object)null && (Object)(object)whisperer != (Object)null && (Object)(object)whisperer != (Object)(object)localPlayer; if (flag && WhisperNetwork.IsTarget(localPlayer)) { GhostVoice.Apply(whisperer); _mutedHost = false; return; } if (GhostVoice.Applied) { GhostVoice.Remove(); } bool flag2 = flag; if (_mutedHost && !flag2) { GhostVoice.RefreshVanillaVoiceState(); } _mutedHost = flag2; } } internal static class WhisperNetwork { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnMessage; } private const string MessageName = "PhantomWhisper.State"; public const float HeartbeatSeconds = 1f; private const float TimeoutSeconds = 3.5f; private static float _lastHeard; private static CustomMessagingManager _registeredOn; public static bool Active { get; private set; } public static ulong WhispererId { get; private set; } public static ulong TargetId { get; private set; } public static void EnsureRegistered() { //IL_004d: 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: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = (((Object)(object)singleton != (Object)null) ? singleton.CustomMessagingManager : null); if (val == null) { _registeredOn = null; } else if (val != _registeredOn) { val.UnregisterNamedMessageHandler("PhantomWhisper.State"); object obj = <>O.<0>__OnMessage; if (obj == null) { HandleNamedMessageDelegate val2 = OnMessage; <>O.<0>__OnMessage = val2; obj = (object)val2; } val.RegisterNamedMessageHandler("PhantomWhisper.State", (HandleNamedMessageDelegate)obj); _registeredOn = val; Plugin.Log.LogDebug((object)"Registered whisper message handler."); } } public static void ExpireIfStale() { if (Active && Time.unscaledTime - _lastHeard > 3.5f) { Plugin.Log.LogDebug((object)"Whisper state timed out, clearing."); Active = false; } } public static void ResetState() { Active = false; } public static PlayerControllerB GetWhisperer() { return GetPlayer(WhispererId); } public static bool IsTarget(PlayerControllerB player) { if (Active && (Object)(object)player != (Object)null) { return player.playerClientId == TargetId; } return false; } private static PlayerControllerB GetPlayer(ulong id) { if (!Active) { return null; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null || id >= (ulong)instance.allPlayerScripts.Length) { return null; } return instance.allPlayerScripts[id]; } public unsafe static void Broadcast(bool active, ulong whispererId, ulong targetId) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(17, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref whispererId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref targetId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref active, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("PhantomWhisper.State", val, (NetworkDelivery)2); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnMessage(ulong senderClientId, FastBufferReader reader) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && senderClientId == 0L && !singleton.IsHost) { ulong whispererId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref whispererId, default(ForPrimitives)); ulong targetId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref targetId, default(ForPrimitives)); bool active = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref active, default(ForPrimitives)); Active = active; WhispererId = whispererId; TargetId = targetId; _lastHeard = Time.unscaledTime; } } } }