using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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("FullHealth")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.2.0")]
[assembly: AssemblyInformationalVersion("1.5.2+2ef5c2896593a4215c1847712611d35d8cc114f3")]
[assembly: AssemblyProduct("FullHealth")]
[assembly: AssemblyTitle("FullHealth")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.2.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 FullHealth
{
public static class Configuration
{
public static ConfigEntry<bool> Enabled { get; set; }
public static ConfigEntry<decimal> Percent { get; set; }
public static ConfigEntry<WorkMode> WorkMode { get; set; }
public static ConfigEntry<HealMode> HealMode { get; set; }
public static ConfigEntry<PhaseMode> PhaseMode { get; set; }
public static ConfigEntry<int> ExactValue { get; set; }
public static ConfigEntry<HealthPackMode> HealthPackMode { get; set; }
internal static int[] HealthPackModeValues { get; set; } = Array.Empty<int>();
}
public static class ConfigurationHelper
{
private static readonly HealthPackMode[] HealthPackModes = new HealthPackMode[3]
{
HealthPackMode.Small,
HealthPackMode.Medium,
HealthPackMode.Large
};
public static void Bind(ConfigFile config)
{
Configuration.Enabled = config.Bind<bool>("General", "Enabled", true, "Indicates whether the mod is enabled.");
Configuration.Percent = config.Bind<decimal>("General", "Percent", 100m, "Percentage of maximum health to which the player will be healed (Min: 0, Max: 100).\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\n");
Configuration.WorkMode = config.Bind<WorkMode>("General", "WorkMode", WorkMode.Host, "Configures the work mode.\r\nWarning: You are using `HostAndClient` value for this parameter at your own risk.\r\nIt may stop working after future game updates.\r\nIf another lobby players do not agree with this parameter, do not use it.\r\nAlso, the `Percent` parameter may not work correctly with this parameter, since the host or other clients may also perform the heal.\r\nHost - works only on host.\r\nHostAndClient - works on host and client.\r\n");
Configuration.HealMode = config.Bind<HealMode>("General", "HealMode", HealMode.All, "Configures the heal mode.\r\nAll - heal all.\r\nSelf - heal self.");
Configuration.PhaseMode = config.Bind<PhaseMode>("General", "PhaseMode", PhaseMode.All, "Configures in which phases of the game health restoration will be triggered.\r\nLevel - when spawning in the level.\r\nShop - when spawning in the shop. Attention! Healing in the store does not affect the game.\r\nLobby - when spawning in the truck (not menu) before starting the next level.\r\nAll - in all phases.");
Configuration.ExactValue = config.Bind<int>("General", "ExactValue", 0, "Sets the exact value to heal.\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\nIf the value is `0`, then it is disabled.\r\nIt has priority over the `Percent` parameter.\r\nFor example, if you set the value to `15`, you will always receive 15 HP.");
Configuration.HealthPackMode = config.Bind<HealthPackMode>("General", "HealthPackMode", HealthPackMode.None, "Sets healing value options to the same as in health packs, one of which will be applied randomly\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\nIf the value is `None`, then it is disabled.\r\nIt has priority over the `Percent` and `ExactValue` parameters.\r\nFor example, if you set the value to `Medium, Large`, you will receive a random healing of 50HP or 100HP.\r\nSmall - heal 25 HP.\r\nMedium - heal 50 HP.\r\nLarge - heal 100 HP.");
UpdateHealthPackModeValues();
Configuration.HealthPackMode.SettingChanged += delegate
{
UpdateHealthPackModeValues();
};
decimal num = Math.Min(100m, Math.Max(0m, Configuration.Percent.Value));
if (num != Configuration.Percent.Value)
{
Configuration.Percent.Value = num;
}
if (Configuration.ExactValue.Value < 0)
{
Configuration.ExactValue.Value = 0;
}
}
private static void UpdateHealthPackModeValues()
{
List<int> list = new List<int>();
using (IEnumerator<HealthPackMode> enumerator = HealthPackModes.Where((HealthPackMode x) => Configuration.HealthPackMode.Value.HasFlag(x)).GetEnumerator())
{
while (enumerator.MoveNext())
{
switch (enumerator.Current)
{
case HealthPackMode.Small:
list.Add(25);
break;
case HealthPackMode.Medium:
list.Add(50);
break;
case HealthPackMode.Large:
list.Add(100);
break;
}
}
}
Configuration.HealthPackModeValues = list.ToArray();
}
}
public enum HealMode : byte
{
All,
Self
}
[Flags]
public enum HealthPackMode : byte
{
None = 0,
Small = 1,
Medium = 2,
Large = 4,
All = 7
}
[Flags]
public enum PhaseMode : byte
{
Level = 1,
Shop = 2,
Lobby = 4,
All = 7
}
public enum WorkMode : byte
{
Host,
HostAndClient
}
public static class GamePhaseValidateHelper
{
public static bool IsValid()
{
PhaseMode? gamePhaseMode = GetGamePhaseMode();
if (!gamePhaseMode.HasValue)
{
Plugin.Logger.LogDebug((object)"This is not a game phase");
return false;
}
if (!Configuration.PhaseMode.Value.HasFlag(gamePhaseMode.Value))
{
Plugin.Logger.LogDebug((object)"Healing in this phase is disabled in the configuration");
return false;
}
return true;
}
private static PhaseMode? GetGamePhaseMode()
{
if (SemiFunc.RunIsLevel())
{
return PhaseMode.Level;
}
if (SemiFunc.RunIsShop())
{
return PhaseMode.Shop;
}
if (SemiFunc.RunIsLobby())
{
return PhaseMode.Lobby;
}
return null;
}
}
public static class HealHelper
{
public static async Task Heal()
{
if (!(await WaitHelper.Wait()))
{
return;
}
switch (Configuration.HealMode.Value)
{
case HealMode.All:
{
foreach (PlayerAvatar item in PlayersHelper.Get())
{
HealPlayerHelper.Heal(item);
}
break;
}
case HealMode.Self:
if ((Object)(object)SemiFunc.PlayerAvatarLocal() == (Object)null)
{
Plugin.Logger.LogWarning((object)"An error occurred while retrieving data about yourself");
}
else
{
HealPlayerHelper.Heal(SemiFunc.PlayerAvatarLocal());
}
break;
default:
Plugin.Logger.LogWarning((object)"Unknown HealMode value");
break;
}
}
}
public static class HealPlayerHelper
{
private static readonly Random Random = new Random();
public static void Heal(PlayerAvatar player)
{
if ((Object)(object)player?.playerHealth == (Object)null)
{
Plugin.Logger.LogWarning((object)"Player or his health is not set");
return;
}
int health = player.playerHealth.health;
int[] healthPackModeValues = Configuration.HealthPackModeValues;
int num2;
if (healthPackModeValues != null && healthPackModeValues.Length > 0)
{
int num = ((Configuration.HealthPackModeValues.Length != 1) ? Random.Next(0, Configuration.HealthPackModeValues.Length) : 0);
num2 = Configuration.HealthPackModeValues[num];
}
else if (Configuration.ExactValue.Value > 0)
{
num2 = Configuration.ExactValue.Value;
}
else
{
int num3 = decimal.ToInt32((decimal)player.playerHealth.maxHealth * (Configuration.Percent.Value / 100m));
if (health >= num3)
{
Plugin.Logger.LogDebug((object)($"The health of the player '{player.playerName}' is '{health}' " + $"and this is is more or equal than expected '{num3}', so health is not changed"));
return;
}
num2 = num3 - health;
}
player.playerHealth.HealOther(num2, true);
Plugin.Logger.LogDebug((object)($"Player '{player.playerName}' with '{health}' health received '{num2}' HP. " + $"So player should have '{Math.Min(player.playerHealth.maxHealth, health + num2)}' HP"));
}
}
public static class PlayersHelper
{
public static List<PlayerAvatar> Get()
{
List<PlayerAvatar> list = SemiFunc.PlayerGetList();
if (list != null && list.Count > 0)
{
return list;
}
PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
if ((Object)(object)val != (Object)null)
{
return new List<PlayerAvatar>(1) { val };
}
return new List<PlayerAvatar>();
}
}
public static class WaitHelper
{
public static async Task<bool> Wait()
{
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes(1.0));
while (!cts.IsCancellationRequested)
{
Plugin.Logger.LogDebug((object)"Waiting for all players to load");
await Task.Delay(1000, CancellationToken.None);
if (SemiFunc.RunIsLobby())
{
return true;
}
if (PlayersHelper.Get().All((PlayerAvatar x) => x.levelAnimationCompleted))
{
return true;
}
}
Plugin.Logger.LogDebug((object)"Timeout exceeded, operation cancelled");
return false;
}
}
[HarmonyPatch(typeof(RoundDirector), "StartRoundRPC")]
public class FullHealthMultiplayerPatch
{
private static void Postfix()
{
Plugin.Logger.LogDebug((object)"StartRoundRPC patch");
if (!Configuration.Enabled.Value)
{
Plugin.Logger.LogDebug((object)"The mod is disabled, so health is not changed");
}
else if (!SemiFunc.IsMultiplayer())
{
Plugin.Logger.LogDebug((object)"This is not a multiplayer");
}
else if (Configuration.WorkMode.Value == WorkMode.Host && !SemiFunc.IsMasterClient())
{
Plugin.Logger.LogDebug((object)"This is not a host");
}
else if (GamePhaseValidateHelper.IsValid())
{
Task.Run((Func<Task?>)HealHelper.Heal);
}
}
}
[HarmonyPatch(typeof(RoundDirector), "StartRound")]
public class FullHealthSinglePlayerPatch
{
private static void Postfix()
{
Plugin.Logger.LogDebug((object)"StartRound patch");
if (!Configuration.Enabled.Value)
{
Plugin.Logger.LogDebug((object)"The mod is disabled, so HP is not changed");
}
else if (SemiFunc.IsMultiplayer())
{
Plugin.Logger.LogDebug((object)"This is not a single player");
}
else if (GamePhaseValidateHelper.IsValid())
{
Task.Run((Func<Task?>)HealHelper.Heal);
}
}
}
[BepInPlugin("FullHealth", "FullHealth", "1.5.2")]
public class Plugin : BaseUnityPlugin
{
public static ManualLogSource Logger;
private void Awake()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
Logger = ((BaseUnityPlugin)this).Logger;
ConfigurationHelper.Bind(((BaseUnityPlugin)this).Config);
new Harmony("FullHealth").PatchAll();
Logger.LogInfo((object)"Mod 'FullHealth' is loaded!");
Logger.LogInfo((object)"Configuration can be changed in 'BepInEx/config/FullHealth.cfg'");
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "FullHealth";
public const string PLUGIN_NAME = "FullHealth";
public const string PLUGIN_VERSION = "1.5.2";
}
}