Decompiled source of DeadPiece v0.4.1

Deadpiece.dll

Decompiled 12 hours 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.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Combat;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSLZ.VRMK;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using LabFusion.Entities;
using LabFusion.Network;
using LabFusion.Network.Serialization;
using LabFusion.Player;
using LabFusion.Representation;
using LabFusion.SDK.Modules;
using LabFusion.Utilities;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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.4.1", "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 MinVitalityHealth = 25f;

		public const float MaxVitalityHealth = 200f;

		public const float MaxSanityHealth = 500f;

		public const float DefaultUnsetHealth = 100f;

		public static float Resolve(RigManager rm, float fallbackHp)
		{
			if ((Object)(object)rm == (Object)null)
			{
				return Mathf.Max(1f, fallbackHp);
			}
			float num = Mathf.Max(1f, fallbackHp);
			float num2 = ReadMarrowHealth(rm.health);
			float num3 = ReadVitalityHealth(rm.avatar);
			if (num2 > 0f)
			{
				if (num3 > 0f && 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;
			}
			if (vitality > 2f)
			{
				return ClampHealth(vitality, 200f);
			}
			return Mathf.Lerp(25f, 200f, Mathf.Clamp01(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 bool LooksLikeStaleDefaultHealth(float marrowHp, float vitalityHp)
		{
			if (Mathf.Abs(marrowHp - 100f) > 0.5f)
			{
				return false;
			}
			return Mathf.Abs(vitalityHp - marrowHp) > 12f;
		}

		private static float ClampHealth(float hp, float fallback)
		{
			if (hp <= 0f || float.IsNaN(hp) || float.IsInfinity(hp))
			{
				return fallback;
			}
			if (hp > 500f)
			{
				hp = 500f;
			}
			return Mathf.Max(1f, hp);
		}
	}
	[HarmonyPatch(typeof(BaseController), "GetThumbStickAxis")]
	internal static class RightStickPatch
	{
		private static void Postfix(BaseController __instance, ref Vector2 __result)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if (!((Object)(object)__instance == (Object)null) && (int)__instance.handedness == 2)
			{
				RigManager rigManager = Player.RigManager;
				if (!((Object)(object)rigManager == (Object)null) && DeadPieceMod.Instance != null && DeadPieceMod.Instance.ShouldApplyLocalNoLegEffects(rigManager) && !DeadPieceMod.Instance.IsAutoInjectorUseActiveOrRecent() && !DeadPieceMod.Instance.IsCrouchForcingSuppressed())
				{
					__result.y = 0f;
				}
			}
		}
	}
	[HarmonyPatch(typeof(RemapRig), "CrouchHold")]
	internal static class CrouchHoldPostfixPatch
	{
		private static void Postfix(RemapRig __instance)
		{
			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.IsCrouchForcingSuppressed() && !DeadPieceMod.Instance.IsHoldingNimbusGun(rigManager))
			{
				__instance._spineCrouchOff = 0.8f;
				__instance._crouchTarget = 0.8f;
				__instance.vertState = (VertState)3;
			}
		}
	}
	[HarmonyPatch(typeof(RemapRig), "OnAfterFixedUpdate")]
	internal static class ForceCrouchHeight
	{
		private const float TARGET_CROUCH = 0.8f;

		private const float LERP_SPEED = 8f;

		private static void Postfix(RemapRig __instance)
		{
			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.IsCrouchForcingSuppressed() && !DeadPieceMod.Instance.IsHoldingNimbusGun(rigManager))
			{
				float num = Time.deltaTime * 8f;
				__instance._spineCrouchOff = Mathf.Lerp(__instance._spineCrouchOff, 0.8f, num);
				__instance._crouchTarget = Mathf.Lerp(__instance._crouchTarget, 0.8f, num);
				__instance.vertState = (VertState)3;
			}
		}
	}
	[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);
			}
		}
	}
	public class DamageVolume : MonoBehaviour
	{
		private float _damagePerSecond;

		private float _lifetime;

		private float _spawnTime;

		private float _nextTickTime;

		private bool _initialized;

		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)
		{
			if (!_initialized || (Object)(object)other == (Object)null || _damagePerSecond <= 0f || Time.time < _nextTickTime)
			{
				return;
			}
			_nextTickTime = Time.time + 0.25f;
			try
			{
				float num = _damagePerSecond * 0.25f;
				if (num <= 0f)
				{
					return;
				}
				Transform val = ((Component)other).transform;
				for (int i = 0; i < 6; i++)
				{
					if (!((Object)(object)val != (Object)null))
					{
						break;
					}
					RigManager component = ((Component)val).GetComponent<RigManager>();
					if ((Object)(object)component != (Object)null && (Object)(object)component.health != (Object)null && component.health.curr_Health > 0f)
					{
						component.health.Death();
						break;
					}
					val = val.parent;
				}
			}
			catch
			{
			}
		}
	}
	public class DeadPieceMod : MelonMod
	{
		private struct PendingNetworkState
		{
			public ushort Sequence;

			public uint GoneBoneMask;

			public bool IsFallen;
		}

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

		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;
		}

		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> EnableVisualHitMarks;

		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> SharpSeverChance;

		public static MelonPreferences_Entry<float> ExplosionDamageThreshold;

		public static MelonPreferences_Entry<float> ExplosionRadius;

		public static MelonPreferences_Entry<bool> EnableBlastLimbDamage;

		public static MelonPreferences_Entry<bool> EnableExplosiveHarmonyHooks;

		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 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 _locomotionStateSaved;

		private bool _deadpieceNoLegUsageApplied;

		private bool _deadpieceJumpBlocked;

		private bool _deadpieceFallenStateApplied;

		private float _suppressNoLegRagdollUntil = -999f;

		private float _suppressCrouchForcingUntil = -999f;

		private float _lastLocalRespawnTime = -999f;

		private float _lastLocalBothLegsSeveredTime = -999f;

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

		private const string MetadataKey = "DeadPieceLimbs";

		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<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 float _lastRadialBlastDedupeTime = -999f;

		private Vector3 _lastRadialBlastDedupeOrigin;

		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;

		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 float InvincibleHealthThreshold = 9999f;

		private const int LimbDamageBlockRecheckFrames = 30;

		private const float SlowUpdateInterval = 0.5f;

		private const float AvatarSwapCheckInterval = 0.2f;

		private const float MinLimbHealthFloorAbsolute = 8f;

		private const float MinLimbHealthFloorRatio = 0.15f;

		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 float _delayedDeathTime;

		private RigManager _delayedDeathRm;

		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 bool _localRigInitialized;

		private bool _startupScheduled;

		private bool _wasConnected;

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

		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;

		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()
		{
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			NetworkSync.ProcessMainThreadQueue();
			_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)
				{
					MelonLogger.Msg(ConsoleColor.Gray, "Local player RigManager changed, performing respawn cleanup.");
					RemoveRigFromCaches(cachedRm);
					SuppressNoLegRagdollForRespawn();
					ResetLocalNoLegRagdollPermission();
					StabilizeRespawnLocomotion(rigManager);
					RestoreDeadpieceLegUsage(rigManager);
					ResetLimbStats(rigManager, restoreBody: true);
					StabilizeRespawnLocomotion(rigManager);
					_lastTrackedBodyHealth = -1f;
					UpdateLimbPenaltyFlag(rigManager);
					SyncFullState();
					NetworkSync.SendRespawn(PlayerIDManager.LocalSmallID);
				}
			}
			if (!_localRigInitialized)
			{
				if (!_startupScheduled)
				{
					_startupScheduled = true;
					MelonCoroutines.Start(DeferredStartup(rigManager));
				}
				return;
			}
			float deltaTime = Time.deltaTime;
			_avatarSwapTimer += deltaTime;
			if (_avatarSwapTimer >= 0.2f)
			{
				_avatarSwapTimer = 0f;
				HandleAvatarSwap(rigManager);
			}
			HandleRespawnRestore(rigManager);
			HandleStickInputs(deltaTime);
			UpdateAutoInjectorUseWindow();
			UpdateExplosiveUseWindow();
			ProcessPlayerHealing(rigManager);
			ProcessSeveredLimbBleed(rigManager, deltaTime);
			if ((Object)(object)rigManager == (Object)(object)Player.RigManager)
			{
				ApplyMovementConstraints(rigManager);
				EnforceBothLegsGoneState(rigManager);
				if (IsBothLegsGone(rigManager) && (Object)(object)rigManager.physicsRig != (Object)null)
				{
					float num = Mathf.Pow(0.01f, Time.deltaTime);
					Rigidbody rbKnee = rigManager.physicsRig.rbKnee;
					Rigidbody rbFeet = rigManager.physicsRig.rbFeet;
					if ((Object)(object)rbKnee != (Object)null)
					{
						rbKnee.velocity *= num;
						rbKnee.angularVelocity *= num;
					}
					if ((Object)(object)rbFeet != (Object)null)
					{
						rbFeet.velocity *= num;
						rbFeet.angularVelocity *= num;
					}
				}
			}
			_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
					{
						SyncFullState();
						BroadcastBloodColor();
					}
					catch
					{
					}
				}
				else if (!hasServer && _wasConnected)
				{
					_wasConnected = false;
					NetworkSync.ResetSessionState();
				}
				if (hasServer)
				{
					ResyncLocalStateIfNeeded();
					HandleAllRigsRespawnRestore();
					if (_pendingNetworkStates.Count > 0)
					{
						ApplyPendingNetworkStates();
					}
				}
				EnforceInvincibleRigsForAllTracked();
				ProcessRemoteRigsStumpBlood(deltaTime);
				PruneTrackedRigs();
				if (EnableVrEyeBlindness.Value)
				{
					UpdateVrEyeBlindness(rigManager);
				}
			}
			if ((_updateFrame & 7) == 0 && NetworkInfo.HasServer)
			{
				EnforceDismemberedBones();
				EnforceAppliedStates();
				EnforceRemoteFallenState();
			}
			if ((_updateFrame & 3) == 0 && EnableMovementPenalty.Value)
			{
				EnforceLocalHandUsability();
			}
			if ((Object)(object)_delayedDeathRm != (Object)null && Time.time >= _delayedDeathTime)
			{
				RigManager delayedDeathRm = _delayedDeathRm;
				_delayedDeathRm = null;
				if ((Object)(object)delayedDeathRm != (Object)null && (Object)(object)delayedDeathRm.health != (Object)null)
				{
					delayedDeathRm.health.Death();
				}
			}
		}

		public override void OnLateUpdate()
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			RigManager rigManager = Player.RigManager;
			if (!((Object)(object)rigManager == (Object)null) && EnableMovementPenalty.Value && IsBothLegsGone(rigManager) && !((Object)(object)rigManager.avatar == (Object)null) && !((Object)(object)rigManager.avatar.animator == (Object)null))
			{
				Transform boneTransform = rigManager.avatar.animator.GetBoneTransform((HumanBodyBones)0);
				Transform boneTransform2 = rigManager.avatar.animator.GetBoneTransform((HumanBodyBones)10);
				if ((Object)(object)boneTransform != (Object)null && (Object)(object)boneTransform2 != (Object)null)
				{
					Vector3 position = boneTransform2.position;
					boneTransform.position += Vector3.down * 0.5f;
					boneTransform2.position = position;
				}
			}
		}

		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();
			}
			_localRigInitialized = true;
			SetupBoneMenu();
		}

		private void SetupBoneMenu()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: 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);
				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();
		}

		private void SyncExistingRemoteMetadata()
		{
			string value = 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);
					}
					if (PlayerRepUtilities.TryGetReferences(playerID.SmallID, ref val) && (Object)(object)((val != null) ? val.RigManager : null) != (Object)null)
					{
						TrackRig(val.RigManager);
					}
				}
			}
		}

		public override void OnInitializeMelon()
		{
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c6: Expected O, but got Unknown
			//IL_04ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d8: Expected O, but got Unknown
			Instance = this;
			WarnAboutLegacyNetworkPlugin();
			InitializeModules();
			NetworkSync.Initialize();
			_pluginCallbacksRegistered = NetworkSync.RegisterCallbacks(OnPluginLimbHit, OnPluginLimbSever, OnPluginRespawn, OnPluginBloodColor, OnPluginFullState, GetPluginStateMask);
			try
			{
				_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Harmony PatchAll failed: " + ex.Message);
			}
			Category = MelonPreferences.CreateCategory("DeadPiece", "DeadPiece Settings");
			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);
			EnableVisualHitMarks = Category.CreateEntry<bool>("EnableVisualHitMarks", true, "Visual Hit Marks", "Try to use Marrow's visual damage hit system on normal limb damage", 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);
			SharpSeverChance = Category.CreateEntry<float>("SharpSeverChance", 0.35f, "Sharp Sever Chance", "Chance for slicing or stabbing attacks to instantly sever a damaged limb", false, false, (ValueValidator)null, (string)null);
			ExplosionDamageThreshold = Category.CreateEntry<float>("ExplosionDamageThreshold", 35f, "Explosion Threshold", "Minimum raw damage for grenade/mine/blast attacks to count as radial limb damage", 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);
			EnableExplosiveHarmonyHooks = Category.CreateEntry<bool>("EnableExplosiveHarmonyHooks", true, "Explosive Hooks", "Patch grenade (RigidbodyProjectile) and blast (SimpleExplosionForce) detonations", false, false, (ValueValidator)null, (string)null);
			if (EnableExplosiveHarmonyHooks.Value)
			{
				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)
		{
			_playersHealth.Clear();
			_colliderBoneMap.Clear();
			_colliderBoneDistanceMap.Clear();
			_originalScales.Clear();
			_lastHitEffectTimes.Clear();
			_lastStumpBloodEffectTimes.Clear();
			_stumpBloodStartTimes.Clear();
			_lastAvatarHashes.Clear();
			_avatarHealthBaseCache.Clear();
			_avatarHealthHashCache.Clear();
			_trackedRigs.Clear();
			_rigCache.Clear();
			_localRigInitialized = false;
			_lastTrackedBodyHealth = -1f;
			_lastAutoInjectorUseTime = -999f;
			_startupScheduled = false;
			_pendingHealthRefresh.Clear();
			_cachedRm = null;
			_rigWasDead.Clear();
			_pendingNetworkStates.Clear();
			_lastAppliedNetworkSequence.Clear();
			_remoteFallenPlayers.Clear();
			_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;
			_deadpieceJumpBlocked = false;
			_deadpieceFallenStateApplied = false;
			SuppressNoLegRagdollForRespawn();
			ResetLocalNoLegRagdollPermission();
			_savedLocomotionMaxVelocity = 4.5f;
			_savedLocomotionCurrentMaxVelocity = 4.5f;
			_savedLocomotionJumpEnabled = true;
			ClearVrEyeCameraCache();
			ForceClearNoLegRagdollState(Player.RigManager);
			ResetLimbStats(Player.RigManager, restoreBody: true);
			if (_dismemberSounds.Count == 0)
			{
				MelonCoroutines.Start(LoadDismemberSounds());
			}
		}

		private void HandleAvatarSwap(RigManager rm)
		{
			if (!((Object)(object)rm == (Object)null) && !((Object)(object)rm.avatar == (Object)null))
			{
				int hashCode = ((Object)rm.avatar).GetHashCode();
				if (!_lastAvatarHashes.TryGetValue(rm, out var value) || value != hashCode)
				{
					_lastAvatarHashes[rm] = hashCode;
					OnAvatarSwapped(rm);
				}
			}
		}

		private void OnAvatarSwapped(RigManager rm)
		{
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: 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_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: 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;
			}
			_colliderBoneMap.Remove(rm);
			InvalidateAvatarHealthCache(rm);
			InvalidateRigCache(rm);
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				_locomotionStateSaved = false;
			}
			_colliderBoneDistanceMap.Remove(rm);
			if (IsRigLimbDamageBlocked(rm))
			{
				ResetLimbStats(rm, restoreBody: true);
				if ((Object)(object)rm == (Object)(object)Player.RigManager)
				{
					UpdateLimbPenaltyFlag(rm);
				}
				return;
			}
			BuildColliderBoneMap(rm);
			RebuildBoneCache(rm);
			if (!_originalScales.ContainsKey(rm))
			{
				_originalScales[rm] = new Dictionary<HumanBodyBones, Vector3>();
			}
			HashSet<HumanBodyBones> hashSet = new HashSet<HumanBodyBones>();
			HumanBodyBones[] damageBones = DamageBones;
			HumanBodyBones[] array = damageBones;
			foreach (HumanBodyBones val in array)
			{
				Transform cachedBoneTransform = GetCachedBoneTransform(rm, val);
				if ((Object)(object)cachedBoneTransform == (Object)null)
				{
					continue;
				}
				if (_playersHealth.TryGetValue(rm, out var value) && value.TryGetValue(val, out var value2) && value2 <= 0f)
				{
					hashSet.Add(val);
					if (cachedBoneTransform.localScale != Vector3.zero)
					{
						_originalScales[rm][val] = cachedBoneTransform.localScale;
					}
					cachedBoneTransform.localScale = Vector3.zero;
				}
				else if (!_originalScales[rm].ContainsKey(val) || _originalScales[rm][val] == Vector3.zero)
				{
					_originalScales[rm][val] = ((cachedBoneTransform.localScale == Vector3.zero) ? Vector3.one : cachedBoneTransform.localScale);
				}
			}
			RestoreRigMeshes(rm);
			ResetLimbStats(rm, restoreBody: false);
			if (_playersHealth.TryGetValue(rm, out var value3))
			{
				foreach (HumanBodyBones item in hashSet)
				{
					value3[item] = 0f - GetMaxHealthForBone(rm, item);
				}
			}
			RefreshLimbMaxHealth(rm);
			ScheduleDelayedHealthRefresh(rm);
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				UpdateLimbPenaltyFlag(rm);
				SetHandUsable(Player.LeftHand, !IsLeftArmGone(rm));
				SetHandUsable(Player.RightHand, !IsRightArmGone(rm));
			}
		}

		public void OnHealthAvatarSet(Health health, Avatar avatar)
		{
			RigManager rigFromHealth = GetRigFromHealth(health);
			if ((Object)(object)rigFromHealth == (Object)null)
			{
				return;
			}
			TrackRig(rigFromHealth);
			InvalidateAvatarHealthCache(rigFromHealth);
			if (IsRigLimbDamageBlocked(rigFromHealth))
			{
				ResetLimbStats(rigFromHealth, restoreBody: true);
				if ((Object)(object)rigFromHealth == (Object)(object)Player.RigManager)
				{
					UpdateLimbPenaltyFlag(rigFromHealth);
					SetLocalMetadata("");
					SyncFullState();
				}
			}
			else
			{
				RefreshLimbMaxHealth(rigFromHealth);
			}
		}

		private RigManager GetRigFromHealth(Health health)
		{
			if ((Object)(object)health == (Object)null)
			{
				return null;
			}
			return ((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_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: 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 && TryGetPlayerIdForRig(rm, out var playerId))
			{
				_appliedStateMasks.Remove(playerId);
				_remoteFallenPlayers.Remove(playerId);
				if ((Object)(object)rm == (Object)(object)Player.RigManager)
				{
					_lastLeftArmUsable = true;
					_lastRightArmUsable = true;
				}
			}
			if (!_playersHealth.ContainsKey(rm))
			{
				_playersHealth[rm] = new Dictionary<HumanBodyBones, float>();
			}
			if (!_originalScales.ContainsKey(rm))
			{
				_originalScales[rm] = new Dictionary<HumanBodyBones, Vector3>();
			}
			HumanBodyBones[] damageBones = DamageBones;
			RebuildBoneCache(rm);
			RigRuntimeCache orCreateRigCache = GetOrCreateRigCache(rm);
			ResetLimbDamageBlockCache(rm);
			Transform[] boneTransforms = orCreateRigCache.BoneTransforms;
			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))
				{
					if (!_originalScales[rm].ContainsKey(val))
					{
						_originalScales[rm][val] = ((val2.localScale == Vector3.zero) ? Vector3.one : val2.localScale);
					}
					_playersHealth[rm][val] = GetMaxHealthForBone(rm, val);
					if (restoreBody)
					{
						val2.localScale = (_originalScales[rm].TryGetValue(val, out var value) ? value : Vector3.one);
					}
				}
			}
			RebuildLimbGoneFlags(rm);
			if (restoreBody)
			{
				ClearStumpBloodState(rm);
				RestoreRigMeshes(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);
					SuppressCrouchForcingForRestoration();
					SetHandUsable(Player.LeftHand, usable: true);
					SetHandUsable(Player.RightHand, usable: true);
				}
				ResetVisualDamage(rm);
				if ((Object)(object)rm.avatar != (Object)null && (Object)(object)rm.avatar.animator != (Object)null)
				{
					((Behaviour)rm.avatar.animator).enabled = true;
				}
			}
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				_lastTrackedBodyHealth = -1f;
				UpdateRigMissingLimbsFlag(rm);
				SetLocalMetadata("");
			}
			InvalidateAvatarHealthCache(rm);
			RebuildBoneCache(rm);
			UpdateRigMissingLimbsFlag(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;
				}
				SurfaceDataCard val2 = default(SurfaceDataCard);
				if (instance.TryGetDataCard<SurfaceDataCard>(new Barcode("slz.marrow.surfacedata.Flesh"), ref val2) && (Object)(object)val2 != (Object)null)
				{
					_fleshSurfaceCard = new DataCardReference<SurfaceDataCard>(((Scannable)val2).Barcode);
					_fleshCardFound = true;
					MelonLogger.Msg(ConsoleColor.Gray, "Found Flesh surface card by barcode: slz.marrow.surfacedata.Flesh");
					return;
				}
				List<DataCard> dataCards = instance.GetDataCards();
				if (dataCards == null)
				{
					return;
				}
				Enumerator<DataCard> enumerator = dataCards.GetEnumerator();
				while (enumerator.MoveNext())
				{
					DataCard current = enumerator.Current;
					if ((Object)(object)current == (Object)null)
					{
						continue;
					}
					SurfaceDataCard val3 = ((Il2CppObjectBase)current).TryCast<SurfaceDataCard>();
					if (!((Object)(object)val3 == (Object)null))
					{
						string text = ((Scannable)val3).Title ?? string.Empty;
						string text2 = text.ToLowerInvariant();
						if (text2.Contains("blood") || text2.Contains("flesh"))
						{
							_fleshSurfaceCard = new DataCardReference<SurfaceDataCard>(((Scannable)val3).Barcode);
							_fleshCardFound = true;
							MelonLogger.Msg($"Found Flesh surface card by title: {text} ({((Scannable)val3).Barcode})");
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Flesh surface scan failed: " + ex.Message);
			}
		}

		private void SetFleshImpactProperties(RigManager rm, HumanBodyBones bone)
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (!EnableImpactBlood.Value || !_fleshCardFound || (Object)(object)rm == (Object)null || !_colliderBoneMap.TryGetValue(rm, out var value))
			{
				return;
			}
			try
			{
				foreach (KeyValuePair<Collider, HumanBodyBones> item in value)
				{
					if (item.Value != bone)
					{
						continue;
					}
					Collider key = item.Key;
					if (!((Object)(object)key == (Object)null))
					{
						ImpactProperties val = ((Component)key).GetComponent<ImpactProperties>();
						if ((Object)(object)val == (Object)null)
						{
							val = ((Component)key).gameObject.AddComponent<ImpactProperties>();
						}
						if ((Object)(object)val != (Object)null)
						{
							val.SurfaceDataCard = _fleshSurfaceCard;
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning($"SetFleshImpactProperties failed for {bone}: {ex.Message}");
			}
		}

		private void ApplyBloodImpactProperties(RigManager rm)
		{
			if (!EnableImpactBlood.Value || (Object)(object)rm == (Object)null)
			{
				return;
			}
			if (!_fleshCardFound)
			{
				TryCacheFleshSurface();
			}
			if (!_fleshCardFound || !_colliderBoneMap.TryGetValue(rm, out var value))
			{
				return;
			}
			try
			{
				foreach (KeyValuePair<Collider, HumanBodyBones> item in value)
				{
					Collider key = item.Key;
					if (!((Object)(object)key == (Object)null))
					{
						ImpactProperties val = ((Component)key).GetComponent<ImpactProperties>();
						if ((Object)(object)val == (Object)null)
						{
							val = ((Component)key).gameObject.AddComponent<ImpactProperties>();
						}
						if ((Object)(object)val != (Object)null)
						{
							val.SurfaceDataCard = _fleshSurfaceCard;
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("ApplyBloodImpactProperties failed: " + ex.Message);
			}
		}

		private static bool IsAvatarBodyCollider(Collider collider, Transform avatarRoot)
		{
			if ((Object)(object)collider == (Object)null || (Object)(object)avatarRoot == (Object)null)
			{
				return false;
			}
			Transform val = ((Component)collider).transform;
			if (!val.IsChildOf(avatarRoot))
			{
				return false;
			}
			for (int i = 0; i < 14; i++)
			{
				if (!((Object)(object)val != (Object)null))
				{
					break;
				}
				if (!((Object)(object)val != (Object)(object)avatarRoot))
				{
					break;
				}
				string text = ((Object)val).name.ToLowerInvariant();
				if (text.Contains("gun") || text.Contains("pistol") || text.Contains("rifle") || text.Contains("weapon") || text.Contains("magazine") || text.Contains("ammo") || text.Contains("slide") || text.Contains("barrel") || text.Contains("trigger") || text.Contains("holster"))
				{
					return false;
				}
				Type il2CppType = ((Object)val).GetIl2CppType();
				if (il2CppType != (Type)null)
				{
					string text2 = il2CppType.FullName ?? string.Empty;
					if (text2.Contains("Gun") || text2.Contains("Weapon") || text2.Contains("Magazine") || text2.Contains("Ammo") || text2.Contains("Firearm") || text2.Contains("Cartridge") || text2.Contains("Projectile"))
					{
						return false;
					}
				}
				val = val.parent;
			}
			return true;
		}

		private void RegrowLimb(RigManager rm, HumanBodyBones bone)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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;
			}
			Transform cachedBoneTransform = GetCachedBoneTransform(rm, bone);
			if (!((Object)(object)cachedBoneTransform == (Object)null))
			{
				if (!_originalScales.TryGetValue(rm, out var value))
				{
					value = new Dictionary<HumanBodyBones, Vector3>();
					_originalScales[rm] = value;
				}
				if (!value.TryGetValue(bone, out var value2) || value2 == Vector3.zero)
				{
					value2 = (value[bone] = Vector3.one);
				}
				cachedBoneTransform.localScale = value2;
				if (_playersHealth.TryGetValue(rm, out var value3))
				{
					value3[bone] = GetMaxHealthForBone(rm, bone);
				}
				ClearStumpBloodState(rm, bone);
			}
		}

		private void ProcessPlayerHealing(RigManager rm)
		{
			if ((Object)(object)rm != (Object)(object)Player.RigManager || (Object)(object)((rm != null) ? rm.health : null) == (Object)null || !rm.health.alive)
			{
				return;
			}
			if (IsRigLimbDamageBlocked(rm) || !_playersHealth.ContainsKey(rm))
			{
				_lastTrackedBodyHealth = rm.health.curr_Health;
				return;
			}
			float curr_Health = rm.health.curr_Health;
			if (_lastTrackedBodyHealth < 0f)
			{
				_lastTrackedBodyHealth = curr_Health;
				return;
			}
			if (curr_Health <= _lastTrackedBodyHealth)
			{
				_lastTrackedBodyHealth = curr_Health;
				return;
			}
			_lastTrackedBodyHealth = curr_Health;
			if (IsAutoInjectorUseActiveOrRecent())
			{
				RestoreMissingLimbsFromHealing(rm);
			}
		}

		public void OnHealthSetFullHealth(Health health, float previousHealth)
		{
			RigManager rigFromHealth = GetRigFromHealth(health);
			if ((Object)(object)rigFromHealth == (Object)null)
			{
				return;
			}
			if ((Object)(object)rigFromHealth == (Object)(object)Player.RigManager)
			{
				if (IsAutoInjectorUseActiveOrRecent())
				{
					RestoreMissingLimbsFromHealing(rigFromHealth);
				}
			}
			else if (IsRemotePlayerRig(rigFromHealth) && health.curr_Health > previousHealth)
			{
				RestoreMissingLimbsFromHealing(rigFromHealth);
			}
		}

		private void UpdateAutoInjectorUseWindow()
		{
			try
			{
				if (IsAutoInjectorUsePressed(Player.LeftHand, Player.LeftController) || IsAutoInjectorUsePressed(Player.RightHand, Player.RightController))
				{
					_lastAutoInjectorUseTime = Time.unscaledTime;
				}
			}
			catch
			{
			}
		}

		public bool IsAutoInjectorUseActiveOrRecent()
		{
			if (Time.unscaledTime - _lastAutoInjectorUseTime <= 0.8f)
			{
				return true;
			}
			return IsAutoInjectorUsePressed(Player.LeftHand, Player.LeftController) || IsAutoInjectorUsePressed(Player.RightHand, Player.RightController);
		}

		private void UpdateExplosiveUseWindow()
		{
			try
			{
				if ((IsExplosiveHeld(Player.LeftHand) || IsExplosiveHeld(Player.RightHand)) && (IsExplosiveDetonatePressed(Player.LeftHand, Player.LeftController) || IsExplosiveDetonatePressed(Player.RightHand, Player.RightController)))
				{
					_lastExplosiveUseTime = Time.unscaledTime;
					TriggerSelfExplosion();
				}
			}
			catch
			{
			}
		}

		public bool IsExplosiveUseActiveOrRecent()
		{
			if (Time.unscaledTime - _lastExplosiveUseTime <= 1.5f)
			{
				return true;
			}
			return false;
		}

		private bool IsExplosiveHeld(Hand hand)
		{
			if ((Object)(object)hand == (Object)null)
			{
				return false;
			}
			try
			{
				HandReciever attachedReceiver = hand.AttachedReceiver;
				if ((Object)(object)attachedReceiver == (Object)null)
				{
					return false;
				}
				if (IsExplosiveObject(((Component)attachedReceiver).gameObject))
				{
					return true;
				}
				IGrippable host = attachedReceiver.Host;
				GameObject gameObject = ((host != null) ? host.GetHostGameObject() : null);
				return IsExplosiveObject(gameObject);
			}
			catch
			{
			}
			return false;
		}

		private bool IsExplosiveObject(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null)
			{
				return false;
			}
			Transform val = gameObject.transform;
			for (int i = 0; i < 8; i++)
			{
				if (!((Object)(object)val != (Object)null))
				{
					break;
				}
				if (LooksLikeExplosiveName(((Object)val).name))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private bool LooksLikeExplosiveName(string objectName)
		{
			if (string.IsNullOrEmpty(objectName))
			{
				return false;
			}
			string text = objectName.ToLowerInvariant();
			return text.Contains("explosive") || text.Contains("micro") || text.Contains("nuke") || text.Contains("dynamite") || text.Contains("c4") || text.Contains("grenade") || text.Contains("prox") || text.Contains("ied") || text.Contains("mine") || (text.Contains("timed") && (text.Contains("explos") || text.Contains("nuke")));
		}

		private bool IsExplosiveDetonatePressed(Hand hand, BaseController controller)
		{
			if ((Object)(object)hand == (Object)null || (Object)(object)controller == (Object)null)
			{
				return false;
			}
			bool primaryInteractionButtonDown = controller.GetPrimaryInteractionButtonDown();
			bool secondaryInteractionButtonDown = controller.GetSecondaryInteractionButtonDown();
			return primaryInteractionButtonDown && secondaryInteractionButtonDown;
		}

		private void TriggerSelfExplosion()
		{
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				return;
			}
			string[] array = new string[41]
			{
				"BaBaCorp.MiscExplosiveDevices.Spawnable.MicroNukeGrenade", "BaBaCorp.MiscExplosiveDevices.Spawnable.TimedNuke", "BaBaCorp.MiscExplosiveDevices.Spawnable.KCB4VoidTunnelingDevice", "BaBaCorp.MiscExplosiveDevices.Spawnable.IncinMissile", "BaBaCorp.MiscExplosiveDevices.Spawnable.Missile", "BaBaCorp.MiscExplosiveDevices.Spawnable.MiniMissile", "BaBaCorp.MiscExplosiveDevices.Spawnable.TimedC4", "BaBaCorp.MiscExplosiveDevices.Spawnable.CelebratoryC4", "BaBaCorp.MiscExplosiveDevices.Spawnable.RemoteIED", "BaBaCorp.MiscExplosiveDevices.Spawnable.TriggeredExplosive",
				"BaBaCorp.MiscExplosiveDevices.Spawnable.ImpactExplosive", "BaBaCorp.MiscExplosiveDevices.Spawnable.TimedExplosive6sec", "BaBaCorp.MiscExplosiveDevices.Spawnable.M72LAW", "BaBaCorp.MiscExplosiveDevices.Spawnable.M72LawINF", "BaBaCorp.MiscExplosiveDevices.Spawnable.ClassicBomb", "BaBaCorp.MiscExplosiveDevices.Spawnable.Dynamite", "BaBaCorp.MiscExplosiveDevices.Spawnable.ProxMine", "BaBaCorp.MiscExplosiveDevices.Spawnable.GiftMine", "BaBaCorp.MiscExplosiveDevices.Spawnable.FragmentationGrenade", "BaBaCorp.MiscExplosiveDevices.Spawnable.IncendiaryGrenade",
				"BaBaCorp.MiscExplosiveDevices.Spawnable.DivinationGrenade", "BaBaCorp.MiscExplosiveDevices.Spawnable.GasGrenade", "BaBaCorp.MiscExplosiveDevices.Spawnable.JoyousGreenGrenade", "BaBaCorp.MiscExplosiveDevices.Spawnable.Stielhandgranate", "BaBaCorp.MiscExplosiveDevices.Spawnable.SmallRocket", "BaBaCorp.MiscExplosiveDevices.Spawnable.PurpleFireworkRocket", "BaBaCorp.MiscExplosiveDevices.Spawnable.GreenFireworkRocket", "BaBaCorp.MiscExplosiveDevices.Spawnable.BlueFireworkRocket", "BaBaCorp.MiscExplosiveDevices.Spawnable.RedFireworkRocket", "BaBaCorp.MiscExplosiveDevices.Spawnable.BirthdayCake",
				"BaBaCorp.MiscExplosiveDevices.Spawnable.Frank", "BaBaCorp.MiscExplosiveDevices.Spawnable.BatteryAcidGlass", "BaBaCorp.MiscExplosiveDevices.Spawnable.CrateWithLid", "BaBaCorp.MiscExplosiveDevices.Spawnable.PropaneTank", "BaBaCorp.MiscExplosiveDevices.Spawnable.FuelTank", "BaBaCorp.MiscExplosiveDevices.Spawnable.SmokeGrenade", "BaBaCorp.MiscExplosiveDevices.Spawnable.Flare", "BaBaCorp.MiscExplosiveDevices.Spawnable.Jarate", "BaBaCorp.MiscExplosiveDevices.Spawnable.Lighter", "BaBaCorp.MiscExplosiveDevices.Spawnable.Pliers",
				"BaBaCorp.MiscExplosiveDevices.Spawnable.ExplosionSmallBigDamage"
			};
			string[] array2 = array;
			foreach (string text in array2)
			{
				try
				{
					TriggerExplosion(rigManager, text);
					MelonLogger.Msg(ConsoleColor.Gray, "Self explosion triggered with: " + text);
					break;
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Explosion failed for " + text + ": " + ex.Message);
				}
			}
		}

		private void TriggerExplosion(RigManager rm, string explosiveBarcode)
		{
			//IL_0031: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			MelonLogger.Msg(ConsoleColor.Gray, "TriggerExplosion: attempting " + explosiveBarcode);
			if ((Object)(object)rm == (Object)null)
			{
				MelonLogger.Error("RigManager is null!");
				return;
			}
			try
			{
				Spawnable val = new Spawnable
				{
					crateRef = new SpawnableCrateReference(explosiveBarcode),
					policyData = null
				};
				if (!val.IsValid())
				{
					MelonLogger.Error("Spawnable is not valid for " + explosiveBarcode);
					return;
				}
				MelonLogger.Msg(ConsoleColor.Gray, "Spawnable valid, spawning at position...");
				Vector3 val2 = ((Component)rm).transform.position + ((Component)rm).transform.forward * 0.5f + ((Component)rm).transform.up * 0.5f;
				Action<GameObject> action = OnExplosiveSpawned;
				AssetSpawner.Spawn(val, val2, Quaternion.identity, new Nullable<Vector3>(Vector3.one), (Transform)null, false, (Nullable<int>)null, Action<GameObject>.op_Implicit(action), (Action<GameObject>)null);
				MelonLogger.Msg(ConsoleColor.Gray, "AssetSpawner.Spawn called for " + explosiveBarcode);
			}
			catch (Exception value)
			{
				MelonLogger.Error($"TriggerExplosion exception for {explosiveBarcode}: {value}");
			}
		}

		private void OnExplosiveSpawned(GameObject spawned)
		{
			MelonLogger.Msg(ConsoleColor.Gray, $"OnExplosiveSpawned called, spawned: {(Object)(object)spawned != (Object)null}");
			if ((Object)(object)spawned != (Object)null)
			{
				MelonLogger.Msg(ConsoleColor.Gray, $"Spawned object name: {((Object)spawned).name}, active: {spawned.activeInHierarchy}");
				Il2CppArrayBase<MonoBehaviour> componentsInChildren = spawned.GetComponentsInChildren<MonoBehaviour>(true);
				MelonLogger.Msg(ConsoleColor.Gray, $"Found {componentsInChildren.Length} components");
				foreach (MonoBehaviour item in componentsInChildren)
				{
					if ((Object)(object)item != (Object)null)
					{
						MelonLogger.Msg(ConsoleColor.Gray, "Component: " + ((object)item).GetType().Name);
					}
				}
				ForceDetonate(spawned);
			}
			else
			{
				MelonLogger.Error("Spawned object is null!");
			}
		}

		private void ForceDetonate(GameObject spawned)
		{
			MelonLogger.Msg(ConsoleColor.Gray, "ForceDetonate called for " + ((Object)spawned).name);
			try
			{
				Il2CppArrayBase<MonoBehaviour> componentsInChildren = spawned.GetComponentsInChildren<MonoBehaviour>(true);
				MelonLogger.Msg(ConsoleColor.Gray, $"Checking {componentsInChildren.Length} components for Detonate/Explode methods");
				foreach (MonoBehaviour item in componentsInChildren)
				{
					if (!((Object)(object)item == (Object)null))
					{
						MethodInfo method = ((object)item).GetType().GetMethod("Detonate", BindingFlags.Instance | BindingFlags.Public);
						if (method != null)
						{
							MelonLogger.Msg(ConsoleColor.Gray, "Found Detonate() on " + ((object)item).GetType().Name + ", invoking...");
							method.Invoke(item, null);
							MelonLogger.Msg(ConsoleColor.Gray, "Detonated via " + ((object)item).GetType().Name + ".Detonate()");
							return;
						}
					}
				}
				Rigidbody component = spawned.GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.useGravity = true;
					component.isKinematic = false;
					MelonLogger.Msg(ConsoleColor.Gray, "Set rigidbody for gravity detonation");
				}
				else
				{
					MelonLogger.Warning("No Rigidbody found on spawned object");
				}
				foreach (MonoBehaviour item2 in componentsInChildren)
				{
					if (!((Object)(object)item2 == (Object)null))
					{
						MethodInfo method2 = ((object)item2).GetType().GetMethod("Explode", BindingFlags.Instance | BindingFlags.Public);
						if (method2 != null)
						{
							MelonLogger.Msg(ConsoleColor.Gray, "Found Explode() on " + ((object)item2).GetType().Name + ", invoking...");
							method2.Invoke(item2, null);
							MelonLogger.Msg(ConsoleColor.Gray, "Detonated via " + ((object)item2).GetType().Name + ".Explode()");
							return;
						}
					}
				}
				MelonLogger.Warning("No detonation method found, object spawned but may not explode automatically");
			}
			catch (Exception value)
			{
				MelonLogger.Error($"ForceDetonate exception: {value}");
			}
		}

		public bool IsHoldingNimbusGun(RigManager rm)
		{
			return IsNimbusGunHand(Player.LeftHand) || IsNimbusGunHand(Player.RightHand);
		}

		private bool IsNimbusGunHand(Hand hand)
		{
			if ((Object)(object)hand == (Object)null)
			{
				return false;
			}
			try
			{
				HandReciever attachedReceiver = hand.AttachedReceiver;
				if ((Object)(object)attachedReceiver == (Object)null)
				{
					return false;
				}
				IGrippable host = attachedReceiver.Host;
				GameObject val = ((host != null) ? host.GetHostGameObject() : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				SpawnableCrate componentInChildren = val.GetComponentInChildren<SpawnableCrate>(true);
				if ((Object)(object)componentInChildren != (Object)null && ((Scannable)componentInChildren).Barcode != (Barcode)null && ((Object)((Scannable)componentInChildren).Barcode).ToString().Contains("Nimbus"))
				{
					return true;
				}
				Transform val2 = val.transform;
				while ((Object)(object)val2 != (Object)null)
				{
					if (((Object)val2).name.Contains("Nimbus"))
					{
						return true;
					}
					val2 = val2.parent;
				}
			}
			catch
			{
			}
			return false;
		}

		private bool IsAutoInjectorUsePressed(Hand hand, BaseController controller)
		{
			if (!IsAutoInjectorHeldByHand(hand))
			{
				return false;
			}
			bool flag = false;
			try
			{
				flag |= (Object)(object)hand != (Object)null && (hand.GetIndexButtonDown() || hand.GetIndexButton());
			}
			catch
			{
			}
			try
			{
				flag |= (Object)(object)controller != (Object)null && (controller.GetPrimaryInteractionButtonDown() || controller.GetPrimaryInteractionButton());
				flag |= (Object)(object)controller != (Object)null && (controller.GetSecondaryInteractionButtonDown() || controller.GetSecondaryInteractionButton());
				flag |= (Object)(object)controller != (Object)null && (controller.GetAButtonDown() || controller.GetAButton());
				flag |= (Object)(object)controller != (Object)null && (controller.GetBButtonDown() || controller.GetBButton());
			}
			catch
			{
			}
			return flag;
		}

		private bool IsAutoInjectorHeldByHand(Hand hand)
		{
			if ((Object)(object)hand == (Object)null)
			{
				return false;
			}
			try
			{
				HandReciever attachedReceiver = hand.AttachedReceiver;
				if ((Object)(object)attachedReceiver == (Object)null)
				{
					return false;
				}
				if (IsAutoInjectorObject(((Component)attachedReceiver).gameObject))
				{
					return true;
				}
				IGrippable host = attachedReceiver.Host;
				GameObject gameObject = ((host != null) ? host.GetHostGameObject() : null);
				return IsAutoInjectorObject(gameObject);
			}
			catch
			{
				return false;
			}
		}

		private bool IsAutoInjectorObject(GameObject gameObject)
		{
			Transform val = (((Object)(object)gameObject != (Object)null) ? gameObject.transform : null);
			for (int i = 0; i < 8; i++)
			{
				if (!((Object)(object)val != (Object)null))
				{
					break;
				}
				if (LooksLikeAutoInjectorName(((Object)val).name))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private bool LooksLikeAutoInjectorName(string objectName)
		{
			if (string.IsNullOrEmpty(objectName))
			{
				return false;
			}
			string text = objectName.ToLowerInvariant();
			string text2 = text.Replace(" ", string.Empty).Replace("_", string.Empty).Replace("-", string.Empty);
			return text2.Contains("autoinjector") || (text2.Contains("auto") && text2.Contains("inject")) || text2.Contains("signalisautoinjector");
		}

		private bool RestoreMissingLimbsFromHealing(RigManager rm)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: 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 false;
			}
			EnsureRigInitialized(rm);
			if (IsRigLimbDamageBlocked(rm))
			{
				ResetLimbStats(rm, restoreBody: true);
				if ((Object)(object)rm != (Object)(object)Player.RigManager && TryGetPlayerIdForRig(rm, out var playerId))
				{
					NetworkSync.SendFullState(playerId, 0u, 0, 0);
				}
				return true;
			}
			if (!_playersHealth.TryGetValue(rm, out var value))
			{
				return false;
			}
			bool flag = false;
			for (int i = 0; i < DamageBones.Length; i++)
			{
				HumanBodyBones val = DamageBones[i];
				float value2;
				bool flag2 = value.TryGetValue(val, out value2) && value2 < 0f;
				if (IsBoneGone(rm, val) || flag2)
				{
					RegrowLimb(rm, val);
					flag = true;
				}
			}
			if (!flag)
			{
				return false;
			}
			RebuildLimbGoneFlags(rm);
			UpdateRigMissingLimbsFlag(rm);
			ResetVisualDamage(rm);
			if ((Object)(object)rm == (Object)(object)Player.RigManager)
			{
				SetHandUsable(Player.LeftHand, !IsLeftArmGone(rm));
				SetHandUsable(Player.RightHand, !IsRightArmGone(rm));
				if (EnableVrEyeBlindness.Value)
				{
					UpdateVrEyeBlindness(rm);
				}
				RestoreLocomotion();
				RestoreOriginalLimits(rm);
				ResetLegRagdoll(rm);
				RestoreDeadpieceLegUsage(rm);
				RestoreNoLegJump(rm);
				RestoreNoLegFallenState(rm);
				SuppressCrouchForcingForRestoration();
			}
			byte playerId3;
			if (NetworkInfo.HasServer)
			{
				byte playerId2;
				if ((Object)(object)rm == (Object)(object)Player.RigManager)
				{
					SyncFullState();
				}
				else if (TryGetPlayerIdForRig(rm, out playerId2))
				{
					uint stateMask = GetStateMask(rm);
					NetworkSync.SendFullState(playerId2, stateMask, 0, 0);
				}
			}
			else if ((Object)(object)rm != (Object)(object)Player.RigManager && TryGetPlayerIdForRig(rm, out playerId3))
			{
				uint stateMask2 = GetStateMask(rm);
				NetworkSync.SendFullState(playerId3, stateMask2, 0, 0);
			}
			return true;
		}

		private void ProcessSeveredLimbBleed(RigManager rm, float deltaTime)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			if (!EnableImpactBlood.Value || (Object)(object)rm == (Object)null || deltaTime <= 0f || !_playersHealth.ContainsKey(rm))
			{
				return;
			}
			float num = Mathf.Max(0f, SeveredLimbBleedDamagePerSecond.Value) * deltaTime;
			if (num <= 0f && !EnableBloodEffects.Value)
			{
				return;
			}
			for (int i = 0; i < DamageBones.Length; i++)
			{
				HumanBodyBones bone = DamageBones[i];
				if (!IsBoneGone(rm, bone))
				{
					continue;
				}
				float remainingStumpBloodLifetime = GetRemainingStumpBloodLifetime(rm, bone, startIfMissing: true);
				if (!(remainingStumpBloodLifetime <= 0.05f))
				{
					if (num > 0f)
					{
						TryApplyBleedAtBone(rm, bone, num);
					}
					TryPlayStumpBloodEffect(rm, bone, remainingStumpBloodLifetime);
				}
			}
		}

		private void ProcessRemoteRigsStumpBlood(float deltaTime)
		{
			if (!EnableImpactBlood.Value || _trackedRigs.Count == 0 || deltaTime <= 0f)
			{
				return;
			}
			RigManager[] array = _trackedRigs.ToArray();
			foreach (RigManager val in array)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)Player.RigManager)
				{
					ProcessSeveredLimbBleed(val, deltaTime);
				}
			}
		}

		private void TryPlayStumpBloodEffect(RigManager rm, HumanBodyBones bone, float remainingBloodLifetime)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			if (!EnableBloodEffects.Value || !EnableGameBloodEffects.Value || (Object)(object)rm == (Object)null || (Object)(object)rm.avatar == (Object)null || (Object)(object)rm.avatar.animator == (Object)null || remainingBloodLifetime <= 0.05f || !ShouldPlayStumpBloodEffect(rm, bone))
			{
				return;
			}
			Transform cachedBoneTransform = GetCachedBoneTransform(rm, bone);
			if (!((Object)(object)cachedBoneTransform == (Object)null))
			{
				Vector3 val = GetBoneStreamDirection(rm, bone, cachedBoneTransform);
				if (val == Vector3.zero)
				{
					val = cachedBoneTransform.up;
				}
				val = ((Vector3)(ref val)).normalized;
				Vector3 val2 = cachedBoneTransform.position + val * 0.04f;
				Color bloodColor = GetBloodColor(rm);
				TryApplyBloodImpactAtBone(rm, bone, val2, 3f, bloodColor);
				if (!TrySpawnVanillaBloodSpawnable(val2, 0.72f, stream: true, Quaternion.LookRotation(val), bloodColor, remainingBloodLifetime))
				{
					TrySpawnGameBlood(val2, 0.55f, bloodColor, remainingBloodLifetime);
				}
			}
		}

		private bool ShouldPlayStumpBloodEffect(RigManager rm, HumanBodyBones bone)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (!_lastStumpBloodEffectTimes.TryGetValue(rm, out var value))
			{
				value = new Dictionary<HumanBodyBones, float>();
				_lastStumpBloodEffectTimes[rm] = value;
			}
			if (value.TryGetValue(bone, out var value2) && Time.time - value2 < 0.45f)
			{
				return false;
			}
			value[bone] = Time.time;
			return true;
		}

		private void MarkStumpBloodStarted(RigManager rm, HumanBodyBones bone)
		{
			//IL_003b: 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)
			if (!((Object)(object)rm == (Object)null))
			{
				if (!_stumpBloodStartTimes.TryGetValue(rm, out var value))
				{
					value = new Dictionary<HumanBodyBones, float>();
					_stumpBloodStartTimes[rm] = value;
				}
				value[bone] = Time.time;
				if (_lastStumpBloodEffectTimes.TryGetValue(rm, out var value2))
				{
					value2.Remove(bone);
				}
			}
		}

		private bool HasStumpBloodStart(RigManager rm, HumanBodyBones bone)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<HumanBodyBones, float> value;
			return (Object)(object)rm != (Object)null && _stumpBloodStartTimes.TryGetValue(rm, out value) && value.ContainsKey(bone);
		}

		private float GetRemainingStumpBloodLifetime(RigManager rm, HumanBodyBones bone, bool startIfMissing)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rm == (Object)null)
			{
				return 0f;
			}
			if (!_stumpBloodStartTimes.TryGetValue(rm, out var value) || !value.TryGetValue(bone, out var value2))
			{
				if (!startIfMissing)
				{
					return 0f;
				}
				MarkStumpBloodStarted(rm, bone);
				value2 = Time.time;
			}
			return Mathf.Max(0f, 10f - (Time.time - value2));
		}

		private void ClearStumpBloodState(RigManager rm)
		{
			if (!((Object)(object)rm == (Object)null))
			{
				_stumpBloodStartTimes.Remove(rm);
				_lastStumpBloodEffectTimes.Remove(rm);
			}
		}

		private void ClearStumpBloodState(RigManager rm, HumanBodyBones bone)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rm == (Object)null)
			{
				return;
			}
			if (_stumpBloodStartTimes.TryGetValue(rm, out var value))
			{
				value.Remove(bone);
				if (value.Count == 0)
				{
					_stumpBloodStartTimes.Remove(rm);
				}
			}
			if (_lastStumpBloodEffectTimes.TryGetValue(rm, out var value2))
			{
				value2.Remove(bone);
				if (value2.Count == 0)
				{
					_lastStumpBloodEffectTimes.Remove(rm);
				}
			}
		}

		private void TryApplyBleedAtBone(RigManager rm, HumanBodyBones bone, float damage)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			TryApplyBloodImpactAtBone(rm, bone, Vector3.zero, damage);
		}

		private bool TryApplyBloodImpactAtBone(RigManager rm, HumanBodyBones bone, Vector3 origin, float damage)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return TryApplyBloodImpactAtBone(rm, bone, origin, damage, Color.white);
		}

		private bool TryApplyBloodImpactAtBone(RigManager rm, HumanBodyBones bone, Vector3 origin, float damage, Color color)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((rm != null) ? rm.avatar : null) == (Object)null || damage <= 0f || !_colliderBoneMap.TryGetValue(rm, out var value))
			{
				return false;
			}
			Collider val = null;
			foreach (KeyValuePair<Collider, HumanBodyBones> item in value)
			{
				if (item.Value == bone)
				{
					val = item.Key;
					break;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				ImpactProperties val2 = ((Component)val).GetComponent<ImpactProperties>();
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = ((Component)val).gameObject.AddComponent<ImpactProperties>();
				}
				if ((Object)(object)val2 == (Object)null)
				{
					return false;
				}
				if (_fleshCardFound)
				{
					val2.SurfaceDataCard = _fleshSurfaceCard;
				}
				Vector3 val3;
				Bounds bounds;
				if (!(origin == Vector3.zero))
				{
					val3 = origin;
				}
				else
				{
					bounds = val.bounds;
					val3 = ((Bounds)(ref bounds)).center;
				}
				Vector3 val4 = val3;
				bounds = val.bounds;
				Vector3 val5 = ((Bounds)(ref bounds)).center - val4;
				if (val5 == Vector3.zero)
				{
					val5 = Vector3.down;
				}
				((Vector3)(ref val5)).Normalize();
				Attack val6 = new Attack
				{
					damage = damage,
					normal = -val5,
					origin = val4,
					direction = val5,
					backFacing = false,
					OrderInPool = 0,
					collider = val,
					attackType = (AttackType)1,
					proxy = null
				};
				val2.ReceiveAttack(val6);
				return true;
			}
			catch
			{
			}
			return false;
		}

		private void SpawnBloodPoolDecal(Vector3 origin, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vector3 val = origin;
				val.y += 2.5f;
				RaycastHit val2 = default(RaycastHit);
				if (Physics.Raycast(val, Vector3.down, ref val2, 5f, -1, (QueryTriggerInteraction)1) && (Object)(object)((RaycastHit)(ref val2)).collider != (Object)null)
				{
					GameObject val3 = new GameObject("DeadPiece_BloodPool");
					val3.transform.position = ((RaycastHit)(ref val2)).point + ((RaycastHit)(ref val2)).normal * 0.005f;
					val3.transform.rotation = Quaternion.LookRotation(((RaycastHit)(ref val2)).normal);
					val3.transform.localScale = Vector3.one * Mathf.Lerp(0.08f, 0.35f, Random.value);
					val3.transform.Rotate(0f, 0f, Random.Range(0f, 360f));
					MeshFilter val4 = val3.AddComponent<MeshFilter>();
					val4.sharedMesh = BuildBloodQuadMesh();
					MeshRenderer val5 = val3.AddComponent<MeshRenderer>();
					((Renderer)val5).material = new Material(Shader.Find("Sprites/Default"));
					((Renderer)val5).material.color = color;
					((Renderer)val5).material.mainTexture = null;
					Object.Destroy((Object)(object)val3, 10f);
				}
			}
			catch
			{
			}
		}

		private IEnumerator DelayedPoolClear(Poolee poolee, float delay)
		{
			yield return (object)new WaitForSeconds(delay);
			if ((Object)(object)poolee != (Object)null && (Object)(object)((Component)poolee).gameObject != (Object)null)
			{
				StopBloodEffectObject(((Component)poolee).gameObject);
				HideRenderers(((Component)poolee).gameObject);
				try
				{
					AssetSpawner.Clear(poolee);
				}
				catch
				{
				}
				if ((Object)(object)((Component)poolee).gameObject != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)poolee).gameObject);
				}
			}
		}

		private IEnumerator RunAsyncSpawnBlood(Spawnable spawnable, Vector3 position, Quaternion rotation, Nullable<Vector3> scale, Color color, float clearDelay)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Poolee result = null;
			bool done = false;
			UniTask<Poolee> task = AssetSpawner.SpawnAsync(spawnable, position, rotation, scale, (Transform)null, false, (Nullable<int>)null, (Action<GameObject>)null, (Action<GameObject>)null, (Action<GameObject>)null);
			Awaiter<Poolee> awaiter = task.GetAwaiter();
			for (int i = 0; i < 60; i++)
			{
				if (done)
				{
					break;
				}
				yield return null;
				try
				{
					result = awaiter.GetResult();
					done = true;
				}
				catch
				{
				}
			}
			if (!((Object)(object)result != (Object)null) || !((Object)(object)((Component)result).gameObject != (Object)null))
			{
				yield break;
			}
			if (color != Color.white)
			{
				Renderer[] renderers = Il2CppArrayBase<Renderer>.op_Implicit(((Component)result).GetComponentsInChildren<Renderer>(true));
				Renderer[] array = renderers;
				foreach (Renderer r in array)
				{
					Material mat = r.material;
					string[] colorProps = new string[5] { "_Color", "_BaseColor", "_TintColor", "_EmissiveColor", "_MainColor" };
					string[] array2 = colorProps;
					foreach (string prop in array2)
					{
						if (mat.HasProperty(prop))
						{
							mat.SetColor(prop, color);
							break;
						}
					}
				}
				ParticleSystem[] particles = Il2CppArrayBase<ParticleSystem>.op_Implicit(((Component)result).GetComponentsInChildren<ParticleSystem>(true));
				ParticleSystem[] array3 = particles;
				foreach (ParticleSystem ps in array3)
				{
					MainModule main = ps.main;
					main.startColor = MinMaxGradient.op_Implicit(color);
				}
			}
			MelonCoroutines.Start(DelayedPoolClear(result, clearDelay));
		}

		private IEnumerator RunAsyncSpawnVanillaBlood(Spawnable spawnable, Vector3 position, Quaternion rotation, Nullable<Vector3> scale, Nullable<int> groupId, Color color, float clearDelay)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Poolee result = null;
			bool done = false;
			UniTask<Poolee> spawnTask = AssetSpawner.SpawnAsync(spawnable, position, rotation, scale, (Transform)null, false, groupId, (Action<GameObject>)null, (Action<GameObject>)null, (Action<GameObject>)null);
			Awaiter<Poolee> awaiter = spawnTask.GetAwaiter();
			for (int i = 0; i < 60; i++)
			{
				if (done)
				{
					break;
				}
				yield return null;
				try
				{
					result = awaiter.GetResult();
					done = true;
				}
				catch
				{
				}
			}
			if ((Object)(object)result == (Object)null)
			{
				if (!_loggedVanillaBloodSpawnableFailure)
				{
					_loggedVanillaBloodSpawnableFailure = true;
					Melo