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.");
}
}