Decompiled source of DeadPiece v0.7.9

Mods/Deadpiece.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using AudioImportLib;
using BoneLib;
using BoneLib.BoneMenu;
using DeadPiece;
using DeadPiece.Networking;
using DeadPiece.Networking.Messages;
using HarmonyLib;
using Il2CppCysharp.Threading.Tasks;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Data;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.AI;
using Il2CppSLZ.Marrow.Combat;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.PuppetMasta;
using Il2CppSLZ.Marrow.VoidLogic;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSLZ.VRMK;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using LabFusion.Entities;
using LabFusion.Marrow.Extenders;
using LabFusion.Network;
using LabFusion.Network.Serialization;
using LabFusion.Player;
using LabFusion.Representation;
using LabFusion.SDK.Modules;
using LabFusion.UI;
using LabFusion.Utilities;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
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(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: MelonColor(255, 139, 0, 0)]
[assembly: MelonInfo(typeof(DeadPieceMod), "DeadPiece", "0.7.6", "VoidIndustries", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonPlatform(/*Could not decode attribute arguments.*/)]
[assembly: AssemblyVersion("0.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 DeadPiece
{
	public static class AvatarHealthResolver
	{
		public const float DefaultUnsetHealth = 100f;

		public static float Resolve(RigManager rm, float fallbackHp)
		{
			if ((Object)(object)rm == (Object)null)
			{
				return Mathf.Max(0.0001f, fallbackHp);
			}
			float num = Mathf.Max(0.0001f, fallbackHp);
			float num2 = ReadMarrowHealth(rm.health);
			float num3 = ReadVitalityHealth(rm.avatar);
			float num4 = ReadCurrentHealth(rm.health);
			if (num4 > num2 && num4 > num3)
			{
				return ClampHealth(num4, num);
			}
			if (num2 > 0f)
			{
				if (num3 > 0f && (num3 > num2 || LooksLikeStaleDefaultHealth(num2, num3)))
				{
					return ClampHealth(num3, num);
				}
				return ClampHealth(num2, num);
			}
			if (num3 > 0f)
			{
				return ClampHealth(num3, num);
			}
			return num;
		}

		public static float ReadVitalityHealth(Avatar avatar)
		{
			if ((Object)(object)avatar == (Object)null)
			{
				return -1f;
			}
			float vitality = avatar.vitality;
			if (vitality <= 0f || float.IsNaN(vitality) || float.IsInfinity(vitality))
			{
				return -1f;
			}
			return vitality;
		}

		private static float ReadMarrowHealth(Health health)
		{
			if ((Object)(object)health == (Object)null)
			{
				return -1f;
			}
			float max_Health = health.max_Health;
			if (max_Health <= 0f || float.IsNaN(max_Health) || float.IsInfinity(max_Health))
			{
				return -1f;
			}
			return max_Health;
		}

		private static float ReadCurrentHealth(Health health)
		{
			if ((Object)(object)health == (Object)null)
			{
				return -1f;
			}
			float curr_Health = health.curr_Health;
			if (curr_Health <= 0f || float.IsNaN(curr_Health) || float.IsInfinity(curr_Health))
			{
				return -1f;
			}
			return curr_Health;
		}

		private static bool LooksLikeStaleDefaultHealth(float marrowHp, float vitalityHp)
		{
			if (Mathf.Abs(marrowHp - 100f) > 0.5f)
			{
				return false;
			}
			return Mathf.Abs(vitalityHp - marrowHp) > 0.0001f;
		}

		private static float ClampHealth(float hp, float fallback)
		{
			if (hp <= 0f || float.IsNaN(hp) || float.IsInfinity(hp))
			{
				return fallback;
			}
			return Mathf.Max(0.0001f, hp);
		}
	}
	[HarmonyPatch(typeof(RemapRig), "CrouchHold")]
	internal static class CrouchHoldPrefixPatch
	{
		private static void Prefix(RemapRig __instance, ref bool crouchInput)
		{
			RigManager rigManager = Player.RigManager;
			if (!((Object)(object)rigManager == (Object)null) && DeadPieceMod.Instance != null && !((Object)(object)rigManager.remapHeptaRig != (Object)(object)__instance) && DeadPieceMod.Instance.ShouldApplyLocalNoLegEffects(rigManager) && !DeadPieceMod.Instance.IsAutoInjectorUseActiveOrRecent() && !DeadPieceMod.Instance.IsHoldingNimbusGun(rigManager))
			{
				crouchInput = true;
			}
		}
	}
	[HarmonyPatch(typeof(PlayerDamageReceiver), "ReceiveAttack")]
	[HarmonyPriority(800)]
	public static class DamageInterceptor
	{
		internal static void Prefix(PlayerDamageReceiver __instance, ref Attack attack, out bool __state)
		{
			__state = false;
			try
			{
				__state = DeadPieceMod.Instance != null && DeadPieceMod.Instance.TryConsumeNonFatalAttack(__instance, attack);
				if (__state)
				{
					attack.damage = 0.1f;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[DeadPiece] Damage prefix failed: " + ex);
			}
		}

		internal static void Postfix(PlayerDamageReceiver __instance, Attack attack, bool __state)
		{
			if (__state)
			{
				return;
			}
			try
			{
				Health health = __instance.health;
				RigManager val = ((health != null) ? health._rigManager : null);
				bool flag = (Object)(object)val != (Object)null && DeadPieceMod.Instance != null && DeadPieceMod.Instance.IsBothLegsGone(val);
				DeadPieceMod.Instance?.OnAttackReceived(__instance, attack);
				bool flag2 = (Object)(object)val != (Object)null && DeadPieceMod.Instance != null && DeadPieceMod.Instance.IsBothLegsGone(val);
				if (!flag && flag2)
				{
					DeadPieceMod.Instance?.SyncLegState(val, 3);
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[DeadPiece] Damage postfix failed: " + ex);
			}
		}
	}
	[RegisterTypeInIl2Cpp]
	public sealed class StumpDamageVolume : MonoBehaviour
	{
		private float _damagePerSecond;

		private float _lifetime;

		private float _spawnTime;

		private float _nextTickTime;

		private float _lastErrorTime = -999f;

		private bool _initialized;

		public StumpDamageVolume(IntPtr pointer)
			: base(pointer)
		{
		}

		public void Initialize(float damagePerSecond, float lifetime)
		{
			_damagePerSecond = damagePerSecond;
			_lifetime = lifetime;
			_spawnTime = Time.time;
			_nextTickTime = 0f;
			_initialized = true;
		}

		private void Update()
		{
			if (_initialized && Time.time - _spawnTime >= _lifetime)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private void OnTriggerStay(Collider other)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_00c6: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized || (Object)(object)other == (Object)null || _damagePerSecond == 0f || Time.time < _nextTickTime)
			{
				return;
			}
			_nextTickTime = Time.time + 0.25f;
			try
			{
				PlayerDamageReceiver componentInParent = ((Component)other).GetComponentInParent<PlayerDamageReceiver>();
				if ((Object)(object)componentInParent == (Object)null || (Object)(object)componentInParent.health == (Object)null || !componentInParent.health.alive)
				{
					return;
				}
				Vector3 val = ((Component)other).transform.position - ((Component)this).transform.position;
				if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
				{
					((Vector3)(ref val)).Normalize();
				}
				Attack val2 = new Attack();
				val2.damage = _damagePerSecond * 0.25f;
				val2.origin = ((Component)this).transform.position;
				val2.direction = val;
				val2.normal = -val;
				val2.collider = other;
				val2.attackType = (AttackType)64;
				DamageVolumeContextPatch.EnterFallbackBody();
				try
				{
					componentInParent.ReceiveAttack(val2);
				}
				finally
				{
					DamageVolumeContextPatch.ExitFallbackBody();
				}
			}
			catch (Exception ex)
			{
				if (Time.time - _lastErrorTime > 1f)
				{
					_lastErrorTime = Time.time;
					MelonLogger.Warning("[DeadPiece] Stump damage volume failed: " + ex.Message);
				}
			}
		}
	}
	[HarmonyPatch(typeof(DamageVolume), "ProcessDamage")]
	internal static class DamageVolumeContextPatch
	{
		private sealed class Contact
		{
			public DamageVolume Volume;

			public RigManager Rig;

			public Rigidbody Rigidbody;

			public Collider Collider;

			public float Damage;

			public bool AttackHandled;

			public bool ApplyLimb;
		}

		[ThreadStatic]
		private static int _depth;

		[ThreadStatic]
		private static int _fallbackBodyDepth;

		[ThreadStatic]
		private static bool _nativeProcess;

		[ThreadStatic]
		private static bool _processAttackHandled;

		[ThreadStatic]
		private static Collider _processCollider;

		[ThreadStatic]
		private static DamageVolume _updateVolume;

		[ThreadStatic]
		private static List<Contact> _contacts;

		[ThreadStatic]
		private static float _updateLastTickTime;

		private static readonly Dictionary<DamageVolume, HashSet<Collider>> TrackedColliders = new Dictionary<DamageVolume, HashSet<Collider>>();

		internal static bool IsActive => _depth > 0;

		internal static bool ShouldDeferLimbDamage => _nativeProcess || _fallbackBodyDepth > 0;

		internal static void Enter()
		{
			_depth++;
		}

		internal static void Exit()
		{
			if (_depth > 0)
			{
				_depth--;
			}
		}

		internal static void EnterFallbackBody()
		{
			Enter();
			_fallbackBodyDepth++;
		}

		internal static void ExitFallbackBody()
		{
			if (_fallbackBodyDepth > 0)
			{
				_fallbackBodyDepth--;
			}
			Exit();
		}

		internal static void MarkAttackHandled(RigManager rigManager)
		{
			if (_nativeProcess && (Object)(object)rigManager != (Object)null)
			{
				_processAttackHandled = true;
			}
		}

		internal static void BeginUpdate(DamageVolume volume)
		{
			_updateVolume = volume;
			_contacts = new List<Contact>();
			_updateLastTickTime = (((Object)(object)volume != (Object)null) ? volume._lastTickTime : 0f);
		}

		internal static void EndUpdate(DamageVolume volume)
		{
			if (!((Object)(object)_updateVolume != (Object)(object)volume))
			{
				List<Contact> contacts = _contacts;
				_contacts = null;
				_updateVolume = null;
				if (contacts.Count > 0 || volume._lastTickTime != _updateLastTickTime)
				{
					AppendTrackedContacts(volume, contacts);
				}
				FlushContacts(contacts);
			}
		}

		internal static void TrackCollider(DamageVolume volume, Collider collider, bool entered)
		{
			if ((Object)(object)volume == (Object)null || (Object)(object)collider == (Object)null)
			{
				return;
			}
			HashSet<Collider> value2;
			if (entered)
			{
				if (!collider.isTrigger)
				{
					if (!TrackedColliders.TryGetValue(volume, out var value))
					{
						value = new HashSet<Collider>();
						TrackedColliders[volume] = value;
					}
					value.Add(collider);
				}
			}
			else if (TrackedColliders.TryGetValue(volume, out value2))
			{
				value2.Remove(collider);
			}
		}

		internal static void ForgetVolume(DamageVolume volume)
		{
			if ((Object)(object)volume != (Object)null)
			{
				TrackedColliders.Remove(volume);
			}
		}

		internal static void ResetTracking()
		{
			TrackedColliders.Clear();
			_contacts = null;
			_updateVolume = null;
			_nativeProcess = false;
			_processAttackHandled = false;
			_processCollider = null;
			_fallbackBodyDepth = 0;
			_depth = 0;
		}

		private static void BeginProcess(Collider collider)
		{
			Enter();
			_nativeProcess = true;
			_processAttackHandled = false;
			_processCollider = collider;
		}

		private static void RegisterContact(DamageVolume volume, Rigidbody rb, float mult)
		{
			if ((Object)(object)volume == (Object)null || (Object)(object)rb == (Object)null)
			{
				return;
			}
			RigManager val = ResolveRigManager(rb);
			if (!((Object)(object)val == (Object)null))
			{
				Contact item = new Contact
				{
					Volume = volume,
					Rig = val,
					Rigidbody = rb,
					Collider = _processCollider,
					Damage = volume._damage * mult,
					AttackHandled = _processAttackHandled,
					ApplyLimb = true
				};
				if ((Object)(object)_updateVolume == (Object)(object)volume && _contacts != null)
				{
					_contacts.Add(item);
					return;
				}
				FlushContacts(new List<Contact> { item });
			}
		}

		private static void EndProcess()
		{
			_nativeProcess = false;
			_processAttackHandled = false;
			_processCollider = null;
			Exit();
		}

		private static bool TryGetActiveTrackedCollider(DamageVolume volume, Rigidbody rb, out Collider activeCollider)
		{
			activeCollider = null;
			if ((Object)(object)volume == (Object)null || (Object)(object)rb == (Object)null || (Object)(object)ResolveRigManager(rb) == (Object)null)
			{
				return true;
			}
			if (!TrackedColliders.TryGetValue(volume, out var value))
			{
				return true;
			}
			Collider[] array = (Collider[])(object)new Collider[value.Count];
			value.CopyTo(array);
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if ((Object)(object)val == (Object)null || !val.enabled || !((Component)val).gameObject.activeInHierarchy)
				{
					value.Remove(val);
					continue;
				}
				Rigidbody val2 = val.attachedRigidbody ?? ((Component)val).GetComponentInParent<Rigidbody>();
				if ((Object)(object)val2 == (Object)(object)rb)
				{
					activeCollider = val;
					return true;
				}
			}
			return false;
		}

		private static RigManager ResolveRigManager(Rigidbody rb)
		{
			RigManager componentInParent = ((Component)rb).GetComponentInParent<RigManager>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return componentInParent;
			}
			PlayerDamageReceiver componentInChildren = ((Component)rb).GetComponentInChildren<PlayerDamageReceiver>(true);
			object result;
			if (componentInChildren == null)
			{
				result = null;
			}
			else
			{
				Health health = componentInChildren.health;
				result = ((health != null) ? health._rigManager : null);
			}
			return (RigManager)result;
		}

		private static void AppendTrackedContacts(DamageVolume volume, List<Contact> contacts)
		{
			if ((Object)(object)volume == (Object)null || contacts == null || !TrackedColliders.TryGetValue(volume, out var value))
			{
				return;
			}
			Collider[] array = (Collider[])(object)new Collider[value.Count];
			value.CopyTo(array);
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if ((Object)(object)val == (Object)null || !val.enabled || !((Component)val).gameObject.activeInHierarchy)
				{
					value.Remove(val);
					continue;
				}
				Rigidbody val2 = val.attachedRigidbody ?? ((Component)val).GetComponentInParent<Rigidbody>();
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				RigManager val3 = ResolveRigManager(val2);
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				float damage = volume._damage * volume._damageScale;
				bool flag = false;
				foreach (Contact contact in contacts)
				{
					if ((Object)(object)contact.Rig == (Object)(object)val3)
					{
						flag |= contact.AttackHandled;
					}
					if ((Object)(object)contact.Rigidbody == (Object)(object)val2)
					{
						damage = contact.Damage;
						if ((Object)(object)contact.Collider == (Object)null)
						{
							contact.ApplyLimb = false;
						}
					}
				}
				contacts.Add(new Contact
				{
					Volume = volume,
					Rig = val3,
					Rigidbody = val2,
					Collider = val,
					Damage = damage,
					AttackHandled = flag,
					ApplyLimb = true
				});
			}
		}

		private static void FlushContacts(List<Contact> contacts)
		{
			if (contacts == null || contacts.Count == 0 || DeadPieceMod.Instance == null)
			{
				return;
			}
			Dictionary<RigManager, List<Contact>> dictionary = new Dictionary<RigManager, List<Contact>>();
			foreach (Contact contact in contacts)
			{
				if (!((Object)(object)contact?.Rig == (Object)null))
				{
					if (!dictionary.TryGetValue(contact.Rig, out var value))
					{
						value = new List<Contact>();
						dictionary[contact.Rig] = value;
					}
					value.Add(contact);
				}
			}
			foreach (KeyValuePair<RigManager, List<Contact>> item in dictionary)
			{
				bool flag = false;
				foreach (Contact item2 in item.Value)
				{
					flag |= item2.AttackHandled;
				}
				bool flag2 = flag;
				HashSet<HumanBodyBones> damagedBones = new HashSet<HumanBodyBones>();
				foreach (Contact item3 in item.Value)
				{
					try
					{
						bool flag3 = DeadPieceMod.Instance.HandleDamageVolumeContact(item3.Volume, item3.Rig, item3.Rigidbody, item3.Collider, item3.Damage, !flag2, item3.ApplyLimb, damagedBones);
						flag2 = flag2 || flag3;
					}
					catch (Exception)
					{
					}
				}
			}
		}

		[HarmonyPrefix]
		private static bool Prefix(DamageVolume __instance, Rigidbody rb, out bool __state)
		{
			__state = TryGetActiveTrackedCollider(__instance, rb, out var activeCollider);
			if (!__state)
			{
				return false;
			}
			BeginProcess(activeCollider);
			return true;
		}

		[HarmonyPostfix]
		private static void Postfix(DamageVolume __instance, Rigidbody rb, float mult, bool __state)
		{
			if (__state)
			{
				RegisterContact(__instance, rb, mult);
			}
		}

		[HarmonyFinalizer]
		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				EndProcess();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(DamageVolume), "OnTriggerEnter")]
	internal static class DamageVolumeEnterPatch
	{
		[HarmonyPrefix]
		private static void Prefix(DamageVolume __instance, Collider other)
		{
			DamageVolumeContextPatch.TrackCollider(__instance, other, entered: true);
		}
	}
	[HarmonyPatch(typeof(DamageVolume), "OnTriggerExit")]
	internal static class DamageVolumeExitPatch
	{
		[HarmonyPrefix]
		private static void Prefix(DamageVolume __instance, Collider other)
		{
			DamageVolumeContextPatch.TrackCollider(__instance, other, entered: false);
		}
	}
	[HarmonyPatch(typeof(DamageVolume), "OnDisable")]
	internal static class DamageVolumeDisablePatch
	{
		[HarmonyPrefix]
		private static void Prefix(DamageVolume __instance)
		{
			DamageVolumeContextPatch.ForgetVolume(__instance);
		}
	}
	[HarmonyPatch(typeof(DamageVolume), "Update")]
	internal static class DamageVolumeUpdatePatch
	{
		[HarmonyPrefix]
		private static void Prefix(DamageVolume __instance)
		{
			DamageVolumeContextPatch.BeginUpdate(__instance);
		}

		[HarmonyPostfix]
		private static void Postfix(DamageVolume __instance)
		{
			DamageVolumeContextPatch.EndUpdate(__instance);
		}

		[HarmonyFinalizer]
		private static Exception Finalizer(DamageVolume __instance, Exception __exception)
		{
			DamageVolumeContextPatch.EndUpdate(__instance);
			return __exception;
		}
	}
	public class DeadPieceMod : MelonMod
	{
		private struct PendingNetworkState
		{
			public ushort Sequence;

			public uint GoneBoneMask;

			public bool IsFallen;
		}

		private struct OneHpHealthSnapshot
		{
			public Health Health;

			public int AvatarHash;

			public float MaxHealth;

			public float CurrentHealth;

			public float ResolvedHealthBase;
		}

		[Flags]
		private enum LimbGoneMask : byte
		{
			None = 0,
			LeftArm = 1,
			RightArm = 2,
			LeftLeg = 4,
			RightLeg = 8,
			Spine = 0x10,
			Hips = 0x20
		}

		private enum ManagedFatalOutcome : byte
		{
			None,
			DirectDelay,
			NativeDying
		}

		private sealed class RigRuntimeCache
		{
			public int AvatarHash;

			public bool Initialized;

			public bool LimbDamageBlocked;

			public int LimbDamageBlockFrame;

			public bool HasMissingLimbs;

			public LimbGoneMask LimbGoneFlags;

			public Transform[] BoneTransforms;

			public VisualDamageController VisualDamage;
		}

		private struct HudPartState
		{
			public bool Valid;

			public bool Destroyed;

			public float Ratio;
		}

		private sealed class PlayerHealthBarView
		{
			public NetworkPlayer Player;

			public RigHealthBar Bar;

			public Transform Root;

			public Image Fill;

			public Color OriginalFillColor;

			public bool HasOriginalFillColor;

			public TMP_Text Text;

			public Color OriginalTextColor;

			public bool HasOriginalTextColor;

			public float LastHealth = float.NaN;

			public float LastMaximum = float.NaN;

			public string LastText;

			public bool SpawnRequested;
		}

		private sealed class NpcLimbState
		{
			public PuppetMaster PuppetMaster;

			public SubBehaviourHealth Health;

			public AIBrain Brain;

			public Animator Animator;

			public Poolee CachedPoolee;

			public ushort CachedEntityId;

			public bool HasCachedEntityId;

			public float NextPooleeResolveTime;

			public float HealthBase;

			public uint GoneMask;

			public readonly Dictionary<HumanBodyBones, Transform> Bones = new Dictionary<HumanBodyBones, Transform>();

			public readonly Dictionary<HumanBodyBones, Vector3> OriginalScales = new Dictionary<HumanBodyBones, Vector3>();

			public readonly Dictionary<HumanBodyBones, List<Transform>> VisualBones = new Dictionary<HumanBodyBones, List<Transform>>();

			public readonly Dictionary<Transform, Vector3> VisualScales = new Dictionary<Transform, Vector3>();

			public readonly HashSet<Transform> HiddenVisualBones = new HashSet<Transform>();

			public readonly Dictionary<HumanBodyBones, List<Renderer>> VisualRenderers = new Dictionary<HumanBodyBones, List<Renderer>>();

			public readonly Dictionary<Renderer, bool> RendererForceOffStates = new Dictionary<Renderer, bool>();

			public readonly Dictionary<Renderer, bool> PendingRendererForceOffStates = new Dictionary<Renderer, bool>();

			public readonly List<NpcVisualMeshState> VisualMeshes = new List<NpcVisualMeshState>();

			public readonly Dictionary<Renderer, float> VisualMeshRetryTimes = new Dictionary<Renderer, float>();

			public readonly Dictionary<Renderer, int> VisualMeshFailureCounts = new Dictionary<Renderer, int>();

			public readonly Dictionary<SkinnedMeshRenderer, bool> NpcRendererUpdateStates = new Dictionary<SkinnedMeshRenderer, bool>();

			public readonly HashSet<SkinnedMeshRenderer> NpcClothUpdateRenderers = new HashSet<SkinnedMeshRenderer>();

			public readonly Dictionary<Animator, AnimatorCullingMode> NpcAnimatorCullingStates = new Dictionary<Animator, AnimatorCullingMode>();

			public bool VisualMeshesInitialized;

			public Renderer[] PendingVisualRenderers;

			public int NextVisualRendererIndex;

			public bool PendingVisualBonesRefresh;

			public int VisualMeshScanPassCount;

			public int VisualScanFailureCount;

			public float NextVisualMeshScanTime;

			public float NextVisualRendererCheckTime;

			public int VisualRendererSignature;

			public float NextDeferredProxyBakeTime;

			public float NextEnforceTime;

			public int EnforceFailureCount;

			public readonly Dictionary<HumanBodyBones, float> LimbHealth = new Dictionary<HumanBodyBones, float>();

			public readonly Dictionary<HumanBodyBones, float> LastDamageTimes = new Dictionary<HumanBodyBones, float>();

			public readonly Dictionary<Collider, bool> ColliderStates = new Dictionary<Collider, bool>();

			public uint ColliderMaskApplied;

			public readonly List<GameObject> BloodEffects = new List<GameObject>();

			public Color NativeBloodColor;

			public bool HasNativeBloodColor;
		}

		private struct PendingNpcNetworkState
		{
			public uint Mask;

			public bool PlayBlood;

			public float ExpiresAt;
		}

		private struct NpcVertexWeights
		{
			public uint Bone0;

			public uint Bone1;

			public uint Bone2;

			public uint Bone3;

			public float Weight0;

			public float Weight1;

			public float Weight2;

			public float Weight3;
		}

		private sealed class NpcVisualMeshState
		{
			public NpcLimbState OwnerState;

			public SkinnedMeshRenderer Renderer;

			public Mesh OriginalMesh;

			public Mesh RuntimeMesh;

			public Mesh StagingMesh;

			public uint[] RendererBoneMasks;

			public NpcVertexWeights[] VertexWeights;

			public int[][] OriginalTriangles;

			public bool UsesBakedProxy;

			public GameObject ProxyObject;

			public MeshFilter ProxyFilter;

			public MeshRenderer ProxyRenderer;

			public Il2CppStructArray<int>[] ProxyTriangles;

			public int ProxyVertexCount;

			public MaterialPropertyBlock ProxyPropertyBlock;

			public LODGroup ProxyLodGroup;

			public bool ProxyLodReplaced;

			public bool ProxyLodBindingRequired;

			public bool ProxyLodRestorePending;

			public float NextProxyRendererSyncTime;

			public float NextProxyBakeTime;

			public float NextProxyLodBindTime;

			public bool HasRendererForceState;

			public bool RendererForceState;

			public bool StagingMeshPrepared;

			public uint StagingMask = uint.MaxValue;

			public int CreatedFrame;

			public uint AppliedMask = uint.MaxValue;
		}

		private struct PlayerInteractionStats
		{
			public float Strength;

			public float Height;

			public float Mass;
		}

		private struct RecentGrabEvidence
		{
			public byte TargetPlayerId;

			public HumanBodyBones Bone;

			public float Time;

			public bool Active;

			public int ReceiverId;
		}

		private struct RecentNpcGrabEvidence
		{
			public ushort EntityId;

			public HumanBodyBones Bone;

			public float Time;

			public bool Active;

			public bool Consumed;

			public int ReceiverId;
		}

		private struct RecentCrushEvidence
		{
			public float Time;
		}

		private struct PendingPlayerInteraction
		{
			public byte SourcePlayerId;

			public byte TargetPlayerId;

			public byte Kind;

			public byte BoneIndex;

			public byte Detail;

			public Vector3 Point;

			public float ExpiresAt;
		}

		private struct PendingNpcInteraction
		{
			public byte SourcePlayerId;

			public ushort EntityId;

			public byte Kind;

			public byte BoneIndex;

			public byte Detail;

			public Vector3 Point;

			public float ExpiresAt;
		}

		public static MelonPreferences_Category Category;

		public static MelonPreferences_Entry<float> MaxLimbHealth;

		public static MelonPreferences_Entry<float> DamageMultiplier;

		public static MelonPreferences_Entry<bool> EnableSoundEffects;

		public static MelonPreferences_Entry<bool> EnableDualStickDeathReset;

		public static MelonPreferences_Entry<float> SeveredLimbBleedDamagePerSecond;

		public static MelonPreferences_Entry<bool> EnableVrEyeBlindness;

		public static MelonPreferences_Entry<bool> EnableHeadSever;

		public static MelonPreferences_Entry<bool> EnableVisualCuts;

		public static MelonPreferences_Entry<bool> EnableAttackTypeBalancing;

		public static MelonPreferences_Entry<float> PiercingDamageMultiplier;

		public static MelonPreferences_Entry<float> BluntDamageMultiplier;

		public static MelonPreferences_Entry<float> SlashDamageMultiplier;

		public static MelonPreferences_Entry<float> StabDamageMultiplier;

		public static MelonPreferences_Entry<float> FireDamageMultiplier;

		public static MelonPreferences_Entry<float> ExplosionRadius;

		public static MelonPreferences_Entry<bool> EnableBlastLimbDamage;

		public static MelonPreferences_Entry<bool> EnableImpactBlood;

		public static MelonPreferences_Entry<bool> EnableBloodEffects;

		public static MelonPreferences_Entry<bool> EnableGameBloodEffects;

		public static MelonPreferences_Entry<bool> EnableFallbackBloodEffects;

		public static MelonPreferences_Entry<string> BloodColorOverride;

		public static MelonPreferences_Entry<bool> EnableMovementPenalty;

		public static MelonPreferences_Entry<bool> EnableSecondLegRagdoll;

		public static MelonPreferences_Entry<bool> HostEnabled;

		public static MelonPreferences_Entry<bool> LimbsEnabled;

		public static MelonPreferences_Entry<bool> OneHpMode;

		public static DeadPieceMod Instance;

		private DataCardReference<SurfaceDataCard> _fleshSurfaceCard;

		private bool _fleshCardFound;

		private readonly List<string> _gameBloodSpawnableBarcodes = new List<string>();

		private bool _bloodSpawnablesScanned;

		private ParticleSystem _gameBloodParticlePrefab;

		private float _lastGameBloodParticleSearchTime = -999f;

		private bool _loggedMissingGameBloodParticles;

		private bool _loggedVanillaBloodSpawnable;

		private bool _loggedVanillaBloodSpawnableFailure;

		private Spawnable _smallBloodSplatterSpawnable;

		private Spawnable _largeBloodSplatterSpawnable;

		private Spawnable _bloodBagBlasterSpawnable;

		private bool _vanillaBloodSpawnablesRegistered;

		public ParticleSystem BloodPrefab;

		private bool _limitsSaved;

		private const float DefaultLocomotionMaxVelocity = 4.5f;

		private float _savedLocomotionMaxVelocity = 4.5f;

		private float _savedLocomotionCurrentMaxVelocity = 4.5f;

		private bool _savedLocomotionJumpEnabled = true;

		private bool _savedLocomotionDoubleJump;

		private bool _locomotionStateSaved;

		private bool _deadpieceNoLegUsageApplied;

		private RigManager _deadpieceUsageRig;

		private Health _deadpieceUsageHealth;

		private int _deadpieceUsageAvatarHash;

		private bool _deadpieceJumpBlocked;

		private RigManager _deadpieceJumpOwnerRig;

		private RemapRig _deadpieceJumpRig;

		private bool _deadpieceFallenStateApplied;

		private int _locomotionRecoveryToken;

		private float _savedUsageHips = 1f;

		private float _savedUsageSpine = 1f;

		private float _savedUsageLegLf = 1f;

		private float _savedUsageLegRt = 1f;

		private float _savedUsageArmLf = 1f;

		private float _savedUsageArmRt = 1f;

		private float _suppressNoLegRagdollUntil = -999f;

		private float _lastLocalRespawnRequestTime = -999f;

		private bool _localNoLegEffectsPermitted;

		private Harmony _harmony = new Harmony("com.deadpiece.patches");

		private const string MetadataKey = "DeadPieceLimbs";

		private const string HostEnabledMetadataKey = "DeadPieceHostEnabled";

		private const string LimbsEnabledMetadataKey = "DeadPieceLimbsEnabled";

		private const string LegStateMetadataKey = "DeadPieceLegState";

		private const string OneHpModeMetadataKey = "DeadPieceOneHpMode";

		private const string HealthBaseMetadataKey = "DeadPieceHealthBase";

		private const float FusionStateResyncInterval = 5f;

		private const float StumpBloodEffectInterval = 0.45f;

		private const float StumpBloodVisualDamage = 3f;

		private const float VanillaBloodLifetimeSeconds = 10f;

		private const float AutoInjectorUseWindowSeconds = 0.8f;

		private const string Explosive_SmallBigDamage = "BaBaCorp.MiscExplosiveDevices.Spawnable.ExplosionSmallBigDamage";

		private const string Explosive_MicroNuke = "BaBaCorp.MiscExplosiveDevices.Spawnable.MicroNukeGrenade";

		private const string Explosive_TimedNuke = "BaBaCorp.MiscExplosiveDevices.Spawnable.TimedNuke";

		private const string Explosive_Dynamite = "BaBaCorp.MiscExplosiveDevices.Spawnable.Dynamite";

		private const string Explosive_C4 = "BaBaCorp.MiscExplosiveDevices.Spawnable.CelebratoryC4";

		private const string Explosive_Grenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.FragmentationGrenade";

		private const string Explosive_TimedC4 = "BaBaCorp.MiscExplosiveDevices.Spawnable.TimedC4";

		private const string Explosive_ProxMine = "BaBaCorp.MiscExplosiveDevices.Spawnable.ProxMine";

		private const string Explosive_GiftMine = "BaBaCorp.MiscExplosiveDevices.Spawnable.GiftMine";

		private const string Explosive_FragGrenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.FragmentationGrenade";

		private const string Explosive_GasGrenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.GasGrenade";

		private const string Explosive_SmokeGrenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.SmokeGrenade";

		private const string Explosive_Flare = "BaBaCorp.MiscExplosiveDevices.Spawnable.Flare";

		private const string Explosive_PropaneTank = "BaBaCorp.MiscExplosiveDevices.Spawnable.PropaneTank";

		private const string Explosive_FuelTank = "BaBaCorp.MiscExplosiveDevices.Spawnable.FuelTank";

		private const string Explosive_Missile = "BaBaCorp.MiscExplosiveDevices.Spawnable.Missile";

		private const string Explosive_MiniMissile = "BaBaCorp.MiscExplosiveDevices.Spawnable.MiniMissile";

		private const string Explosive_DivinationGrenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.DivinationGrenade";

		private const string Explosive_VoidTunneling = "BaBaCorp.MiscExplosiveDevices.Spawnable.KCB4VoidTunnelingDevice";

		private const string Explosive_IncendiaryGrenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.IncendiaryGrenade";

		private const string Explosive_FireworkPurple = "BaBaCorp.MiscExplosiveDevices.Spawnable.PurpleFireworkRocket";

		private const string Explosive_FireworkGreen = "BaBaCorp.MiscExplosiveDevices.Spawnable.GreenFireworkRocket";

		private const string Explosive_FireworkBlue = "BaBaCorp.MiscExplosiveDevices.Spawnable.BlueFireworkRocket";

		private const string Explosive_FireworkRed = "BaBaCorp.MiscExplosiveDevices.Spawnable.RedFireworkRocket";

		private const string Explosive_Lighter = "BaBaCorp.MiscExplosiveDevices.Spawnable.Lighter";

		private const string Explosive_M72Law = "BaBaCorp.MiscExplosiveDevices.Spawnable.M72LAW";

		private const string Explosive_M72LawInf = "BaBaCorp.MiscExplosiveDevices.Spawnable.M72LawINF";

		private const string Explosive_Stielhandgranate = "BaBaCorp.MiscExplosiveDevices.Spawnable.Stielhandgranate";

		private const string Explosive_SmallRocket = "BaBaCorp.MiscExplosiveDevices.Spawnable.SmallRocket";

		private const string Explosive_Jarate = "BaBaCorp.MiscExplosiveDevices.Spawnable.Jarate";

		private const string Explosive_JoyousGreenGrenade = "BaBaCorp.MiscExplosiveDevices.Spawnable.JoyousGreenGrenade";

		private const string Explosive_BatteryAcidGlass = "BaBaCorp.MiscExplosiveDevices.Spawnable.BatteryAcidGlass";

		private const string Explosive_CrateWithLid = "BaBaCorp.MiscExplosiveDevices.Spawnable.CrateWithLid";

		private const string Explosive_RemoteIED = "BaBaCorp.MiscExplosiveDevices.Spawnable.RemoteIED";

		private const string Explosive_ClassicBomb = "BaBaCorp.MiscExplosiveDevices.Spawnable.ClassicBomb";

		private const string Explosive_Frank = "BaBaCorp.MiscExplosiveDevices.Spawnable.Frank";

		private const string Explosive_BirthdayCake = "BaBaCorp.MiscExplosiveDevices.Spawnable.BirthdayCake";

		private const string Explosive_IncinMissile = "BaBaCorp.MiscExplosiveDevices.Spawnable.IncinMissile";

		private const string Explosive_TriggeredExplosive = "BaBaCorp.MiscExplosiveDevices.Spawnable.TriggeredExplosive";

		private const string Explosive_TimedExplosive6s = "BaBaCorp.MiscExplosiveDevices.Spawnable.TimedExplosive6sec";

		private const string Explosive_ImpactExplosive = "BaBaCorp.MiscExplosiveDevices.Spawnable.ImpactExplosive";

		private const string Explosive_Pliers = "BaBaCorp.MiscExplosiveDevices.Spawnable.Pliers";

		private readonly Dictionary<RigManager, Dictionary<HumanBodyBones, float>> _playersHealth = new Dictionary<RigManager, Dictionary<HumanBodyBones, float>>();

		private readonly Dictionary<RigManager, Dictionary<Collider, HumanBodyBones>> _colliderBoneMap = new Dictionary<RigManager, Dictionary<Collider, HumanBodyBones>>();

		private readonly Dictionary<RigManager, Dictionary<Collider, float>> _colliderBoneDistanceMap = new Dictionary<RigManager, Dictionary<Collider, float>>();

		private readonly Dictionary<RigManager, Dictionary<HumanBodyBones, Vector3>> _originalScales = new Dictionary<RigManager, Dictionary<HumanBodyBones, Vector3>>();

		private readonly Dictionary<int, Dictionary<HumanBodyBones, Vector3>> _avatarScaleBaselines = new Dictionary<int, Dictionary<HumanBodyBones, Vector3>>();

		private readonly Dictionary<RigManager, uint> _hiddenBoneMasks = new Dictionary<RigManager, uint>();

		private readonly Dictionary<RigManager, Dictionary<HumanBodyBones, float>> _lastLimbDamageTimes = new Dictionary<RigManager, Dictionary<HumanBodyBones, float>>();

		private readonly Dictionary<RigManager, Dictionary<Collider, bool>> _legColliderOriginalStates = new Dictionary<RigManager, Dictionary<Collider, bool>>();

		private readonly Dictionary<RigManager, Dictionary<HumanBodyBones, float>> _lastHitEffectTimes = new Dictionary<RigManager, Dictionary<HumanBodyBones, float>>();

		private readonly Dictionary<RigManager, Dictionary<HumanBodyBones, float>> _lastStumpBloodEffectTimes = new Dictionary<RigManager, Dictionary<HumanBodyBones, float>>();

		private readonly Dictionary<RigManager, Dictionary<HumanBodyBones, float>> _stumpBloodStartTimes = new Dictionary<RigManager, Dictionary<HumanBodyBones, float>>();

		private readonly Dictionary<byte, PendingNetworkState> _pendingNetworkStates = new Dictionary<byte, PendingNetworkState>();

		private readonly Dictionary<byte, uint> _appliedStateMasks = new Dictionary<byte, uint>();

		private readonly HashSet<byte> _remoteFallenPlayers = new HashSet<byte>();

		private bool _lastLeftArmUsable = true;

		private bool _lastRightArmUsable = true;

		private readonly Dictionary<byte, ushort> _lastAppliedNetworkSequence = new Dictionary<byte, ushort>();

		private ushort _localStateSequence;

		private uint _lastSentStateMask = uint.MaxValue;

		private float _lastStateSyncTime = -999f;

		private readonly List<AudioClip> _dismemberSounds = new List<AudioClip>();

		public AudioClip DismemberSound;

		private string _soundFolder;

		private readonly Dictionary<RigManager, int> _lastAvatarHashes = new Dictionary<RigManager, int>();

		private readonly Dictionary<string, Color> _avatarBloodOverrides = new Dictionary<string, Color>();

		private readonly Dictionary<RigManager, Color> _bloodColorOverrides = new Dictionary<RigManager, Color>();

		private readonly Dictionary<RigManager, byte> _bloodColorIndexOverrides = new Dictionary<RigManager, byte>();

		private string _lastBroadcastedBloodColor = "Auto";

		private bool _boneMenuCreated;

		private BoolElement _hostMenuToggle;

		private BoolElement _limbsMenuToggle;

		private BoolElement _oneHpMenuToggle;

		private bool _serverEnabled = true;

		private bool _lastPublishedHostEnabled;

		private bool _lastPublishedLimbsEnabled;

		private bool _lastPublishedOneHpMode;

		private float _lastPublishedHealthBase = -1f;

		private bool _healthBasePublished;

		private bool _settingsPublished;

		private bool _wasHostForSettings;

		private bool _serverOneHpMode;

		private bool _leftHandDisabledByDeadPiece;

		private bool _rightHandDisabledByDeadPiece;

		private readonly Dictionary<byte, bool> _playerLimbsEnabled = new Dictionary<byte, bool>();

		private readonly Dictionary<byte, float> _networkHealthBaseOverrides = new Dictionary<byte, float>();

		private readonly Dictionary<RigManager, OneHpHealthSnapshot> _oneHpHealthSnapshots = new Dictionary<RigManager, OneHpHealthSnapshot>();

		public static readonly HumanBodyBones[] DamageBones;

		private static readonly HashSet<HumanBodyBones> FatalBones;

		private static readonly HashSet<HumanBodyBones> TorsoBones;

		private static readonly HashSet<HumanBodyBones> CoreNearestExcludeBones;

		private const float NearestBoneMaxDistance = 0.75f;

		private const float NearestBoneTorsoBias = 0.35f;

		private const float TorsoBoneMaxHitDistance = 0.4f;

		private const float MaxColliderBoneWorldDistance = 0.55f;

		private const float MaxColliderHierarchyDepth = 5f;

		private const int LimbDamageBlockRecheckFrames = 30;

		private const float SlowUpdateInterval = 0.5f;

		private const float LimbHealthRegenDelay = 3f;

		private const float LimbHealthRegenFractionPerSecond = 0.1f;

		private const float AvatarSwapCheckInterval = 0.2f;

		private static readonly HumanBodyBones[] NetworkBones;

		public static readonly Dictionary<HumanBodyBones, int> BoneToIndexMap;

		private static readonly string[] GameBloodPositiveKeywords;

		private static readonly string[] GameBloodNegativeKeywords;

		private static readonly string[] VanillaBloodSpawnableBarcodes;

		private const string VanillaBloodBlasterBarcode = "c1534c5a-1ea0-4156-920b-f8a8426c6173";

		private const float DualStickResetCooldown = 0.75f;

		private const float DualStickTapPairMaxGap = 0.25f;

		private const float DualStickTapWindowDuration = 1.75f;

		private const int DualStickResetTapCount = 2;

		private RigManager _cachedRm;

		private float _lastTrackedBodyHealth = -1f;

		private float _lastAutoInjectorUseTime = -999f;

		private float _lastExplosiveUseTime = -999f;

		private const float ExplosiveUseWindowSeconds = 1.5f;

		private int _dualStickResetTapCount;

		private float _dualStickResetTapWindowExpire;

		private float _lastLeftStickTapTime = -999f;

		private float _lastRightStickTapTime = -999f;

		private float _lastDualStickTapRegisteredTime = -999f;

		private float _lastDualStickResetTime = -999f;

		private readonly Dictionary<RigManager, bool> _rigWasDead = new Dictionary<RigManager, bool>();

		private readonly Dictionary<RigManager, float> _avatarHealthBaseCache = new Dictionary<RigManager, float>();

		private readonly Dictionary<RigManager, int> _avatarHealthHashCache = new Dictionary<RigManager, int>();

		private readonly HashSet<RigManager> _trackedRigs = new HashSet<RigManager>();

		private float _slowUpdateTimer;

		private float _avatarSwapTimer;

		private RigManager _vrEyeCacheRig;

		private bool _vrEyeCamerasCached;

		private bool _lastAppliedLeftEyeBlind;

		private bool _lastAppliedRightEyeBlind;

		private readonly List<Camera> _vrLeftEyeCameras = new List<Camera>();

		private readonly List<Camera> _vrRightEyeCameras = new List<Camera>();

		private readonly Dictionary<Camera, bool> _vrCameraEnabledBackup = new Dictionary<Camera, bool>();

		private readonly Dictionary<Camera, GameObject> _vrEyeBlackoutObjects = new Dictionary<Camera, GameObject>();

		private int _updateFrame;

		private int _sceneGeneration;

		private bool _localRigInitialized;

		private bool _startupScheduled;

		private bool _wasConnected;

		private readonly HashSet<RigManager> _pendingHealthRefresh = new HashSet<RigManager>();

		private readonly HashSet<byte> _pendingRespawnPlayers = new HashSet<byte>();

		private readonly HashSet<byte> _activeRespawnRestoreWorkers = new HashSet<byte>();

		private readonly HashSet<byte> _hardRespawnRecoveryPlayers = new HashSet<byte>();

		private bool _localRespawnRestoreWorkerActive;

		private bool _hardLocalRespawnRecovery;

		private bool _healingRestoreInProgress;

		private ManagedFatalOutcome _managedFatalOutcome;

		private RigManager _managedFatalRig;

		private Health _managedFatalHealth;

		private Player_Health _managedFatalPlayerHealth;

		private HumanBodyBones _managedFatalBone = (HumanBodyBones)55;

		private int _managedFatalSceneGeneration = -1;

		private int _managedFatalToken;

		private bool _allowManagedDyingEntry;

		private bool _managedFatalHasSavedPlayerState;

		private HealthMode _managedFatalSavedHealthMode;

		private float _managedFatalSavedDeathTimeAmount;

		private float _managedFatalSavedDeathTimeReduction;

		private float _managedFatalSavedCurrDeathTime;

		private bool _managedFatalSavedSlowMoOnDeath;

		private float _managedFatalSavedCurrentHealth;

		private readonly Dictionary<RigManager, RigRuntimeCache> _rigCache = new Dictionary<RigManager, RigRuntimeCache>();

		private Mesh _bloodQuadMesh;

		private bool _pluginCallbacksRegistered;

		private const float DamageVolumeLifetime = 8f;

		private const float DamageVolumeRadius = 0.12f;

		private const float DamageVolumeDps = 2f;

		public static MelonPreferences_Entry<bool> EnableLocalHealthHud;

		public static MelonPreferences_Entry<bool> EnablePlayerHealthBars;

		private const float LocalHealthHudUpdateInterval = 0.1f;

		private const float PlayerHealthBarCleanupTimeout = 30f;

		private static readonly Vector3 LocalHealthHudOffset;

		private static readonly Color HudGreen;

		private static readonly Color HudYellow;

		private static readonly Color HudRed;

		private static readonly Color HudViolet;

		private static readonly Color HudUnavailable;

		private static readonly HumanBodyBones[] HudHeadBones;

		private static readonly HumanBodyBones[] HudBodyBones;

		private static readonly HumanBodyBones[] HudLeftArmBones;

		private static readonly HumanBodyBones[] HudRightArmBones;

		private static readonly HumanBodyBones[] HudLeftLegBones;

		private static readonly HumanBodyBones[] HudRightLegBones;

		private BoolElement _localHealthHudMenuToggle;

		private BoolElement _playerHealthBarsMenuToggle;

		private GameObject _localHealthHudRoot;

		private Text _localHealthHudText;

		private RigManager _localHealthHudRig;

		private Transform _localHealthHudAnchor;

		private float _localHealthHudTimer;

		private float _nextLocalHealthHudCreateTime;

		private readonly Dictionary<byte, PlayerHealthBarView> _playerHealthBarViews = new Dictionary<byte, PlayerHealthBarView>();

		private readonly HashSet<byte> _seenPlayerHealthBars = new HashSet<byte>();

		private static readonly Color DefaultNpcBloodColor;

		private Material _npcBloodParticleMaterial;

		private const float NpcDestroyedBodyDespawnDelay = 1.5f;

		private const float NpcDestroyedBodyDespawnResolveTimeout = 3.5f;

		private readonly Dictionary<PuppetMaster, NpcLimbState> _npcLimbStates = new Dictionary<PuppetMaster, NpcLimbState>();

		private readonly Dictionary<SubBehaviourHealth, PuppetMaster> _npcHealthOwners = new Dictionary<SubBehaviourHealth, PuppetMaster>();

		private readonly Dictionary<ushort, PuppetMaster> _npcEntityLookup = new Dictionary<ushort, PuppetMaster>();

		private readonly HashSet<PuppetMaster> _npcPendingBodyDespawns = new HashSet<PuppetMaster>();

		private readonly HashSet<PuppetMaster> _npcTerminalBodyDespawns = new HashSet<PuppetMaster>();

		private int _npcVisualScanBudget;

		private int _npcProxyBuildBudget;

		private int _npcProxyRefreshBudget;

		private int _npcEnforceCursor;

		private float _nextNpcStateRepairTime;

		private const float PendingNpcStateLifetime = 5f;

		private const int MaxPendingNpcStates = 128;

		private readonly Dictionary<ushort, PendingNpcNetworkState> _pendingNpcNetworkStates = new Dictionary<ushort, PendingNpcNetworkState>();

		private float _lastPendingNpcResolveTime = -999f;

		private const float LocalInteractionCooldown = 0.8f;

		private const float HostInteractionCooldown = 1f;

		private const float InteractionMinimumRatio = 1f;

		private const float InteractionGuaranteedRatio = 3f;

		private const float CrushContactResetTime = 0.3f;

		private const float RipPullHoldTime = 0.15f;

		private const float CrushOverlapInterval = 0.1f;

		private const float HostCrushObservationInterval = 0.05f;

		private const float HostCrushEvidenceLifetime = 1.5f;

		private const float HostPendingInteractionLifetime = 1.5f;

		private const string InteractionStatsMetadataKey = "DeadPieceInteractionStats";

		private const byte RipRightHandDetail = 1;

		private const byte RipPullDetail = 2;

		private const byte CrushFootballDetail = 0;

		private const byte CrushLeftFootDetail = 1;

		private const byte CrushRightFootDetail = 2;

		private readonly Dictionary<ulong, float> _localInteractionCooldowns = new Dictionary<ulong, float>();

		private readonly Dictionary<ulong, float> _hostInteractionCooldowns = new Dictionary<ulong, float>();

		private readonly Dictionary<ulong, float> _localCrushContactTimes = new Dictionary<ulong, float>();

		private readonly Dictionary<ushort, RecentGrabEvidence> _recentGrabEvidence = new Dictionary<ushort, RecentGrabEvidence>();

		private readonly Dictionary<ushort, RecentNpcGrabEvidence> _recentNpcGrabEvidence = new Dictionary<ushort, RecentNpcGrabEvidence>();

		private readonly Dictionary<ulong, RecentCrushEvidence> _recentPlayerCrushEvidence = new Dictionary<ulong, RecentCrushEvidence>();

		private readonly Dictionary<ulong, RecentCrushEvidence> _recentNpcCrushEvidence = new Dictionary<ulong, RecentCrushEvidence>();

		private readonly Dictionary<ulong, PendingPlayerInteraction> _pendingPlayerInteractions = new Dictionary<ulong, PendingPlayerInteraction>();

		private readonly Dictionary<ulong, PendingNpcInteraction> _pendingNpcInteractions = new Dictionary<ulong, PendingNpcInteraction>();

		private readonly Dictionary<byte, PlayerInteractionStats> _networkInteractionStats = new Dictionary<byte, PlayerInteractionStats>();

		private static readonly Il2CppReferenceArray<Collider> CrushOverlapBuffer;

		private float _lastInteractionCooldownPrune = -999f;

		private HandReciever _leftRipPullReceiver;

		private HandReciever _rightRipPullReceiver;

		private float _leftRipPullStart = -1f;

		private float _rightRipPullStart = -1f;

		private bool _leftRipPullConsumed;

		private bool _rightRipPullConsumed;

		private float _nextCrushOverlapTime;

		private float _nextHostCrushObservationTime;

		private PlayerInteractionStats _lastPublishedInteractionStats;

		private bool _interactionStatsPublished;

		private static Dictionary<HumanBodyBones, int> BuildBoneToIndexMap()
		{
			Dictionary<HumanBodyBones, int> dictionary = new Dictionary<HumanBodyBones, int>(DamageBones.Length);
			for (int i = 0; i < DamageBones.Length; i++)
			{
				dictionary[DamageBones[i]] = i;
			}
			return dictionary;
		}

		public override void OnUpdate()
		{
			NetworkSync.ProcessMainThreadQueue();
			ProcessNpcRuntime(Time.deltaTime);
			ProcessPendingNpcNetworkStates();
			_updateFrame++;
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				return;
			}
			if ((Object)(object)_cachedRm == (Object)null || (Object)(object)_cachedRm != (Object)(object)rigManager)
			{
				RigManager cachedRm = _cachedRm;
				_cachedRm = rigManager;
				_localRigInitialized = false;
				_startupScheduled = false;
				if ((Object)(object)cachedRm != (Object)null)
				{
					DestroyLocalHealthHud();
					MelonLogger.Msg(ConsoleColor.Gray, "Local player RigManager changed, performing respawn cleanup.");
					RemoveRigFromCaches(cachedRm);
					SuppressNoLegRagdollForRespawn();
					ResetLocalNoLegRagdollPermission();
					_lastTrackedBodyHealth = -1f;
					RequestLocalRespawnReset(rigManager);
				}
			}
			if (!_localRigInitialized)
			{
				if (!_startupScheduled)
				{
					_startupScheduled = true;
					MelonCoroutines.Start(DeferredStartup(rigManager));
				}
				return;
			}
			UpdateHealthDisplays();
			float deltaTime = Time.deltaTime;
			_avatarSwapTimer += deltaTime;
			if (_avatarSwapTimer >= 0.2f)
			{
				_avatarSwapTimer = 0f;
				HandleAvatarSwap(rigManager);
				RigManager[] array = _trackedRigs.ToArray();
				foreach (RigManager val in array)
				{
					if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)rigManager)
					{
						HandleAvatarSwap(val);
					}
				}
			}
			HandleRespawnRestore(rigManager);
			UpdateManagedFatalOutcome();
			if (!NetworkInfo.HasServer || NetworkInfo.IsHost)
			{
				SetServerOneHpMode(OneHpMode?.Value ?? false);
			}
			MaintainOneHpHealth(rigManager, refill: false);
			if (NetworkInfo.HasServer && PlayerIDManager.LocalID != null)
			{
				PublishLocalHealthBase(force: false);
			}
			EnforceLocalFatalState(rigManager);
			HandleStickInputs(deltaTime);
			ProcessLocalRipGrabs();
			UpdateAutoInjectorUseWindow();
			UpdateExplosiveUseWindow();
			ProcessPlayerHealing(rigManager);
			ProcessLimbHealthRegeneration(deltaTime);
			ProcessSeveredLimbBleed(rigManager, deltaTime);
			if ((Object)(object)rigManager == (Object)(object)Player.RigManager)
			{
				ApplyMovementConstraints(rigManager);
			}
			_slowUpdateTimer += deltaTime;
			if (_slowUpdateTimer >= 0.5f)
			{
				_slowUpdateTimer = 0f;
				try
				{
					string text = BloodColorOverride?.Value ?? "Auto";
					if (text != _lastBroadcastedBloodColor)
					{
						_lastBroadcastedBloodColor = text;
						BroadcastBloodColor();
					}
				}
				catch
				{
				}
				bool hasServer = NetworkInfo.HasServer;
				if (hasServer && !_wasConnected)
				{
					_wasConnected = true;
					NetworkSync.RegisterHandlers();
					try
					{
						_settingsPublished = false;
						RefreshSessionSettingsFromMetadata();
						PublishLocalSettings(force: true);
						SyncFullState();
						BroadcastBloodColor();
					}
					catch
					{
					}
				}
				else if (!hasServer && _wasConnected)
				{
					_wasConnected = false;
					NetworkSync.ResetSessionState();
					_serverEnabled = HostEnabled?.Value ?? true;
					SetServerOneHpMode(OneHpMode?.Value ?? false);
					_settingsPublished = false;
					_healthBasePublished = false;
					_interactionStatsPublished = false;
					_playerLimbsEnabled.Clear();
					_networkHealthBaseOverrides.Clear();
					_networkInteractionStats.Clear();
					ClearPlayerInteractionState();
				}
				if (hasServer)
				{
					RefreshSessionSettingsFromMetadata();
					PublishLocalSettings(force: false);
					ResyncLocalStateIfNeeded();
					HandleAllRigsRespawnRestore();
					if (_pendingNetworkStates.Count > 0)
					{
						ApplyPendingNetworkStates();
					}
				}
				EnforceInvincibleRigsForAllTracked();
				ProcessRemoteRigsStumpBlood(deltaTime);
				PruneTrackedRigs();
				if (EnableVrEyeBlindness.Value)
				{
					UpdateVrEyeBlindness(rigManager);
				}
			}
			if ((_updateFrame & 7) == 0 && NetworkInfo.HasServer)
			{
				EnforceRemoteFallenState();
			}
			if ((_updateFrame & 3) == 0)
			{
				EnforceLocalHandUsability();
			}
		}

		public override void OnLateUpdate()
		{
			if (!IsSessionEnabled())
			{
				RestoreAllLegColliderStates();
				return;
			}
			EnforceDismemberedBones();
			EnforceLegColliderStates();
			EnforceNpcDismemberment();
		}

		private IEnumerator DeferredStartup(RigManager rigManager)
		{
			yield return null;
			if (!_fleshCardFound)
			{
				TryCacheFleshSurface();
			}
			if ((Object)(object)rigManager != (Object)null)
			{
				EnsureRigInitialized(rigManager);
				TrackRig(rigManager);
			}
			if (NetworkInfo.HasServer)
			{
				SyncExistingRemoteMetadata();
				ApplyPendingNetworkStates();
				RefreshSessionSettingsFromMetadata();
			}
			_localRigInitialized = true;
			SetupBoneMenu();
			PublishLocalSettings(force: true);
			if (NetworkInfo.HasServer && PlayerIDManager.LocalID != null)
			{
				NetworkSync.SendRespawn(PlayerIDManager.LocalSmallID);
			}
		}

		private void SetupBoneMenu()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			if (_boneMenuCreated)
			{
				return;
			}
			_boneMenuCreated = true;
			try
			{
				Page val = Page.Root.CreatePage("Deadpiece", Color.red, 0, true);
				_hostMenuToggle = val.CreateBool("Host On-Off", Color.red, IsSessionEnabled(), (Action<bool>)OnHostSettingChanged);
				_limbsMenuToggle = val.CreateBool("Limbs On-Off", Color.red, LimbsEnabled?.Value ?? true, (Action<bool>)OnLimbsSettingChanged);
				_oneHpMenuToggle = val.CreateBool("All Players 1 HP", Color.red, IsOneHpModeEnabled(), (Action<bool>)OnOneHpSettingChanged);
				_localHealthHudMenuToggle = val.CreateBool("Health HUD", Color.red, EnableLocalHealthHud?.Value ?? false, (Action<bool>)OnLocalHealthHudSettingChanged);
				_playerHealthBarsMenuToggle = val.CreateBool("Player HP Bars", Color.red, EnablePlayerHealthBars?.Value ?? false, (Action<bool>)OnPlayerHealthBarsSettingChanged);
				val.CreateFunction("Red", Color.red, (Action)delegate
				{
					SetBloodColor("Red");
				});
				val.CreateFunction("Green", Color.green, (Action)delegate
				{
					SetBloodColor("Green");
				});
				val.CreateFunction("Blue", Color.blue, (Action)delegate
				{
					SetBloodColor("Blue");
				});
				val.CreateFunction("Yellow", Color.yellow, (Action)delegate
				{
					SetBloodColor("Yellow");
				});
				val.CreateFunction("Black", Color.gray, (Action)delegate
				{
					SetBloodColor("Black");
				});
				val.CreateFunction("White", Color.white, (Action)delegate
				{
					SetBloodColor("White");
				});
				val.CreateFunction("Purple", new Color(0.5f, 0.02f, 0.6f), (Action)delegate
				{
					SetBloodColor("Purple");
				});
				val.CreateFunction("Orange", new Color(1f, 0.5f, 0f), (Action)delegate
				{
					SetBloodColor("Orange");
				});
				val.CreateFunction("Pink", new Color(1f, 0.4f, 0.7f), (Action)delegate
				{
					SetBloodColor("Pink");
				});
				val.CreateFunction("Auto", Color.white, (Action)delegate
				{
					SetBloodColor("Auto");
				});
				MelonLogger.Msg(ConsoleColor.Gray, "BoneMenu registered. Blood Color menu available.");
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Failed to setup BoneMenu: " + ex.Message);
			}
		}

		private void SetBloodColor(string color)
		{
			BloodColorOverride.Value = color;
			_lastBroadcastedBloodColor = color;
			BroadcastBloodColor();
		}

		public bool IsSessionEnabled()
		{
			if (!NetworkInfo.HasServer || NetworkInfo.IsHost)
			{
				return HostEnabled?.Value ?? true;
			}
			return _serverEnabled;
		}

		public bool IsOneHpModeEnabled()
		{
			if (!NetworkInfo.HasServer || NetworkInfo.IsHost)
			{
				return OneHpMode?.Value ?? false;
			}
			return _serverOneHpMode;
		}

		public bool IsLimbDestructionAllowed(RigManager rm)
		{
			if (!IsSessionEnabled() || (Object)(object)rm == (Object)null)
			{
				return false;
			}
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				return LimbsEnabled?.Value ?? true;
			}
			byte playerId;
			return !TryGetPlayerIdForRig(rm, out playerId) || GetPlayerLimbsEnabled(playerId);
		}

		internal bool CanHostApplyNetworkLimbDamage(byte playerId)
		{
			if (!IsSessionEnabled() || !GetPlayerLimbsEnabled(playerId))
			{
				return false;
			}
			if (!IsOneHpModeEnabled() && NetworkInfo.HasServer && NetworkInfo.IsHost && PlayerIDManager.LocalID != null && playerId != PlayerIDManager.LocalSmallID && !_networkHealthBaseOverrides.ContainsKey(playerId))
			{
				return false;
			}
			return true;
		}

		internal bool CanHostApplyPlayerInteraction(byte playerId)
		{
			return IsSessionEnabled() && GetPlayerLimbsEnabled(playerId);
		}

		internal bool CanHostSynchronizeNetworkLimbState(byte playerId)
		{
			return IsSessionEnabled() && GetPlayerLimbsEnabled(playerId);
		}

		private bool GetPlayerLimbsEnabled(byte playerId)
		{
			bool enabled;
			return !TryGetKnownPlayerLimbsEnabled(playerId, out enabled) || enabled;
		}

		private bool TryGetKnownPlayerLimbsEnabled(byte playerId, out bool enabled)
		{
			enabled = false;
			if (PlayerIDManager.LocalID != null && playerId == PlayerIDManager.LocalSmallID)
			{
				enabled = LimbsEnabled?.Value ?? true;
				return true;
			}
			if (_playerLimbsEnabled.TryGetValue(playerId, out enabled))
			{
				return true;
			}
			try
			{
				string value = default(string);
				foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
				{
					if (playerID == null || playerID.SmallID != playerId)
					{
						continue;
					}
					PlayerMetadata metadata = playerID.Metadata;
					if (((metadata != null) ? metadata.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceLimbsEnabled", ref value))
					{
						enabled = ParseEnabledSetting(value);
						_playerLimbsEnabled[playerId] = enabled;
						return true;
					}
					break;
				}
			}
			catch
			{
			}
			enabled = false;
			return false;
		}

		private static bool ParseEnabledSetting(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return true;
			}
			return value == "1" || value.Equals("true", StringComparison.OrdinalIgnoreCase) || value.Equals("on", StringComparison.OrdinalIgnoreCase);
		}

		private static bool ParseOneHpSetting(string value)
		{
			return !string.IsNullOrWhiteSpace(value) && ParseEnabledSetting(value);
		}

		private void OnHostSettingChanged(bool enabled)
		{
			if (NetworkInfo.HasServer && !NetworkInfo.IsHost)
			{
				if (_hostMenuToggle != null)
				{
					_hostMenuToggle.Value = _serverEnabled;
				}
				return;
			}
			HostEnabled.Value = enabled;
			_serverEnabled = enabled;
			PublishLocalSettings(force: true);
			if (!enabled)
			{
				ClearPlayerInteractionState();
				RestoreAllTrackedLimbStates(NetworkInfo.IsHost);
			}
		}

		private void OnLimbsSettingChanged(bool enabled)
		{
			LimbsEnabled.Value = enabled;
			if (PlayerIDManager.LocalID != null)
			{
				_playerLimbsEnabled[PlayerIDManager.LocalSmallID] = enabled;
			}
			PublishLocalSettings(force: true);
			if (!enabled)
			{
				RigManager rigManager = Player.RigManager;
				if ((Object)(object)rigManager != (Object)null)
				{
					ResetLimbStats(rigManager, restoreBody: true);
				}
				if (NetworkInfo.HasServer && PlayerIDManager.LocalID != null)
				{
					NetworkSync.SendRespawn(PlayerIDManager.LocalSmallID);
				}
			}
		}

		private void OnOneHpSettingChanged(bool enabled)
		{
			if (NetworkInfo.HasServer && !NetworkInfo.IsHost)
			{
				if (_oneHpMenuToggle != null)
				{
					_oneHpMenuToggle.Value = _serverOneHpMode;
				}
			}
			else
			{
				OneHpMode.Value = enabled;
				SetServerOneHpMode(enabled);
				PublishLocalSettings(force: true);
			}
		}

		private void PublishLocalSettings(bool force)
		{
			if (PlayerIDManager.LocalID == null)
			{
				return;
			}
			bool flag = !NetworkInfo.HasServer || NetworkInfo.IsHost;
			if (flag != _wasHostForSettings)
			{
				force = true;
				_wasHostForSettings = flag;
			}
			bool flag2 = LimbsEnabled?.Value ?? true;
			if (force || !_settingsPublished || flag2 != _lastPublishedLimbsEnabled)
			{
				SetLocalMetadataValue("DeadPieceLimbsEnabled", flag2 ? "1" : "0");
				_lastPublishedLimbsEnabled = flag2;
				_playerLimbsEnabled[PlayerIDManager.LocalSmallID] = flag2;
			}
			if (flag)
			{
				bool flag3 = HostEnabled?.Value ?? true;
				if (force || !_settingsPublished || flag3 != _lastPublishedHostEnabled)
				{
					SetLocalMetadataValue("DeadPieceHostEnabled", flag3 ? "1" : "0");
					_lastPublishedHostEnabled = flag3;
				}
				_serverEnabled = flag3;
				bool flag4 = OneHpMode?.Value ?? false;
				if (force || !_settingsPublished || flag4 != _lastPublishedOneHpMode)
				{
					SetLocalMetadataValue("DeadPieceOneHpMode", flag4 ? "1" : "0");
					_lastPublishedOneHpMode = flag4;
				}
				SetServerOneHpMode(flag4);
			}
			PublishLocalHealthBase(force);
			PublishLocalInteractionStats(force);
			_settingsPublished = true;
		}

		private void RefreshSessionSettingsFromMetadata()
		{
			if (!NetworkInfo.HasServer || NetworkInfo.IsHost)
			{
				_serverEnabled = HostEnabled?.Value ?? true;
				SetServerOneHpMode(OneHpMode?.Value ?? false);
				if (_hostMenuToggle != null)
				{
					_hostMenuToggle.Value = _serverEnabled;
				}
				return;
			}
			bool serverEnabled = true;
			bool serverOneHpMode = false;
			try
			{
				byte b = 0;
				string value = default(string);
				string value2 = default(string);
				foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
				{
					if (playerID == null || playerID.SmallID != b)
					{
						continue;
					}
					PlayerMetadata metadata = playerID.Metadata;
					if (((metadata != null) ? metadata.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceHostEnabled", ref value))
					{
						serverEnabled = ParseEnabledSetting(value);
					}
					PlayerMetadata metadata2 = playerID.Metadata;
					if (((metadata2 != null) ? metadata2.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceOneHpMode", ref value2))
					{
						serverOneHpMode = ParseOneHpSetting(value2);
					}
					break;
				}
			}
			catch
			{
			}
			SetServerEnabled(serverEnabled);
			SetServerOneHpMode(serverOneHpMode);
		}

		private void SetServerEnabled(bool enabled)
		{
			bool flag = _serverEnabled != enabled;
			_serverEnabled = enabled;
			if (_hostMenuToggle != null)
			{
				_hostMenuToggle.Value = enabled;
			}
			if (flag && !enabled)
			{
				ClearPlayerInteractionState();
				RestoreAllTrackedLimbStates(broadcast: false);
			}
		}

		private void SetServerOneHpMode(bool enabled)
		{
			bool flag = _serverOneHpMode != enabled;
			_serverOneHpMode = enabled;
			if (_oneHpMenuToggle != null)
			{
				_oneHpMenuToggle.Value = enabled;
			}
			if (flag)
			{
				ApplyOneHpModeTransition(enabled);
				_healthBasePublished = false;
			}
		}

		private void HandlePlayerLimbsSetting(byte playerId, bool enabled)
		{
			_playerLimbsEnabled[playerId] = enabled;
			if (enabled)
			{
				return;
			}
			_pendingNetworkStates.Remove(playerId);
			_appliedStateMasks.Remove(playerId);
			if (TryResolvePlayerRig(playerId, out var rigManager))
			{
				ResetLimbStats(rigManager, restoreBody: true);
				if (NetworkInfo.IsHost)
				{
					NetworkSync.SendFullState(playerId, 0u, GetBloodColorIndexForRig(rigManager), 0);
				}
			}
		}

		private void PublishLocalHealthBase(bool force)
		{
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null || PlayerIDManager.LocalID == null)
			{
				return;
			}
			if (IsOneHpModeEnabled())
			{
				MaintainOneHpHealth(rigManager, refill: false);
			}
			float num = AvatarHealthResolver.Resolve(rigManager, MaxLimbHealth.Value);
			if (IsOneHpModeEnabled() && _oneHpHealthSnapshots.TryGetValue(rigManager, out var value) && (Object)(object)value.Health == (Object)(object)rigManager.health)
			{
				num = value.ResolvedHealthBase;
			}
			if (!(num <= 0f) && !float.IsNaN(num) && !float.IsInfinity(num))
			{
				if (force || !_healthBasePublished || Mathf.Abs(num - _lastPublishedHealthBase) > Mathf.Max(0.0001f, num * 0.0001f))
				{
					SetLocalMetadataValue("DeadPieceHealthBase", num.ToString("R", CultureInfo.InvariantCulture));
					_lastPublishedHealthBase = num;
					_healthBasePublished = true;
				}
				_networkHealthBaseOverrides[PlayerIDManager.LocalSmallID] = num;
			}
		}

		private void HandlePlayerHealthBase(byte playerId, string value)
		{
			RigManager rigManager2;
			if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || result <= 0f || float.IsNaN(result) || float.IsInfinity(result))
			{
				if (_networkHealthBaseOverrides.Remove(playerId) && TryResolvePlayerRig(playerId, out var rigManager))
				{
					GetAvatarHealthBase(rigManager);
				}
			}
			else if (TryResolvePlayerRig(playerId, out rigManager2))
			{
				bool flag = _avatarHealthBaseCache.ContainsKey(rigManager2) && _avatarHealthHashCache.ContainsKey(rigManager2);
				_networkHealthBaseOverrides[playerId] = result;
				if (flag)
				{
					GetAvatarHealthBase(rigManager2);
					return;
				}
				InvalidateAvatarHealthCache(rigManager2);
				RefreshLimbMaxHealth(rigManager2);
			}
			else
			{
				_networkHealthBaseOverrides[playerId] = result;
			}
		}

		private void ApplyOneHpModeTransition(bool enabled)
		{
			HashSet<RigManager> hashSet = new HashSet<RigManager>(_trackedRigs);
			if ((Object)(object)Player.RigManager != (Object)null)
			{
				hashSet.Add(Player.RigManager);
			}
			foreach (RigManager item in hashSet)
			{
				if (!((Object)(object)item == (Object)null))
				{
					if (enabled)
					{
						MaintainOneHpHealth(item, refill: true);
						RebaseTrackedLimbHealthForOneHp(item, enabling: true);
					}
					else
					{
						RestoreOneHpHealth(item);
						RebaseTrackedLimbHealthForOneHp(item, enabling: false);
					}
				}
			}
			if (!enabled)
			{
				RigManager[] array = _oneHpHealthSnapshots.Keys.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					RestoreOneHpHealth(array[i]);
				}
			}
		}

		private void MaintainOneHpHealth(RigManager rm, bool refill)
		{
			if (!IsOneHpModeEnabled() || (Object)(object)rm == (Object)null || (Object)(object)rm.health == (Object)null)
			{
				return;
			}
			Health health = rm.health;
			int num = (((Object)(object)rm.avatar != (Object)null) ? ((Object)rm.avatar).GetHashCode() : 0);
			bool flag = false;
			if (_oneHpHealthSnapshots.TryGetValue(rm, out var value) && (Object)(object)value.Health != (Object)(object)health)
			{
				_oneHpHealthSnapshots.Remove(rm);
			}
			else if (_oneHpHealthSnapshots.TryGetValue(rm, out value) && value.AvatarHash != num)
			{
				float num2 = ((health.curr_Health > 0f && !float.IsNaN(health.curr_Health) && !float.IsInfinity(health.curr_Health)) ? Mathf.Clamp01(health.curr_Health) : 0f);
				float num3 = AvatarHealthResolver.ReadVitalityHealth(rm.avatar);
				float num4 = ((num3 > 0f && !float.IsNaN(num3) && !float.IsInfinity(num3)) ? num3 : value.ResolvedHealthBase);
				value.AvatarHash = num;
				value.ResolvedHealthBase = Mathf.Max(0.0001f, num4);
				value.MaxHealth = value.ResolvedHealthBase;
				value.CurrentHealth = value.ResolvedHealthBase * num2;
				_oneHpHealthSnapshots[rm] = value;
			}
			if (!_oneHpHealthSnapshots.ContainsKey(rm))
			{
				float num5 = health.max_Health;
				if (num5 <= 0f || float.IsNaN(num5) || float.IsInfinity(num5))
				{
					num5 = AvatarHealthResolver.Resolve(rm, MaxLimbHealth.Value);
				}
				float num6 = health.curr_Health;
				if (float.IsNaN(num6) || float.IsInfinity(num6))
				{
					num6 = num5;
				}
				_oneHpHealthSnapshots[rm] = new OneHpHealthSnapshot
				{
					Health = health,
					AvatarHash = num,
					MaxHealth = Mathf.Max(0.0001f, num5),
					CurrentHealth = num6,
					ResolvedHealthBase = AvatarHealthResolver.Resolve(rm, MaxLimbHealth.Value)
				};
				flag = true;
			}
			health.max_Health = 1f;
			if (health.alive && health.curr_Health > 0f && (refill || flag || health.curr_Health > 1f))
			{
				health.curr_Health = 1f;
			}
			InvalidateAvatarHealthCache(rm);
		}

		private void RestoreOneHpHealth(RigManager rm)
		{
			if ((Object)(object)rm == (Object)null || !_oneHpHealthSnapshots.TryGetValue(rm, out var value))
			{
				return;
			}
			_oneHpHealthSnapshots.Remove(rm);
			Health health = rm.health;
			int num = (((Object)(object)rm.avatar != (Object)null) ? ((Object)rm.avatar).GetHashCode() : 0);
			if (!((Object)(object)health == (Object)null) && !((Object)(object)health != (Object)(object)value.Health) && num == value.AvatarHash)
			{
				float num2 = ((health.curr_Health > 0f && !float.IsNaN(health.curr_Health) && !float.IsInfinity(health.curr_Health)) ? Mathf.Clamp01(health.curr_Health) : 0f);
				health.max_Health = Mathf.Max(0.0001f, value.MaxHealth);
				if (health.alive && health.curr_Health > 0f)
				{
					float num3 = ((value.CurrentHealth > 0f && !float.IsNaN(value.CurrentHealth) && !float.IsInfinity(value.CurrentHealth)) ? value.CurrentHealth : health.max_Health);
					health.curr_Health = Mathf.Clamp(num3 * num2, 0f, health.max_Health);
				}
				InvalidateAvatarHealthCache(rm);
			}
		}

		private void RestoreAllOneHpHealthStates()
		{
			RigManager[] array = _oneHpHealthSnapshots.Keys.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				RestoreOneHpHealth(array[i]);
			}
			_oneHpHealthSnapshots.Clear();
		}

		private void RebaseTrackedLimbHealthForOneHp(RigManager rm, bool enabling)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			InvalidateAvatarHealthCache(rm);
			if ((Object)(object)rm == (Object)null || !_playersHealth.TryGetValue(rm, out var value))
			{
				return;
			}
			for (int i = 0; i < DamageBones.Length; i++)
			{
				HumanBodyBones val = DamageBones[i];
				if (value.TryGetValue(val, out var value2))
				{
					if (value2 > 0f && !IsBoneGone(rm, val))
					{
						value[val] = (enabling ? 1f : (GetMaxHealthForBone(rm, val) * Mathf.Clamp01(value2)));
					}
					else
					{
						value[val] = 0f - GetMaxHealthForBone(rm, val);
					}
				}
			}
		}

		private void RestoreAllTrackedLimbStates(bool broadcast)
		{
			HashSet<RigManager> hashSet = new HashSet<RigManager>(_trackedRigs);
			if ((Object)(object)Player.RigManager != (Object)null)
			{
				hashSet.Add(Player.RigManager);
			}
			foreach (RigManager item in hashSet)
			{
				if ((Object)(object)item != (Object)null)
				{
					ResetLimbStats(item, restoreBody: true);
				}
			}
			_pendingNetworkStates.Clear();
			_appliedStateMasks.Clear();
			_remoteFallenPlayers.Clear();
			if (!broadcast)
			{
				return;
			}
			try
			{
				foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
				{
					if (playerID != null)
					{
						NetworkSync.SendFullState(playerID.SmallID, 0u, 0, 0);
					}
				}
			}
			catch
			{
			}
		}

		private void SetLocalMetadataValue(string key, string value)
		{
			try
			{
				PlayerMetadata metadata = LocalPlayer.Metadata;
				if (((metadata != null) ? metadata.Metadata : null) != null)
				{
					LocalPlayer.Metadata.Metadata.TrySetMetadata(key, value);
					return;
				}
			}
			catch
			{
			}
			PlayerID localID = PlayerIDManager.LocalID;
			object obj2;
			if (localID == null)
			{
				obj2 = null;
			}
			else
			{
				PlayerMetadata metadata2 = localID.Metadata;
				obj2 = ((metadata2 != null) ? metadata2.Metadata : null);
			}
			if (obj2 != null)
			{
				PlayerIDManager.LocalID.Metadata.Metadata.TrySetMetadata(key, value);
			}
		}

		private void SyncExistingRemoteMetadata()
		{
			string value = default(string);
			string value2 = default(string);
			string value3 = default(string);
			string value4 = default(string);
			RigRefs val = default(RigRefs);
			foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
			{
				if (playerID != null && !playerID.IsMe)
				{
					PlayerMetadata metadata = playerID.Metadata;
					if (((metadata != null) ? metadata.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceLimbs", ref value))
					{
						ApplyNetworkState(playerID.SmallID, value);
					}
					PlayerMetadata metadata2 = playerID.Metadata;
					if (((metadata2 != null) ? metadata2.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceLimbsEnabled", ref value2))
					{
						HandlePlayerLimbsSetting(playerID.SmallID, ParseEnabledSetting(value2));
					}
					PlayerMetadata metadata3 = playerID.Metadata;
					if (((metadata3 != null) ? metadata3.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceHealthBase", ref value3))
					{
						HandlePlayerHealthBase(playerID.SmallID, value3);
					}
					PlayerMetadata metadata4 = playerID.Metadata;
					if (((metadata4 != null) ? metadata4.Metadata : null) != null && playerID.Metadata.Metadata.TryGetMetadata("DeadPieceInteractionStats", ref value4))
					{
						HandlePlayerInteractionStats(playerID.SmallID, value4);
					}
					if (PlayerRepUtilities.TryGetReferences(playerID.SmallID, ref val) && (Object)(object)((val != null) ? val.RigManager : null) != (Object)null)
					{
						TrackRig(val.RigManager);
					}
				}
			}
		}

		public override void OnInitializeMelon()
		{
			//IL_050d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0517: Expected O, but got Unknown
			//IL_051f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0529: Expected O, but got Unknown
			Instance = this;
			WarnAboutLegacyNetworkPlugin();
			InitializeModules();
			NetworkSync.Initialize();
			_pluginCallbacksRegistered = NetworkSync.RegisterCallbacks(OnPluginLimbHit, OnPluginLimbSever, OnPluginRespawn, OnPluginBloodColor, OnPluginFullState, GetPluginStateMask, OnPluginInteractionRequest, OnPluginInteractionAuthoritative);
			NetworkSync.RegisterNpcCallbacks(OnPluginNpcInteractionRequest, OnPluginNpcInteractionAuthoritative);
			try
			{
				_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Harmony PatchAll failed: " + ex.Message);
			}
			Category = MelonPreferences.CreateCategory("DeadPiece", "DeadPiece Settings");
			HostEnabled = Category.CreateEntry<bool>("HostEnabled", true, "Host On-Off", "Only the host can enable or disable DeadPiece for the server", false, false, (ValueValidator)null, (string)null);
			LimbsEnabled = Category.CreateEntry<bool>("LimbsEnabled", true, "Limbs On-Off", "Allow your limbs to be destroyed", false, false, (ValueValidator)null, (string)null);
			OneHpMode = Category.CreateEntry<bool>("OneHpMode", false, "All Players 1 HP", "Host-only setting that gives every player and limb exactly 1 HP", false, false, (ValueValidator)null, (string)null);
			EnableLocalHealthHud = Category.CreateEntry<bool>("EnableLocalHealthHud", false, "Health HUD", "Show a compact upper-right readout for your overall and limb health", false, false, (ValueValidator)null, (string)null);
			EnablePlayerHealthBars = Category.CreateEntry<bool>("EnablePlayerHealthBars", false, "Player HP Bars", "Show overhead health bars for other players on this client", false, false, (ValueValidator)null, (string)null);
			_serverOneHpMode = OneHpMode.Value;
			MaxLimbHealth = Category.CreateEntry<float>("MaxLimbHealth", 100f, "Fallback Avatar HP", "Fallback max health when the avatar does not expose player health (limb HP scales from avatar max health)", false, false, (ValueValidator)null, (string)null);
			DamageMultiplier = Category.CreateEntry<float>("DamageMultiplier", 1f, "Damage Multiplier", "Multiplier for damage dealt to limbs", false, false, (ValueValidator)null, (string)null);
			EnableImpactBlood = Category.CreateEntry<bool>("EnableImpactBlood", true, "Severed Limb Bleed", "Tiny damage ticks at severed bone tips (vanilla impact blood)", false, false, (ValueValidator)null, (string)null);
			EnableBloodEffects = Category.CreateEntry<bool>("EnableBloodEffects", true, "Blood Effects", "Enable blood particle effects on dismemberment", false, false, (ValueValidator)null, (string)null);
			EnableGameBloodEffects = Category.CreateEntry<bool>("EnableGameBloodEffects", true, "Game Blood", "Clone already-loaded BONELAB blood VFX instead of shipping custom particles", false, false, (ValueValidator)null, (string)null);
			EnableFallbackBloodEffects = Category.CreateEntry<bool>("EnableProceduralFallbackBlood", true, "Procedural Fallback", "Always keep a lightweight procedural blood fallback when native BONELAB blood VFX cannot be spawned", false, false, (ValueValidator)null, (string)null);
			BloodColorOverride = Category.CreateEntry<string>("BloodColorOverride", "Auto", "Blood Color Override", "Force a specific blood color for all avatars (Auto = use per-avatar rules)", false, false, (ValueValidator)null, (string)null);
			EnableMovementPenalty = Category.CreateEntry<bool>("EnableMovementPenalty", true, "Movement Penalty", "Apply movement restrictions and body lowering when legs are lost", false, false, (ValueValidator)null, (string)null);
			EnableSecondLegRagdoll = Category.CreateEntry<bool>("EnableSecondLegRagdoll", false, "Second Leg Ragdoll", "Deprecated; DeadPiece no longer changes player leg physics", false, false, (ValueValidator)null, (string)null);
			SeveredLimbBleedDamagePerSecond = Category.CreateEntry<float>("SeveredLimbBleedDamagePerSecond", 0.01f, "Bleed DPS", "Damage per second at each severed limb tip", false, false, (ValueValidator)null, (string)null);
			EnableDualStickDeathReset = Category.CreateEntry<bool>("EnableDualStickDeathReset", true, "Dual Stick Death Reset", "Double-tap both thumbsticks to die and respawn (full reset)", false, false, (ValueValidator)null, (string)null);
			EnableSoundEffects = Category.CreateEntry<bool>("EnableSoundEffects", true, "Sound Effects", "Enable sound effects on dismemberment", false, false, (ValueValidator)null, (string)null);
			EnableVrEyeBlindness = Category.CreateEntry<bool>("EnableVrEyeBlindness", true, "VR Eye Blindness", "When an eye is destroyed, blackout that eye in the VR headset (local player only)", false, false, (ValueValidator)null, (string)null);
			EnableHeadSever = Category.CreateEntry<bool>("EnableHeadSever", true, "Head Sever", "Make lethal head damage use stronger decapitation effects", false, false, (ValueValidator)null, (string)null);
			EnableVisualCuts = Category.CreateEntry<bool>("EnableAvatarCutMasks", false, "Visual Cuts", "Experimental Marrow cut masks; keep off if the avatar shows square artifacts", false, false, (ValueValidator)null, (string)null);
			EnableAttackTypeBalancing = Category.CreateEntry<bool>("EnableAttackTypeBalancing", true, "Attack Type Balancing", "Use different limb damage rules for piercing, blunt, sharp, fire, and explosion-like attacks", false, false, (ValueValidator)null, (string)null);
			PiercingDamageMultiplier = Category.CreateEntry<float>("PiercingDamageMultiplier", 1.35f, "Piercing Damage", "Limb damage multiplier for piercing attacks", false, false, (ValueValidator)null, (string)null);
			BluntDamageMultiplier = Category.CreateEntry<float>("BluntDamageMultiplier", 0.65f, "Blunt Damage", "Limb damage multiplier for blunt attacks", false, false, (ValueValidator)null, (string)null);
			SlashDamageMultiplier = Category.CreateEntry<float>("SlashDamageMultiplier", 1.55f, "Slash Damage", "Limb damage multiplier for slicing attacks", false, false, (ValueValidator)null, (string)null);
			StabDamageMultiplier = Category.CreateEntry<float>("StabDamageMultiplier", 1.75f, "Stab Damage", "Limb damage multiplier for stabbing attacks", false, false, (ValueValidator)null, (string)null);
			FireDamageMultiplier = Category.CreateEntry<float>("FireDamageMultiplier", 0.25f, "Fire Damage", "Limb damage multiplier for fire attacks", false, false, (ValueValidator)null, (string)null);
			ExplosionRadius = Category.CreateEntry<float>("ExplosionRadius", 2.25f, "Explosion Radius", "Default blast radius for limb damage when the weapon does not provide one", false, false, (ValueValidator)null, (string)null);
			EnableBlastLimbDamage = Category.CreateEntry<bool>("EnableBlastLimbDamage", true, "Grenade & Mine Limbs", "Radial limb damage from grenades, mines, and other explosions", false, false, (ValueValidator)null, (string)null);
			ExplosivePatches.TryRegister(_harmony);
			FusionRigLoadingFix.TryRegister(_harmony);
			MultiplayerHooking.OnPlayerJoined += new PlayerUpdate(OnPlayerJoined);
			MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeft);
			PlayerID.OnMetadataChangedEvent += OnMetadataChanged;
			NetworkPlayer.OnNetworkRigCreated += OnNetworkRigCreated;
			PropertyInfo property = typeof(MelonUtils).GetProperty("UserDataDirectory", BindingFlags.Static | BindingFlags.Public);
			_soundFolder = Path.Combine((string)property.GetValue(null), "DeadPiece", "Sounds");
			try
			{
				if (!Directory.Exists(_soundFolder))
				{
					Directory.CreateDirectory(_soundFolder);
				}
			}
			catch (Exception ex2)
			{
				MelonLogger.Warning("Sound folder setup failed: " + ex2.Message);
			}
			LoadBloodColorOverrides();
		}

		private static void WarnAboutLegacyNetworkPlugin()
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					string text = assemblies[i]?.GetName()?.Name ?? string.Empty;
					if (text.Equals("DeadPiecePlugin", StringComparison.OrdinalIgnoreCase))
					{
						MelonLogger.Error("[DeadPiece] Legacy Plugins/DeadPiecePlugin.dll is loaded. Remove it from every client; it conflicts with the integrated Fusion transport and can break joins.");
						break;
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[DeadPiece] Could not check for the legacy network plugin: " + ex.Message);
			}
		}

		public override void OnLateInitializeMelon()
		{
			if (!NetworkSync.RegisterHandlers())
			{
				MelonLogger.Error("[DeadPiece] Fusion network handlers are unavailable; multiplayer sync is disabled.");
			}
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			RigManager rigManager = Player.RigManager;
			ResetHealthDisplaysForScene();
			ResetNpcRuntimeForScene();
			CancelManagedFatalOutcome((Object)(object)_managedFatalHealth != (Object)null && _managedFatalHealth.alive);
			RestoreAllOneHpHealthStates();
			RestoreAllLegColliderStates();
			ResetLimbStats(rigManager, restoreBody: true);
			RestoreDeadpieceLegUsage(rigManager);
			RestoreNoLegJump(rigManager);
			RestoreNoLegFallenState(rigManager);
			_sceneGeneration++;
			_locomotionRecoveryToken++;
			_playersHealth.Clear();
			_colliderBoneMap.Clear();
			_colliderBoneDistanceMap.Clear();
			_originalScales.Clear();
			_avatarScaleBaselines.Clear();
			_hiddenBoneMasks.Clear();
			_lastLimbDamageTimes.Clear();
			_legColliderOriginalStates.Clear();
			_lastHitEffectTimes.Clear();
			_lastStumpBloodEffectTimes.Clear();
			_stumpBloodStartTimes.Clear();
			_lastAvatarHashes.Clear();
			_avatarHealthBaseCache.Clear();
			_avatarHealthHashCache.Clear();
			_trackedRigs.Clear();
			_rigCache.Clear();
			_localRigInitialized = false;
			_lastTrackedBodyHealth = -1f;
			_lastAutoInjectorUseTime = -999f;
			_lastLocalRespawnRequestTime = -999f;
			_startupScheduled = false;
			_pendingHealthRefresh.Clear();
			_pendingRespawnPlayers.Clear();
			_activeRespawnRestoreWorkers.Clear();
			_hardRespawnRecoveryPlayers.Clear();
			_localRespawnRestoreWorkerActive = false;
			_hardLocalRespawnRecovery = false;
			_healingRestoreInProgress = false;
			_cachedRm = null;
			_rigWasDead.Clear();
			_pendingNetworkStates.Clear();
			_lastAppliedNetworkSequence.Clear();
			_appliedStateMasks.Clear();
			_remoteFallenPlayers.Clear();
			ClearPlayerInteractionState();
			_bloodColorOverrides.Clear();
			_bloodColorIndexOverrides.Clear();
			_playerLimbsEnabled.Clear();
			_networkHealthBaseOverrides.Clear();
			_networkInteractionStats.Clear();
			_oneHpHealthSnapshots.Clear();
			_settingsPublished = false;
			_healthBasePublished = false;
			_interactionStatsPublished = false;
			_lastPublishedHealthBase = -1f;
			_wasHostForSettings = false;
			_serverEnabled = HostEnabled?.Value ?? true;
			_serverOneHpMode = OneHpMode?.Value ?? false;
			_lastLeftArmUsable = true;
			_lastRightArmUsable = true;
			_localStateSequence = 0;
			_lastSentStateMask = uint.MaxValue;
			_lastStateSyncTime = -999f;
			_dualStickResetTapCount = 0;
			_fleshCardFound = false;
			_gameBloodParticlePrefab = null;
			_lastGameBloodParticleSearchTime = -999f;
			_loggedMissingGameBloodParticles = false;
			_loggedVanillaBloodSpawnable = false;
			_loggedVanillaBloodSpawnableFailure = false;
			_smallBloodSplatterSpawnable = null;
			_largeBloodSplatterSpawnable = null;
			_bloodBagBlasterSpawnable = null;
			_vanillaBloodSpawnablesRegistered = false;
			_locomotionStateSaved = false;
			_deadpieceNoLegUsageApplied = false;
			_deadpieceUsageRig = null;
			_deadpieceUsageHealth = null;
			_deadpieceUsageAvatarHash = 0;
			_deadpieceJumpBlocked = false;
			_deadpieceJumpOwnerRig = null;
			_deadpieceJumpRig = null;
			_deadpieceFallenStateApplied = false;
			_savedUsageHips = 1f;
			_savedUsageSpine = 1f;
			_savedUsageLegLf = 1f;
			_savedUsageLegRt = 1f;
			_savedUsageArmLf = 1f;
			_savedUsageArmRt = 1f;
			SuppressNoLegRagdollForRespawn();
			ResetLocalNoLegRagdollPermission();
			_savedLocomotionMaxVelocity = 4.5f;
			_savedLocomotionCurrentMaxVelocity = 4.5f;
			_savedLocomotionJumpEnabled = true;
			_savedLocomotionDoubleJump = true;
			ClearVrEyeCameraCache();
			ForceClearNoLegRagdollState(rigManager);
			_leftHandDisabledByDeadPiece = false;
			_rightHandDisabledByDeadPiece = false;
			NetworkSync.ResetSessionState();
			DamageVolumeContextPatch.ResetTracking();
			if (_dismemberSounds.Count == 0)
			{
				MelonCoroutines.Start(LoadDismemberSounds());
			}
		}

		private void HandleAvatarSwap(RigManager rm)
		{
			if ((Object)(object)rm == (Object)null || (Object)(object)rm.avatar == (Object)null)
			{
				return;
			}
			int hashCode = ((Object)rm.avatar).GetHashCode();
			int value;
			bool flag = _lastAvatarHashes.TryGetValue(rm, out value);
			if ((flag && value == hashCode) || (Object)(object)rm.avatar.animator == (Object)null)
			{
				return;
			}
			if ((Object)(object)rm == (Object)(object)Player.RigManager && _deadpieceNoLegUsageApplied && _deadpieceUsageAvatarHash != hashCode)
			{
				RestoreDeadpieceLegUsage(_deadpieceUsageRig);
			}
			if ((Object)(object)rm == (Object)(object)Player.RigManager && flag && _deadpieceJumpBlocked)
			{
				RestoreNoLegJump(_deadpieceJumpOwnerRig);
			}
			if (OnAvatarSwapped(rm))
			{
				_lastAvatarHashes[rm] = hashCode;
				MaintainOneHpHealth(rm, refill: true);
				if ((Object)(object)rm == (Object)(object)Player.RigManager && NetworkInfo.HasServer && PlayerIDManager.LocalID != null)
				{
					PublishLocalHealthBase(force: true);
					PublishLocalInteractionStats(force: true);
				}
			}
		}

		private void PrepareAvatarSwap(RigManager rm)
		{
			if (!((Object)(object)rm == (Object)null))
			{
				RestoreLegColliderState(rm);
				_originalScales.Remove(rm);
				_colliderBoneMap.Remove(rm);
				_colliderBoneDistanceMap.Remove(rm);
				InvalidateAvatarHealthCache(rm);
				InvalidateRigCache(rm);
				_lastAvatarHashes.Remove(rm);
				if ((Object)(object)rm == (Object)(object)Player.RigManager)
				{
					_locomotionStateSaved = false;
				}
			}
		}

		private bool OnAvatarSwapped(RigManager rm)
		{
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			if (!IsRigAvatarReadyForLimbRestore(rm))
			{
				return false;
			}
			PrepareAvatarSwap(rm);
			bool value = default(bool);
			uint num = ((!((Object)(object)rm.health != (Object)null && rm.health.alive && _rigWasDead.TryGetValue(rm, out value) && value)) ? GetStateMask(rm) : 0u);
			BuildColliderBoneMap(rm);
			RebuildBoneCache(rm);
			LoadAvatarScaleBaseline(rm);
			if (IsRigLimbDamageBlocked(rm))
			{
				ResetLimbStats(rm, restoreBody: true);
				if ((Object)(object)rm == (Object)(object)Player.RigManager)
				{
					UpdateLimbPenaltyFlag(rm);
				}
				return true;
			}
			HumanBodyBones[] damageBones = DamageBones;
			ResetLimbStats(rm, restoreBody: false);
			if (num == 0)
			{
				_hiddenBoneMasks.Remove(rm);
			}
			else
			{
				_hiddenBoneMasks[rm] = num;
			}
			if (_playersHealth.TryGetValue(rm, out var value2))
			{
				for (int i = 0; i < damageBones.Length; i++)
				{
					HumanBodyBones val = damageBones[i];
					Transform cachedBoneTransform = GetCachedBoneTransform(rm, val);
					bool flag = (num & (uint)(1 << i)) != 0;
					if ((Object)(object)cachedBoneTransform != (Object)null && flag)
					{
						cachedBoneTransform.localScale = Vector3.zero;
					}
					else if ((Object)(object)cachedBoneTransform != (Object)null && ShouldRepairBoneScale(rm, val, cachedBoneTransform))
					{
						cachedBoneTransform.localScale = ResolveRestorableBoneScale(rm, val, cachedBoneTransform);
					}
					if (flag)
					{
						value2[val] = 0f - GetMaxHealthForBone(rm, val);
					}
				}
			}
			RebuildLimbGoneFlags(rm);
			SyncLegColliderState(rm);
			RefreshLimbMaxHealth(rm);
			ScheduleDelayedHealthRefresh(rm);
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				UpdateLimbPenaltyFlag(rm);
				SetHandUsable(Player.LeftHand, !IsLeftArmGone(rm));
				SetHandUsable(Player.RightHand, !IsRightArmGone(rm));
				EnforceLocalFatalState(rm);
			}
			return true;
		}

		public void OnHealthAvatarChanging(Health health)
		{
			if (!((Object)(object)health == (Object)null))
			{
				RigManager rigFromHealth = GetRigFromHealth(health);
				RestoreOneHpHealth(rigFromHealth);
				RestoreOutgoingAvatarScales(rigFromHealth);
				if (_deadpieceNoLegUsageApplied && (Object)(object)_deadpieceUsageHealth == (Object)(object)health)
				{
					RestoreDeadpieceLegUsage(_deadpieceUsageRig);
				}
				if (_deadpieceJumpBlocked && (Object)(object)_deadpieceJumpOwnerRig == (Object)(object)rigFromHealth)
				{
					RestoreNoLegJump(rigFromHealth);
				}
				PrepareAvatarSwap(rigFromHealth);
			}
		}

		public void OnHealthAvatarSet(Health health, Avatar avatar)
		{
			RigManager rigFromHealth = GetRigFromHealth(health);
			if (!((Object)(object)rigFromHealth == (Object)null))
			{
				TrackRig(rigFromHealth);
				_lastAvatarHashes.Remove(rigFromHealth);
				HandleAvatarSwap(rigFromHealth);
				MaintainOneHpHealth(rigFromHealth, refill: true);
				ScheduleDelayedHealthRefresh(rigFromHealth);
				if ((Object)(object)rigFromHealth == (Object)(object)Player.RigManager && NetworkInfo.HasServer && PlayerIDManager.LocalID != null)
				{
					PublishLocalHealthBase(force: true);
					PublishLocalInteractionStats(force: true);
				}
			}
		}

		private RigManager GetRigFromHealth(Health health)
		{
			if ((Object)(object)health == (Object)null)
			{
				return null;
			}
			return health._rigManager ?? ((Component)health).GetComponentInParent<RigManager>();
		}

		private void ScheduleDelayedHealthRefresh(RigManager rm)
		{
			if (!((Object)(object)rm == (Object)null) && _pendingHealthRefresh.Add(rm))
			{
				MelonCoroutines.Start(DelayedRefreshLimbHealth(rm));
			}
		}

		private IEnumerator DelayedRefreshLimbHealth(RigManager rm)
		{
			yield return null;
			yield return null;
			_pendingHealthRefresh.Remove(rm);
			if (!((Object)(object)rm == (Object)null))
			{
				InvalidateAvatarHealthCache(rm);
				if (!IsRigLimbDamageBlocked(rm))
				{
					RefreshLimbMaxHealth(rm);
				}
			}
		}

		public void EnsureRigInitialized(RigManager rm)
		{
			RigRuntimeCache orCreateRigCache = GetOrCreateRigCache(rm);
			if (orCreateRigCache.Initialized && _playersHealth.ContainsKey(rm) && _colliderBoneMap.ContainsKey(rm))
			{
				TrackRig(rm);
				return;
			}
			if (!_playersHealth.ContainsKey(rm))
			{
				ResetLimbStats(rm, restoreBody: false);
			}
			if (!_colliderBoneMap.ContainsKey(rm))
			{
				BuildColliderBoneMap(rm);
			}
			RebuildBoneCache(rm);
			orCreateRigCache.Initialized = true;
			TrackRig(rm);
		}

		private void ResetLimbStats(RigManager rm, bool restoreBody)
		{
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rm == (Object)null || (Object)(object)rm.avatar == (Object)null || (Object)(object)rm.avatar.animator == (Object)null)
			{
				return;
			}
			if (restoreBody && (Object)(object)rm == (Object)(object)Player.RigManager && _managedFatalOutcome != ManagedFatalOutcome.None)
			{
				CancelManagedFatalOutcome((Object)(object)_managedFatalHealth != (Object)null && _managedFatalHealth.alive);
			}
			InvalidateAvatarHealthCache(rm);
			uint stateMask = GetStateMask(rm);
			_lastLimbDamageTimes.Remove(rm);
			bool flag = IsArmGoneInMask(stateMask, left: true);
			bool flag2 = IsArmGoneInMask(stateMask, left: false);
			bool flag3 = AreBothLegsGoneInMask(stateMask);
			if (!_playersHealth.ContainsKey(rm))
			{
				_playersHealth[rm] = new Dictionary<HumanBodyBones, float>();
			}
			if (!_originalScales.ContainsKey(rm))
			{
				int hashCode = ((Object)rm.avatar).GetHashCode();
				if (_avatarScaleBaselines.TryGetValue(hashCode, out var value))
				{
					_originalScales[rm] = new Dictionary<HumanBodyBones, Vector3>(value);
				}
				else
				{
					_originalScales[rm] = new Dictionary<HumanBodyBones, Vector3>();
				}
			}
			HumanBodyBones[] damageBones = DamageBones;
			RebuildBoneCache(rm);
			RigRuntimeCache orCreateRigCache = GetOrCreateRigCache(rm);
			ResetLimbDamageBlockCache(rm);
			Transform[] boneTransforms = orCreateRigCache.BoneTransforms;
			HashSet<HumanBodyBones> hashSet = new HashSet<HumanBodyBones>();
			for (int i = 0; i < damageBones.Length; i++)
			{
				HumanBodyBones val = damageBones[i];
				Transform val2 = ((boneTransforms != null && i < boneTransforms.Length) ? boneTransforms[i] : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					hashSet.Add(val);
					bool flag4 = restoreBody && (IsBoneInMask(stateMask, val) || ShouldRepairBoneScale(rm, val, val2));
					CaptureOriginalBoneScale(rm, val, val2);
					_playersHealth[rm][val] = GetMaxHealthForBone(rm, val);
					if (flag4)
					{
						val2.localScale = ResolveRestorableBoneScale(rm, val, val2);
					}
				}
			}
			HumanBodyBones[] array = _playersHealth[rm].Keys.ToArray();
			for (int j = 0; j < array.Length; j++)
			{
				if (!hashSet.Contains(array[j]))
				{
					_playersHealth[rm].Remove(array[j]);
				}
			}
			if (restoreBody)
			{
				ClearAppliedLimbState(rm);
				_hiddenBoneMasks.Remove(rm);
			}
			RebuildLimbGoneFlags(rm);
			if (restoreBody)
			{
				ClearStumpBloodState(rm);
				if ((Object)(object)rm == (Object)(object)Player.RigManager)
				{
					ClearVrEyeCameraCache();
					_lastAppliedLeftEyeBlind = false;
					_lastAppliedRightEyeBlind = false;
					UpdateVrEyeBlindness(rm);
					RestoreLocomotion();
					RestoreOriginalLimits(rm);
					ResetLegRagdoll(rm);
					RestoreDeadpieceLegUsage(rm);
					RestoreNoLegJump(rm);
					RestoreNoLegFallenState(rm);
					if (flag3)
					{
						ReleaseDeadpieceCrouchState(rm);
					}
					if (flag || _leftHandDisabledByDeadPiece)
					{
						SetHandUsable(Player.LeftHand, usable: true);
					}
					if (flag2 || _rightHandDisabledByDeadPiece)
					{
						SetHandUsable(Player.RightHand, usable: true);
					}
					_lastLeftArmUsable = true;
					_lastRightArmUsable = true;
				}
				ResetVisualDamage(rm);
			}
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				_lastTrackedBodyHealth = -1f;
				UpdateRigMissingLimbsFlag(rm);
				SetLocalMetadata("");
			}
			GetAvatarHealthBase(rm);
			RebuildBoneCache(rm);
			UpdateRigMissingLimbsFlag(rm);
			SyncLegColliderState(rm);
			if (restoreBody && (Object)(object)rm != (Object)(object)Player.RigManager && IsRemotePlayerRig(rm))
			{
				ApplyRemoteLegEffects(rm);
			}
			BuildColliderBoneMap(rm);
		}

		private void TryCacheFleshSurface()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			if (_fleshCardFound)
			{
				return;
			}
			try
			{
				AssetWarehouse instance = AssetWarehouse.Instance;
				if (instance == null || !AssetWarehouse.ready)
				{
					return;
				}
				SurfaceDataCard val = default(SurfaceDataCard);
				if (instance.TryGetDataCard<SurfaceDataCard>(new Barcode("SLZ.Backlot.SurfaceDataCard.Blood"), ref val) && (Object)(object)val != (Object)null)
				{
					_fleshSurfaceCard = new DataCardReference<SurfaceDataCard>(((Scannable)val).Barcode);
					_fleshCardFound = true;
					MelonLogger.Msg(ConsoleColor.Gray, "Found vanilla Blood surface card: SLZ.Backlot.SurfaceDataCard.Blood");
					return;
				}
				SurfaceDataC