Decompiled source of Health Regen v2.8.6

plugins/HealthRegen.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("HealthRegen Mod Reborn")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HealthRegen Mod Reborn")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("6e057aef-14c1-4898-8cea-05c265beced3")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.rybirb.healthregen", "HealthRegen", "2.8.6")]
public class HealthRegenMod : BaseUnityPlugin
{
	[HarmonyPatch(typeof(PlayerHealth), "Start")]
	public class RegenPatch
	{
		private static void Postfix(PlayerHealth __instance)
		{
			if ((Object)(object)__instance != (Object)null)
			{
				GameObject gameObject = ((Component)__instance).gameObject;
				if ((Object)(object)gameObject.GetComponent<RegenUpdater>() == (Object)null)
				{
					gameObject.AddComponent<RegenUpdater>().Init(__instance);
					Log.LogInfo((object)$"[{DateTime.Now:HH:mm:ss}] [Patch] RegenUpdater attached to {((Object)__instance).name}.");
				}
			}
		}
	}

	public class RegenUpdater : MonoBehaviour
	{
		private float updateThrottle = 0f;

		private static float timeSinceLastRegen = 0f;

		private static float timeSinceLastLog = 0f;

		private static Dictionary<string, int> lastKnownHealth = new Dictionary<string, int>();

		private static bool isMultiplayer = false;

		private PlayerHealth playerHealth;

		private FieldInfo healthField;

		private FieldInfo maxHealthField;

		private FieldInfo playerAvatarField;

		public void Init(PlayerHealth instance)
		{
			playerHealth = instance;
			Type type = ((object)playerHealth).GetType();
			healthField = type.GetField("health", BindingFlags.Instance | BindingFlags.NonPublic);
			maxHealthField = type.GetField("maxHealth", BindingFlags.Instance | BindingFlags.NonPublic);
			playerAvatarField = type.GetField("playerAvatar", BindingFlags.Instance | BindingFlags.NonPublic);
			LogInfo("[Init] Reflection setup complete.");
		}

		private void Start()
		{
			isMultiplayer = PhotonNetwork.IsConnected;
			LogInfo("[Start] RegenUpdater Start() called for " + ((Object)playerHealth).name + ". Game mode: " + (isMultiplayer ? "Multiplayer" : "Singleplayer"));
		}

		private void Update()
		{
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			updateThrottle += Time.deltaTime;
			if (updateThrottle < 1f)
			{
				return;
			}
			updateThrottle = 0f;
			if (isMultiplayer && !PhotonNetwork.IsMasterClient)
			{
				LogInfo("[Update] Skipping regen — not master client.");
				return;
			}
			timeSinceLastRegen += 1f;
			timeSinceLastLog += 1f;
			PlayerHealth[] array = Object.FindObjectsOfType<PlayerHealth>();
			int num = 0;
			PlayerHealth[] array2 = array;
			foreach (PlayerHealth obj in array2)
			{
				int num2 = (int)healthField.GetValue(obj);
				if (num2 > 0)
				{
					num++;
				}
			}
			int num3 = 20 * (int)Mathf.Pow(2f, (float)Mathf.Max(0, num - 1));
			if (timeSinceLastLog >= 5f)
			{
				LogInfo("------------------------------------");
				object arg = array.Length;
				object arg2 = num;
				Scene activeScene = SceneManager.GetActiveScene();
				LogInfo($"[Log] Player count: {arg}, Alive: {arg2} — Scene: {((Scene)(ref activeScene)).name}");
				LogInfo($"[Log] Regen Interval: {num3} seconds");
				int num4 = 1;
				PlayerHealth[] array3 = array;
				foreach (PlayerHealth val in array3)
				{
					object avatarObj = playerAvatarField?.GetValue(val);
					string nickname = GetNickname(avatarObj);
					int num5 = (int)healthField.GetValue(val);
					int num6 = (int)maxHealthField.GetValue(val);
					LogInfo($"Player {num4++}: {((Object)val).name} ({nickname}) — {num5}/{num6} HP");
				}
				LogInfo("------------------------------------");
				timeSinceLastLog = 0f;
			}
			if (!(timeSinceLastRegen >= (float)num3))
			{
				return;
			}
			PlayerHealth[] array4 = array;
			int value = default(int);
			foreach (PlayerHealth val2 in array4)
			{
				object obj2 = playerAvatarField?.GetValue(val2);
				PlayerAvatar val3 = (PlayerAvatar)((obj2 is PlayerAvatar) ? obj2 : null);
				if (val3 == null)
				{
					LogInfo("[Regen] Skipping player — Avatar is null or invalid.");
					continue;
				}
				int num7 = (int)healthField.GetValue(val2);
				int num8 = (int)maxHealthField.GetValue(val2);
				string key = SemiFunc.PlayerGetSteamID(val3);
				if (!lastKnownHealth.ContainsKey(key))
				{
					lastKnownHealth[key] = num7;
				}
				if (num7 < lastKnownHealth[key])
				{
					LogInfo($"[Detect] Health dropped! {num7}/{num8} (was {lastKnownHealth[key]})");
				}
				lastKnownHealth[key] = num7;
				if (num7 >= num8)
				{
					LogInfo("[Regen] Skipping " + GetNickname(val3) + " — already at max HP.");
					continue;
				}
				int num9 = 1;
				StatsManager instance = StatsManager.instance;
				if (instance != null && (instance.playerUpgradeHealth?.TryGetValue(key, out value)).GetValueOrDefault())
				{
					num9 += value;
				}
				int num10 = Mathf.Min(num7 + num9, num8);
				if (isMultiplayer)
				{
					PhotonView component = ((Component)val2).GetComponent<PhotonView>();
					if (component != null)
					{
						component.RPC("UpdateHealthRPC", (RpcTarget)0, new object[3] { num10, num8, false });
					}
				}
				else
				{
					healthField.SetValue(val2, num10);
				}
				LogInfo($"[Regen] {GetNickname(val3)} → +{num9} → {num10}/{num8}");
				lastKnownHealth[key] = num10;
			}
			timeSinceLastRegen = 0f;
		}

		private string GetNickname(object avatarObj)
		{
			try
			{
				object obj = ((avatarObj is PlayerAvatar) ? avatarObj : null);
				object obj2;
				if (obj == null)
				{
					obj2 = null;
				}
				else
				{
					PhotonView photonView = ((PlayerAvatar)obj).photonView;
					if (photonView == null)
					{
						obj2 = null;
					}
					else
					{
						Player owner = photonView.Owner;
						obj2 = ((owner != null) ? owner.NickName : null);
					}
				}
				if (obj2 == null)
				{
					obj2 = "Unknown";
				}
				return (string)obj2;
			}
			catch
			{
				return "Unknown";
			}
		}

		private void LogInfo(string msg)
		{
			Log.LogInfo((object)$"[{DateTime.Now:HH:mm:ss}] {msg}");
		}
	}

	internal static ManualLogSource Log;

	private void Awake()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[Init] HealthRegen is loading...");
		Harmony val = new Harmony("com.rybirb.healthregen");
		val.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[Init] Patching complete.");
	}
}