Decompiled source of ReviveHeadInTruckOrExtractionPointToxinFork v1.3.0

ReviveHeadInTruckOrExtractionPoint.dll

Decompiled a month ago
using System;
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.Logging;
using HarmonyLib;
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-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Autodesk.Fbx")]
[assembly: IgnoresAccessChecksTo("Discord.Sdk")]
[assembly: IgnoresAccessChecksTo("Domain_Reload")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("FbxBuildTestAssets")]
[assembly: IgnoresAccessChecksTo("Klattersynth")]
[assembly: IgnoresAccessChecksTo("Photon3Unity3D")]
[assembly: IgnoresAccessChecksTo("PhotonChat")]
[assembly: IgnoresAccessChecksTo("PhotonRealtime")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.API")]
[assembly: IgnoresAccessChecksTo("PhotonVoice")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime.Public")]
[assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization")]
[assembly: IgnoresAccessChecksTo("Sirenix.Utilities")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.Formats.Fbx.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.MemoryProfiler")]
[assembly: IgnoresAccessChecksTo("Unity.Postprocessing.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Splines")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Antlr3.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Core")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Flow")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.State")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("websocket-sharp")]
[assembly: AssemblyCompany("Hypn,Toxin")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+fabf6491c804bd6ec3995a3208ef8aaaeb5ebe44")]
[assembly: AssemblyProduct("ReviveHeadInTruckOrExtractionPoint")]
[assembly: AssemblyTitle("ReviveHeadInTruckOrExtractionPoint")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 Hypn
{
	[BepInPlugin("HT.ReviveHeadInTruckOrExtractionPoint", "ReviveHeadInTruckOrExtractionPoint (Toxin fork, original: Hypn)", "1.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGUID = "HT.ReviveHeadInTruckOrExtractionPoint";

		public const string ModName = "ReviveHeadInTruckOrExtractionPoint (Toxin fork, original: Hypn)";

		public const string ModAuthors = "Hypn, Toxin";

		public const string ModVersion = "1.2.1";

		private readonly Harmony harmony = new Harmony("HT.ReviveHeadInTruckOrExtractionPoint");

		internal static Plugin Instance { get; private set; }

		public static ManualLogSource Logger { get; private set; }

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin ReviveHeadInTruckOrExtractionPoint (Toxin fork, original: Hypn) v1.2.1 is loaded! Authors: Hypn, Toxin");
			harmony.PatchAll();
		}

		internal void Unpatch()
		{
			harmony.UnpatchSelf();
		}
	}
}
namespace Hypn.Patches
{
	[HarmonyPatch(typeof(PlayerDeathHead), "Update")]
	internal class HypnReviveInTruckAndExtractionPatch
	{
		private class ReviveInfo
		{
			private PlayerAvatar Player;

			private int Count = 0;

			private DateTime LastReviveTime = DateTime.UtcNow;

			private float OutsideLevelTimer = 0f;

			private bool Reviving = false;

			private Queue<string> Messages = new Queue<string>();

			private DateTime lastMessageTime = DateTime.MinValue;

			public PlayerAvatar GetPlayer()
			{
				return Player;
			}

			public int GetCount()
			{
				return Count;
			}

			public ReviveInfo SetCount(int value)
			{
				Count = value;
				return this;
			}

			public ReviveInfo IncCount()
			{
				Count++;
				return this;
			}

			public DateTime GetLastReviveTime()
			{
				return LastReviveTime;
			}

			public ReviveInfo SetLastReviveTime(DateTime time)
			{
				LastReviveTime = time;
				return this;
			}

			public float GetOutsideLevelTimer()
			{
				return OutsideLevelTimer;
			}

			public ReviveInfo SetOutsideLevelTimer(float value)
			{
				OutsideLevelTimer = value;
				return this;
			}

			public bool IsReviving()
			{
				return Reviving;
			}

			public ReviveInfo SetReviving(bool value)
			{
				Reviving = value;
				return this;
			}

			public ReviveInfo AddMessage(string message)
			{
				Messages.Enqueue(message);
				return this;
			}

			public string GetMessage()
			{
				return Messages.Dequeue();
			}

			public ReviveInfo ClearMessages()
			{
				Messages.Clear();
				return this;
			}

			public ReviveInfo DropReviving()
			{
				return ClearMessages().SetReviving(value: false);
			}

			public bool AnyMessages()
			{
				return Messages.Count > 0;
			}

			public ReviveInfo SetPlayer(PlayerAvatar player)
			{
				Player = player;
				return this;
			}

			public ReviveInfo TryAddMessageWithCooldown(string message)
			{
				DateTime utcNow = DateTime.UtcNow;
				if ((utcNow - lastMessageTime).TotalSeconds >= (double)MessageCooldownSeconds)
				{
					AddMessage(message);
					lastMessageTime = utcNow;
				}
				return this;
			}
		}

		private static readonly Dictionary<string, ReviveInfo> _reviveTracker = new Dictionary<string, ReviveInfo>();

		private static readonly object _reviveLock = new object();

		private static readonly int ReviveCooldownSeconds = 300;

		private static readonly float MessageCooldownSeconds = 30f;

		private static int Level = 0;

		private static int Currency = 0;

		private static float updateThrottle = 0f;

		private static readonly string[] Countdown = new string[6] { "Five", "Four", "Three", "Two", "One", "Zero" };

		private static void TryTeleportPlayer(ReviveInfo info)
		{
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			PlayerAvatar player = info.GetPlayer();
			PlayerDeathHead playerDeathHead = player.playerDeathHead;
			int num = playerDeathHead.roomVolumeCheck?.CurrentRooms?.Count ?? 1;
			if (num > 0)
			{
				info.SetOutsideLevelTimer(0f);
				return;
			}
			info.SetOutsideLevelTimer(info.GetOutsideLevelTimer() + Time.deltaTime);
			if ((Object)(object)playerDeathHead.physGrabObject == (Object)null)
			{
				LogInfo("Player \"" + player.playerName + "\" could not be teleported — no physGrabObject.");
			}
			else if (!(info.GetOutsideLevelTimer() < 5f))
			{
				if ((player.playerDeathHead?.roomVolumeCheck?.inTruck).GetValueOrDefault())
				{
					LogInfo("Teleporting player \"" + player.playerName + "\" to truck");
					playerDeathHead.physGrabObject.Teleport(((Component)TruckSafetySpawnPoint.instance).transform.position, ((Component)TruckSafetySpawnPoint.instance).transform.rotation);
				}
				else
				{
					LogInfo("Teleporting player \"" + player.playerName + "\" to extraction point");
					playerDeathHead.physGrabObject.Teleport(RoundDirector.instance.extractionPointCurrent.safetySpawn.position, RoundDirector.instance.extractionPointCurrent.safetySpawn.rotation);
				}
			}
		}

		private static void Revive(ReviveInfo info)
		{
			if (!info.AnyMessages() && !PlayerIsTalking(info))
			{
				PlayerAvatar player = info.GetPlayer();
				int num = CalculateReviveCost(info);
				Currency = Math.Max(Currency - num, 0);
				SemiFunc.StatSetRunCurrency(Currency);
				int num2 = (int)((float)player.playerHealth.maxHealth * 0.2f);
				info.IncCount().SetLastReviveTime(DateTime.UtcNow).SetReviving(value: false);
				LogInfo($"Reviving player \"{player.playerName}\" with {num2}/{player.playerHealth.maxHealth} health (revives: {info.GetCount()}). Revive cost: {num}. Currency left: {Currency}.");
				player.Revive((player.playerDeathHead?.roomVolumeCheck?.inTruck).GetValueOrDefault());
				player.playerHealth.HealOther(num2 - 1, true);
				TryTeleportPlayer(info);
			}
		}

		private static void Speak(ReviveInfo info)
		{
			if (info.AnyMessages() && !PlayerIsTalking(info))
			{
				string message = info.GetMessage();
				LogInfo("Player \"" + info.GetPlayer().playerName + "\" talk " + message);
				info.GetPlayer().ChatMessageSend(message);
			}
		}

		private static void LogInfo(string message)
		{
			Plugin.Logger.LogInfo((object)$"[{DateTime.Now:HH:mm:ss.fff}] {message}");
		}

		private static bool PlayerIsTalking(ReviveInfo info)
		{
			if (info.GetPlayer().voiceChat.ttsVoice.isSpeaking)
			{
				LogInfo("Player \"" + info.GetPlayer().playerName + "\" is talking");
				return true;
			}
			return false;
		}

		private static void CheckCurrentLevel(int currentlevel)
		{
			if (Level == currentlevel)
			{
				return;
			}
			LogInfo("New level. Reset count revive");
			Level = currentlevel;
			foreach (ReviveInfo value in _reviveTracker.Values)
			{
				LogInfo(((Object)value.GetPlayer()).name + " revive reset");
				value.SetCount(0);
			}
		}

		private static bool CooldownPassed(ReviveInfo info)
		{
			int num = ((info.GetCount() == 0) ? ReviveCooldownSeconds : ((int)(DateTime.UtcNow - info.GetLastReviveTime()).TotalSeconds));
			if (num >= ReviveCooldownSeconds)
			{
				return true;
			}
			LogInfo($"Player \"{info.GetPlayer().playerName}\" cannot be revived, cooldown: {num} .");
			return false;
		}

		private static int CalculateReviveCost(ReviveInfo info)
		{
			return (info.GetCount() > 0) ? (Math.Min(Level, 20) + info.GetCount() - 1) : 0;
		}

		private static bool IsCostPayable(ReviveInfo info)
		{
			int num = CalculateReviveCost(info);
			if (Currency >= num)
			{
				return true;
			}
			LogInfo($"Player \"{info.GetPlayer().playerName}\" cannot be revived, not enough money. Revive cost: ${num}k. Current currency: ${Currency}k");
			return false;
		}

		private static bool IsCanRevive(ReviveInfo info)
		{
			PlayerDeathHead playerDeathHead = info.GetPlayer().playerDeathHead;
			if (((Object)(object)playerDeathHead.physGrabObject?.impactDetector?.currentCart != (Object)null) ? true : false)
			{
				LogInfo("Player \"" + info.GetPlayer().playerName + "\" inCart");
				info.DropReviving();
				return false;
			}
			bool flag = playerDeathHead.roomVolumeCheck?.inTruck ?? false;
			bool flag2 = playerDeathHead.roomVolumeCheck?.inExtractionPoint ?? false;
			if (!flag && !flag2)
			{
				LogInfo("Player \"" + info.GetPlayer().playerName + "\" is not in the truck or at the extraction point");
				info.DropReviving();
				return false;
			}
			if (!CooldownPassed(info))
			{
				info.DropReviving();
				info.TryAddMessageWithCooldown("I can't revive yet, still on cooldown");
				return false;
			}
			if (!IsCostPayable(info))
			{
				info.DropReviving();
				info.TryAddMessageWithCooldown("We are don't have enough currency to revive");
				return false;
			}
			return true;
		}

		private static void StartReviving(ReviveInfo info)
		{
			info.AddMessage($"My revival will cost ${CalculateReviveCost(info)}k");
			info.AddMessage("Reviving");
			string[] countdown = Countdown;
			foreach (string message in countdown)
			{
				info.AddMessage(message);
			}
			info.SetReviving(value: true);
		}

		private static void Postfix(PlayerDeathHead __instance)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			updateThrottle += Time.deltaTime;
			if (updateThrottle < 1f)
			{
				return;
			}
			updateThrottle = 0f;
			PlayerAvatar val = __instance?.playerAvatar;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string steamID = val.steamID;
			if (string.IsNullOrEmpty(steamID) || (Object)(object)__instance == (Object)null || !__instance.triggered)
			{
				return;
			}
			lock (_reviveLock)
			{
				_reviveTracker.TryGetValue(steamID, out ReviveInfo value);
				if (value == null)
				{
					value = new ReviveInfo();
					_reviveTracker[steamID] = value;
				}
				value.SetPlayer(val);
				CheckCurrentLevel(StatsManager.instance.GetRunStatLevel() + 1);
				Currency = SemiFunc.StatGetRunCurrency();
				Speak(value);
				if (IsCanRevive(value))
				{
					if (value.IsReviving())
					{
						Revive(value);
					}
					else
					{
						StartReviving(value);
					}
				}
			}
		}
	}
}