Decompiled source of MaskedPlus v1.0.0

MaskedPlus.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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.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("MaskedPlus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MaskedPlus")]
[assembly: AssemblyTitle("MaskedPlus")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MaskedPlus
{
	internal static class MaskedPatches
	{
		private static readonly FieldRef<MaskedPlayerEnemy, PlayerControllerB> lastPlayerKilled = AccessTools.FieldRefAccess<MaskedPlayerEnemy, PlayerControllerB>("lastPlayerKilled");

		private static readonly HashSet<int> handled = new HashSet<int>();

		private static Item comedyItem;

		private static Item tragedyItem;

		[HarmonyPatch(typeof(MaskedPlayerEnemy), "KillEnemy")]
		[HarmonyPostfix]
		private static void OnKilled(MaskedPlayerEnemy __instance, bool destroy)
		{
			if (destroy || (Object)(object)__instance == (Object)null || !handled.Add(((Object)__instance).GetInstanceID()))
			{
				return;
			}
			try
			{
				bool flag = WoreComedy(__instance);
				HideMask(__instance);
				bool flag2 = ReturnBody(__instance);
				if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer)
				{
					DropMask(__instance, flag);
				}
				Plugin.Log.LogInfo((object)("Masked died: dropped a " + (flag ? "Comedy" : "Tragedy") + " mask" + (flag2 ? ", player body returned" : "") + "."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Masked death: " + ex));
			}
		}

		private static bool WoreComedy(MaskedPlayerEnemy m)
		{
			GameObject[] maskTypes = m.maskTypes;
			if (maskTypes != null && maskTypes.Length > 1)
			{
				if ((Object)(object)maskTypes[0] != (Object)null && maskTypes[0].activeSelf)
				{
					return true;
				}
				if ((Object)(object)maskTypes[1] != (Object)null && maskTypes[1].activeSelf)
				{
					return false;
				}
			}
			return m.maskTypeIndex == 0;
		}

		private static void HideMask(MaskedPlayerEnemy m)
		{
			if (m.maskTypes != null)
			{
				GameObject[] maskTypes = m.maskTypes;
				foreach (GameObject val in maskTypes)
				{
					if ((Object)(object)val != (Object)null)
					{
						val.SetActive(false);
					}
				}
			}
			if (m.maskEyesGlow != null)
			{
				MeshRenderer[] maskEyesGlow = m.maskEyesGlow;
				foreach (MeshRenderer val2 in maskEyesGlow)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						((Renderer)val2).enabled = false;
					}
				}
			}
			if ((Object)(object)m.maskEyesGlowLight != (Object)null)
			{
				((Behaviour)m.maskEyesGlowLight).enabled = false;
			}
		}

		private static void DropMask(MaskedPlayerEnemy m, bool comedy)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0048: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			Item val = FindMask(comedy);
			if ((Object)(object)val == (Object)null || (Object)(object)val.spawnPrefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Mask item not found, nothing dropped.");
				return;
			}
			Vector3 val2 = ((Component)m).transform.position + Vector3.up * 1.8f;
			Transform val3 = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.spawnedScrapContainer : null);
			GameObject obj = Object.Instantiate<GameObject>(val.spawnPrefab, val2, Quaternion.identity, val3);
			GrabbableObject component = obj.GetComponent<GrabbableObject>();
			component.fallTime = 0f;
			component.isInFactory = !((EnemyAI)m).isOutside;
			int num = Value(val);
			component.SetScrapValue(num);
			NetworkObject component2 = obj.GetComponent<NetworkObject>();
			component2.Spawn(false);
			RoundManager.Instance.SyncScrapValuesClientRpc((NetworkObjectReference[])(object)new NetworkObjectReference[1] { NetworkObjectReference.op_Implicit(component2) }, new int[1] { num });
		}

		private static int Value(Item item)
		{
			if (Plugin.ValueMode.Value == MaskValue.Custom)
			{
				return Mathf.Max(0, Plugin.CustomValue.Value);
			}
			float num = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.scrapValueMultiplier : 0.4f);
			int num2 = Mathf.Min(item.minValue, item.maxValue);
			int num3 = Mathf.Max(item.minValue, item.maxValue);
			return Mathf.Max(1, (int)((float)Random.Range(num2, num3) * num));
		}

		private static Item FindMask(bool comedy)
		{
			if (comedy && (Object)(object)comedyItem != (Object)null)
			{
				return comedyItem;
			}
			if (!comedy && (Object)(object)tragedyItem != (Object)null)
			{
				return tragedyItem;
			}
			List<Item> itemsList = StartOfRound.Instance.allItemsList.itemsList;
			string key = (comedy ? "Comedy" : "Tragedy");
			Item result = ((IEnumerable<Item>)itemsList).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && ((Object)i).name == key + "Mask")) ?? ((IEnumerable<Item>)itemsList).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && i.itemName != null && i.itemName.IndexOf(key, StringComparison.OrdinalIgnoreCase) >= 0));
			if (comedy)
			{
				comedyItem = result;
			}
			else
			{
				tragedyItem = result;
			}
			return result;
		}

		private static bool ReturnBody(MaskedPlayerEnemy m)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB mimickingPlayer = m.mimickingPlayer;
			if ((Object)(object)mimickingPlayer == (Object)null || !mimickingPlayer.isPlayerDead || (Object)(object)mimickingPlayer.deadBody == (Object)null || !mimickingPlayer.deadBody.deactivated)
			{
				return false;
			}
			DeadBodyInfo deadBody = mimickingPlayer.deadBody;
			if ((Object)(object)RoundManager.Instance != (Object)null)
			{
				foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies)
				{
					MaskedPlayerEnemy val = (MaskedPlayerEnemy)(object)((spawnedEnemy is MaskedPlayerEnemy) ? spawnedEnemy : null);
					if (val != null && (Object)(object)lastPlayerKilled.Invoke(val) == (Object)(object)mimickingPlayer)
					{
						lastPlayerKilled.Invoke(val) = null;
					}
				}
			}
			((Component)deadBody).gameObject.SetActive(true);
			deadBody.deactivated = false;
			deadBody.attachedTo = null;
			deadBody.attachedLimb = null;
			deadBody.secondaryAttachedTo = null;
			deadBody.secondaryAttachedLimb = null;
			deadBody.matchPositionExactly = false;
			deadBody.SetBodyPartsKinematic(false);
			deadBody.SetRagdollPositionSafely(((Component)m).transform.position + Vector3.up * 0.5f, true);
			Transform[] componentsInChildren = ((Component)deadBody).GetComponentsInChildren<Transform>(true);
			foreach (Transform val2 in componentsInChildren)
			{
				if (((Object)val2).name.StartsWith("HeadMask"))
				{
					((Component)val2).gameObject.SetActive(false);
				}
			}
			Renderer[] componentsInChildren2 = ((Component)m).GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				componentsInChildren2[i].enabled = false;
			}
			Collider[] componentsInChildren3 = ((Component)m).GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				componentsInChildren3[i].enabled = false;
			}
			ScanNodeProperties[] componentsInChildren4 = ((Component)m).GetComponentsInChildren<ScanNodeProperties>(true);
			foreach (ScanNodeProperties val3 in componentsInChildren4)
			{
				if ((Object)(object)((Component)val3).gameObject != (Object)(object)((Component)m).gameObject)
				{
					((Component)val3).gameObject.SetActive(false);
				}
			}
			return true;
		}
	}
	internal static class MaskHide
	{
		private static readonly FieldRef<HauntedMaskItem, bool> maskOn = AccessTools.FieldRefAccess<HauntedMaskItem, bool>("maskOn");

		private static readonly FieldRef<HauntedMaskItem, bool> finishedAttaching = AccessTools.FieldRefAccess<HauntedMaskItem, bool>("finishedAttaching");

		private static readonly Dictionary<(EnemyAI, PlayerControllerB), float> revealed = new Dictionary<(EnemyAI, PlayerControllerB), float>();

		private static bool MaskUp(PlayerControllerB p)
		{
			if ((Object)(object)p != (Object)null && !p.isPlayerDead)
			{
				GrabbableObject currentlyHeldObjectServer = p.currentlyHeldObjectServer;
				HauntedMaskItem val = (HauntedMaskItem)(object)((currentlyHeldObjectServer is HauntedMaskItem) ? currentlyHeldObjectServer : null);
				if (val != null && maskOn.Invoke(val))
				{
					return !finishedAttaching.Invoke(val);
				}
			}
			return false;
		}

		internal static bool Hidden(EnemyAI enemy, PlayerControllerB p)
		{
			if (!Plugin.HideEnabled.Value || (Object)(object)enemy == (Object)null || !MaskUp(p))
			{
				return false;
			}
			if (revealed.TryGetValue((enemy, p), out var value))
			{
				if (Time.time < value)
				{
					return false;
				}
				revealed.Remove((enemy, p));
			}
			return true;
		}

		[HarmonyPatch(typeof(EnemyAICollisionDetect), "OnTriggerStay")]
		[HarmonyPrefix]
		private static void Touch(EnemyAICollisionDetect __instance, Collider other)
		{
			if (!Plugin.HideEnabled.Value || (Object)(object)other == (Object)null || (Object)(object)__instance.mainScript == (Object)null || !((Component)other).CompareTag("Player"))
			{
				return;
			}
			PlayerControllerB component = ((Component)other).GetComponent<PlayerControllerB>();
			if (MaskUp(component))
			{
				if (revealed.Count > 64)
				{
					revealed.Clear();
				}
				revealed[(__instance.mainScript, component)] = Time.time + Mathf.Max(0f, Plugin.RevealSeconds.Value);
			}
		}

		[HarmonyPatch(typeof(EnemyAI), "PlayerIsTargetable")]
		[HarmonyPostfix]
		private static void Targetable(EnemyAI __instance, PlayerControllerB playerScript, ref bool __result)
		{
			if (__result && Hidden(__instance, playerScript))
			{
				__result = false;
			}
		}

		[HarmonyPatch(typeof(EnemyAI), "CheckLineOfSightForPlayer")]
		[HarmonyPostfix]
		private static void Sight(EnemyAI __instance, ref PlayerControllerB __result)
		{
			if (Hidden(__instance, __result))
			{
				__result = null;
			}
		}

		[HarmonyPatch(typeof(EnemyAI), "CheckLineOfSightForClosestPlayer")]
		[HarmonyPostfix]
		private static void ClosestSight(EnemyAI __instance, ref PlayerControllerB __result)
		{
			if (Hidden(__instance, __result))
			{
				__result = null;
			}
		}

		[HarmonyPatch(typeof(EnemyAI), "GetClosestPlayer")]
		[HarmonyPostfix]
		private static void Closest(EnemyAI __instance, ref PlayerControllerB __result)
		{
			if (Hidden(__instance, __result))
			{
				__result = null;
			}
		}

		[HarmonyPatch(typeof(EnemyAI), "GetAllPlayersInLineOfSight")]
		[HarmonyPostfix]
		private static void AllInSight(EnemyAI __instance, ref PlayerControllerB[] __result)
		{
			if (__result == null || __result.Length == 0)
			{
				return;
			}
			int num = 0;
			for (int i = 0; i < __result.Length; i++)
			{
				if (Hidden(__instance, __result[i]))
				{
					num++;
				}
			}
			if (num == 0)
			{
				return;
			}
			if (num == __result.Length)
			{
				__result = null;
				return;
			}
			PlayerControllerB[] array = (PlayerControllerB[])(object)new PlayerControllerB[__result.Length - num];
			int j = 0;
			int num2 = 0;
			for (; j < __result.Length; j++)
			{
				if (!Hidden(__instance, __result[j]))
				{
					array[num2++] = __result[j];
				}
			}
			__result = array;
		}
	}
	public enum MaskValue
	{
		Vanilla,
		Custom
	}
	[BepInPlugin("Miles.MaskedPlus", "MaskedPlus", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "Miles.MaskedPlus";

		public const string Name = "MaskedPlus";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<MaskValue> ValueMode;

		internal static ConfigEntry<int> CustomValue;

		internal static ConfigEntry<bool> HideEnabled;

		internal static ConfigEntry<float> RevealSeconds;

		private void Awake()
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			ValueMode = ((BaseUnityPlugin)this).Config.Bind<MaskValue>("Mask", "ValueMode", MaskValue.Vanilla, "How much a dropped mask is worth. Vanilla = the same random value a Comedy/Tragedy mask found as scrap gets (its normal range times the game's scrap value multiplier, so mods that change scrap value per moon apply too). Custom = always CustomValue. Only the host's setting counts.");
			CustomValue = ((BaseUnityPlugin)this).Config.Bind<int>("Mask", "CustomValue", 52, "Value of a dropped mask when ValueMode is Custom.");
			HideEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Hide behind a mask", "Enabled", true, "Holding a Comedy or Tragedy mask up to your face makes enemies ignore you, even up close. Touching one gives you away and it attacks (a Nutcracker kicks, a Forest Keeper grabs you). Holding the mask up too long still does what masks do. Everyone in the lobby should use the same setting.");
			RevealSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Hide behind a mask", "RevealSeconds", 10f, "After you touch an enemy with the mask up, it sees you for this many seconds (it keeps seeing you while you touch it).");
			Harmony val = new Harmony("Miles.MaskedPlus");
			val.PatchAll(typeof(MaskedPatches));
			val.PatchAll(typeof(MaskHide));
			Log.LogInfo((object)"MaskedPlus 1.0.0 loaded.");
		}
	}
}