Decompiled source of FrailtyPlus v1.0.1

FrailtyPlus.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CSync.Extensions;
using CSync.Lib;
using Frailty.Manager;
using Frailty.Patches;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Frailty")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Frailty")]
[assembly: AssemblyTitle("Frailty")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 Frailty
{
	internal static class Compat
	{
		internal static bool UsualScrap;

		private static MethodInfo acPerkScale;

		private static FieldInfo acDelta;

		private static FieldInfo acFrame;

		private static FieldInfo bandageUses;

		internal static void Init(Harmony harmony)
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_0178: Expected O, but got Unknown
			Assembly assembly = FindPluginAssembly("Emil.UsualScrap");
			if (assembly != null)
			{
				try
				{
					Type type = assembly.GetType("UsualScrap.Behaviors.BandagesScript", throwOnError: false);
					Type type2 = assembly.GetType("UsualScrap.Behaviors.MedicalKitScript", throwOnError: false);
					MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "Heal", Type.EmptyTypes, (Type[])null) : null);
					MethodInfo methodInfo2 = ((type != null) ? AccessTools.Method(type, "ItemActivate", (Type[])null, (Type[])null) : null);
					bandageUses = ((type != null) ? AccessTools.Field(type, "uses") : null);
					MethodInfo methodInfo3 = ((type2 != null) ? AccessTools.Method(type2, "Heal", new Type[2]
					{
						typeof(int),
						typeof(bool)
					}, (Type[])null) : null);
					if (methodInfo != null && methodInfo2 != null && bandageUses != null && methodInfo3 != null)
					{
						harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Compat), "BandageHeal", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Compat), "BandageActivate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(Compat), "MedkitHealPrefix", (Type[])null), new HarmonyMethod(typeof(Compat), "MedkitHealPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						UsualScrap = true;
						Plugin.mls.LogInfo((object)"UsualScrap found: Bandages and the Medical Kit treat bleeding. Laceration / Fatal Bleeding are possible.");
					}
					else
					{
						Plugin.mls.LogWarning((object)"UsualScrap changed: Bandages / Medical Kit don't treat bleeding, no Laceration / Fatal Bleeding.");
					}
				}
				catch (Exception ex)
				{
					Plugin.mls.LogError((object)("UsualScrap compatibility: " + ex));
				}
			}
			Assembly assembly2 = FindPluginAssembly("com.potatoepet.AdvancedCompany");
			if (assembly2 != null)
			{
				Type type3 = assembly2.GetType("AdvancedCompany.Patches.PlayerControllerB", throwOnError: false);
				if (type3 != null)
				{
					acPerkScale = AccessTools.Method(type3, "StaminaPerkScale", (Type[])null, (Type[])null);
					acDelta = AccessTools.Field(type3, "FrailtyStaminaDelta");
					acFrame = AccessTools.Field(type3, "FrailtyStaminaFrame");
				}
				Plugin.mls.LogInfo((object)((acPerkScale != null) ? "AdvancedCompany found: its perks can reduce the leg injury drain." : "AdvancedCompany found, but not the SleepyMiles v.81 version: perks don't change the leg injury drain."));
			}
		}

		private static Assembly FindPluginAssembly(string guid)
		{
			if (Chainloader.PluginInfos.TryGetValue(guid, out var value) && (Object)(object)value.Instance != (Object)null)
			{
				return ((object)value.Instance).GetType().Assembly;
			}
			return null;
		}

		internal static float PerkDrainScale(PlayerControllerB player, float delta)
		{
			if (acPerkScale == null)
			{
				return 1f;
			}
			try
			{
				return Convert.ToSingle(acPerkScale.Invoke(null, new object[2] { player, delta }));
			}
			catch
			{
				acPerkScale = null;
				return 1f;
			}
		}

		internal static void ReportStaminaDelta(float delta)
		{
			if (acDelta == null || acFrame == null)
			{
				return;
			}
			try
			{
				acDelta.SetValue(null, delta);
				acFrame.SetValue(null, Time.frameCount);
			}
			catch
			{
				acDelta = null;
			}
		}

		private static ConditionManager LocalManagerIf(PlayerControllerB p)
		{
			if ((Object)(object)p == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)p != (Object)(object)GameNetworkManager.Instance.localPlayerController)
			{
				return null;
			}
			return ((Component)p).GetComponent<ConditionManager>();
		}

		private static bool HasBleed(ConditionManager m)
		{
			return (Object)(object)m != (Object)null && (m.HasCondition(ConditionType.Bleeding) || m.HasCondition(ConditionType.Laceration) || m.HasCondition(ConditionType.FatalBleeding));
		}

		private static void BandageActivate(GrabbableObject __instance, bool buttonDown)
		{
			try
			{
				PlayerControllerB playerHeldBy = __instance.playerHeldBy;
				if (buttonDown && !((Object)(object)playerHeldBy == (Object)null) && playerHeldBy.health >= 100 && HasBleed(LocalManagerIf(playerHeldBy)))
				{
					AccessTools.Method(((object)__instance).GetType(), "HealServerRpc", (Type[])null, (Type[])null)?.Invoke(__instance, null);
				}
			}
			catch (Exception ex)
			{
				Plugin.mls.LogWarning((object)("Bandage: " + ex.Message));
			}
		}

		private static bool BandageHeal(GrabbableObject __instance)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PlayerControllerB playerHeldBy = __instance.playerHeldBy;
				ConditionManager conditionManager = LocalManagerIf(playerHeldBy);
				if ((Object)(object)conditionManager != (Object)null && FrailtyConfig.Instance != null)
				{
					conditionManager.Treat(FrailtyConfig.Instance.BandageSeconds.Value);
				}
				if ((Object)(object)playerHeldBy != (Object)null && playerHeldBy.health >= 100)
				{
					int num = (int)bandageUses.GetValue(__instance);
					if (num > 1 && num <= 3)
					{
						bandageUses.SetValue(__instance, num - 1);
					}
					else if (num == 1)
					{
						if (__instance.heldByPlayerOnServer && (Object)(object)__instance.playerHeldBy != (Object)null)
						{
							__instance.playerHeldBy.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true);
						}
						Object.Destroy((Object)(object)((Component)__instance).gameObject);
					}
					return false;
				}
			}
			catch (Exception ex)
			{
				Plugin.mls.LogWarning((object)("Bandage: " + ex.Message));
			}
			return true;
		}

		private static void MedkitHealPrefix(GrabbableObject __instance, int playerID, bool otherplayer, out int __state)
		{
			__state = -1;
			try
			{
				PlayerControllerB val = (otherplayer ? StartOfRound.Instance.allPlayerScripts[playerID] : __instance.playerHeldBy);
				if ((Object)(object)LocalManagerIf(val) != (Object)null)
				{
					__state = val.health;
				}
			}
			catch
			{
			}
		}

		private static void MedkitHealPostfix(GrabbableObject __instance, int playerID, bool otherplayer, int __state)
		{
			if (__state < 0 || FrailtyConfig.Instance == null)
			{
				return;
			}
			try
			{
				PlayerControllerB val = (otherplayer ? StartOfRound.Instance.allPlayerScripts[playerID] : __instance.playerHeldBy);
				if (!((Object)(object)val == (Object)null) && val.health > __state)
				{
					ConditionManager conditionManager = LocalManagerIf(val);
					if ((Object)(object)conditionManager != (Object)null)
					{
						conditionManager.Treat(FrailtyConfig.Instance.MedkitSecondsPerSecond.Value * (otherplayer ? 0.1f : 0.3f));
					}
				}
			}
			catch
			{
			}
		}
	}
	public static class FrailtyApi
	{
		public const int Version = 1;

		public static readonly List<Func<float>> NewDebuffTimeScale = new List<Func<float>>();

		public static readonly List<Func<bool>> SuppressEffects = new List<Func<bool>>();

		public static ConditionManager Local
		{
			get
			{
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				return ((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ConditionManager>() : null;
			}
		}

		public static event Action<PlayerControllerB, int, CauseOfDeath, bool> LocalPlayerHurt;

		public static event Action<ConditionType, float> LocalDebuffGained;

		public static bool Has(ConditionType type)
		{
			return (Object)(object)Local != (Object)null && Local.HasCondition(type);
		}

		public static void Cure(params ConditionType[] types)
		{
			ConditionManager local = Local;
			if (!((Object)(object)local == (Object)null) && types != null)
			{
				foreach (ConditionType type in types)
				{
					local.RemoveCondition(type, cured: true);
				}
			}
		}

		public static void CureAll()
		{
			ConditionManager local = Local;
			if ((Object)(object)local != (Object)null)
			{
				local.ClearAllConditions();
			}
		}

		internal static float TimeScale()
		{
			float num = 1f;
			foreach (Func<float> item in NewDebuffTimeScale)
			{
				try
				{
					num *= Math.Max(0f, item());
				}
				catch
				{
				}
			}
			return num;
		}

		internal static bool Suppressed()
		{
			foreach (Func<bool> suppressEffect in SuppressEffects)
			{
				try
				{
					if (suppressEffect())
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		internal static void RaiseHurt(PlayerControllerB p, int taken, CauseOfDeath cause, bool fall)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				FrailtyApi.LocalPlayerHurt?.Invoke(p, taken, cause, fall);
			}
			catch (Exception ex)
			{
				Plugin.mls.LogError((object)("LocalPlayerHurt listener: " + ex));
			}
		}

		internal static void RaiseGained(ConditionType t, float seconds)
		{
			try
			{
				FrailtyApi.LocalDebuffGained?.Invoke(t, seconds);
			}
			catch (Exception ex)
			{
				Plugin.mls.LogError((object)("LocalDebuffGained listener: " + ex));
			}
		}
	}
	public class FrailtyConfig : SyncedConfig2<FrailtyConfig>
	{
		private const string Effects = "1. Effects";

		private const string Injuries = "2. Injuries";

		private const string Chances = "3. Chances";

		private const string Times = "4. Times";

		private const string Fall = "5. Fall damage";

		private const string Bleed = "6. Bleeding";

		private const string Lac = "7. Laceration and Fatal Bleeding";

		private const string Para = "8. Paralyze";

		private const string Other = "9. Other";

		public ConfigEntry<bool> KeepHudClear;

		public readonly SyncedEntry<int>[] FallMin = new SyncedEntry<int>[4];

		[SyncedEntryField]
		public SyncedEntry<int> FallMin1;

		[SyncedEntryField]
		public SyncedEntry<int> FallMin2;

		[SyncedEntryField]
		public SyncedEntry<int> FallMin3;

		[SyncedEntryField]
		public SyncedEntry<int> FallMin4;

		public readonly SyncedEntry<float>[] FallFracChance = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> FallFracChance1;

		[SyncedEntryField]
		public SyncedEntry<float> FallFracChance2;

		[SyncedEntryField]
		public SyncedEntry<float> FallFracChance3;

		[SyncedEntryField]
		public SyncedEntry<float> FallFracChance4;

		public readonly SyncedEntry<float>[] FallFracTime = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> FallFracTime1;

		[SyncedEntryField]
		public SyncedEntry<float> FallFracTime2;

		[SyncedEntryField]
		public SyncedEntry<float> FallFracTime3;

		[SyncedEntryField]
		public SyncedEntry<float> FallFracTime4;

		public readonly SyncedEntry<float>[] FallBrokenChance = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenChance1;

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenChance2;

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenChance3;

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenChance4;

		public readonly SyncedEntry<float>[] FallBrokenTime = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenTime1;

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenTime2;

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenTime3;

		[SyncedEntryField]
		public SyncedEntry<float> FallBrokenTime4;

		public readonly SyncedEntry<int>[] BleedMin = new SyncedEntry<int>[4];

		[SyncedEntryField]
		public SyncedEntry<int> BleedMin1;

		[SyncedEntryField]
		public SyncedEntry<int> BleedMin2;

		[SyncedEntryField]
		public SyncedEntry<int> BleedMin3;

		[SyncedEntryField]
		public SyncedEntry<int> BleedMin4;

		public readonly SyncedEntry<float>[] BleedChance = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> BleedChance1;

		[SyncedEntryField]
		public SyncedEntry<float> BleedChance2;

		[SyncedEntryField]
		public SyncedEntry<float> BleedChance3;

		[SyncedEntryField]
		public SyncedEntry<float> BleedChance4;

		public readonly SyncedEntry<float>[] BleedTime = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> BleedTime1;

		[SyncedEntryField]
		public SyncedEntry<float> BleedTime2;

		[SyncedEntryField]
		public SyncedEntry<float> BleedTime3;

		[SyncedEntryField]
		public SyncedEntry<float> BleedTime4;

		public readonly SyncedEntry<float>[] LacChance = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> LacChance1;

		[SyncedEntryField]
		public SyncedEntry<float> LacChance2;

		[SyncedEntryField]
		public SyncedEntry<float> LacChance3;

		[SyncedEntryField]
		public SyncedEntry<float> LacChance4;

		public readonly SyncedEntry<float>[] StabLacChance = new SyncedEntry<float>[4];

		[SyncedEntryField]
		public SyncedEntry<float> StabLacChance1;

		[SyncedEntryField]
		public SyncedEntry<float> StabLacChance2;

		[SyncedEntryField]
		public SyncedEntry<float> StabLacChance3;

		[SyncedEntryField]
		public SyncedEntry<float> StabLacChance4;

		public ConfigEntry<bool> LogRolls;

		public ConfigEntry<int> FatalFlashEvery;

		private const string Pct = " (0 to 1, e.g. 0.25 = 25%)";

		public static FrailtyConfig Instance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> FracturedLeg { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> BrokenLeg { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> FracturedArm { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> BrokenArm { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> Bleeding { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> Blindness { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> Concussed { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> ParalyzeOn { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FracturedArmChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BrokenArmChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BleedingChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BlindnessChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ConcussedChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> CoilHeadConcussChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> ScaleWithDamage { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FracturedArmTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BrokenArmTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BleedingTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BlindnessTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ConcussedTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> CoilHeadConcussMin { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> CoilHeadConcussMax { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FractureToBrokenPercent { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> MaxLegTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> HitWindow { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> StabExtraChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> StabTimeMultiplier { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> LacerationOn { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> LacerationTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> Forgiving { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FatalDamageMin { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FatalDamageMax { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FatalTimeMin { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> FatalTimeMax { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ForgivingInterval { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> BandageSeconds { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> MedkitSecondsPerSecond { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> MaxFatalTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> MaxBleedingTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalyzeChance { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalyzeRollInterval { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalyzeTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalyzeExtraTime { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> ParalyzeMaxTier { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalysisChancePerTier { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalysisMin { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalysisMax { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> ParalysisLookSpeed { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<float> SteamInterval { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> PerksReduceLegDrain { get; private set; }

		private static ConfigDescription R(string desc, float min, float max)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new ConfigDescription(desc, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>());
		}

		private static ConfigDescription RI(string desc, int min, int max)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new ConfigDescription(desc, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), Array.Empty<object>());
		}

		public FrailtyConfig(ConfigFile cfg)
			: base("Chaos.Frailty")
		{
			Instance = this;
			KeepHudClear = cfg.Bind<bool>("1. Effects", "Keep HUD clear", true, "Client side only (your own setting, not synced). Blindness (glare) and concussion (blur, red tint, dark edges) only affect the game view, so your HUD stays readable. Off = Frailty's original: the whole screen, HUD included. Takes effect the next time you join a lobby.");
			FracturedLeg = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Fractured Leg", true, "Can players fracture their legs (fall damage)?");
			BrokenLeg = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Broken Leg", true, "Can players break their legs (fall damage)?");
			FracturedArm = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Fractured Arm", true, "Can players fracture their arms (hits, blasts, kicks, crushing)?");
			BrokenArm = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Broken Arm", true, "Can players break their arms?");
			Bleeding = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Bleeding", true, "Can players bleed?");
			Blindness = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Blindness", true, "Can steam valves blind players?");
			Concussed = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Concussed", true, "Can players get concussed?");
			ParalyzeOn = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "2. Injuries", "Paralyze", true, "Can Circuit Bees (electrocution) give Paralyze?");
			FracturedArmChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3. Chances", "Fractured Arm Chance", 0.2f, R("Chance per hit (bludgeoning, blast, kick, inertia, crushing) to fracture an arm (0 to 1, e.g. 0.25 = 25%)", 0f, 1f));
			BrokenArmChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3. Chances", "Broken Arm Chance", 0.2f, R("Chance per hit to turn a fractured arm into a broken one (0 to 1, e.g. 0.25 = 25%)", 0f, 1f));
			BleedingChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3. Chances", "Bleeding Chance", 0.25f, R("Chance to bleed from the Fan, Snipping (Barber) and other bleeding hits that are not Mauling / Stabbing. Gunshots always bleed (0 to 1, e.g. 0.25 = 25%)", 0f, 1f));
			BlindnessChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3. Chances", "Blindness Chance", 0.1f, R("Chance per roll near a burst steam valve (0 to 1, e.g. 0.25 = 25%)", 0f, 1f));
			ConcussedChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3. Chances", "Concussed Chance", 0.1f, R("Chance per hit (bludgeoning, blast, kick, inertia, crushing) to get concussed (0 to 1, e.g. 0.25 = 25%)", 0f, 1f));
			CoilHeadConcussChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3. Chances", "Coil-Head Concussion Chance", 1f, R("Chance that a Coil-Head hit (strangulation) concusses you (0 to 1, e.g. 0.25 = 25%)", 0f, 1f));
			ScaleWithDamage = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "3. Chances", "Scale chance with damage", false, "Off: every matching hit uses the chance above. On: the chance is lower while you are healthy and hits are small, and reaches the full chance when you are badly hurt or take a big hit (Frailty's original idea). Fall damage and bleeding use their own tiers and are not scaled.");
			FracturedArmTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Fractured Arm Time", 120f, R("Seconds a fractured arm lasts.", 1f, 3600f));
			BrokenArmTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Broken Arm Time", 180f, R("Seconds a broken arm lasts.", 1f, 3600f));
			BleedingTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Bleeding Time", 45f, R("Seconds of bleeding from Gunshots, the Fan and Snipping.", 1f, 3600f));
			BlindnessTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Blindness Time", 15f, R("Seconds blindness lasts.", 1f, 600f));
			ConcussedTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Concussed Time", 30f, R("Seconds a concussion from hits lasts.", 1f, 600f));
			CoilHeadConcussMin = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Coil-Head Concussion Min", 5f, R("Coil-Head concussion lasts a random time between Min and Max seconds.", 1f, 120f));
			CoilHeadConcussMax = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "4. Times", "Coil-Head Concussion Max", 10f, R("Coil-Head concussion lasts a random time between Min and Max seconds.", 1f, 120f));
			int[] array = new int[4] { 10, 30, 50, 80 };
			float[] array2 = new float[4] { 0.1f, 0.15f, 0.25f, 0.3f };
			float[] array3 = new float[4] { 20f, 40f, 60f, 120f };
			float[] array4 = new float[4] { 0f, 0f, 0.1f, 0.25f };
			float[] array5 = new float[4] { 0f, 0f, 90f, 150f };
			for (int i = 0; i < 4; i++)
			{
				string text = "Tier " + (i + 1) + " ";
				FallMin[i] = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "5. Fall damage", text + "minimum damage", array[i], RI("Fall damage actually taken (after damage reduction) needed for this tier. Below Tier 1 nothing happens.", 1, 100));
				FallFracChance[i] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "5. Fall damage", text + "Fractured Leg chance", array2[i], R("Chance of a Fractured Leg (0 to 1, e.g. 0.25 = 25%). Rolled after Broken Leg.", 0f, 1f));
				FallFracTime[i] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "5. Fall damage", text + "Fractured Leg time", array3[i], R("Seconds the Fractured Leg lasts.", 1f, 3600f));
				FallBrokenChance[i] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "5. Fall damage", text + "Broken Leg chance", array4[i], R("Chance of a Broken Leg (0 to 1, e.g. 0.25 = 25%). Rolled first.", 0f, 1f));
				FallBrokenTime[i] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "5. Fall damage", text + "Broken Leg time", (array5[i] > 0f) ? array5[i] : 90f, R("Seconds the Broken Leg lasts.", 1f, 3600f));
			}
			FallMin1 = FallMin[0];
			FallMin2 = FallMin[1];
			FallMin3 = FallMin[2];
			FallMin4 = FallMin[3];
			FallFracChance1 = FallFracChance[0];
			FallFracChance2 = FallFracChance[1];
			FallFracChance3 = FallFracChance[2];
			FallFracChance4 = FallFracChance[3];
			FallFracTime1 = FallFracTime[0];
			FallFracTime2 = FallFracTime[1];
			FallFracTime3 = FallFracTime[2];
			FallFracTime4 = FallFracTime[3];
			FallBrokenChance1 = FallBrokenChance[0];
			FallBrokenChance2 = FallBrokenChance[1];
			FallBrokenChance3 = FallBrokenChance[2];
			FallBrokenChance4 = FallBrokenChance[3];
			FallBrokenTime1 = FallBrokenTime[0];
			FallBrokenTime2 = FallBrokenTime[1];
			FallBrokenTime3 = FallBrokenTime[2];
			FallBrokenTime4 = FallBrokenTime[3];
			FractureToBrokenPercent = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "5. Fall damage", "Fracture to Broken conversion %", 100f, R("When a leg injury upgrades or stacks, the Fractured time carried over counts this much (100 = all of it). Fractured + Fractured = Broken.", 0f, 200f));
			MaxLegTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "5. Fall damage", "Max leg injury time", 0f, R("Longest a leg injury can last after stacking, in seconds. 0 = no limit.", 0f, 3600f));
			int[] array6 = new int[4] { 10, 30, 50, 80 };
			float[] array7 = new float[4] { 0.15f, 0.25f, 0.35f, 0.4f };
			float[] array8 = new float[4] { 20f, 45f, 60f, 90f };
			float[] array9 = new float[4] { 0f, 0f, 0.1f, 0.25f };
			float[] array10 = new float[4] { 0f, 0.1f, 0.25f, 0.4f };
			for (int j = 0; j < 4; j++)
			{
				string text2 = "Tier " + (j + 1) + " ";
				BleedMin[j] = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "6. Bleeding", text2 + "minimum damage", array6[j], RI("Damage needed for this tier. Hits of the same kind within the hit window are added up. Below Tier 1 nothing happens.", 1, 200));
				BleedChance[j] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", text2 + "Bleeding chance", array7[j], R("Chance of Bleeding (0 to 1, e.g. 0.25 = 25%).", 0f, 1f));
				BleedTime[j] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", text2 + "Bleeding time", array8[j], R("Seconds of Bleeding.", 1f, 3600f));
				LacChance[j] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", text2 + "Laceration chance", array9[j], R("Chance of a Laceration instead (Mauling, Baboon Hawk) (0 to 1, e.g. 0.25 = 25%). Rolled first. Needs UsualScrap.", 0f, 1f));
				StabLacChance[j] = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", text2 + "Laceration chance (Stabbing)", array10[j], R("Chance of a Laceration from Stabbing / Scratching (0 to 1, e.g. 0.25 = 25%). Rolled first. Needs UsualScrap.", 0f, 1f));
			}
			BleedMin1 = BleedMin[0];
			BleedMin2 = BleedMin[1];
			BleedMin3 = BleedMin[2];
			BleedMin4 = BleedMin[3];
			BleedChance1 = BleedChance[0];
			BleedChance2 = BleedChance[1];
			BleedChance3 = BleedChance[2];
			BleedChance4 = BleedChance[3];
			BleedTime1 = BleedTime[0];
			BleedTime2 = BleedTime[1];
			BleedTime3 = BleedTime[2];
			BleedTime4 = BleedTime[3];
			LacChance1 = LacChance[0];
			LacChance2 = LacChance[1];
			LacChance3 = LacChance[2];
			LacChance4 = LacChance[3];
			StabLacChance1 = StabLacChance[0];
			StabLacChance2 = StabLacChance[1];
			StabLacChance3 = StabLacChance[2];
			StabLacChance4 = StabLacChance[3];
			HitWindow = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", "Hit window", 3f, R("Hits of the same kind within this many seconds are added up to pick the tier.", 0f, 10f));
			StabExtraChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", "Stabbing extra Bleeding chance", 0.1f, R("Stabbing / Scratching: added to the Bleeding chance of every tier (0 to 1, e.g. 0.25 = 25%).", 0f, 1f));
			StabTimeMultiplier = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", "Stabbing Bleeding time multiplier", 1.5f, R("Stabbing / Scratching: Bleeding lasts this many times longer.", 0.1f, 5f));
			LacerationOn = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "7. Laceration and Fatal Bleeding", "Enable Laceration and Fatal Bleeding", true, "Only works when UsualScrap is installed (its Bandages / Medical Kit are the only cure). Without it, those rolls give normal Bleeding.");
			LacerationTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Laceration time", 20f, R("Seconds before a Laceration turns into Fatal Bleeding. A Bandage or Medical Kit turns it back into Bleeding.", 1f, 600f));
			Forgiving = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "7. Laceration and Fatal Bleeding", "Forgiving Fatal Bleeding", false, "Off: Fatal Bleeding deals a random total (Min..Max damage) over a random time (Min..Max seconds), so it kills unless you heal or treat it. On: only 1 HP every few seconds (interval below) instead.");
			FatalDamageMin = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Fatal Bleeding damage Min", 150f, R("Smallest total damage.", 1f, 1000f));
			FatalDamageMax = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Fatal Bleeding damage Max", 200f, R("Largest total damage.", 1f, 1000f));
			FatalTimeMin = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Fatal Bleeding time Min", 60f, R("Shortest time in seconds.", 1f, 3600f));
			FatalTimeMax = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Fatal Bleeding time Max", 180f, R("Longest time in seconds.", 1f, 3600f));
			ForgivingInterval = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Forgiving interval", 3f, R("Forgiving Fatal Bleeding: seconds between each 1 HP of damage.", 0.5f, 30f));
			BandageSeconds = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Bandage seconds per use", 20f, R("Each Bandage use takes this many seconds off Bleeding / Fatal Bleeding (and turns a Laceration back into Bleeding).", 0f, 600f));
			MedkitSecondsPerSecond = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Medical Kit seconds per second", 20f, R("While the Medical Kit is actually healing you, every second of healing takes this many seconds off Bleeding / Fatal Bleeding.", 0f, 600f));
			MaxFatalTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "7. Laceration and Fatal Bleeding", "Max Fatal Bleeding time", 240f, R("Hits during Fatal Bleeding add time (and damage) to it, up to this many seconds left. 0 = no limit.", 0f, 3600f));
			MaxBleedingTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "6. Bleeding", "Max Bleeding time", 180f, R("Bleeding stacks its time up to this many seconds (also the Bleeding a Laceration turns back into). 0 = no limit.", 0f, 3600f));
			ParalyzeChance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Chance per bee hit", 0.1f, R("Chance a Circuit Bee hit gives (or raises) Paralyze (0 to 1, e.g. 0.25 = 25%).", 0f, 1f));
			ParalyzeRollInterval = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Roll interval", 1f, R("At most one roll per this many seconds (bees hit every 0.4 s).", 0f, 10f));
			ParalyzeTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Paralyze time", 60f, R("Seconds Paralyze lasts (also the most it can have).", 1f, 600f));
			ParalyzeExtraTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Extra time per tier", 10f, R("Another successful roll: +1 tier and this many seconds (up to Paralyze time).", 0f, 600f));
			ParalyzeMaxTier = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "8. Paralyze", "Max tier", 3, RI("Highest Paralyze tier. The tier never drops; Paralyze ends when its time runs out.", 1, 10));
			ParalysisChancePerTier = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Paralysis chance per tier", 0.05f, R("Every second while Paralyzed: this chance x tier to get Paralysis (tier 1/2/3 = 5/10/15%) (0 to 1, e.g. 0.25 = 25%).", 0f, 1f));
			ParalysisMin = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Paralysis Min", 1f, R("Paralysis (can't move) lasts a random time between Min and Max seconds.", 0.1f, 30f));
			ParalysisMax = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Paralysis Max", 2f, R("Paralysis (can't move) lasts a random time between Min and Max seconds.", 0.1f, 30f));
			ParalysisLookSpeed = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "8. Paralyze", "Paralysis camera speed", 0.35f, R("How fast the camera turns during Paralysis (1 = normal).", 0f, 1f));
			SteamInterval = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "9. Other", "Steam blindness roll interval", 1f, R("Seconds between blindness rolls while you stand near a burst steam valve (Frailty rolled every frame). No rolls while already blind.", 0.25f, 10f));
			PerksReduceLegDrain = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "9. Other", "AdvancedCompany perks reduce leg drain", true, "Needs AdvancedCompany. On: Sprint endurance / Bodybuilder perks reduce the extra stamina drain from leg injuries, like they reduce normal drain.");
			FatalFlashEvery = cfg.Bind<int>("1. Effects", "Fatal Bleeding red flash", 5, RI("Client side only. Your screen flashes red (like a small hit, no sound or flinch) every this many HP Fatal Bleeding takes. 0 = no flash.", 0, 50));
			LogRolls = cfg.Bind<bool>("9. Other", "Log injury rolls", false, "Client side only. Write every fall damage / bleeding roll to the BepInEx log (damage, tier, chance, result), to check the numbers while testing.");
			ConfigManager.Register<FrailtyConfig>((SyncedConfig2<FrailtyConfig>)this);
		}
	}
	[HarmonyPatch]
	public static class HealthSync
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnHealth;
		}

		private const string Msg = "Chaos.Frailty.Health";

		private static NetworkManager registeredOn;

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPostfix]
		private static void Register()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && !((Object)(object)registeredOn == (Object)(object)singleton))
			{
				registeredOn = singleton;
				CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
				object obj = <>O.<0>__OnHealth;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = OnHealth;
					<>O.<0>__OnHealth = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("Chaos.Frailty.Health", (HandleNamedMessageDelegate)obj);
			}
		}

		public unsafe static void Send(PlayerControllerB player)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || (Object)(object)player == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			try
			{
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1);
				try
				{
					int num = (int)player.playerClientId;
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref player.health, default(ForPrimitives));
					if (singleton.IsServer)
					{
						singleton.CustomMessagingManager.SendNamedMessageToAll("Chaos.Frailty.Health", val, (NetworkDelivery)2);
					}
					else
					{
						singleton.CustomMessagingManager.SendNamedMessage("Chaos.Frailty.Health", 0uL, val, (NetworkDelivery)2);
					}
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				Plugin.mls.LogWarning((object)("Health sync: " + ex.Message));
			}
		}

		private unsafe static void OnHealth(ulong sender, FastBufferReader r)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				int health = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref health, default(ForPrimitives));
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton.IsServer && sender != singleton.LocalClientId)
				{
					FastBufferWriter val = default(FastBufferWriter);
					((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1);
					try
					{
						((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref health, default(ForPrimitives));
						singleton.CustomMessagingManager.SendNamedMessageToAll("Chaos.Frailty.Health", val, (NetworkDelivery)2);
					}
					finally
					{
						((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
					}
				}
				PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null);
				if (array != null && num >= 0 && num < array.Length)
				{
					PlayerControllerB val2 = array[num];
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)GameNetworkManager.Instance.localPlayerController) && !val2.isPlayerDead)
					{
						val2.health = health;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.mls.LogWarning((object)("Health sync: " + ex.Message));
			}
		}
	}
	[BepInPlugin("Chaos.Frailty", "FrailtyPlus", "1.0.1")]
	[BepInDependency("com.sigurd.csync", "5.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "Chaos.Frailty";

		public const string NAME = "FrailtyPlus";

		public const string VERSION = "1.0.1";

		public static Plugin Instance;

		public static ManualLogSource mls;

		public static byte[] assetFile;

		private readonly Harmony harmony = new Harmony("Chaos.Frailty");

		private void Awake()
		{
			Instance = this;
			mls = ((BaseUnityPlugin)this).Logger;
			try
			{
				assetFile = ReadResource("Frailty.frailty.bundle");
				new FrailtyConfig(((BaseUnityPlugin)this).Config);
				Content.Load();
				harmony.PatchAll(typeof(Plugin).Assembly);
				Compat.Init(harmony);
				mls.LogInfo((object)"FrailtyPlus 1.0.1 loaded.");
			}
			catch (Exception ex)
			{
				mls.LogError((object)("FrailtyPlus failed to load: " + ex));
			}
		}

		internal static byte[] ReadResource(string name)
		{
			using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream(name);
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}
	}
	public class Content
	{
		public static AssetBundle MainContent;

		private static readonly Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>();

		public static void Load()
		{
			MainContent = AssetBundle.LoadFromMemory(Plugin.assetFile);
			if ((Object)(object)MainContent == (Object)null)
			{
				Plugin.mls.LogError((object)"Frailty's asset bundle could not be loaded.");
			}
		}

		public static Sprite Icon(string name)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (sprites.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = null;
			try
			{
				byte[] array = Plugin.ReadResource("Frailty.Icons." + name + ".png");
				if (array != null)
				{
					Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
					ImageConversion.LoadImage(val, array);
					((Texture)val).wrapMode = (TextureWrapMode)1;
					((Texture)val).filterMode = (FilterMode)1;
					value = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
					((Object)value).name = name;
				}
			}
			catch (Exception ex)
			{
				Plugin.mls.LogWarning((object)("Icon " + name + ": " + ex.Message));
			}
			sprites[name] = value;
			return value;
		}
	}
}
namespace Frailty.Patches
{
	[HarmonyPatch(typeof(HUDManager))]
	internal class HUDPatch
	{
		public static GameObject conditionHud;

		public static Transform bleedingHud;

		public static Transform blindnessHud;

		public static Transform brokenArmHud;

		public static Transform brokenLegHud;

		public static Transform concussedHud;

		public static Transform fracturedArmHud;

		public static Transform fracturedLegHud;

		public static Volume concussed;

		public static Volume blindness;

		public static Transform lacerationHud;

		public static Transform fatalBleedingHud;

		public static Transform paralyzeHud;

		public static Transform paralyze2Hud;

		public static Transform paralyze3Hud;

		public static Transform paralysisHud;

		private const float VolumePriority = 36f;

		private const float SlotScale = 0.75f;

		private static readonly Dictionary<Transform, TextMeshProUGUI> times = new Dictionary<Transform, TextMeshProUGUI>();

		[HarmonyPatch(typeof(HUDManager), "Awake")]
		[HarmonyPrefix]
		private static void Awake(HUDManager __instance)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Expected O, but got Unknown
			try
			{
				ClearTimes();
				conditionHud = Object.Instantiate<GameObject>(Content.MainContent.LoadAsset<GameObject>("Assets/custom/Frailty/hud/Conditions.prefab"), new Vector3(15.85f, 23.4f, -5.55f), Quaternion.Euler(0f, -14.141f, 0f), __instance.HUDContainer.transform);
				RectTransform component = conditionHud.GetComponent<RectTransform>();
				component.anchoredPosition3D = new Vector3(15.85f, 23.4f, -5.55f);
				((Transform)component).rotation = Quaternion.Euler(0f, -14.141f, 0f);
				((Transform)component).localScale = Vector3.one;
				bleedingHud = conditionHud.transform.Find("Bleeding");
				blindnessHud = conditionHud.transform.Find("Blindness");
				brokenArmHud = conditionHud.transform.Find("BrokenArm");
				brokenLegHud = conditionHud.transform.Find("BrokenLeg");
				concussedHud = conditionHud.transform.Find("Concussed");
				fracturedArmHud = conditionHud.transform.Find("FracturedArm");
				fracturedLegHud = conditionHud.transform.Find("FracturedLeg");
				lacerationHud = CloneSlot("Laceration");
				fatalBleedingHud = CloneSlot("FatalBleeding");
				paralyzeHud = CloneSlot("Paralyze");
				paralyze2Hud = CloneSlot("Paralyze2");
				paralyze3Hud = CloneSlot("Paralyze3");
				paralysisHud = CloneSlot("Paralysis");
			}
			catch (Exception ex)
			{
				Plugin.mls.LogError((object)("Condition icons: " + ex));
			}
			try
			{
				GameObject val = ((Component)__instance).gameObject;
				if (FrailtyConfig.Instance == null || FrailtyConfig.Instance.KeepHudClear.Value)
				{
					val = new GameObject("FrailtyPlus Effect Volumes")
					{
						layer = 0
					};
					val.transform.SetParent(((Component)__instance).transform, false);
					val.layer = 0;
				}
				concussed = MakeVolume(val, "Assets/custom/Frailty/volumes/concussed.asset");
				blindness = MakeVolume(val, "Assets/custom/Frailty/volumes/blind.asset");
			}
			catch (Exception ex2)
			{
				Plugin.mls.LogError((object)("Effect volumes: " + ex2));
			}
		}

		private static Volume MakeVolume(GameObject holder, string asset)
		{
			Volume val = holder.AddComponent<Volume>();
			val.isGlobal = true;
			val.weight = 0f;
			val.priority = 36f;
			val.sharedProfile = Content.MainContent.LoadAsset<VolumeProfile>(asset);
			return val;
		}

		private static Transform CloneSlot(string name)
		{
			if ((Object)(object)bleedingHud == (Object)null)
			{
				return null;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)bleedingHud).gameObject, conditionHud.transform);
			((Object)val).name = name;
			val.SetActive(false);
			Sprite val2 = Content.Icon(name);
			Image[] componentsInChildren = val.GetComponentsInChildren<Image>(true);
			foreach (Image val3 in componentsInChildren)
			{
				if (!((Object)(object)((Component)val3).gameObject == (Object)(object)val) && (Object)(object)val2 != (Object)null)
				{
					val3.sprite = val2;
					val3.preserveAspect = true;
				}
			}
			return val.transform;
		}

		private static TextMeshProUGUI TimeText(Transform slot)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if (times.TryGetValue(slot, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			HUDManager instance = HUDManager.Instance;
			GameObject val = new GameObject("Time", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(slot, false);
			RectTransform val2 = (RectTransform)val.transform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0.5f, 0f);
			((Transform)val2).localScale = Vector3.one / 0.75f;
			val2.sizeDelta = new Vector2(55f, 14f);
			val2.anchoredPosition = new Vector2(0f, 18.833334f);
			value = val.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)instance != (Object)null && instance.controlTipLines != null && instance.controlTipLines.Length != 0 && (Object)(object)instance.controlTipLines[0] != (Object)null)
			{
				((TMP_Text)value).font = ((TMP_Text)instance.controlTipLines[0]).font;
				((TMP_Text)value).fontSharedMaterial = ((TMP_Text)instance.controlTipLines[0]).fontSharedMaterial;
			}
			((TMP_Text)value).fontSize = 11f;
			((TMP_Text)value).alignment = (TextAlignmentOptions)1026;
			((TMP_Text)value).enableWordWrapping = false;
			((TMP_Text)value).overflowMode = (TextOverflowModes)0;
			((Graphic)value).raycastTarget = false;
			times[slot] = value;
			return value;
		}

		internal static string FormatTime(float s)
		{
			int num = Mathf.CeilToInt(Mathf.Max(0f, s));
			return (num >= 60) ? $"{num / 60}:{num % 60:00}" : (num + "s");
		}

		internal static void UpdateTimes(IEnumerable<Condition> conditions)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)conditionHud == (Object)null)
			{
				return;
			}
			foreach (Condition condition in conditions)
			{
				Transform val = ((condition.type == ConditionType.Paralyze) ? ParalyzeTier(condition.tier) : Get(condition.type));
				if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeSelf)
				{
					TextMeshProUGUI val2 = TimeText(val);
					if (!((Object)(object)val2 == (Object)null))
					{
						float timeLeft = condition.TimeLeft;
						((TMP_Text)val2).text = ((timeLeft > 0f && timeLeft < 36000f) ? FormatTime(timeLeft) : "");
						((Graphic)val2).color = new Color(1f, 1f, 1f, (timeLeft <= 5f) ? 0.55f : 1f);
					}
				}
			}
		}

		internal static void ClearTimes()
		{
			times.Clear();
		}

		internal static Transform ParalyzeTier(int tier)
		{
			return (tier >= 3) ? paralyze3Hud : ((tier == 2) ? paralyze2Hud : paralyzeHud);
		}

		internal static Transform Get(ConditionType type)
		{
			return (Transform)(type switch
			{
				ConditionType.BrokenLeg => brokenLegHud, 
				ConditionType.BrokenArm => brokenArmHud, 
				ConditionType.FracturedLeg => fracturedLegHud, 
				ConditionType.FracturedArm => fracturedArmHud, 
				ConditionType.Bleeding => bleedingHud, 
				ConditionType.Blindness => blindnessHud, 
				ConditionType.Concussed => concussedHud, 
				ConditionType.Laceration => lacerationHud, 
				ConditionType.FatalBleeding => fatalBleedingHud, 
				ConditionType.Paralyze => paralyzeHud, 
				ConditionType.Paralysis => paralysisHud, 
				_ => null, 
			});
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal class PlayerPatch
	{
		[HarmonyPatch("Awake")]
		[HarmonyPrefix]
		private static void Awake(PlayerControllerB __instance)
		{
			if (!Object.op_Implicit((Object)(object)((Component)__instance).gameObject.GetComponent<ConditionManager>()))
			{
				((Component)__instance).gameObject.AddComponent<ConditionManager>();
			}
		}

		[HarmonyPatch("DamagePlayer")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void DamagePrefix(PlayerControllerB __instance, out int __state)
		{
			__state = __instance.health;
		}

		[HarmonyPatch("DamagePlayer")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void DamagePostfix(PlayerControllerB __instance, int __state, CauseOfDeath causeOfDeath, bool fallDamage)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((NetworkBehaviour)__instance).IsOwner || __instance.isPlayerDead)
				{
					return;
				}
				int num = __state - __instance.health;
				if (num > 0)
				{
					bool fall = fallDamage || (int)causeOfDeath == 2;
					FrailtyApi.RaiseHurt(__instance, num, causeOfDeath, fall);
					ConditionManager component = ((Component)__instance).GetComponent<ConditionManager>();
					if ((Object)(object)component != (Object)null)
					{
						component.OnDamage(causeOfDeath, num, fall, BaboonPatch.Attacking);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.mls.LogError((object)("DamagePlayer: " + ex));
			}
		}

		private static float LookScale()
		{
			ConditionManager local = FrailtyApi.Local;
			if ((Object)(object)local == (Object)null || !local.Frozen || FrailtyConfig.Instance == null)
			{
				return 1f;
			}
			return FrailtyConfig.Instance.ParalysisLookSpeed.Value;
		}

		[HarmonyPatch("CalculateNormalLookingInput")]
		[HarmonyPrefix]
		private static void NormalLook(PlayerControllerB __instance, ref Vector2 inputVector)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (((NetworkBehaviour)__instance).IsOwner)
			{
				inputVector *= LookScale();
			}
		}

		[HarmonyPatch("CalculateSmoothLookingInput")]
		[HarmonyPrefix]
		private static void SmoothLook(PlayerControllerB __instance, ref Vector2 inputVector)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (((NetworkBehaviour)__instance).IsOwner)
			{
				inputVector *= LookScale();
			}
		}

		[HarmonyPatch("Jump_performed")]
		[HarmonyPrefix]
		private static bool Jump(PlayerControllerB __instance)
		{
			if (!((NetworkBehaviour)__instance).IsOwner)
			{
				return true;
			}
			ConditionManager component = ((Component)__instance).GetComponent<ConditionManager>();
			return (Object)(object)component == (Object)null || !component.Frozen;
		}
	}
	[HarmonyPatch(typeof(BaboonBirdAI), "OnCollideWithPlayer")]
	internal class BaboonPatch
	{
		internal static bool Attacking;

		[HarmonyPrefix]
		private static void Prefix()
		{
			Attacking = true;
		}

		[HarmonyFinalizer]
		private static void Finalizer()
		{
			Attacking = false;
		}
	}
	[HarmonyPatch(typeof(SteamValveHazard), "Update")]
	internal class SteamValveHazardPatch
	{
		private static readonly FieldRef<SteamValveHazard, bool> burst = AccessTools.FieldRefAccess<SteamValveHazard, bool>("valveHasBurst");

		private static readonly FieldRef<SteamValveHazard, bool> repaired = AccessTools.FieldRefAccess<SteamValveHazard, bool>("valveHasBeenRepaired");

		[HarmonyPostfix]
		private static void Update(SteamValveHazard __instance)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if (!((Object)(object)val == (Object)null) && burst.Invoke(__instance) && !repaired.Invoke(__instance) && !((Object)(object)__instance.valveAudio == (Object)null) && !(Vector3.Distance(((Component)val).transform.position, ((Component)__instance.valveAudio).transform.position) >= 9f))
			{
				ConditionManager component = ((Component)val).GetComponent<ConditionManager>();
				if ((Object)(object)component != (Object)null)
				{
					component.OnDamage(OtherDamageType.Gaz);
				}
			}
		}
	}
}
namespace Frailty.Manager
{
	public class Condition
	{
		public float startTime;

		public float duration;

		public ConditionType type;

		public int tier = 1;

		public float bleedTime;

		public float damageLeft;

		public float pending;

		public float TimeLeft => startTime + duration - Time.realtimeSinceStartup;
	}
	public class ConditionManager : MonoBehaviour
	{
		public enum StackMode
		{
			Refresh,
			Add
		}

		public List<Condition> conditions = new List<Condition>();

		private PlayerControllerB player;

		private float concussedIntensity;

		private float blindnessIntensity;

		private bool weSetBleeding;

		private bool weSetMove;

		private float nextParalysisRoll;

		private float nextParalyzeHitRoll;

		private int bleedSyncDamage;

		private int flashDamage;

		private float bleedSyncTime;

		private readonly List<KeyValuePair<float, int>> maulHits = new List<KeyValuePair<float, int>>();

		private readonly List<KeyValuePair<float, int>> stabHits = new List<KeyValuePair<float, int>>();

		private static readonly FieldRef<PlayerControllerB, float> timeSinceSwitchingSlots = AccessTools.FieldRefAccess<PlayerControllerB, float>("timeSinceSwitchingSlots");

		private static readonly FieldRef<PlayerControllerB, bool> isWalking = AccessTools.FieldRefAccess<PlayerControllerB, bool>("isWalking");

		private static readonly MethodInfo discardRoutine = AccessTools.Method(typeof(PlayerControllerB), "waitToEndOfFrameToDiscard", (Type[])null, (Type[])null);

		private float nextDrop;

		private float nextSteamRoll;

		private static readonly HashSet<string> logged = new HashSet<string>();

		private static FrailtyConfig Cfg => FrailtyConfig.Instance;

		internal bool IsLocal => (Object)(object)player != (Object)null && (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)player == (Object)(object)GameNetworkManager.Instance.localPlayerController;

		internal bool Frozen => weSetMove;

		internal static bool LacerationAvailable => FrailtyConfig.Instance != null && FrailtyConfig.Instance.LacerationOn.Value && Compat.UsualScrap;

		private void Start()
		{
			player = ((Component)this).GetComponent<PlayerControllerB>();
		}

		private void Update()
		{
			if ((Object)(object)player == (Object)null || !IsLocal)
			{
				return;
			}
			if (player.isPlayerDead)
			{
				if (conditions.Count > 0)
				{
					ClearAllConditions();
				}
				ResetEffects();
				ApplyVolumes();
				return;
			}
			if (!player.isPlayerControlled || Cfg == null)
			{
				ApplyVolumes();
				return;
			}
			foreach (Condition item in conditions.ToList())
			{
				if (item.TimeLeft <= 0f)
				{
					Expire(item);
				}
			}
			try
			{
				HUDPatch.UpdateTimes(conditions);
			}
			catch (Exception e)
			{
				LogOnce("times", e);
			}
			bool suppressed = FrailtyApi.Suppressed();
			try
			{
				ApplyEffects(suppressed);
			}
			catch (Exception e2)
			{
				LogOnce("effects", e2);
			}
			ApplyVolumes();
		}

		private void ApplyVolumes()
		{
			if ((Object)(object)HUDPatch.concussed != (Object)null)
			{
				HUDPatch.concussed.weight = Mathf.Lerp(HUDPatch.concussed.weight, concussedIntensity, 2f * Time.deltaTime);
			}
			if ((Object)(object)HUDPatch.blindness != (Object)null)
			{
				HUDPatch.blindness.weight = Mathf.Lerp(HUDPatch.blindness.weight, blindnessIntensity, 2f * Time.deltaTime);
			}
		}

		private void ApplyEffects(bool suppressed)
		{
			float deltaTime = Time.deltaTime;
			if ((HasCondition(ConditionType.Bleeding) || HasCondition(ConditionType.Laceration) || HasCondition(ConditionType.FatalBleeding)) && !suppressed)
			{
				player.healthRegenerateTimer = 1f;
				player.bleedingHeavily = true;
				weSetBleeding = true;
			}
			else if (weSetBleeding)
			{
				player.bleedingHeavily = player.criticallyInjured;
				weSetBleeding = false;
			}
			Condition condition = Get(ConditionType.FatalBleeding);
			if (condition != null)
			{
				TickFatal(condition, suppressed, deltaTime);
			}
			if (!suppressed)
			{
				if (HasCondition(ConditionType.BrokenLeg))
				{
					LegDrain(1.5f, deltaTime);
				}
				else if (HasCondition(ConditionType.FracturedLeg))
				{
					LegDrain(1.2f, deltaTime);
				}
			}
			if (!suppressed)
			{
				if (HasCondition(ConditionType.BrokenArm))
				{
					ArmDrop(15f);
				}
				else if (HasCondition(ConditionType.FracturedArm))
				{
					ArmDrop(25f);
				}
			}
			blindnessIntensity = ((!suppressed && HasCondition(ConditionType.Blindness)) ? 1f : 0f);
			concussedIntensity = ((!suppressed && HasCondition(ConditionType.Concussed)) ? 1f : 0f);
			Condition condition2 = Get(ConditionType.Paralyze);
			if (condition2 != null && !suppressed && !HasCondition(ConditionType.Paralysis) && Time.time >= nextParalysisRoll)
			{
				nextParalysisRoll = Time.time + 1f;
				float num = Mathf.Clamp01(Cfg.ParalysisChancePerTier.Value * (float)condition2.tier);
				if (Random.value < num)
				{
					float seconds = Random.Range(Cfg.ParalysisMin.Value, Mathf.Max(Cfg.ParalysisMin.Value, Cfg.ParalysisMax.Value));
					AddCondition(ConditionType.Paralysis, seconds, StackMode.Refresh, scale: false);
				}
			}
			if (!suppressed && HasCondition(ConditionType.Paralysis))
			{
				player.disableMoveInput = true;
				weSetMove = true;
			}
			else if (weSetMove)
			{
				player.disableMoveInput = false;
				weSetMove = false;
			}
		}

		private void ResetEffects()
		{
			blindnessIntensity = (concussedIntensity = 0f);
			if (weSetBleeding)
			{
				player.bleedingHeavily = player.criticallyInjured;
				weSetBleeding = false;
			}
			if (weSetMove)
			{
				player.disableMoveInput = false;
				weSetMove = false;
			}
		}

		private void LegDrain(float factor, float dt)
		{
			float num = 1f;
			if (player.drunkness > 0.02f)
			{
				num *= Mathf.Abs(StartOfRound.Instance.drunknessSpeedEffect.Evaluate(player.drunkness) - 1.25f);
			}
			float num2 = 0f;
			if (player.isSprinting)
			{
				num2 = (0f - dt) / (player.sprintTime / factor) * player.carryWeight * num;
			}
			else if (player.isMovementHindered > 0 && isWalking.Invoke(player))
			{
				num2 = (0f - dt) / (player.sprintTime / factor) * num * 0.5f;
			}
			if (!(num2 >= 0f))
			{
				float sprintMeter = player.sprintMeter;
				if (Cfg.PerksReduceLegDrain.Value)
				{
					num2 *= Compat.PerkDrainScale(player, num2);
				}
				player.sprintMeter = Mathf.Clamp01(sprintMeter + num2);
				Compat.ReportStaminaDelta(player.sprintMeter - sprintMeter);
			}
		}

		private void ArmDrop(float maxLb)
		{
			GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer;
			if ((Object)(object)currentlyHeldObjectServer == (Object)null || (Object)(object)currentlyHeldObjectServer.itemProperties == (Object)null)
			{
				return;
			}
			float num = Mathf.Clamp(currentlyHeldObjectServer.itemProperties.weight - 1f, 0f, 100f) * 105f;
			if (!(num <= maxLb) && !(timeSinceSwitchingSlots.Invoke(player) < 0.2f) && !player.isGrabbingObjectAnimation && !player.isTypingChat && !player.inSpecialInteractAnimation && !player.activatingItem && !(Time.time < nextDrop))
			{
				nextDrop = Time.time + 0.5f;
				if (discardRoutine != null && discardRoutine.Invoke(player, null) is IEnumerator enumerator)
				{
					((MonoBehaviour)player).StartCoroutine(enumerator);
				}
			}
		}

		private void TickFatal(Condition c, bool suppressed, float dt)
		{
			float num = Mathf.Max(0.01f, c.TimeLeft);
			float num2 = Mathf.Min(c.damageLeft, c.damageLeft / num * dt);
			c.damageLeft -= num2;
			if (!suppressed)
			{
				c.pending += num2;
				if (c.pending >= 1f)
				{
					int num3 = Mathf.FloorToInt(c.pending);
					c.pending -= num3;
					BleedDamage(num3);
				}
			}
		}

		private void BleedDamage(int n)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (n <= 0 || player.isPlayerDead)
			{
				return;
			}
			int num = player.health - n;
			if (num <= 0)
			{
				Plugin.mls.LogInfo((object)"Bled out.");
				player.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 0, default(Vector3), false);
				return;
			}
			int health = player.health;
			player.health = num;
			bool flag = false;
			int value = Cfg.FatalFlashEvery.Value;
			if (value > 0)
			{
				flashDamage += n;
				if (num < 20)
				{
					flag = health >= 20;
				}
				else if (flashDamage >= value)
				{
					flag = true;
				}
				if (flag)
				{
					flashDamage = 0;
				}
			}
			HUDManager.Instance.UpdateHealthUI(num, flag);
			HUDManager.Instance.SetCracksOnVisor((float)num);
			if (num < 10 && !player.criticallyInjured)
			{
				player.MakeCriticallyInjured(true);
			}
			bleedSyncDamage += n;
			if (bleedSyncDamage >= 5 || num < 20 || Time.time - bleedSyncTime > 5f)
			{
				HealthSync.Send(player);
				bleedSyncDamage = 0;
				bleedSyncTime = Time.time;
			}
		}

		private void StartFatal(float scale)
		{
			float num = Random.Range(Cfg.FatalTimeMin.Value, Mathf.Max(Cfg.FatalTimeMin.Value, Cfg.FatalTimeMax.Value));
			float num2 = (Cfg.Forgiving.Value ? (num / Mathf.Max(0.1f, Cfg.ForgivingInterval.Value)) : Random.Range(Cfg.FatalDamageMin.Value, Mathf.Max(Cfg.FatalDamageMin.Value, Cfg.FatalDamageMax.Value)));
			num *= scale;
			num2 *= scale;
			Condition condition = AddCondition(ConditionType.FatalBleeding, num, StackMode.Refresh, scale: false);
			if (condition != null)
			{
				condition.damageLeft = num2;
				condition.pending = 0f;
			}
		}

		internal void Shorten(Condition c, float seconds)
		{
			if (c != null && !(seconds <= 0f))
			{
				float timeLeft = c.TimeLeft;
				if (c.type == ConditionType.FatalBleeding && timeLeft > 0f)
				{
					c.damageLeft *= Mathf.Clamp01((timeLeft - seconds) / timeLeft);
				}
				c.duration -= seconds;
				if (c.TimeLeft <= 0f)
				{
					RemoveCondition(c.type);
				}
			}
		}

		internal void Treat(float seconds)
		{
			Condition condition = Get(ConditionType.Laceration);
			if (condition != null)
			{
				float num = Mathf.Max(1f, condition.bleedTime);
				RemoveCondition(ConditionType.Laceration);
				Condition condition2 = Get(ConditionType.Bleeding);
				float num2 = ((condition2 != null) ? Mathf.Max(0f, condition2.TimeLeft) : 0f);
				AddCondition(ConditionType.Bleeding, Mathf.Max(0.1f, CapBleed(num2 + num) - num2), StackMode.Add, scale: false);
				Plugin.mls.LogDebug((object)("Laceration treated: back to Bleeding (" + num.ToString("0") + " s)."));
			}
			else
			{
				Shorten(Get(ConditionType.FatalBleeding), seconds);
				Shorten(Get(ConditionType.Bleeding), seconds);
			}
		}

		public void OnDamage(CauseOfDeath damageType, int damage)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			OnDamage(damageType, damage, fall: false, baboonHawk: false);
		}

		internal void OnDamage(CauseOfDeath cause, int damage, bool fall, bool baboonHawk)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected I4, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Invalid comparison between Unknown and I4
			if (!IsLocal || Cfg == null || damage <= 0 || player.isPlayerDead)
			{
				return;
			}
			if (baboonHawk)
			{
				RollBleedTier(maulHits, damage, stabbing: false);
				return;
			}
			switch (cause - 1)
			{
			case 1:
				RollLegs(damage);
				break;
			case 0:
			case 2:
			case 7:
			case 11:
			case 15:
				if (Cfg.Concussed.Value && Roll(Cfg.ConcussedChance.Value, damage))
				{
					AddCondition(ConditionType.Concussed, Cfg.ConcussedTime.Value);
				}
				RollArm(damage);
				break;
			case 5:
				RollBleedTier(maulHits, damage, stabbing: false);
				break;
			case 13:
			case 17:
				RollBleedTier(stabHits, damage, stabbing: true);
				break;
			case 6:
			case 14:
			case 16:
				if (Cfg.Bleeding.Value && ((int)cause == 7 || Roll(Cfg.BleedingChance.Value, damage)))
				{
					GainBleeding(Cfg.BleedingTime.Value);
				}
				break;
			case 3:
				if (Cfg.Concussed.Value && Random.value < Cfg.CoilHeadConcussChance.Value)
				{
					float seconds = Random.Range(Cfg.CoilHeadConcussMin.Value, Mathf.Max(Cfg.CoilHeadConcussMin.Value, Cfg.CoilHeadConcussMax.Value));
					AddCondition(ConditionType.Concussed, seconds);
				}
				break;
			case 10:
				RollParalyze();
				break;
			case 4:
			case 8:
			case 9:
			case 12:
				break;
			}
		}

		public void OnDamage(OtherDamageType damageType)
		{
			if (IsLocal && Cfg != null && damageType == OtherDamageType.Gaz && Cfg.Blindness.Value && !HasCondition(ConditionType.Blindness) && !(Time.time < nextSteamRoll))
			{
				nextSteamRoll = Time.time + Cfg.SteamInterval.Value;
				if (Roll(Cfg.BlindnessChance.Value, 0))
				{
					AddCondition(ConditionType.Blindness, Cfg.BlindnessTime.Value);
				}
			}
		}

		public void OnDeath()
		{
			if (IsLocal)
			{
				ClearAllConditions();
			}
		}

		private bool Roll(float chance, int damage)
		{
			chance = Mathf.Clamp01(chance);
			if (Cfg.ScaleWithDamage.Value && damage > 0)
			{
				float num = Mathf.Max(1, player.health + damage);
				float num2 = Mathf.Clamp01((1f + (1f - Mathf.Min(num, 100f) / 100f)) / 2f + (float)damage / num / 2f);
				chance *= num2;
			}
			return Random.value < chance;
		}

		private void RollArm(int damage)
		{
			bool flag = HasCondition(ConditionType.FracturedArm);
			bool flag2 = HasCondition(ConditionType.BrokenArm);
			if (!Roll(flag ? Cfg.BrokenArmChance.Value : Cfg.FracturedArmChance.Value, damage))
			{
				return;
			}
			if (!flag && !flag2)
			{
				if (Cfg.FracturedArm.Value)
				{
					AddCondition(ConditionType.FracturedArm, Cfg.FracturedArmTime.Value);
				}
			}
			else if (Cfg.BrokenArm.Value)
			{
				RemoveCondition(ConditionType.FracturedArm);
				AddCondition(ConditionType.BrokenArm, Cfg.BrokenArmTime.Value);
			}
		}

		private void RollLegs(int damage)
		{
			int num = -1;
			for (int i = 0; i < 4; i++)
			{
				if (damage >= Cfg.FallMin[i].Value)
				{
					num = i;
				}
			}
			if (num < 0)
			{
				return;
			}
			float num2 = Cfg.FractureToBrokenPercent.Value / 100f;
			bool flag = Cfg.BrokenLeg.Value && Random.value < Cfg.FallBrokenChance[num].Value;
			bool flag2 = !flag && Cfg.FracturedLeg.Value && Random.value < Cfg.FallFracChance[num].Value;
			LogRoll(string.Format("Fall damage {0} (tier {1}): Broken {2} -> {3}, Fractured {4} -> {5}", damage, num + 1, Pct(Cfg.FallBrokenChance[num].Value), flag ? "YES" : "no", Pct(Cfg.FallFracChance[num].Value), flag2 ? "YES" : (flag ? "-" : "no")));
			if (flag || flag2)
			{
				float num3 = FrailtyApi.TimeScale();
				Condition condition = Get(ConditionType.FracturedLeg);
				Condition condition2 = Get(ConditionType.BrokenLeg);
				float num4 = (flag ? Cfg.FallBrokenTime[num].Value : Cfg.FallFracTime[num].Value) * num3;
				float num5;
				if (flag)
				{
					num5 = ((condition2 != null) ? (condition2.TimeLeft + num4) : ((condition == null) ? num4 : (num4 + Mathf.Max(0f, condition.TimeLeft) * num2)));
				}
				else if (condition2 != null)
				{
					num5 = condition2.TimeLeft + num4 * num2;
				}
				else if (condition == null || !Cfg.BrokenLeg.Value)
				{
					num5 = ((condition == null) ? num4 : (condition.TimeLeft + num4));
				}
				else
				{
					num5 = (Mathf.Max(0f, condition.TimeLeft) + num4) * num2;
					flag = true;
				}
				if (Cfg.MaxLegTime.Value > 0f)
				{
					num5 = Mathf.Min(num5, Cfg.MaxLegTime.Value);
				}
				num5 = Mathf.Max(1f, num5);
				if (flag || condition2 != null)
				{
					RemoveCondition(ConditionType.FracturedLeg);
					SetCondition(ConditionType.BrokenLeg, num5);
					LogRoll(string.Format("  -> Broken Leg, {0:0} s (had Fractured: {1}, had Broken: {2})", num5, (condition != null) ? (condition.TimeLeft.ToString("0") + " s") : "no", (condition2 != null) ? (condition2.TimeLeft.ToString("0") + " s") : "no"));
				}
				else
				{
					SetCondition(ConditionType.FracturedLeg, num5);
					LogRoll($"  -> Fractured Leg, {num5:0} s");
				}
			}
		}

		private void RollBleedTier(List<KeyValuePair<float, int>> hits, int damage, bool stabbing)
		{
			if (!Cfg.Bleeding.Value)
			{
				return;
			}
			float now = Time.time;
			hits.Add(new KeyValuePair<float, int>(now, damage));
			hits.RemoveAll((KeyValuePair<float, int> h) => now - h.Key > Cfg.HitWindow.Value);
			int num = hits.Sum((KeyValuePair<float, int> h) => h.Value);
			int num2 = -1;
			for (int num3 = 0; num3 < 4; num3++)
			{
				if (num >= Cfg.BleedMin[num3].Value)
				{
					num2 = num3;
				}
			}
			if (num2 >= 0)
			{
				float num4 = Cfg.BleedTime[num2].Value * (stabbing ? Cfg.StabTimeMultiplier.Value : 1f);
				float num5 = (stabbing ? Cfg.StabLacChance[num2].Value : Cfg.LacChance[num2].Value);
				float num6 = Mathf.Clamp01(Cfg.BleedChance[num2].Value + (stabbing ? Cfg.StabExtraChance.Value : 0f));
				bool flag = LacerationAvailable && num5 > 0f && Random.value < num5;
				bool flag2 = !flag && Random.value < num6;
				LogRoll(string.Format("{0} {1} (total {2} in {3:0} s, tier {4}): Laceration {5} -> {6}, Bleeding {7} -> {8}", stabbing ? "Stabbing" : "Mauling", damage, num, Cfg.HitWindow.Value, num2 + 1, Pct(LacerationAvailable ? num5 : 0f), flag ? "YES" : "no", Pct(num6), flag2 ? "YES" : (flag ? "-" : "no")));
				if (flag)
				{
					GainLaceration(num4);
				}
				else if (flag2)
				{
					GainBleeding(num4);
				}
			}
		}

		private void GainBleeding(float seconds)
		{
			float num = seconds * FrailtyApi.TimeScale();
			Condition condition = Get(ConditionType.FatalBleeding);
			if (condition != null)
			{
				ExtendFatal(condition, num);
				return;
			}
			Condition condition2 = Get(ConditionType.Laceration);
			if (condition2 != null)
			{
				condition2.bleedTime = CapBleed(condition2.bleedTime + num);
				return;
			}
			Condition condition3 = Get(ConditionType.Bleeding);
			float num2 = ((condition3 != null) ? Mathf.Max(0f, condition3.TimeLeft) : 0f);
			float num3 = CapBleed(num2 + num) - num2;
			if (num3 > 0f)
			{
				AddCondition(ConditionType.Bleeding, num3, StackMode.Add, scale: false);
			}
		}

		private static float CapBleed(float seconds)
		{
			float value = Cfg.MaxBleedingTime.Value;
			return (value > 0f) ? Mathf.Min(seconds, value) : seconds;
		}

		private void GainLaceration(float bleedSeconds)
		{
			float num = FrailtyApi.TimeScale();
			float num2 = bleedSeconds * num;
			Condition condition = Get(ConditionType.FatalBleeding);
			if (condition != null)
			{
				ExtendFatal(condition, num2);
				return;
			}
			Condition condition2 = Get(ConditionType.Laceration);
			if (condition2 != null)
			{
				condition2.bleedTime = CapBleed(condition2.bleedTime + num2);
				return;
			}
			Condition condition3 = Get(ConditionType.Bleeding);
			float num3 = ((condition3 != null) ? Mathf.Max(0f, condition3.TimeLeft) : 0f);
			if (condition3 != null)
			{
				RemoveCondition(ConditionType.Bleeding);
			}
			Condition condition4 = AddCondition(ConditionType.Laceration, Cfg.LacerationTime.Value * num, StackMode.Refresh, scale: false);
			if (condition4 != null)
			{
				condition4.bleedTime = CapBleed(num2 + num3);
			}
		}

		private void ExtendFatal(Condition fatal, float seconds)
		{
			float num = Mathf.Max(0.01f, fatal.TimeLeft);
			float value = Cfg.MaxFatalTime.Value;
			if (value > 0f)
			{
				seconds = Mathf.Min(seconds, Mathf.Max(0f, value - num));
			}
			if (!(seconds <= 0f))
			{
				fatal.damageLeft += fatal.damageLeft / num * seconds;
				fatal.duration += seconds;
				FrailtyApi.RaiseGained(ConditionType.FatalBleeding, seconds);
			}
		}

		private void RollParalyze()
		{
			if (!Cfg.ParalyzeOn.Value || Time.time < nextParalyzeHitRoll)
			{
				return;
			}
			nextParalyzeHitRoll = Time.time + Cfg.ParalyzeRollInterval.Value;
			if (Random.value >= Cfg.ParalyzeChance.Value)
			{
				return;
			}
			float num = FrailtyApi.TimeScale();
			float num2 = Cfg.ParalyzeTime.Value * num;
			Condition condition = Get(ConditionType.Paralyze);
			if (condition == null)
			{
				Condition condition2 = AddCondition(ConditionType.Paralyze, num2, StackMode.Refresh, scale: false);
				if (condition2 != null)
				{
					condition2.tier = 1;
				}
			}
			else
			{
				condition.tier = Mathf.Min(condition.tier + 1, Mathf.Max(1, Cfg.ParalyzeMaxTier.Value));
				float num3 = Mathf.Min(num2, condition.TimeLeft + Cfg.ParalyzeExtraTime.Value * num);
				condition.startTime = Time.realtimeSinceStartup;
				condition.duration = num3;
				FrailtyApi.RaiseGained(ConditionType.Paralyze, num3);
				ShowConditionHUD(ConditionType.Paralyze);
			}
		}

		private void Expire(Condition c)
		{
			if (c.type == ConditionType.Laceration)
			{
				RemoveCondition(ConditionType.Laceration);
				if (LacerationAvailable)
				{
					StartFatal(FrailtyApi.TimeScale());
				}
				else
				{
					AddCondition(ConditionType.Bleeding, Mathf.Max(1f, c.bleedTime), StackMode.Add, scale: false);
				}
			}
			else
			{
				RemoveCondition(c.type);
			}
		}

		public Condition Get(ConditionType type)
		{
			for (int i = 0; i < conditions.Count; i++)
			{
				if (conditions[i].type == type)
				{
					return conditions[i];
				}
			}
			return null;
		}

		public bool HasCondition(ConditionType type)
		{
			return Get(type) != null;
		}

		public Condition AddCondition(ConditionType type, float seconds, StackMode mode = StackMode.Refresh, bool scale = true)
		{
			if ((Object)(object)player == (Object)null || player.isPlayerDead || seconds <= 0f)
			{
				return null;
			}
			if (scale)
			{
				seconds *= FrailtyApi.TimeScale();
			}
			if (seconds <= 0f)
			{
				return null;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Condition condition = Get(type);
			if (condition == null)
			{
				condition = new Condition
				{
					type = type,
					startTime = realtimeSinceStartup,
					duration = seconds
				};
				conditions.Add(condition);
			}
			else
			{
				float num = Mathf.Max(0f, condition.TimeLeft);
				condition.startTime = realtimeSinceStartup;
				condition.duration = ((mode == StackMode.Add) ? (num + seconds) : Mathf.Max(num, seconds));
			}
			ShowConditionHUD(type);
			FrailtyApi.RaiseGained(type, seconds);
			Plugin.mls.LogDebug((object)$"Player '{player.playerUsername}' got {type} ({condition.duration:0.0} s).");
			return condition;
		}

		private void SetCondition(ConditionType type, float seconds)
		{
			Condition condition = Get(type);
			if (condition == null)
			{
				condition = new Condition
				{
					type = type
				};
				conditions.Add(condition);
			}
			condition.startTime = Time.realtimeSinceStartup;
			condition.duration = seconds;
			ShowConditionHUD(type);
			FrailtyApi.RaiseGained(type, seconds);
		}

		public void RemoveCondition(ConditionType type, bool cured = false)
		{
			Condition condition = Get(type);
			if (condition == null)
			{
				return;
			}
			conditions.Remove(condition);
			if (IsLocal)
			{
				HideConditionHUD(type);
				if (type == ConditionType.Blindness)
				{
					blindnessIntensity = 0f;
				}
				if (type == ConditionType.Concussed)
				{
					concussedIntensity = 0f;
				}
			}
			Plugin.mls.LogDebug((object)string.Format("Player '{0}' lost {1}{2}.", player?.playerUsername, type, cured ? " (cured)" : ""));
		}

		public void ClearAllConditions()
		{
			foreach (Condition item in conditions.ToList())
			{
				RemoveCondition(item.type);
			}
			maulHits.Clear();
			stabHits.Clear();
		}

		public void AddConditionServerRpc(ConditionType type, float duration)
		{
			AddCondition(type, duration, StackMode.Refresh, scale: false);
		}

		public void AddConditionClientRpc(ConditionType type, float duration)
		{
			AddCondition(type, duration, StackMode.Refresh, scale: false);
		}

		public void RemoveConditionServerRpc(ConditionType type)
		{
			RemoveCondition(type);
		}

		public void RemoveConditionClientRpc(ConditionType type)
		{
			RemoveCondition(type);
		}

		private void ShowConditionHUD(ConditionType type)
		{
			if (!IsLocal)
			{
				return;
			}
			Transform val = HUDPatch.Get(type);
			if (type == ConditionType.Paralyze)
			{
				int tier = Get(ConditionType.Paralyze)?.tier ?? 1;
				val = HUDPatch.ParalyzeTier(tier);
				for (int i = 1; i <= 3; i++)
				{
					Transform val2 = HUDPatch.ParalyzeTier(i);
					if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)val && ((Component)val2).gameObject.activeSelf)
					{
						HideSlot(val2);
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				val.SetAsFirstSibling();
				((Component)val).gameObject.SetActive(true);
				Animator component = ((Component)val).GetComponent<Animator>();
				if ((Object)(object)component != (Object)null)
				{
					component.SetBool("Visible", true);
				}
			}
			catch
			{
			}
		}

		private void HideConditionHUD(ConditionType type)
		{
			if (type == ConditionType.Paralyze)
			{
				for (int i = 1; i <= 3; i++)
				{
					HideSlot(HUDPatch.ParalyzeTier(i));
				}
			}
			else
			{
				HideSlot(HUDPatch.Get(type));
			}
		}

		private void HideSlot(Transform hud)
		{
			if ((Object)(object)hud == (Object)null)
			{
				return;
			}
			try
			{
				Animator component = ((Component)hud).GetComponent<Animator>();
				if ((Object)(object)component == (Object)null || !((Component)hud).gameObject.activeInHierarchy)
				{
					((Component)hud).gameObject.SetActive(false);
					return;
				}
				component.SetBool("Visible", false);
				((MonoBehaviour)this).StartCoroutine(WaitFade(component));
			}
			catch
			{
			}
		}

		public IEnumerator WaitFade(Animator anim)
		{
			yield return (object)new WaitUntil((Func<bool>)delegate
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				int result;
				if (!((Object)(object)anim == (Object)null) && ((Component)anim).gameObject.activeInHierarchy)
				{
					AnimatorStateInfo currentAnimatorStateInfo = anim.GetCurrentAnimatorStateInfo(1);
					result = ((((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime >= 1f) ? 1 : 0);
				}
				else
				{
					result = 1;
				}
				return (byte)result != 0;
			});
			if ((Object)(object)anim != (Object)null && !anim.GetBool("Visible"))
			{
				((Component)anim).gameObject.SetActive(false);
			}
		}

		private static string Pct(float v)
		{
			return Mathf.RoundToInt(v * 100f) + "%";
		}

		private static void LogRoll(string text)
		{
			if (Cfg != null && Cfg.LogRolls.Value)
			{
				Plugin.mls.LogInfo((object)text);
			}
		}

		private static void LogOnce(string key, Exception e)
		{
			if (logged.Add(key))
			{
				Plugin.mls.LogError((object)("FrailtyPlus " + key + ": " + e));
			}
		}
	}
	public enum ConditionType
	{
		BrokenLeg,
		BrokenArm,
		FracturedLeg,
		FracturedArm,
		Bleeding,
		Blindness,
		Concussed,
		Laceration,
		FatalBleeding,
		Paralyze,
		Paralysis
	}
	public enum OtherDamageType
	{
		Gaz
	}
}