Decompiled source of AUTORESPAWN v1.1.1

BepInEx/plugins/AutoRespawn.dll

Decompiled a day 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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
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("AutoRespawn")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AutoRespawn")]
[assembly: AssemblyTitle("AutoRespawn")]
[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 AutoRespawn
{
	[BepInPlugin("com.flavi.autorespawn", "Auto Respawn", "1.1.1")]
	public class AutoRespawnPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource LogSource;

		internal static ConfigEntry<int> RespawnTime;

		internal static ConfigEntry<int> RespawnHealth;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			LogSource = ((BaseUnityPlugin)this).Logger;
			RespawnTime = ((BaseUnityPlugin)this).Config.Bind<int>("Auto Respawn", "Respawn Time", 60, new ConfigDescription("Seconds before a dead player respawns. Chat notices adjust to this value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(30, 120), Array.Empty<object>()));
			RespawnHealth = ((BaseUnityPlugin)this).Config.Bind<int>("Auto Respawn", "Health After Respawn", 50, new ConfigDescription("Health the player has after being revived.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>()));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Iniciando Auto Respawn 1.1.1.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Configuración: respawn en " + RespawnTime.Value + " segundos; vida al revivir " + RespawnHealth.Value + "."));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"El primer aviso de chat se enviará después de 5 segundos.");
			AutoRespawnController.Initialize();
			harmony = new Harmony("com.flavi.autorespawn");
			try
			{
				harmony.PatchAll();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Falló PatchAll de Harmony: " + ex));
				throw;
			}
			int num = 0;
			foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
			{
				num++;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Parche activo: " + patchedMethod.DeclaringType.Name + "." + patchedMethod.Name));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Auto Respawn cargado correctamente. Parches: " + num));
		}

		private void OnDestroy()
		{
			if (((BaseUnityPlugin)this).Logger != null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Componente BepInEx destruido; los parches Harmony se conservan.");
			}
		}
	}
	internal static class AutoRespawnController
	{
		private const int AnnouncementDelaySeconds = 5;

		private static readonly HashSet<PlayerAvatar> pending = new HashSet<PlayerAvatar>();

		private static readonly HashSet<PlayerAvatar> observedDead = new HashSet<PlayerAvatar>();

		private static readonly FieldInfo DeadSetField = AccessTools.Field(typeof(PlayerAvatar), "deadSet");

		private static bool deadSetErrorLogged;

		private static bool zoneCheckErrorLogged;

		internal static void Initialize()
		{
			if (DeadSetField == null)
			{
				LogErrorOnce("No se encontró PlayerAvatar.deadSet; la detección desde PlayerAvatar.Update no estará disponible.");
			}
			else
			{
				LogInfo("Detección preparada desde PlayerAvatar.Update y deadSet.");
			}
		}

		internal static void CheckDeadState(PlayerAvatar player)
		{
			if ((Object)(object)player == (Object)null || DeadSetField == null)
			{
				return;
			}
			bool flag;
			try
			{
				flag = (bool)DeadSetField.GetValue(player);
			}
			catch (Exception ex)
			{
				LogErrorOnce("No se pudo leer PlayerAvatar.deadSet: " + ex);
				return;
			}
			if (flag)
			{
				if (observedDead.Add(player))
				{
					LogInfo("Update del jugador detectó deadSet=true, avatar=" + ((Object)player).GetInstanceID() + ".");
					OnPlayerDeath(player, "PlayerAvatar.Update/deadSet");
				}
			}
			else
			{
				observedDead.Remove(player);
			}
		}

		internal static void OnPlayerDeath(PlayerAvatar player, string source)
		{
			if ((Object)(object)player == (Object)null)
			{
				LogWarning("Hook de muerte " + source + " recibió un PlayerAvatar nulo.");
				return;
			}
			observedDead.Add(player);
			bool flag = SemiFunc.IsMultiplayer();
			bool isMasterClient = PhotonNetwork.IsMasterClient;
			int instanceID = ((Object)player).GetInstanceID();
			LogInfo("Muerte detectada por " + source + ", avatar=" + instanceID + ", multiplayer=" + flag + ", masterClient=" + isMasterClient + ".");
			string disabledLocation = GetDisabledLocation();
			if (disabledLocation != null)
			{
				pending.Remove(player);
				LogInfo("Auto Respawn desactivado: muerte en " + disabledLocation + ", avatar=" + instanceID + ".");
				return;
			}
			if (flag && !isMasterClient)
			{
				LogInfo("No inicia cuenta atrás para avatar=" + instanceID + " porque este cliente no es el anfitrión.");
				return;
			}
			if (!pending.Add(player))
			{
				LogInfo("Ya había una cuenta atrás pendiente para avatar=" + instanceID + ".");
				return;
			}
			GameDirector instance = GameDirector.instance;
			if ((Object)(object)instance == (Object)null)
			{
				pending.Remove(player);
				LogError("No se pudo iniciar el contador: GameDirector.instance es nulo.");
				return;
			}
			int value = AutoRespawnPlugin.RespawnTime.Value;
			int value2 = AutoRespawnPlugin.RespawnHealth.Value;
			int announcementDelay = 5;
			LogInfo("Inicia cuenta atrás de " + value + " segundos para avatar=" + instanceID + " usando GameDirector.");
			((MonoBehaviour)instance).StartCoroutine(RespawnCountdown(player, value, value2, announcementDelay));
		}

		private static IEnumerator RespawnCountdown(PlayerAvatar player, int countdownSeconds, int targetHealth, int announcementDelay)
		{
			int avatarId = (((Object)(object)player != (Object)null) ? ((Object)player).GetInstanceID() : 0);
			LogInfo("Coroutine de cuenta atrás iniciada para avatar=" + avatarId + ", duración=" + countdownSeconds + " s, vida objetivo=" + targetHealth + " HP, primer aviso después de " + announcementDelay + " s.");
			List<int> announcements = GetAnnouncementTimes(countdownSeconds, announcementDelay);
			float num = 0f;
			for (int i = 0; i < announcements.Count; i++)
			{
				int seconds = announcements[i];
				float targetElapsed = ((seconds == countdownSeconds) ? announcementDelay : (countdownSeconds - seconds));
				float num2 = targetElapsed - num;
				if (num2 > 0f)
				{
					yield return (object)new WaitForSecondsRealtime(num2);
					num = targetElapsed;
				}
				if (!IsCountdownActive(player, avatarId, seconds) || !SendCountdownMessage(player, avatarId, seconds))
				{
					yield break;
				}
			}
			float num3 = (float)countdownSeconds - num;
			if (num3 > 0f)
			{
				yield return (object)new WaitForSecondsRealtime(num3);
			}
			if (!IsCountdownActive(player, avatarId, 0))
			{
				pending.Remove(player);
				LogInfo("La cuenta atrás terminó/canceló antes de revivir avatar=" + avatarId + ".");
				yield break;
			}
			try
			{
				player.Revive(false);
				LogInfo("Revive(false) llamado para avatar=" + avatarId + ".");
			}
			catch (Exception ex)
			{
				LogError("Revive(false) falló para avatar=" + avatarId + ": " + ex);
			}
			LogInfo("Esperando 0.25 s antes de solicitar curación para avatar=" + avatarId + ".");
			yield return (object)new WaitForSecondsRealtime(0.25f);
			try
			{
				if ((Object)(object)player != (Object)null && (Object)(object)player.playerHealth != (Object)null)
				{
					LogInfo("Solicitando HealOther(" + (targetHealth - 1) + ") para avatar=" + avatarId + " con objetivo de " + targetHealth + " HP.");
					player.playerHealth.HealOther(targetHealth - 1, true);
					LogInfo("HealOther terminó sin excepción para avatar=" + avatarId + ".");
				}
				else
				{
					LogWarning("No se aplicó curación: avatar o PlayerHealth nulo para avatar=" + avatarId + ".");
				}
				LogInfo("Flujo de revive completado para avatar=" + avatarId + ".");
			}
			catch (Exception ex2)
			{
				LogError("Falló el flujo de curación para avatar=" + avatarId + ": " + ex2);
			}
			bool flag = pending.Remove(player);
			LogInfo("Limpieza de cuenta atrás para avatar=" + avatarId + "; entrada pendiente eliminada=" + flag + ".");
		}

		private static List<int> GetAnnouncementTimes(int countdownSeconds, int announcementDelay)
		{
			List<int> list = new List<int> { countdownSeconds };
			int[] array = new int[5] { 60, 40, 20, 10, 5 };
			foreach (int num in array)
			{
				float num2 = countdownSeconds - num;
				if (num < countdownSeconds && num2 > (float)announcementDelay && !list.Contains(num))
				{
					list.Add(num);
				}
			}
			list.Sort((int left, int right) => right.CompareTo(left));
			return list;
		}

		private static bool IsCountdownActive(PlayerAvatar player, int avatarId, int seconds)
		{
			if ((Object)(object)player != (Object)null && pending.Contains(player))
			{
				string disabledLocation = GetDisabledLocation();
				if (disabledLocation == null)
				{
					return true;
				}
				pending.Remove(player);
				LogInfo("Cuenta atrás cancelada para avatar=" + avatarId + " al entrar en " + disabledLocation + ".");
				return false;
			}
			if ((Object)(object)player == (Object)null)
			{
				pending.Remove(player);
			}
			LogInfo("Cuenta atrás detenida para avatar=" + avatarId + " antes del aviso de " + seconds + " segundos.");
			return false;
		}

		private static string GetDisabledLocation()
		{
			try
			{
				if (SemiFunc.RunIsArena())
				{
					return "arena";
				}
				if (SemiFunc.RunIsShop())
				{
					return "shop";
				}
			}
			catch (Exception ex)
			{
				if (!zoneCheckErrorLogged)
				{
					zoneCheckErrorLogged = true;
					LogWarning("No se pudo comprobar si la zona es arena o tienda: " + ex);
				}
			}
			return null;
		}

		private static bool SendCountdownMessage(PlayerAvatar player, int avatarId, int seconds)
		{
			if ((Object)(object)player == (Object)null || !pending.Contains(player))
			{
				LogInfo("No se envió el aviso de " + seconds + " segundos porque la cuenta atrás ya no está activa para avatar=" + avatarId + ".");
				return false;
			}
			string text = "Respawn in " + seconds + " seconds";
			LogInfo("Enviando aviso de cuenta atrás para avatar=" + avatarId + ": " + text + ".");
			try
			{
				player.ChatMessageSend(text);
				LogInfo("ChatMessageSend terminó sin excepción para avatar=" + avatarId + ", aviso=" + seconds + " segundos.");
				return true;
			}
			catch (Exception ex)
			{
				LogWarning("Falló el aviso de " + seconds + " segundos para avatar=" + avatarId + ": " + ex);
				return true;
			}
		}

		internal static void OnPlayerRevived(PlayerAvatar player)
		{
			if ((Object)(object)player != (Object)null)
			{
				bool flag = pending.Remove(player);
				LogInfo("Hook ReviveRPC para avatar=" + ((Object)player).GetInstanceID() + "; cuenta atrás pendiente eliminada=" + flag + ".");
			}
			else
			{
				LogWarning("Hook ReviveRPC recibió un PlayerAvatar nulo.");
			}
		}

		private static void LogInfo(string message)
		{
			if (AutoRespawnPlugin.LogSource != null)
			{
				AutoRespawnPlugin.LogSource.LogInfo((object)message);
			}
		}

		private static void LogWarning(string message)
		{
			if (AutoRespawnPlugin.LogSource != null)
			{
				AutoRespawnPlugin.LogSource.LogWarning((object)message);
			}
		}

		private static void LogError(string message)
		{
			if (AutoRespawnPlugin.LogSource != null)
			{
				AutoRespawnPlugin.LogSource.LogError((object)message);
			}
		}

		private static void LogErrorOnce(string message)
		{
			if (!deadSetErrorLogged)
			{
				deadSetErrorLogged = true;
				LogError(message);
			}
		}
	}
	[HarmonyPatch]
	internal static class PlayerAvatarUpdateDeathPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(PlayerAvatar), "Update", (Type[])null, (Type[])null);
		}

		private static void Postfix(PlayerAvatar __instance)
		{
			AutoRespawnController.CheckDeadState(__instance);
		}
	}
	[HarmonyPatch]
	internal static class PlayerDeathPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(PlayerAvatar), "PlayerDeath", new Type[1] { typeof(int) }, (Type[])null);
		}

		private static void Postfix(PlayerAvatar __instance)
		{
			AutoRespawnController.OnPlayerDeath(__instance, "PlayerDeath");
		}
	}
	[HarmonyPatch]
	internal static class PlayerDeathRpcPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(PlayerAvatar), "PlayerDeathRPC", (Type[])null, (Type[])null);
		}

		private static void Postfix(PlayerAvatar __instance)
		{
			AutoRespawnController.OnPlayerDeath(__instance, "PlayerDeathRPC");
		}
	}
	[HarmonyPatch]
	internal static class PlayerDeathDonePatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(PlayerAvatar), "PlayerDeathDone", (Type[])null, (Type[])null);
		}

		private static void Prefix(PlayerAvatar __instance)
		{
			AutoRespawnController.OnPlayerDeath(__instance, "PlayerDeathDone");
		}
	}
	[HarmonyPatch]
	internal static class PlayerRevivePatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(PlayerAvatar), "ReviveRPC", (Type[])null, (Type[])null);
		}

		private static void Postfix(PlayerAvatar __instance)
		{
			AutoRespawnController.OnPlayerRevived(__instance);
		}
	}
}