Decompiled source of HealOnLobby v1.0.8

HealOnLobbyMod.dll

Decompiled 2 days ago
using System;
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.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
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: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("HealOnLobbyMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Heals players when entering the lobby")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6")]
[assembly: AssemblyProduct("HealOnLobbyMod")]
[assembly: AssemblyTitle("HealOnLobbyMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.6.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;
		}
	}
}
[BepInPlugin("HealOnLobby", "Heal On Lobby Mod", "1.0.8")]
public class HealOnLobbyMod : BaseUnityPlugin
{
	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> HealToMaxEnabled;

	internal static ConfigEntry<bool> HealByPercentEnabled;

	internal static ConfigEntry<int> HealPercentAmount;

	internal static ConfigEntry<int> HealAmount;

	internal static ConfigEntry<int> HealChance;

	public static HealOnLobbyMod Instance { get; private set; }

	private void Awake()
	{
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Expected O, but got Unknown
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Expected O, but got Unknown
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		HealToMaxEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "1. Heal To Max Health", true, "Heal players to full health in the lobby. Overrides other settings if enabled.");
		HealByPercentEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "2. Heal By Percent Enabled", false, "Heal players by a percentage of their max health (if 'Heal To Max Health' is off).");
		HealPercentAmount = ((BaseUnityPlugin)this).Config.Bind<int>("1. General", "3. Heal Percent Amount", 50, new ConfigDescription("The percentage (0-100) of max health to restore (used if 'Heal By Percent Enabled' is on).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		HealAmount = ((BaseUnityPlugin)this).Config.Bind<int>("1. General", "4. Heal Amount", 100, "Fixed amount of health to restore (used if both 'Heal To Max Health' and 'Heal By Percent Enabled' are off).");
		HealChance = ((BaseUnityPlugin)this).Config.Bind<int>("2. Heal Chance", "Heal Chance", 100, new ConfigDescription("The chance (0-100) that healing will occur when entering the lobby. 100 means healing always happens (if conditions are met).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		new Harmony("HealOnLobby").PatchAll();
		Log.LogInfo((object)"HealOnLobbyMod loaded! All configurations loaded.");
	}
}
[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
public static class RunManager_ChangeLevel_Patch
{
	private const string HealRpcName = "RPC_HealPlayer";

	private static void Postfix(RunManager __instance)
	{
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Invalid comparison between Unknown and I4
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Invalid comparison between Unknown and I4
		//IL_04f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
		string text = null;
		string text2 = "Level - Lobby";
		try
		{
			if ((Object)(object)__instance.levelCurrent == (Object)null)
			{
				HealOnLobbyMod.Log.LogWarning((object)"RunManager.ChangeLevel Postfix: __instance.levelCurrent is null.");
				return;
			}
			text = ((Object)__instance.levelCurrent).name;
			HealOnLobbyMod.Log.LogInfo((object)("RunManager.ChangeLevel Postfix: Current level is '" + text + "'"));
			if (!(text == text2))
			{
				return;
			}
			bool isMasterClient = PhotonNetwork.IsMasterClient;
			ClientState networkClientState = PhotonNetwork.NetworkClientState;
			HealOnLobbyMod.Log.LogInfo((object)$"Checking conditions: IsMasterClient={isMasterClient}, NetworkClientState={networkClientState}");
			bool flag = (int)networkClientState == 14 || (int)networkClientState == 0;
			if (isMasterClient || flag)
			{
				string arg = (isMasterClient ? "Host" : (flag ? "Single-player" : "Unknown"));
				HealOnLobbyMod.Log.LogInfo((object)$"Conditions met ({arg}, State: {networkClientState}). Processing healing IMMEDIATELY...");
				try
				{
					int value = HealOnLobbyMod.HealChance.Value;
					if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null)
					{
						HealOnLobbyMod.Log.LogInfo((object)$"Found {GameDirector.instance.PlayerList.Count} player(s) in list.");
						foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
						{
							if ((Object)(object)player != (Object)null && (Object)(object)player.playerHealth != (Object)null)
							{
								string text3 = player.playerName ?? "NULL_NAME";
								PhotonView component = ((Component)player).GetComponent<PhotonView>();
								if ((Object)(object)component == (Object)null && isMasterClient)
								{
									HealOnLobbyMod.Log.LogWarning((object)("Player '" + text3 + "' has no PhotonView. Skipping RPC."));
									continue;
								}
								int health = player.playerHealth.health;
								int maxHealth = player.playerHealth.maxHealth;
								HealOnLobbyMod.Log.LogInfo((object)$"--- Player '{text3}' (ViewID: {((component != null) ? component.ViewID : 0)}): Health READ as {health}/{maxHealth} ---");
								int num = 0;
								bool value2 = HealOnLobbyMod.HealToMaxEnabled.Value;
								bool value3 = HealOnLobbyMod.HealByPercentEnabled.Value;
								int value4 = HealOnLobbyMod.HealPercentAmount.Value;
								int value5 = HealOnLobbyMod.HealAmount.Value;
								num = (value2 ? (maxHealth - health) : ((!value3) ? Mathf.Min(value5, maxHealth - health) : Mathf.Min(Mathf.CeilToInt((float)maxHealth * ((float)value4 / 100f)), maxHealth - health)));
								num = Mathf.Max(0, num);
								if (num > 0)
								{
									HealOnLobbyMod.Log.LogInfo((object)$"Player '{text3}': Calculated HEAL AMOUNT: {num}");
									int num2 = Random.Range(1, 101);
									bool flag2 = num2 <= value;
									HealOnLobbyMod.Log.LogInfo((object)$"Player '{text3}': Heal chance check: Rolled {num2} vs Chance {value}. Allowed: {flag2}");
									if (flag2)
									{
										if (isMasterClient)
										{
											if ((Object)(object)component != (Object)null)
											{
												HealOnLobbyMod.Log.LogInfo((object)string.Format("MP Host: Sending RPC '{0}' for player '{1}' (ViewID: {2}) with HEAL AMOUNT {3}.", "RPC_HealPlayer", text3, component.ViewID, num));
												component.RPC("RPC_HealPlayer", (RpcTarget)3, new object[1] { num });
											}
											else
											{
												HealOnLobbyMod.Log.LogWarning((object)("MP Host: PhotonView became null for player '" + text3 + "' before sending RPC."));
											}
										}
										else if (flag)
										{
											int num3 = Mathf.Min(num, maxHealth - health);
											num3 = Mathf.Max(0, num3);
											if (num3 > 0)
											{
												HealOnLobbyMod.Log.LogInfo((object)$"SP (State: {networkClientState}): Calling playerHealth.Heal directly for player '{text3}' (ViewID: {((component != null) ? component.ViewID : 0)}) with ACTUAL amount {num3}.");
												player.playerHealth.Heal(num3, false);
											}
											else
											{
												HealOnLobbyMod.Log.LogInfo((object)("SP: Calculated actual heal is 0 for player '" + text3 + "'. Skipping direct call."));
											}
										}
									}
									else
									{
										HealOnLobbyMod.Log.LogInfo((object)$"Player '{text3}' healing skipped due to chance ({num2} > {value}).");
									}
								}
								else
								{
									HealOnLobbyMod.Log.LogInfo((object)$"Player '{text3}' needs no healing ({health}/{maxHealth}). No action taken.");
								}
							}
							else
							{
								HealOnLobbyMod.Log.LogWarning((object)"Player entry is null or has no PlayerHealth component.");
							}
						}
						HealOnLobbyMod.Log.LogInfo((object)"Immediate healing processing loop completed.");
					}
					else
					{
						HealOnLobbyMod.Log.LogWarning((object)"GameDirector or PlayerList is null.");
					}
					return;
				}
				catch (Exception arg2)
				{
					HealOnLobbyMod.Log.LogError((object)$"Exception occurred during immediate healing processing: {arg2}");
					return;
				}
			}
			HealOnLobbyMod.Log.LogInfo((object)$"Client role detected (IsMasterClient: {isMasterClient}, NetworkClientState: {networkClientState}). Waiting for host RPC...");
		}
		catch (Exception arg3)
		{
			HealOnLobbyMod.Log.LogError((object)$"Exception occurred in RunManager_ChangeLevel_Patch.Postfix: {arg3}");
		}
	}
}
[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
public static class PlayerAvatar_Awake_Patch
{
	private static void Postfix(PlayerAvatar __instance)
	{
		try
		{
			GameObject gameObject = ((Component)__instance).gameObject;
			if ((Object)(object)gameObject.GetComponent<PlayerHealSync>() == (Object)null)
			{
				gameObject.AddComponent<PlayerHealSync>();
				HealOnLobbyMod.Log.LogInfo((object)("Added PlayerHealSync component to " + ((Object)gameObject).name));
			}
		}
		catch (Exception arg)
		{
			HealOnLobbyMod.Log.LogError((object)$"Exception in PlayerAvatar_Awake_Patch.Postfix: {arg}");
		}
	}
}
public class PlayerHealSync : MonoBehaviourPun
{
	private PlayerHealth playerHealth;

	private void Awake()
	{
		playerHealth = ((Component)this).GetComponent<PlayerHealth>();
		if ((Object)(object)playerHealth == (Object)null)
		{
			Debug.LogError((object)"PlayerHealSync: Could not find PlayerHealth component!");
		}
	}

	[PunRPC]
	public void RPC_HealPlayer(int healAmount, PhotonMessageInfo info)
	{
		//IL_0000: 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_0029: Unknown result type (might be due to invalid IL or missing references)
		if (!info.Sender.IsMasterClient)
		{
			PhotonView photonView = ((MonoBehaviourPun)this).photonView;
			object arg = ((photonView != null) ? photonView.ViewID : 0);
			Player sender = info.Sender;
			Debug.LogWarning((object)string.Format("PlayerHealSync (ViewID: {0}): Received RPC_HealPlayer from non-MasterClient {1}. Ignoring.", arg, ((sender != null) ? sender.NickName : null) ?? "N/A"));
			return;
		}
		PhotonView photonView2 = ((MonoBehaviourPun)this).photonView;
		object arg2 = ((photonView2 != null) ? photonView2.ViewID : 0);
		Player sender2 = info.Sender;
		Debug.Log((object)string.Format("PlayerHealSync (ViewID: {0}): RPC_HealPlayer received from {1} with amount {2}.", arg2, ((sender2 != null) ? sender2.NickName : null) ?? "N/A", healAmount));
		if ((Object)(object)playerHealth != (Object)null)
		{
			int health = playerHealth.health;
			int maxHealth = playerHealth.maxHealth;
			int num = Mathf.Min(healAmount, maxHealth - health);
			num = Mathf.Max(0, num);
			object[] array = new object[4];
			PhotonView photonView3 = ((MonoBehaviourPun)this).photonView;
			array[0] = ((photonView3 != null) ? photonView3.ViewID : 0);
			array[1] = health;
			array[2] = maxHealth;
			array[3] = num;
			Debug.Log((object)string.Format("PlayerHealSync (ViewID: {0}): Current health is {1}/{2}. Calculated actual heal amount: {3}.", array));
			if (num > 0)
			{
				playerHealth.Heal(num, false);
				PhotonView photonView4 = ((MonoBehaviourPun)this).photonView;
				Debug.Log((object)$"PlayerHealSync (ViewID: {((photonView4 != null) ? photonView4.ViewID : 0)}): Called playerHealth.Heal({num}). New health should be {Mathf.Min(health + num, maxHealth)}.");
			}
			else
			{
				PhotonView photonView5 = ((MonoBehaviourPun)this).photonView;
				Debug.Log((object)$"PlayerHealSync (ViewID: {((photonView5 != null) ? photonView5.ViewID : 0)}): Actual heal amount is zero. No heal needed.");
			}
		}
		else
		{
			PhotonView photonView6 = ((MonoBehaviourPun)this).photonView;
			Debug.LogError((object)$"PlayerHealSync (ViewID: {((photonView6 != null) ? photonView6.ViewID : 0)}): Received RPC_HealPlayer but PlayerHealth component is missing!");
		}
	}
}
namespace HealOnStoreMod
{
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "HealOnLobbyMod";

		public const string PLUGIN_NAME = "HealOnLobbyMod";

		public const string PLUGIN_VERSION = "1.0.6";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}