Decompiled source of SpookyCompany v1.1.0

LCGhostMod.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance;
using DobieWan.config;
using GameNetcodeStuff;
using HarmonyLib;
using LethalNetworkAPI;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("LCGhostMod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.0.0.0")]
[assembly: AssemblyInformationalVersion("0.0.0-alpha.0.20+89218321cbbbc3c0e399b0d99bdc4d85c88162dd")]
[assembly: AssemblyProduct("LCGhostMod")]
[assembly: AssemblyTitle("LCGhostMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.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;
		}
	}
}
namespace DobieWan
{
	[HarmonyPatch(typeof(RoundManager))]
	internal class RoundManagerPatch
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void RoundManagerAwake()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			GameObject val = GameObject.Find("Systems");
			GameObject val2 = new GameObject("GhostManager", new Type[1] { typeof(GhostManager) });
			val2.transform.SetParent(val.transform);
		}
	}
	[BepInPlugin("LCGhostMod", "LCGhostMod", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony m_harmony = new Harmony("LCGhostMod");

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		internal AssetBundle AssetBundle { get; private set; }

		internal EventConfigs EventConfigs { get; } = new EventConfigs();


		internal SfxPlayerConfigs SfxPlayerConfigs { get; } = new SfxPlayerConfigs();


		public Plugin()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Instance = this;
		}

		private void Awake()
		{
			Log.LogInfo((object)"LCGhostMod is awake!");
			InitConfigs();
			TryLoadAssetBundle();
			ApplyPluginPatch();
		}

		private void ApplyPluginPatch()
		{
			m_harmony.PatchAll(typeof(RoundManagerPatch));
		}

		private void TryLoadAssetBundle()
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (directoryName == null)
			{
				Log.LogError((object)"Failed to get bundle directory.");
				return;
			}
			AssetBundle = AssetBundle.LoadFromFile(Path.Combine(directoryName, "lcghostmodassets"));
			if ((Object)(object)AssetBundle == (Object)null)
			{
				Log.LogError((object)"Failed to load custom assets.");
			}
			else
			{
				Log.LogInfo((object)"LCGhostMod bundle loaded!");
			}
		}

		private void InitConfigs()
		{
			((IConfigs)EventConfigs).Initialize(((BaseUnityPlugin)this).Config);
			((IConfigs)SfxPlayerConfigs).Initialize(((BaseUnityPlugin)this).Config);
		}
	}
	internal class GhostManager : MonoBehaviour
	{
		private LethalClientMessage<HauntVictimEventData> m_hauntVictimEvent = null;

		private LethalClientMessage<VictimHauntedEventData> m_victimHauntedEvent = null;

		private PlayerGhostEventDetector m_ghostEventDetector = null;

		private PlayerGhostSfxPlayer m_ghostSfxPlayer = null;

		private void Start()
		{
			m_ghostEventDetector = new PlayerGhostEventDetector(TriggerHauntVictimEvent);
			m_ghostSfxPlayer = new PlayerGhostSfxPlayer();
			m_hauntVictimEvent = new LethalClientMessage<HauntVictimEventData>("HauntVictim", (Action<HauntVictimEventData>)null, (Action<HauntVictimEventData, ulong>)ReceiveHauntVictimEvent);
			m_victimHauntedEvent = new LethalClientMessage<VictimHauntedEventData>("VictimHaunted", (Action<VictimHauntedEventData>)null, (Action<VictimHauntedEventData, ulong>)ReceiveVictimHauntedEvent);
			Plugin.Log.LogInfo((object)"GhostManager initialized!");
		}

		private void LateUpdate()
		{
			m_ghostEventDetector.Simulate();
		}

		private void TriggerHauntVictimEvent(PlayerControllerB forPlayer)
		{
			ulong actualClientId = forPlayer.actualClientId;
			Plugin.Log.LogInfo((object)$"Triggered haunt victim event for {actualClientId}");
			HauntVictimEventData hauntVictimEventData = new HauntVictimEventData(actualClientId);
			m_hauntVictimEvent.SendAllClients(hauntVictimEventData, true, false);
		}

		private void ReceiveHauntVictimEvent(HauntVictimEventData data, ulong fromUser)
		{
			PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController;
			Plugin.Log.LogInfo((object)$"Haunt victim received for user {data.SpectatedUserId}. This user is {localPlayerController.actualClientId}");
			if (localPlayerController.actualClientId == data.SpectatedUserId)
			{
				string text = m_ghostSfxPlayer.PlaySfx();
				Plugin.Log.LogInfo((object)("Haunt victim: Playing clip " + text));
				TriggerVictimHauntedEvent(text);
			}
		}

		private void TriggerVictimHauntedEvent(string audioClipName)
		{
			Plugin.Log.LogInfo((object)("Triggered victim haunted event with clip " + audioClipName));
			VictimHauntedEventData victimHauntedEventData = new VictimHauntedEventData(audioClipName);
			m_victimHauntedEvent.SendAllClients(victimHauntedEventData, true, false);
		}

		private void ReceiveVictimHauntedEvent(VictimHauntedEventData data, ulong fromUser)
		{
			PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController;
			Plugin.Log.LogInfo((object)$"Victim haunted received from user {fromUser}. The spectated user is {localPlayerController.actualClientId}");
			if (!((Object)(object)localPlayerController.spectatedPlayerScript == (Object)null))
			{
				ulong actualClientId = localPlayerController.spectatedPlayerScript.actualClientId;
				if (actualClientId == fromUser)
				{
					Plugin.Log.LogInfo((object)("Victim haunted: Playing clip: " + data.ClipName));
					m_ghostSfxPlayer.PlaySpectatorSfx(data.ClipName);
				}
			}
		}
	}
	internal class PlayerGhostEventDetector
	{
		private readonly EventConfigs m_configs = null;

		private readonly Action<PlayerControllerB> m_hauntVictimEventAction = null;

		private float m_lastGhostNoiseTime = 0f;

		private float m_cooldownTime = 0f;

		internal PlayerGhostEventDetector(Action<PlayerControllerB> hauntVictimEventAction)
		{
			m_configs = Plugin.Instance.EventConfigs;
			m_hauntVictimEventAction = hauntVictimEventAction;
			m_cooldownTime = GetRandomCooldownTime();
		}

		internal void Simulate()
		{
			StartOfRound instance = StartOfRound.Instance;
			PlayerControllerB localPlayerController = instance.localPlayerController;
			DissonanceComms voiceChatModule = instance.voiceChatModule;
			if (!localPlayerController.isPlayerDead || m_lastGhostNoiseTime + m_cooldownTime > Time.time || (Object)(object)voiceChatModule == (Object)null || (Object)(object)localPlayerController.spectatedPlayerScript == (Object)null)
			{
				return;
			}
			string localPlayerName = voiceChatModule.LocalPlayerName;
			if (!string.IsNullOrEmpty(localPlayerName))
			{
				VoicePlayerState val = voiceChatModule.FindPlayer(voiceChatModule.LocalPlayerName);
				if (val != null && val.IsSpeaking && !(val.Amplitude < m_configs.MinMicAmpToTriggerGhost.Value))
				{
					TriggerGhostEvent(localPlayerController.spectatedPlayerScript);
				}
			}
		}

		private void TriggerGhostEvent(PlayerControllerB toPlayer)
		{
			m_lastGhostNoiseTime = Time.time;
			m_cooldownTime = GetRandomCooldownTime();
			m_hauntVictimEventAction?.Invoke(toPlayer);
		}

		private float GetRandomCooldownTime()
		{
			float value = m_configs.EventTriggerCooldownMin.Value;
			float value2 = m_configs.EventTriggerCooldownMax.Value;
			return value + Random.value * (value2 - value);
		}
	}
	internal class PlayerGhostSfxPlayer
	{
		private readonly SfxPlayerConfigs m_configs = null;

		private readonly AudioSource m_audioSource = null;

		private readonly AudioClip[] m_ghostSfx = null;

		private int m_ghostSfxIndex = 0;

		internal PlayerGhostSfxPlayer()
		{
			m_configs = Plugin.Instance.SfxPlayerConfigs;
			AssetBundle assetBundle = Plugin.Instance.AssetBundle;
			if (!((Object)(object)assetBundle == (Object)null))
			{
				m_ghostSfx = assetBundle.LoadAllAssets<AudioClip>();
				m_ghostSfx.Shuffle();
				ManualLogSource log = Plugin.Log;
				AudioClip[] ghostSfx = m_ghostSfx;
				log.LogInfo((object)$"Loaded {((ghostSfx != null) ? ghostSfx.Length : 0)} audio clips");
				GameObject val = GameObject.Find("Systems/Audios/SFX");
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogError((object)"Failed to find SFX object");
				}
				else if (!val.TryGetComponent<AudioSource>(ref m_audioSource))
				{
					Plugin.Log.LogError((object)"Failed to find SFX audio source");
				}
				else
				{
					Plugin.Log.LogInfo((object)"PlayerGhostSfxPlayer successfully initialized!");
				}
			}
		}

		internal string PlaySfx()
		{
			if (m_ghostSfx == null || m_ghostSfx.Length == 0 || (Object)(object)m_audioSource == (Object)null)
			{
				return null;
			}
			Plugin.Log.LogInfo((object)"ooOOOOOOOoooooOOOoo");
			AudioClip nextAudioClip = GetNextAudioClip();
			Utility.PlayAudioClipLocalOnly(m_audioSource, nextAudioClip, m_configs.RandomizePitchRange.Value, m_configs.RandomizeVolumeRange.Value);
			return ((Object)nextAudioClip).name;
		}

		internal AudioClip GetNextAudioClip()
		{
			if (m_ghostSfxIndex >= m_ghostSfx.Length)
			{
				ReshuffleAudioClips();
				m_ghostSfxIndex = 0;
			}
			return m_ghostSfx[m_ghostSfxIndex++];
		}

		internal void ReshuffleAudioClips()
		{
			AudioClip val = m_ghostSfx[^1];
			m_ghostSfx.Shuffle();
			AudioClip val2 = m_ghostSfx[0];
			if (m_ghostSfx.Length > 1 && (Object)(object)val == (Object)(object)val2)
			{
				m_ghostSfx[0] = m_ghostSfx[^1];
				m_ghostSfx[^1] = val2;
			}
		}

		internal void PlaySpectatorSfx(string clipName)
		{
			if (!TryGetAudioClipByName(clipName, out var audioClip))
			{
				audioClip = m_ghostSfx[Random.Range(0, m_ghostSfx.Length)];
			}
			Utility.PlayAudioClipLocalOnly(m_audioSource, audioClip, m_configs.RandomizePitchRange.Value, m_configs.RandomizeVolumeRange.Value);
		}

		private bool TryGetAudioClipByName(string clipName, out AudioClip audioClip)
		{
			for (int i = 0; i < m_ghostSfx.Length; i++)
			{
				AudioClip val = m_ghostSfx[i];
				if ((Object)(object)val != (Object)null && ((Object)val).name == clipName)
				{
					audioClip = val;
					return true;
				}
			}
			audioClip = null;
			return false;
		}
	}
	[Serializable]
	internal struct HauntVictimEventData
	{
		[SerializeField]
		private ulong m_spectatedUserId;

		internal ulong SpectatedUserId
		{
			readonly get
			{
				return m_spectatedUserId;
			}
			private set
			{
				m_spectatedUserId = value;
			}
		}

		internal HauntVictimEventData(ulong mSpectatedUserId)
		{
			m_spectatedUserId = 0uL;
			SpectatedUserId = mSpectatedUserId;
		}
	}
	[Serializable]
	internal struct VictimHauntedEventData
	{
		[SerializeField]
		private string m_clipName;

		internal string ClipName
		{
			readonly get
			{
				return m_clipName;
			}
			private set
			{
				m_clipName = value;
			}
		}

		internal VictimHauntedEventData(string clipName)
		{
			m_clipName = null;
			ClipName = clipName;
		}
	}
	internal static class Utility
	{
		internal static void Shuffle<T>(this IList<T> list)
		{
			for (int i = 0; i < list.Count; i++)
			{
				T value = list[i];
				int index = Random.Range(i, list.Count);
				list[i] = list[index];
				list[index] = value;
			}
		}

		internal static bool Contains<T>(this IList<T> list, T value) where T : IEquatable<T>
		{
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].Equals(value))
				{
					return true;
				}
			}
			return false;
		}

		internal static void PlayAudioClipLocalOnly(AudioSource audioSource, AudioClip clip, float randomizePitchRange = 0f, float randomizeVolumeRange = 0f, float volume = 1f)
		{
			if (randomizePitchRange > 0f)
			{
				audioSource.pitch = Random.Range(1f - randomizePitchRange, 1f + randomizePitchRange);
			}
			float num = 1f;
			if (randomizeVolumeRange > 0f)
			{
				num = Random.Range(volume - randomizeVolumeRange, volume + randomizeVolumeRange);
			}
			audioSource.PlayOneShot(clip, num);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "LCGhostMod";

		public const string PLUGIN_NAME = "LCGhostMod";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace DobieWan.config
{
	internal class EventConfigs : IConfigs
	{
		private const string GROUP_NAME = "GHOST EVENT";

		internal ConfigEntry<float> MinMicAmpToTriggerGhost { get; private set; }

		internal ConfigEntry<float> EventTriggerCooldownMin { get; private set; }

		internal ConfigEntry<float> EventTriggerCooldownMax { get; private set; }

		void IConfigs.Initialize(ConfigFile config)
		{
			MinMicAmpToTriggerGhost = config.Bind<float>("GHOST EVENT", "Microphone Amp Threshold", 0.4f, "This is the threshold amplitude for your microphone input to trigger a ghost event. A higher value means you must speak louder to trigger the event. Recommended between 0.1 and 2.0.");
			EventTriggerCooldownMin = config.Bind<float>("GHOST EVENT", "Min Cooldown Time", 2f, "Minimum length of time between ghost events triggered by you.");
			EventTriggerCooldownMax = config.Bind<float>("GHOST EVENT", "Max Cooldown Time", 5f, "Maximum length of time between ghost events triggered by you.");
		}
	}
	public interface IConfigs
	{
		internal void Initialize(ConfigFile config);
	}
	internal class SfxPlayerConfigs : IConfigs
	{
		private const string GROUP_NAME = "SFX";

		internal ConfigEntry<float> RandomizePitchRange { get; private set; }

		internal ConfigEntry<float> RandomizeVolumeRange { get; private set; }

		void IConfigs.Initialize(ConfigFile config)
		{
			RandomizePitchRange = config.Bind<float>("SFX", "Randomize Pitch Range", 0.2f, "Bigger number means ghost SFX pitch can be randomized further from normal.");
			RandomizeVolumeRange = config.Bind<float>("SFX", "Randomize Volume Range", 0.2f, "Bigger number means ghost SFX volume can be randomized further from normal.");
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}