Decompiled source of ManyLives v1.0.0

ManyLives.dll

Decompiled 7 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.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
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(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("ManyLives")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+7041b3d7886ea27dabc546a333c8cf4428fa2d4c")]
[assembly: AssemblyProduct("live multiple lives")]
[assembly: AssemblyTitle("ManyLives")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ManyLives
{
	public class NetworkManager : MonoBehaviourPunCallbacks
	{
		public static NetworkManager Instance { get; private set; }

		private void Awake()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				if (!Object.op_Implicit((Object)(object)((MonoBehaviourPun)this).photonView))
				{
					PhotonView val = ((Component)this).gameObject.AddComponent<PhotonView>();
					val.ViewID = 999;
					val.Synchronization = (ViewSynchronization)0;
					val.ObservedComponents = new List<Component>();
					val.OwnershipTransfer = (OwnershipOption)0;
				}
			}
			else
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		public override void OnJoinedRoom()
		{
			((MonoBehaviourPunCallbacks)this).OnJoinedRoom();
			if (Object.op_Implicit((Object)(object)((MonoBehaviourPun)this).photonView) && !((MonoBehaviourPun)this).photonView.IsMine)
			{
				Plugin.Log.LogInfo((object)"Taking ownership of NetworkManager PhotonView");
				((MonoBehaviourPun)this).photonView.TransferOwnership(PhotonNetwork.LocalPlayer);
			}
			Plugin.Lives = Plugin.MaxLives;
			Plugin.TotalDeaths = 0;
			Plugin.hasSetHealth = false;
			Plugin.Log.LogInfo((object)$"Joined room - Reset lives to {Plugin.Lives}");
		}

		[PunRPC]
		public void SyncLivesRPC(int lives, int deaths)
		{
			Plugin.Lives = lives;
			Plugin.TotalDeaths = deaths;
			Plugin.hasSetHealth = false;
			Plugin.Log.LogInfo((object)$"Received sync - Lives: {lives}, Deaths: {deaths}");
		}

		[PunRPC]
		public void SetPlayerHealthRPC(int targetHealth)
		{
			try
			{
				PlayerAvatar val = GameDirector.instance?.PlayerList?.Find((PlayerAvatar p) => p.photonView.IsMine);
				if ((Object)(object)val != (Object)null && (Object)(object)val.playerHealth != (Object)null)
				{
					Traverse val2 = Traverse.Create((object)val.playerHealth);
					val2.Field("health").SetValue((object)targetHealth);
					Plugin.Log.LogInfo((object)$"RPC set health to {targetHealth} for local player");
					val.playerHealth.Heal(0, true);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Error in SetPlayerHealthRPC: " + ex.Message));
			}
		}

		public void SyncToAll(int lives, int deaths)
		{
			if (!SemiFunc.IsMultiplayer() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				if (Object.op_Implicit((Object)(object)((MonoBehaviourPun)this).photonView) && ((MonoBehaviourPun)this).photonView.ViewID != 0)
				{
					Plugin.Log.LogInfo((object)$"Syncing lives ({lives}) and deaths ({deaths}) with ViewID: {((MonoBehaviourPun)this).photonView.ViewID}");
					((MonoBehaviourPun)this).photonView.RPC("SyncLivesRPC", (RpcTarget)0, new object[2] { lives, deaths });
				}
				else
				{
					Plugin.Log.LogError((object)"PhotonView not properly initialized for sync!");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Error syncing lives and deaths: " + ex.Message));
			}
		}

		public void SetHealthForAll(int targetHealth)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)((MonoBehaviourPun)this).photonView) && ((MonoBehaviourPun)this).photonView.ViewID != 0)
				{
					Plugin.Log.LogInfo((object)$"Setting health to {targetHealth} for all players");
					((MonoBehaviourPun)this).photonView.RPC("SetPlayerHealthRPC", (RpcTarget)0, new object[1] { targetHealth });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Error setting health for all: " + ex.Message));
			}
		}
	}
	[BepInPlugin("ManyLives", "live multiple lives", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		public static int MaxLives = 3;

		public static int Lives = 3;

		public static int TotalDeaths = 0;

		public static float HealthMultiplier = 1f;

		public static bool hasSetHealth = false;

		private GameObject networkManagerObj;

		public static Plugin Instance { get; private set; }

		public void Awake()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Plugin ManyLives is loaded!");
			Harmony val = new Harmony("ManyLives");
			val.PatchAll();
		}

		public void Start()
		{
			((MonoBehaviour)this).StartCoroutine(CreateNetworkManager());
		}

		private IEnumerator CreateNetworkManager()
		{
			yield return null;
			if ((Object)(object)networkManagerObj == (Object)null)
			{
				networkManagerObj = new GameObject("ManyLivesNetworkManager");
				networkManagerObj.AddComponent<NetworkManager>();
				Object.DontDestroyOnLoad((Object)(object)networkManagerObj);
				Log.LogInfo((object)"Created NetworkManager");
			}
		}

		public static bool ShouldHandleGameLogic()
		{
			return !SemiFunc.IsMultiplayer() || PhotonNetwork.IsMasterClient;
		}

		public static void SyncLivesAndDeaths()
		{
			if ((Object)(object)NetworkManager.Instance != (Object)null)
			{
				NetworkManager.Instance.SyncToAll(Lives, TotalDeaths);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Update")]
	public static class PlayerAvatar_Update_Patch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			if (Plugin.hasSetHealth || Plugin.TotalDeaths <= 0 || (SemiFunc.IsMultiplayer() && !__instance.photonView.IsMine))
			{
				return;
			}
			PlayerHealth playerHealth = __instance.playerHealth;
			if ((Object)(object)playerHealth == (Object)null)
			{
				return;
			}
			Traverse val = Traverse.Create((object)playerHealth);
			if (val.Field("healthSet").GetValue<bool>())
			{
				float num = ((Plugin.TotalDeaths == 1) ? 0.5f : 0.01f);
				int value = val.Field("maxHealth").GetValue<int>();
				int num2 = Mathf.Max(1, Mathf.RoundToInt((float)value * num));
				Plugin.Log.LogInfo((object)$"Setting health - Max: {value}, Target: {num2}");
				if (SemiFunc.IsMultiplayer())
				{
					NetworkManager.Instance?.SetHealthForAll(num2);
					playerHealth.Heal(0, true);
					playerHealth.HealOther(0, true);
					playerHealth.HealOtherRPC(0, true);
				}
				else
				{
					val.Field("health").SetValue((object)num2);
					playerHealth.Heal(0, true);
					playerHealth.HealOther(0, true);
					playerHealth.HealOtherRPC(0, true);
				}
				Plugin.hasSetHealth = true;
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	public static class RunManager_ChangeLevel_Patch
	{
		private static void Postfix(RunManager __instance, bool _completedLevel, bool _levelFailed)
		{
			if (Plugin.ShouldHandleGameLogic())
			{
				if (_levelFailed || (Object)(object)__instance.levelCurrent == (Object)(object)__instance.levelLobby || (Object)(object)__instance.levelCurrent == (Object)(object)__instance.levelLobbyMenu)
				{
					Plugin.Log.LogInfo((object)$"Going to {((Object)__instance.levelCurrent).name} - Resetting lives from {Plugin.Lives} to {Plugin.MaxLives}");
					Plugin.Lives = Plugin.MaxLives;
					Plugin.TotalDeaths = 0;
					Plugin.SyncLivesAndDeaths();
				}
				else
				{
					Plugin.Log.LogInfo((object)$"Restarting level - Keeping lives at {Plugin.Lives} and deaths at {Plugin.TotalDeaths}");
					Plugin.hasSetHealth = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "Update")]
	public static class RunManager_Update_Patch
	{
		private static void Postfix(RunManager __instance)
		{
			if (!Plugin.ShouldHandleGameLogic())
			{
				return;
			}
			bool value = Traverse.Create((object)__instance).Field("allPlayersDead").GetValue<bool>();
			bool value2 = Traverse.Create((object)__instance).Field("restarting").GetValue<bool>();
			if (value && !value2)
			{
				Plugin.TotalDeaths++;
				Plugin.Lives--;
				Plugin.Log.LogInfo((object)$"All players dead - TotalDeaths: {Plugin.TotalDeaths}, Lives remaining: {Plugin.Lives}");
				Plugin.SyncLivesAndDeaths();
				if (Plugin.Lives <= 0)
				{
					Plugin.Log.LogInfo((object)"No lives remaining - Transitioning to arena");
					__instance.ChangeLevel(false, true, (ChangeLevelType)0);
				}
				else
				{
					Plugin.Log.LogInfo((object)$"Restarting level with {Plugin.Lives} lives remaining");
					__instance.ChangeLevel(false, false, (ChangeLevelType)0);
				}
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ManyLives";

		public const string PLUGIN_NAME = "live multiple lives";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}