Decompiled source of MaskTheDead v1.0.6

BepInEx/plugins/mask-the-dead/MaskTheDead.dll

Decompiled 6 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
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 GameNetcodeStuff;
using HarmonyLib;
using MaskTheDead.Components;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;

[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("MaskTheDead")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("If a dead body is next to a mask there's a chance that the body is possesed")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("MaskTheDead")]
[assembly: AssemblyTitle("MaskTheDead")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.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 MaskTheDead
{
	public class Configuration
	{
		private ConfigEntry<float> configPossessionChance;

		private ConfigEntry<int> configPossessionDelayTime;

		private ConfigEntry<int> configRetryPossesionTime;

		private ConfigEntry<int> configMaskPossessionRange;

		private ConfigFile File;

		public float PossesionChance => configPossessionChance.Value;

		public int RetryPossesionTime => configRetryPossesionTime.Value;

		public int PossessionDelayTime => configPossessionDelayTime.Value;

		public int MaskPossessionRange => configMaskPossessionRange.Value;

		public int RetryPossesionMinTime => 10000;

		public Configuration(ConfigFile config)
		{
			File = config;
			configPossessionChance = File.Bind<float>("General", "PossessionChance", 1f, "A number between 0 and 1 (included) representing the percentage of a body being possessed by a nearby mask");
			configRetryPossesionTime = File.Bind<int>("General", "RetryPossesionTime", RetryPossesionMinTime, $"Time in milliseconds after which a mask will attempt to posses a nearby body again, less than {RetryPossesionMinTime} milliseconds is ignored");
			configMaskPossessionRange = File.Bind<int>("General", "MaskPossessionRange", 10, "Range, in units, of the mask repossession trigger. Only bodies close enough will be considered for repossessions!");
			configPossessionDelayTime = File.Bind<int>("General", "PossessionDelayTime", 0, "Time in milliseconds before the body is actually possessed");
		}
	}
	[BepInPlugin("MaskTheDead", "MaskTheDead", "1.0.3")]
	public class Plugin : BaseUnityPlugin
	{
		public static ManualLogSource TheLogger;

		public static Configuration TheConfiguration;

		public Plugin()
		{
			TheConfiguration = new Configuration(((BaseUnityPlugin)this).Config);
		}

		private void Awake()
		{
			TheLogger = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin MaskTheDead is loaded!");
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "MaskTheDead";

		public const string PLUGIN_NAME = "MaskTheDead";

		public const string PLUGIN_VERSION = "1.0.3";
	}
}
namespace MaskTheDead.Patchers
{
	[HarmonyPatch]
	public class GrabbableObjectMaskPatcher
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(GrabbableObject), "Start")]
		private static bool StartPatch(ref GrabbableObject __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				Plugin.TheLogger.LogFatal((object)"This instance is null, cannot patch item");
				return true;
			}
			GrabbableObject obj = __instance;
			HauntedMaskItem val = (HauntedMaskItem)(object)((obj is HauntedMaskItem) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			if (!GameNetworkManager.Instance.isHostingGame)
			{
				Plugin.TheLogger.LogInfo((object)"Avoding patch for non hosts");
				return true;
			}
			Plugin.TheLogger.LogInfo((object)"Adding mask watcher to haunted mask!");
			((Component)val).gameObject.AddComponent<MaskTheDeadComponent>();
			return true;
		}
	}
}
namespace MaskTheDead.Components
{
	public class MaskTheDeadComponent : NetworkBehaviour
	{
		private readonly Dictionary<GameObject, Coroutine> _NearbyBodies = new Dictionary<GameObject, Coroutine>();

		private HauntedMaskItem _MaskComponent;

		private SphereCollider _Collider;

		public bool PossessionChancePassed => Random.value < Plugin.TheConfiguration.PossesionChance;

		public bool CanRetryPossession => Plugin.TheConfiguration.RetryPossesionTime >= Plugin.TheConfiguration.RetryPossesionMinTime;

		private bool CanComponentUseRagdoll(RagdollGrabbableObject ragdoll)
		{
			if (((GrabbableObject)ragdoll).isHeld || ((GrabbableObject)ragdoll).isHeldByEnemy)
			{
				Plugin.TheLogger.LogInfo((object)"Cant attempt possession of held ragdoll...");
				return false;
			}
			if (((GrabbableObject)ragdoll).isInShipRoom)
			{
				Plugin.TheLogger.LogInfo((object)"Cant attempt possession of body in ship");
				return false;
			}
			return true;
		}

		private void Awake()
		{
			_MaskComponent = ((Component)this).gameObject.GetComponent<HauntedMaskItem>();
			_Collider = ((Component)this).gameObject.AddComponent<SphereCollider>();
			_Collider.radius = Plugin.TheConfiguration.MaskPossessionRange;
			((Collider)_Collider).isTrigger = true;
		}

		private void Update()
		{
			if (ShouldDispose())
			{
				Dispose();
				return;
			}
			bool num = ((GrabbableObject)_MaskComponent).isHeldByEnemy || ((GrabbableObject)_MaskComponent).isHeld;
			bool isInShipRoom = ((GrabbableObject)_MaskComponent).isInShipRoom;
			bool isInElevator = ((GrabbableObject)_MaskComponent).isInElevator;
			if (num || isInShipRoom || isInElevator)
			{
				((Collider)_Collider).enabled = false;
				CleanupCoroutines();
			}
			else
			{
				((Collider)_Collider).enabled = true;
			}
		}

		public override void OnDestroy()
		{
			Plugin.TheLogger.LogInfo((object)"Destroying repossession component!");
			CleanupCoroutines();
			((NetworkBehaviour)this).OnDestroy();
		}

		public override void OnNetworkDespawn()
		{
			Plugin.TheLogger.LogInfo((object)"Network despawn, cleaning repossession component!");
			((NetworkBehaviour)this).OnNetworkDespawn();
			CleanupCoroutines();
		}

		private void OnTriggerEnter(Collider other)
		{
			if (ShouldDispose())
			{
				Dispose();
				return;
			}
			RagdollGrabbableObject colliderRagdoll = GetColliderRagdoll(other);
			if ((Object)(object)colliderRagdoll == (Object)null)
			{
				Plugin.TheLogger.LogInfo((object)"Object in mask range is not ragdoll");
			}
			else if (CanComponentUseRagdoll(colliderRagdoll) && !_NearbyBodies.ContainsKey(((Component)colliderRagdoll).gameObject))
			{
				_NearbyBodies.Add(((Component)colliderRagdoll).gameObject, null);
				AttemptPossesion(colliderRagdoll);
			}
		}

		private void OnTriggerExit(Collider other)
		{
			Plugin.TheLogger.LogInfo((object)$"Something is leaving the mask range, i have {_NearbyBodies.Count} body nearby!");
			RagdollGrabbableObject colliderRagdoll = GetColliderRagdoll(other);
			Coroutine value;
			if ((Object)(object)colliderRagdoll == (Object)null)
			{
				Plugin.TheLogger.LogInfo((object)"The collider leaving is not of a ragdoll");
			}
			else if (_NearbyBodies.TryGetValue(((Component)colliderRagdoll).gameObject, out value))
			{
				if (value != null)
				{
					Plugin.TheLogger.LogInfo((object)"Stopping coroutine for body leaving mask range");
					((MonoBehaviour)this).StopCoroutine(value);
				}
				_NearbyBodies.Remove(((Component)colliderRagdoll).gameObject);
			}
			else
			{
				Plugin.TheLogger.LogWarning((object)"Body leaving mask range was not in dicitonary of bodies!");
			}
		}

		private RagdollGrabbableObject GetColliderRagdoll(Collider other)
		{
			RagdollGrabbableObject component = ((Component)other).gameObject.GetComponent<RagdollGrabbableObject>();
			Plugin.TheLogger.LogInfo((object)$"Collided with {((Component)other).gameObject}");
			if ((Object)(object)component != (Object)null && component.bodyID.Value >= 0)
			{
				Plugin.TheLogger.LogInfo((object)"Found ragdoll, lucky!");
				return component;
			}
			if (!((Object)other).name.Contains(".L") && !((Object)other).name.Contains(".R"))
			{
				Plugin.TheLogger.LogInfo((object)"This is not a bone!");
				return null;
			}
			Plugin.TheLogger.LogInfo((object)"Collided with a bone of a character, finding ragdoll on parents");
			return ((Component)other).gameObject.GetComponentInParent<RagdollGrabbableObject>();
		}

		private IEnumerator RepossessionCoroutine(object o)
		{
			yield return (object)new WaitForSeconds((float)(Plugin.TheConfiguration.RetryPossesionTime / 1000));
			yield return (object)new WaitForEndOfFrame();
			RagdollGrabbableObject val = (RagdollGrabbableObject)((o is RagdollGrabbableObject) ? o : null);
			if ((Object)(object)val != (Object)null && _NearbyBodies.TryGetValue(((Component)val).gameObject, out var _))
			{
				Plugin.TheLogger.LogInfo((object)"Ragdoll timer elapsed, attempting possession!");
				_NearbyBodies[((Component)val).gameObject] = null;
				AttemptPossesion(val);
			}
			else
			{
				Plugin.TheLogger.LogWarning((object)"Ragdoll was deleted, stopping repossession retry!");
			}
		}

		private void CleanupCoroutines()
		{
			if (_NearbyBodies.Count == 0)
			{
				return;
			}
			Plugin.TheLogger.LogInfo((object)"Cleaning up all nearby bodies!");
			foreach (KeyValuePair<GameObject, Coroutine> nearbyBody in _NearbyBodies)
			{
				if (nearbyBody.Value != null)
				{
					((MonoBehaviour)this).StopCoroutine(nearbyBody.Value);
				}
			}
			_NearbyBodies.Clear();
		}

		private IEnumerator AttemptPossesion(RagdollGrabbableObject ragdoll)
		{
			_ = ((Component)ragdoll).gameObject;
			if (Plugin.TheConfiguration.PossessionDelayTime > 0)
			{
				yield return (object)new WaitForSeconds((float)(Plugin.TheConfiguration.PossessionDelayTime / 1000));
				yield return (object)new WaitForEndOfFrame();
			}
			if (!_NearbyBodies.ContainsKey(((Component)ragdoll).gameObject))
			{
				yield break;
			}
			if (!PossessionChancePassed)
			{
				Plugin.TheLogger.LogInfo((object)"Chance failed, will not possess body");
				if (CanRetryPossession)
				{
					Plugin.TheLogger.LogInfo((object)"Starting coroutine for repossession");
					if (_NearbyBodies[((Component)ragdoll).gameObject] != null)
					{
						((MonoBehaviour)this).StopCoroutine(_NearbyBodies[((Component)ragdoll).gameObject]);
					}
					Coroutine value = ((MonoBehaviour)this).StartCoroutine("RepossessionCoroutine", (object)ragdoll);
					_NearbyBodies[((Component)ragdoll).gameObject] = value;
				}
			}
			else
			{
				Plugin.TheLogger.LogInfo((object)"Chance for possession OK");
				BeginPossession(ragdoll);
			}
		}

		private void BeginPossession(RagdollGrabbableObject ragdoll)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			Plugin.TheLogger.LogInfo((object)"!!POSSESSING DEAD BODY!!");
			HauntedMaskItem component = ((Component)this).gameObject.GetComponent<HauntedMaskItem>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.TheLogger.LogFatal((object)"Could not find mask item component, aborting possession!");
				return;
			}
			Plugin.TheLogger.LogInfo((object)"Spawning mimic");
			if ((Object)(object)ragdoll.ragdoll.playerScript == (Object)null)
			{
				Plugin.TheLogger.LogFatal((object)"Ragdoll's player script is null, aborting mimic spawn!");
				return;
			}
			((NetworkBehaviour)component).NetworkObject.RemoveOwnership();
			((NetworkBehaviour)ragdoll).NetworkObject.RemoveOwnership();
			SetPreviouslyHeldByOf(component, ragdoll.ragdoll.playerScript);
			component.CreateMimicServerRpc(((GrabbableObject)ragdoll).isInFactory, ((Component)ragdoll.ragdoll).transform.position);
			if ((Object)(object)component != (Object)null)
			{
				((NetworkBehaviour)component).NetworkObject.Despawn(true);
				Object.Destroy((Object)(object)component);
				if ((Object)(object)((GrabbableObject)component).radarIcon != (Object)null && (Object)(object)((Component)((GrabbableObject)component).radarIcon).gameObject != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)((GrabbableObject)component).radarIcon).gameObject);
				}
			}
			if ((Object)(object)ragdoll != (Object)null)
			{
				((NetworkBehaviour)ragdoll).NetworkObject.Despawn(true);
				Object.Destroy((Object)(object)ragdoll);
			}
		}

		private void SetPreviouslyHeldByOf(HauntedMaskItem item, PlayerControllerB c)
		{
			((object)item).GetType().GetField("previousPlayerHeldBy", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(item, c);
			Plugin.TheLogger.LogInfo((object)"Set private field previousPlayerHeldBy");
		}

		private bool HasFinishedAttaching(HauntedMaskItem item)
		{
			return (bool)((object)item).GetType().GetField("finishedAttaching", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(item);
		}

		private bool ShouldDispose()
		{
			if (HasFinishedAttaching(_MaskComponent))
			{
				Plugin.TheLogger.LogInfo((object)">> MASK HAS ATTACHED ITSELF TO SOMEONE <<");
				Plugin.TheLogger.LogInfo((object)">> KILLING MASKTHEDEAD COMPONENT       <<");
				return true;
			}
			if ((Object)(object)_MaskComponent == (Object)null || (Object)(object)_Collider == (Object)null)
			{
				Plugin.TheLogger.LogWarning((object)">> MASK OR COLLIDER IS NULL <<");
				Plugin.TheLogger.LogInfo((object)">> KILLING MASKTHEDEAD COMPONENT       <<");
				return true;
			}
			if (StartOfRound.Instance.currentLevel.levelID == 3)
			{
				return true;
			}
			return false;
		}

		private void Dispose()
		{
			Plugin.TheLogger.LogInfo((object)">> DISPOSING <<");
			Object.Destroy((Object)(object)this);
		}
	}
}