Decompiled source of LethalBodySwap v1.0.0

LethalBodySwap.dll

Decompiled 4 days 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 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("LethalBodySwap")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LethalBodySwap")]
[assembly: AssemblyTitle("LethalBodySwap")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LethalBodySwap
{
	[BepInPlugin("Dex.LethalBodySwap", "Lethal Body Swap", "1.0.0")]
	public class LethalBodySwapModBase : BaseUnityPlugin
	{
		private const string modGUID = "Dex.LethalBodySwap";

		private const string modName = "Lethal Body Swap";

		private const string modVersion = "1.0.0";

		private readonly Harmony harmony = new Harmony("Dex.LethalBodySwap");

		public static LethalBodySwapModBase Instance;

		internal ManualLogSource Logger;

		internal static ConfigEntry<Key> SwapKey;

		internal static ConfigEntry<bool> IncludeDeadPlayers;

		private void Awake()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
				Logger = Logger.CreateLogSource("Dex.LethalBodySwap");
				Logger.LogInfo((object)"Initialized Lethal Body Swap!");
				SwapKey = ((BaseUnityPlugin)this).Config.Bind<Key>("General", "SwapKey", (Key)25, "Key that once pressed by the host will force all players to swap.");
				IncludeDeadPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Experimental", "IncludeDeadPlayers", false, "If true, will include dead bodies as possible teleport destinations (latet anguis in herba)");
				harmony.PatchAll(Assembly.GetExecutingAssembly());
			}
			else
			{
				Object.Destroy((Object)(object)this);
			}
		}
	}
	internal static class SwapManager
	{
		private const string TeleportMessageName = "RandomSwap_TeleportTo";

		private static bool _registered;

		private static bool _swapInProgress = false;

		private static float _lastSwapTime = -999f;

		private const float SwapCooldown = 1f;

		private static AudioClip teleportSound;

		internal static void RegisterMessageHandler()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && !_registered)
			{
				singleton.CustomMessagingManager.RegisterNamedMessageHandler("RandomSwap_TeleportTo", new HandleNamedMessageDelegate(OnReceiveTeleportMessage));
				_registered = true;
				LethalBodySwapModBase.Instance.Logger.LogInfo((object)$"RandomSwap message handler registered. IsHost={singleton.IsHost}, IsClient={singleton.IsClient}, LocalClientId={singleton.LocalClientId}");
			}
		}

		internal static void UnregisterMessageHandler()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton != (Object)null && singleton.CustomMessagingManager != null && _registered)
			{
				singleton.CustomMessagingManager.UnregisterNamedMessageHandler("RandomSwap_TeleportTo");
			}
			_registered = false;
		}

		private static void OnReceiveTeleportMessage(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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)
			//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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			float num = default(float);
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num, default(ForPrimitives));
			float num2 = default(float);
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num2, default(ForPrimitives));
			float num3 = default(float);
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num3, default(ForPrimitives));
			float num4 = default(float);
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num4, default(ForPrimitives));
			bool insideFactory = default(bool);
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref insideFactory, default(ForPrimitives));
			bool inHangarShipRoom = default(bool);
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref inHangarShipRoom, default(ForPrimitives));
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val == (Object)null)
			{
				LethalBodySwapModBase.Instance.Logger.LogWarning((object)"RandomSwap: received teleport message but local player controller is null.");
				return;
			}
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(num, num2, num3);
			try
			{
				val.averageVelocity = 0f;
				val.velocityLastFrame = Vector3.zero;
				val.TeleportPlayer(val2, true, num4, false, true);
				RefreshPlayerEnvironment(val, insideFactory, inHangarShipRoom);
				PlayTeleportSound(val);
			}
			catch (Exception arg)
			{
				LethalBodySwapModBase.Instance.Logger.LogError((object)$"RandomSwap: teleport failed: {arg}");
			}
		}

		private static void RefreshPlayerEnvironment(PlayerControllerB player, bool insideFactory, bool inHangarShipRoom)
		{
			//IL_001d: 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)
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			player.averageVelocity = 0f;
			player.velocityLastFrame = Vector3.zero;
			AudioReverbPresets val = Object.FindObjectOfType<AudioReverbPresets>();
			if ((Object)(object)val != (Object)null)
			{
				try
				{
					int num = (insideFactory ? 2 : 3);
					val.audioPresets[num].ChangeAudioReverbForPlayer(player);
				}
				catch (Exception arg)
				{
					LethalBodySwapModBase.Instance.Logger.LogWarning((object)$"RandomSwap: failed to refresh audio reverb: {arg}");
				}
			}
			player.isInsideFactory = insideFactory;
			player.isInHangarShipRoom = !insideFactory && inHangarShipRoom;
			player.isInElevator = false;
			for (int i = 0; i < player.ItemSlots.Length; i++)
			{
				if ((Object)(object)player.ItemSlots[i] != (Object)null)
				{
					player.ItemSlots[i].isInFactory = insideFactory;
				}
			}
			if ((Object)(object)player.ItemOnlySlot != (Object)null)
			{
				player.ItemOnlySlot.isInFactory = insideFactory;
			}
			if ((Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)(object)player && (Object)(object)TimeOfDay.Instance != (Object)null)
			{
				bool flag = insideFactory || inHangarShipRoom;
				TimeOfDay.Instance.insideLighting = flag;
				TimeOfDay.Instance.SetInsideLightingDimness(true, flag);
			}
		}

		internal static void TriggerRandomSwap(bool includeDead = false)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsHost)
			{
				LethalBodySwapModBase.Instance.Logger.LogWarning((object)"RandomSwap: only the host can trigger a swap.");
				return;
			}
			if (_swapInProgress)
			{
				LethalBodySwapModBase.Instance.Logger.LogInfo((object)"RandomSwap: ignored because a swap is already in progress.");
				return;
			}
			if (Time.unscaledTime - _lastSwapTime < 1f)
			{
				LethalBodySwapModBase.Instance.Logger.LogInfo((object)"RandomSwap: ignored because the swap cooldown is active.");
				return;
			}
			_swapInProgress = true;
			_lastSwapTime = Time.unscaledTime;
			try
			{
				StartRandomSwapInternal(singleton, includeDead);
			}
			finally
			{
				_swapInProgress = false;
			}
		}

		private unsafe static void StartRandomSwapInternal(NetworkManager nm, bool includeDead)
		{
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null)
			{
				LethalBodySwapModBase.Instance.Logger.LogWarning((object)"RandomSwap: StartOfRound not ready.");
				return;
			}
			List<PlayerControllerB> list = new List<PlayerControllerB>();
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && (includeDead || !val.isPlayerDead))
				{
					list.Add(val);
				}
			}
			int count = list.Count;
			if (count < 2)
			{
				LethalBodySwapModBase.Instance.Logger.LogInfo((object)"RandomSwap: fewer than 2 eligible players, nothing to swap.");
				return;
			}
			Vector3[] array = (Vector3[])(object)new Vector3[count];
			float[] array2 = new float[count];
			bool[] array3 = new bool[count];
			bool[] array4 = new bool[count];
			for (int j = 0; j < count; j++)
			{
				PlayerControllerB val2 = list[j];
				Transform val3 = (((Object)(object)val2.thisPlayerBody != (Object)null) ? val2.thisPlayerBody : ((Component)val2).transform);
				array[j] = val3.position;
				array2[j] = val3.eulerAngles.y;
				array3[j] = val2.isInsideFactory;
				array4[j] = val2.isInHangarShipRoom;
			}
			int[] array5 = SattoloDerangement(count);
			for (int k = 0; k < count; k++)
			{
				if (array5[k] == k)
				{
					LethalBodySwapModBase.Instance.Logger.LogError((object)"RandomSwap: derangement check failed. Aborting.");
					return;
				}
			}
			int num = 0;
			FastBufferWriter val6 = default(FastBufferWriter);
			for (int l = 0; l < count; l++)
			{
				PlayerControllerB val4 = list[l];
				Vector3 val5 = array[array5[l]];
				float num2 = array2[array5[l]];
				bool insideFactory = array3[array5[l]];
				bool inHangarShipRoom = array4[array5[l]];
				if (((NetworkBehaviour)val4).IsOwner)
				{
					val4.TeleportPlayer(val5, true, num2, false, true);
					RefreshPlayerEnvironment(val4, insideFactory, inHangarShipRoom);
					PlayTeleportSound(val4);
					num++;
				}
				else if (nm.ConnectedClientsIds.Contains(((NetworkBehaviour)val4).OwnerClientId))
				{
					((FastBufferWriter)(ref val6))..ctor(18, (Allocator)2, -1);
					try
					{
						((FastBufferWriter)(ref val6)).WriteValueSafe<float>(ref val5.x, default(ForPrimitives));
						((FastBufferWriter)(ref val6)).WriteValueSafe<float>(ref val5.y, default(ForPrimitives));
						((FastBufferWriter)(ref val6)).WriteValueSafe<float>(ref val5.z, default(ForPrimitives));
						((FastBufferWriter)(ref val6)).WriteValueSafe<float>(ref num2, default(ForPrimitives));
						((FastBufferWriter)(ref val6)).WriteValueSafe<bool>(ref insideFactory, default(ForPrimitives));
						((FastBufferWriter)(ref val6)).WriteValueSafe<bool>(ref inHangarShipRoom, default(ForPrimitives));
						nm.CustomMessagingManager.SendNamedMessage("RandomSwap_TeleportTo", ((NetworkBehaviour)val4).OwnerClientId, val6, (NetworkDelivery)3);
					}
					finally
					{
						((IDisposable)(*(FastBufferWriter*)(&val6))/*cast due to .constrained prefix*/).Dispose();
					}
					num++;
				}
			}
			LethalBodySwapModBase.Instance.Logger.LogInfo((object)($"RandomSwap: completed ONE {count}-player derangement " + $"({num} teleport instructions dispatched)."));
		}

		private static int[] SattoloDerangement(int n)
		{
			if (n < 2)
			{
				throw new ArgumentException("Derangement requires at least 2 elements.", "n");
			}
			int[] array = new int[n];
			for (int i = 0; i < n; i++)
			{
				array[i] = i;
			}
			Random random = new Random();
			for (int num = n - 1; num > 0; num--)
			{
				int num2 = random.Next(0, num);
				ref int reference = ref array[num];
				ref int reference2 = ref array[num2];
				int num3 = array[num2];
				int num4 = array[num];
				reference = num3;
				reference2 = num4;
			}
			return array;
		}

		private static void PlayTeleportSound(PlayerControllerB player)
		{
			FindTeleportSound();
			if (!((Object)(object)teleportSound == (Object)null) && !((Object)(object)player == (Object)null) && !((Object)(object)player.movementAudio == (Object)null))
			{
				player.movementAudio.PlayOneShot(teleportSound);
			}
		}

		private static void FindTeleportSound()
		{
			if ((Object)(object)teleportSound != (Object)null)
			{
				return;
			}
			AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>();
			AudioClip[] array2 = array;
			foreach (AudioClip val in array2)
			{
				if (!((Object)(object)val == (Object)null) && ((Object)val).name == "ShipTeleporterBeam")
				{
					teleportSound = val;
					LethalBodySwapModBase.Instance.Logger.LogInfo((object)("Found teleporter sound: " + ((Object)val).name));
					return;
				}
			}
			LethalBodySwapModBase.Instance.Logger.LogWarning((object)"Could not find ShipTeleporterBeam.");
		}
	}
}
namespace LethalBodySwap.Patches
{
	[HarmonyPatch(typeof(NetworkManager))]
	internal static class NetworkManagerPatches
	{
		[HarmonyPatch("StartClient")]
		[HarmonyPostfix]
		private static void StartClient_Postfix()
		{
			SwapManager.RegisterMessageHandler();
		}

		[HarmonyPatch("StartHost")]
		[HarmonyPostfix]
		private static void StartHost_Postfix()
		{
			SwapManager.RegisterMessageHandler();
		}

		[HarmonyPatch("Shutdown")]
		[HarmonyPrefix]
		private static void Shutdown_Prefix()
		{
			SwapManager.UnregisterMessageHandler();
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class PlayerControllerBPatches
	{
		private static bool _keyWasPressed;

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void Update_Postfix(PlayerControllerB __instance)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && Keyboard.current != null && ((ButtonControl)Keyboard.current[LethalBodySwapModBase.SwapKey.Value]).wasPressedThisFrame && !((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsHost)
			{
				LethalBodySwapModBase.Instance.Logger.LogInfo((object)"Swap key pressed.");
				if ((Object)(object)StartOfRound.Instance == (Object)null)
				{
					LethalBodySwapModBase.Instance.Logger.LogInfo((object)"Ignored: no active round.");
				}
				else
				{
					SwapManager.TriggerRandomSwap(LethalBodySwapModBase.IncludeDeadPlayers.Value);
				}
			}
		}
	}
	[HarmonyPatch(typeof(TimeOfDay))]
	internal static class TimeOfDayPatch
	{
		[HarmonyPatch("OnHourChanged")]
		[HarmonyPostfix]
		private static void OnHourChanged_Postfix(TimeOfDay __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsServer && !((Object)(object)StartOfRound.Instance == (Object)null))
			{
				LethalBodySwapModBase.Instance.Logger.LogInfo((object)$"RandomSwap: hour changed to {__instance.hour}. Triggering player swap.");
				SwapManager.TriggerRandomSwap(LethalBodySwapModBase.IncludeDeadPlayers.Value);
			}
		}
	}
}