Decompiled source of BloodVFX v1.1.0

Y4NGZBloodFX.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.VFX;
using Y4NGZCompany.Experience.BloodFX;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("ClientNetworkTransform")]
[assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")]
[assembly: IgnoresAccessChecksTo("DissonanceVoip")]
[assembly: IgnoresAccessChecksTo("DunGen")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")]
[assembly: IgnoresAccessChecksTo("EasyTextEffects")]
[assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Jobs")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")]
[assembly: IgnoresAccessChecksTo("Unity.Services.QoS")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Relay")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")]
[assembly: IgnoresAccessChecksTo("Unity.XR.Management")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Y4NGZBloodFX")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Blood and gore visual effects for Y4NGZ mods")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+9184c02852afe42c780eb4a5dcb48b0a5c306c26")]
[assembly: AssemblyProduct("Y4NGZBloodFX")]
[assembly: AssemblyTitle("Y4NGZBloodFX")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[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 Y4NGZBloodFX
{
	[BepInPlugin("com.y4ngz.bloodfx", "BloodVFX", "1.1.0")]
	public sealed class BloodFXPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.y4ngz.bloodfx";

		public const string PluginName = "BloodVFX";

		public const string PluginVersion = "1.1.0";

		private Harmony _harmony;

		public static BloodFXPlugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("com.y4ngz.bloodfx");
			BloodFXBootstrap.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, _harmony);
			Log.LogInfo((object)"BloodVFX 1.1.0 loaded.");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "Y4NGZBloodFX";

		public const string PLUGIN_NAME = "Y4NGZBloodFX";

		public const string PLUGIN_VERSION = "1.1.0";
	}
}
namespace Y4NGZCompany.Experience.BloodFX
{
	public static class BloodFXAssets
	{
		internal sealed class PooledParticle
		{
			private readonly GameObject _instance;

			private readonly ParticleSystem[] _systems;

			private readonly Burst[][] _originalBursts;

			private readonly float[][] _originalCounts;

			private readonly MinMaxGradient[] _originalStartColors;

			private readonly bool[] _playsByItself;

			private readonly Vector3 _baseScale;

			internal PooledParticle(GameObject instance)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d6: Invalid comparison between Unknown and I4
				//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_0126: 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_0156: Unknown result type (might be due to invalid IL or missing references)
				//IL_015b: Unknown result type (might be due to invalid IL or missing references)
				_instance = instance;
				_baseScale = instance.transform.localScale;
				_systems = instance.GetComponentsInChildren<ParticleSystem>(true);
				_originalBursts = new Burst[_systems.Length][];
				_originalCounts = new float[_systems.Length][];
				_originalStartColors = (MinMaxGradient[])(object)new MinMaxGradient[_systems.Length];
				_playsByItself = new bool[_systems.Length];
				for (int i = 0; i < _systems.Length; i++)
				{
					_playsByItself[i] = !IsSubEmitterOfAnother(_systems, i);
				}
				for (int j = 0; j < _systems.Length; j++)
				{
					ParticleSystem obj = _systems[j];
					CollisionModule collision = obj.collision;
					((CollisionModule)(ref collision)).enabled = false;
					MainModule main = obj.main;
					((MainModule)(ref main)).playOnAwake = false;
					if ((int)((MainModule)(ref main)).stopAction == 2)
					{
						((MainModule)(ref main)).stopAction = (ParticleSystemStopAction)0;
					}
					EmissionModule emission = obj.emission;
					Burst[] array = (Burst[])(object)new Burst[((EmissionModule)(ref emission)).burstCount];
					((EmissionModule)(ref emission)).GetBursts(array);
					_originalBursts[j] = array;
					float[] array2 = new float[array.Length];
					for (int k = 0; k < array.Length; k++)
					{
						int num = k;
						MinMaxCurve count = ((Burst)(ref array[k])).count;
						array2[num] = ((MinMaxCurve)(ref count)).constant;
					}
					_originalCounts[j] = array2;
					_originalStartColors[j] = ((MainModule)(ref main)).startColor;
				}
				Light[] componentsInChildren = instance.GetComponentsInChildren<Light>(true);
				for (int l = 0; l < componentsInChildren.Length; l++)
				{
					Object.Destroy((Object)(object)componentsInChildren[l]);
				}
				ParticleSystemRenderer[] componentsInChildren2 = instance.GetComponentsInChildren<ParticleSystemRenderer>(true);
				foreach (ParticleSystemRenderer obj2 in componentsInChildren2)
				{
					((Renderer)obj2).shadowCastingMode = (ShadowCastingMode)0;
					((Renderer)obj2).receiveShadows = false;
				}
			}

			private static bool IsSubEmitterOfAnother(ParticleSystem[] systems, int index)
			{
				//IL_000f: 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)
				ParticleSystem val = systems[index];
				for (int i = 0; i < systems.Length; i++)
				{
					if (i == index)
					{
						continue;
					}
					SubEmittersModule subEmitters = systems[i].subEmitters;
					if (!((SubEmittersModule)(ref subEmitters)).enabled)
					{
						continue;
					}
					for (int j = 0; j < ((SubEmittersModule)(ref subEmitters)).subEmittersCount; j++)
					{
						if ((Object)(object)((SubEmittersModule)(ref subEmitters)).GetSubEmitterSystem(j) == (Object)(object)val)
						{
							return true;
						}
					}
				}
				return false;
			}

			internal void Play(Vector3 position, Quaternion rotation, float emissionMultiplier, Color tint, float scale)
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: 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_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_009f: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_instance == (Object)null || _systems.Length == 0)
				{
					return;
				}
				Transform transform = _instance.transform;
				transform.SetPositionAndRotation(position, rotation);
				transform.localScale = _baseScale * Mathf.Max(0.01f, scale);
				bool tinted = BloodFXTint.IsTinted(tint);
				for (int i = 0; i < _systems.Length; i++)
				{
					ParticleSystem val = _systems[i];
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					ApplyStartColor(val, i, tinted, tint);
					Burst[] array = _originalBursts[i];
					if (array.Length != 0)
					{
						float[] array2 = _originalCounts[i];
						EmissionModule emission = val.emission;
						for (int j = 0; j < array.Length; j++)
						{
							Burst val2 = array[j];
							((Burst)(ref val2)).count = new MinMaxCurve(Mathf.Max(1f, array2[j] * emissionMultiplier));
							((EmissionModule)(ref emission)).SetBurst(j, val2);
						}
					}
				}
				_instance.SetActive(true);
				for (int k = 0; k < _systems.Length; k++)
				{
					if ((Object)(object)_systems[k] != (Object)null)
					{
						_systems[k].Stop(false, (ParticleSystemStopBehavior)0);
					}
				}
				for (int l = 0; l < _systems.Length; l++)
				{
					if ((Object)(object)_systems[l] != (Object)null && _playsByItself[l])
					{
						_systems[l].Play(false);
					}
				}
			}

			private void ApplyStartColor(ParticleSystem system, int index, bool tinted, Color tint)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				MainModule main = system.main;
				MinMaxGradient startColor = _originalStartColors[index];
				if (!tinted)
				{
					((MainModule)(ref main)).startColor = startColor;
					return;
				}
				float num = (((int)((MinMaxGradient)(ref startColor)).mode == 0) ? ((MinMaxGradient)(ref startColor)).color.a : 1f);
				((MainModule)(ref main)).startColor = new MinMaxGradient(new Color(tint.r, tint.g, tint.b, num));
			}
		}

		internal const string BundleFileName = "y4ngz-bloodfx.bundle";

		internal const string ManifestFileName = "y4ngz-bloodfx.manifest.json";

		internal const int BurstPoolSize = 8;

		internal const int SprayPoolSize = 4;

		internal const int MistPoolSize = 2;

		private const string PoolRootName = "Y4NGZCompany_BloodFXPool";

		private const string DecalRootName = "Y4NGZCompany_BloodFXDecals";

		private static bool _loaded;

		private static AssetBundle _bundle;

		private static GameObject[] _burstPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _chutePrefabs = Array.Empty<GameObject>();

		private static GameObject[] _sprayPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _woundPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _wallPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _spotsPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _bodyPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _puddlePrefabs = Array.Empty<GameObject>();

		private static GameObject[] _vfxSpikePrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibHeadPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibArmLeftPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibArmRightPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibLegLeftPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibLegRightPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibChunkPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _footprintLeftPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _footprintRightPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibTorsoPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _gibPelvisPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _mistPrefabs = Array.Empty<GameObject>();

		private static GameObject[] _plumePrefabs = Array.Empty<GameObject>();

		private static AudioClip[] _gibBurstClips = Array.Empty<AudioClip>();

		private static AudioClip[] _gibLandClips = Array.Empty<AudioClip>();

		private static Transform _poolRoot;

		private static Transform _decalRoot;

		private static PooledParticle[] _burstPool = Array.Empty<PooledParticle>();

		private static PooledParticle[] _sprayPool = Array.Empty<PooledParticle>();

		private static PooledParticle[] _mistPool = Array.Empty<PooledParticle>();

		private static int _burstCursor;

		private static int _sprayCursor;

		private static int _mistCursor;

		public static bool IsAvailable
		{
			get
			{
				EnsureLoaded();
				if ((Object)(object)_bundle != (Object)null)
				{
					return _wallPrefabs.Length + _spotsPrefabs.Length > 0;
				}
				return false;
			}
		}

		internal static GameObject[] ChutePrefabs
		{
			get
			{
				EnsureLoaded();
				return _chutePrefabs;
			}
		}

		internal static GameObject WallPrefab => Pick(_wallPrefabs);

		internal static GameObject SpotsPrefab => Pick(_spotsPrefabs);

		internal static GameObject WoundPrefab => Pick(_woundPrefabs);

		internal static GameObject BodyPrefab => Pick(_bodyPrefabs);

		internal static GameObject PuddlePrefab => Pick(_puddlePrefabs);

		public static bool HasBodyPrefabs => _bodyPrefabs.Length != 0;

		internal static bool HasGibAssets
		{
			get
			{
				EnsureLoaded();
				return BloodFXGibPrefabContract.IsCompleteKit(_gibHeadPrefabs.Length, _gibArmLeftPrefabs.Length, _gibArmRightPrefabs.Length, _gibLegLeftPrefabs.Length, _gibLegRightPrefabs.Length, _gibChunkPrefabs.Length);
			}
		}

		internal static bool HasFootprintAssets
		{
			get
			{
				EnsureLoaded();
				if (_footprintLeftPrefabs.Length != 0)
				{
					return _footprintRightPrefabs.Length != 0;
				}
				return false;
			}
		}

		internal static GameObject[] GibHeadPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibHeadPrefabs;
			}
		}

		internal static GameObject[] GibArmLeftPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibArmLeftPrefabs;
			}
		}

		internal static GameObject[] GibArmRightPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibArmRightPrefabs;
			}
		}

		internal static GameObject[] GibLegLeftPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibLegLeftPrefabs;
			}
		}

		internal static GameObject[] GibLegRightPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibLegRightPrefabs;
			}
		}

		internal static GameObject[] GibChunkPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibChunkPrefabs;
			}
		}

		internal static GameObject[] FootprintLeftPrefabs
		{
			get
			{
				EnsureLoaded();
				return _footprintLeftPrefabs;
			}
		}

		internal static GameObject[] FootprintRightPrefabs
		{
			get
			{
				EnsureLoaded();
				return _footprintRightPrefabs;
			}
		}

		internal static GameObject[] GibTorsoPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibTorsoPrefabs;
			}
		}

		internal static GameObject[] GibPelvisPrefabs
		{
			get
			{
				EnsureLoaded();
				return _gibPelvisPrefabs;
			}
		}

		internal static AudioClip[] GibBurstClips
		{
			get
			{
				EnsureLoaded();
				return _gibBurstClips;
			}
		}

		internal static AudioClip[] GibLandClips
		{
			get
			{
				EnsureLoaded();
				return _gibLandClips;
			}
		}

		internal static GameObject[] PlumePrefabs
		{
			get
			{
				EnsureLoaded();
				return _plumePrefabs;
			}
		}

		internal static GameObject[] SpotsPrefabs
		{
			get
			{
				EnsureLoaded();
				return _spotsPrefabs;
			}
		}

		internal static GameObject[] WallPrefabs
		{
			get
			{
				EnsureLoaded();
				return _wallPrefabs;
			}
		}

		internal static GameObject[] PuddlePrefabs
		{
			get
			{
				EnsureLoaded();
				return _puddlePrefabs;
			}
		}

		internal static bool HasMist
		{
			get
			{
				EnsureLoaded();
				return _mistPool.Length != 0;
			}
		}

		internal static Transform DecalRoot
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_decalRoot == (Object)null)
				{
					_decalRoot = new GameObject("Y4NGZCompany_BloodFXDecals").transform;
				}
				return _decalRoot;
			}
		}

		internal static PooledParticle TakeBurst()
		{
			EnsureLoaded();
			if (_burstPool.Length == 0)
			{
				return null;
			}
			PooledParticle result = _burstPool[_burstCursor];
			_burstCursor = (_burstCursor + 1) % _burstPool.Length;
			return result;
		}

		internal static PooledParticle TakeSpray()
		{
			EnsureLoaded();
			if (_sprayPool.Length == 0)
			{
				return null;
			}
			PooledParticle result = _sprayPool[_sprayCursor];
			_sprayCursor = (_sprayCursor + 1) % _sprayPool.Length;
			return result;
		}

		internal static PooledParticle TakeMist()
		{
			EnsureLoaded();
			if (_mistPool.Length == 0)
			{
				return null;
			}
			PooledParticle result = _mistPool[_mistCursor];
			_mistCursor = (_mistCursor + 1) % _mistPool.Length;
			return result;
		}

		private static GameObject Pick(GameObject[] pool)
		{
			EnsureLoaded();
			if (pool.Length != 0)
			{
				return pool[Random.Range(0, pool.Length)];
			}
			return null;
		}

		private static void EnsureLoaded()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			if (!TryLoadManifest(out var manifest, out var reason))
			{
				ManualLogSource log = BloodFXBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Y4NGZCompany.BloodFX] Manifest 'y4ngz-bloodfx.manifest.json' unavailable (" + reason + "); blood VFX disabled."));
				}
				return;
			}
			_bundle = FindLoadedBundle(manifest);
			if ((Object)(object)_bundle == (Object)null)
			{
				string text = ResolveRuntimeAssetPath("y4ngz-bloodfx.bundle");
				if (string.IsNullOrWhiteSpace(text))
				{
					ManualLogSource log2 = BloodFXBootstrap.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"[Y4NGZCompany.BloodFX] Bundle 'y4ngz-bloodfx.bundle' was not found next to the plugin; blood VFX disabled.");
					}
					return;
				}
				_bundle = AssetBundle.LoadFromFile(text);
				if ((Object)(object)_bundle == (Object)null)
				{
					ManualLogSource log3 = BloodFXBootstrap.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)("[Y4NGZCompany.BloodFX] AssetBundle.LoadFromFile returned null for '" + text + "'; blood VFX disabled."));
					}
					return;
				}
				ManualLogSource log4 = BloodFXBootstrap.Log;
				if (log4 != null)
				{
					log4.LogInfo((object)("[Y4NGZCompany.BloodFX] Loaded blood VFX bundle from '" + text + "'."));
				}
			}
			BloodFXManifestCategories bloodFXManifestCategories = manifest.categories ?? new BloodFXManifestCategories();
			_burstPrefabs = LoadCategory(bloodFXManifestCategories.burst, "burst");
			_chutePrefabs = LoadCategory(bloodFXManifestCategories.chute, "chute");
			_sprayPrefabs = LoadCategory(bloodFXManifestCategories.spray, "spray");
			_woundPrefabs = LoadCategory(bloodFXManifestCategories.wound, "wound");
			_wallPrefabs = LoadCategory(bloodFXManifestCategories.wall, "wall");
			_spotsPrefabs = LoadCategory(bloodFXManifestCategories.spots, "spots");
			_bodyPrefabs = LoadCategory(bloodFXManifestCategories.body, "body");
			_puddlePrefabs = LoadCategory(bloodFXManifestCategories.puddle, "puddle");
			_vfxSpikePrefabs = LoadCategory(bloodFXManifestCategories.vfx_spike, "vfx_spike");
			_gibHeadPrefabs = LoadGibCategory(bloodFXManifestCategories.gib_head, "gib_head");
			_gibArmLeftPrefabs = LoadGibCategory(bloodFXManifestCategories.gib_arm_l, "gib_arm_l");
			_gibArmRightPrefabs = LoadGibCategory(bloodFXManifestCategories.gib_arm_r, "gib_arm_r");
			_gibLegLeftPrefabs = LoadGibCategory(bloodFXManifestCategories.gib_leg_l, "gib_leg_l");
			_gibLegRightPrefabs = LoadGibCategory(bloodFXManifestCategories.gib_leg_r, "gib_leg_r");
			_gibChunkPrefabs = LoadGibCategory(bloodFXManifestCategories.gib_chunk, "gib_chunk");
			_footprintLeftPrefabs = LoadFootprintCategory(bloodFXManifestCategories.footprint_l, "footprint_l");
			_footprintRightPrefabs = LoadFootprintCategory(bloodFXManifestCategories.footprint_r, "footprint_r");
			BloodFXManifestPlan bloodFXManifestPlan = BloodFXManifestPlan.Describe(bloodFXManifestCategories);
			for (int i = 0; i < bloodFXManifestPlan.InfoLines.Count; i++)
			{
				ManualLogSource log5 = BloodFXBootstrap.Log;
				if (log5 != null)
				{
					log5.LogInfo((object)("[Y4NGZCompany.BloodFX] " + bloodFXManifestPlan.InfoLines[i]));
				}
			}
			_gibTorsoPrefabs = (bloodFXManifestPlan.IsListed(BloodFXOptionalCategories.GibTorso) ? LoadGibCategory(bloodFXManifestCategories.gib_torso, "gib_torso") : Array.Empty<GameObject>());
			_gibPelvisPrefabs = (bloodFXManifestPlan.IsListed(BloodFXOptionalCategories.GibPelvis) ? LoadGibCategory(bloodFXManifestCategories.gib_pelvis, "gib_pelvis") : Array.Empty<GameObject>());
			_mistPrefabs = (bloodFXManifestPlan.IsListed(BloodFXOptionalCategories.Mist) ? KeepValid(LoadCategory(bloodFXManifestCategories.mist, "mist"), "mist", DescribeMistViolation) : Array.Empty<GameObject>());
			_gibBurstClips = (bloodFXManifestPlan.IsListed(BloodFXOptionalCategories.SfxGibBurst) ? LoadNamed<AudioClip>(bloodFXManifestCategories.sfx_gib_burst, "sfx_gib_burst") : Array.Empty<AudioClip>());
			_gibLandClips = (bloodFXManifestPlan.IsListed(BloodFXOptionalCategories.SfxGibLand) ? LoadNamed<AudioClip>(bloodFXManifestCategories.sfx_gib_land, "sfx_gib_land") : Array.Empty<AudioClip>());
			_plumePrefabs = (bloodFXManifestPlan.IsListed(BloodFXOptionalCategories.Plume) ? KeepValid(LoadCategory(bloodFXManifestCategories.plume, "plume"), "plume", DescribePlumeViolation) : Array.Empty<GameObject>());
			BuildPools();
			BloodFXDecalSurfaces.Probe();
			bool flag = BloodFXGibPrefabContract.IsCompleteKit(_gibHeadPrefabs.Length, _gibArmLeftPrefabs.Length, _gibArmRightPrefabs.Length, _gibLegLeftPrefabs.Length, _gibLegRightPrefabs.Length, _gibChunkPrefabs.Length);
			bool flag2 = _footprintLeftPrefabs.Length != 0 && _footprintRightPrefabs.Length != 0;
			ManualLogSource log6 = BloodFXBootstrap.Log;
			if (log6 != null)
			{
				log6.LogInfo((object)("[Y4NGZCompany.BloodFX] Assets ready: " + $"burst={_burstPrefabs.Length}, chute={_chutePrefabs.Length}, " + $"spray={_sprayPrefabs.Length}, wound={_woundPrefabs.Length}, " + $"wall={_wallPrefabs.Length}, spots={_spotsPrefabs.Length}, " + $"body={_bodyPrefabs.Length}, puddle={_puddlePrefabs.Length}, " + $"vfxSpike={_vfxSpikePrefabs.Length}, " + $"gibHead={_gibHeadPrefabs.Length}, gibArmL={_gibArmLeftPrefabs.Length}, " + $"gibArmR={_gibArmRightPrefabs.Length}, gibLegL={_gibLegLeftPrefabs.Length}, " + $"gibLegR={_gibLegRightPrefabs.Length}, gibChunk={_gibChunkPrefabs.Length}, " + $"footprintL={_footprintLeftPrefabs.Length}, footprintR={_footprintRightPrefabs.Length}, " + $"gibTorso={_gibTorsoPrefabs.Length}, gibPelvis={_gibPelvisPrefabs.Length}, " + $"mist={_mistPrefabs.Length}, sfxGibBurst={_gibBurstClips.Length}, sfxGibLand={_gibLandClips.Length}, " + $"plume={_plumePrefabs.Length}, " + $"gibAssets={flag}, footprintAssets={flag2}."));
			}
			if (!flag)
			{
				ManualLogSource log7 = BloodFXBootstrap.Log;
				if (log7 != null)
				{
					log7.LogInfo((object)"[Y4NGZCompany.BloodFX] Gib deaths unavailable: the bundle has no complete gib kit (head, both arms, both legs and at least one chunk). Bodies stay intact, and this player does not announce gib support, so a lobby with it keeps gib deaths off.");
				}
			}
			if (!flag2)
			{
				ManualLogSource log8 = BloodFXBootstrap.Log;
				if (log8 != null)
				{
					log8.LogInfo((object)"[Y4NGZCompany.BloodFX] Bloody footprints unavailable: the bundle has no left and right footprint decals.");
				}
			}
			if (bloodFXManifestCategories.vfx_spike != null && bloodFXManifestCategories.vfx_spike.Length != 0)
			{
				BloodFXVfxGraphSpike.Arm((_vfxSpikePrefabs.Length != 0) ? _vfxSpikePrefabs[0] : null, bloodFXManifestCategories.vfx_spike[0]);
			}
		}

		private static void BuildPools()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			if (_burstPrefabs.Length == 0 && _sprayPrefabs.Length == 0 && _mistPrefabs.Length == 0)
			{
				return;
			}
			GameObject val = new GameObject("Y4NGZCompany_BloodFXPool");
			val.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)val);
			_poolRoot = val.transform;
			try
			{
				_burstPool = BuildPool(_burstPrefabs, 8);
				_sprayPool = BuildPool(_sprayPrefabs, 4);
				_mistPool = BuildPool(_mistPrefabs, 2);
			}
			finally
			{
				val.SetActive(true);
			}
		}

		private static PooledParticle[] BuildPool(GameObject[] prefabs, int count)
		{
			if (prefabs.Length == 0)
			{
				return Array.Empty<PooledParticle>();
			}
			PooledParticle[] array = new PooledParticle[count];
			for (int i = 0; i < count; i++)
			{
				GameObject val = prefabs[i % prefabs.Length];
				GameObject val2 = Object.Instantiate<GameObject>(val, _poolRoot, false);
				((Object)val2).name = ((Object)val).name + "_Pooled" + i;
				val2.SetActive(false);
				array[i] = new PooledParticle(val2);
			}
			return array;
		}

		private static GameObject[] LoadCategory(string[] names, string categoryKey)
		{
			return LoadNamed<GameObject>(names, categoryKey);
		}

		private static T[] LoadNamed<T>(string[] names, string categoryKey) where T : Object
		{
			if (names == null || names.Length == 0)
			{
				return Array.Empty<T>();
			}
			T[] array = new T[names.Length];
			int num = 0;
			for (int i = 0; i < names.Length; i++)
			{
				T val = LoadAssetByName<T>(_bundle, names[i]);
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log = BloodFXBootstrap.Log;
					if (log != null)
					{
						log.LogWarning((object)("[Y4NGZCompany.BloodFX] Manifest category '" + categoryKey + "' lists '" + names[i] + "', which the bundle does not contain."));
					}
				}
				else
				{
					array[num++] = val;
				}
			}
			if (num == array.Length)
			{
				return array;
			}
			T[] array2 = new T[num];
			Array.Copy(array, array2, num);
			return array2;
		}

		private static GameObject[] LoadGibCategory(string[] names, string categoryKey)
		{
			return KeepValid(LoadCategory(names, categoryKey), categoryKey, DescribeGibViolation);
		}

		private static GameObject[] LoadFootprintCategory(string[] names, string categoryKey)
		{
			return KeepValid(LoadCategory(names, categoryKey), categoryKey, DescribeFootprintViolation);
		}

		private static GameObject[] KeepValid(GameObject[] prefabs, string categoryKey, Func<GameObject, string> describeViolation)
		{
			int num = 0;
			for (int i = 0; i < prefabs.Length; i++)
			{
				string text = describeViolation(prefabs[i]);
				if (text != null)
				{
					ManualLogSource log = BloodFXBootstrap.Log;
					if (log != null)
					{
						log.LogWarning((object)("[Y4NGZCompany.BloodFX] Manifest category '" + categoryKey + "' prefab '" + ((Object)prefabs[i]).name + "' rejected: " + text + "."));
					}
				}
				else
				{
					prefabs[num++] = prefabs[i];
				}
			}
			if (num == prefabs.Length)
			{
				return prefabs;
			}
			GameObject[] array = (GameObject[])(object)new GameObject[num];
			Array.Copy(prefabs, array, num);
			return array;
		}

		private static string DescribeGibViolation(GameObject prefab)
		{
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			MeshFilter val = ((componentsInChildren.Length == 1) ? componentsInChildren[0] : null);
			Collider[] components = prefab.GetComponents<Collider>();
			Collider val2 = ((components.Length == 1) ? components[0] : null);
			BloodFXGibPrefabShape shape = new BloodFXGibPrefabShape(componentsInChildren.Length, (Object)(object)val != (Object)null && (Object)(object)val.sharedMesh != (Object)null, (Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent<MeshRenderer>() != (Object)null, prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true).Length, prefab.GetComponentsInChildren<Collider>(true).Length, components.Length, val2 is BoxCollider || val2 is SphereCollider || val2 is CapsuleCollider, prefab.GetComponentsInChildren<Rigidbody>(true).Length);
			return BloodFXGibPrefabContract.FindViolation(in shape);
		}

		private static string DescribeFootprintViolation(GameObject prefab)
		{
			if (!((Object)(object)prefab.GetComponent<DecalProjector>() != (Object)null))
			{
				return "needs an HDRP DecalProjector on its root";
			}
			return null;
		}

		private static string DescribeMistViolation(GameObject prefab)
		{
			return BloodFXMistPrefabContract.FindViolation(prefab.GetComponentsInChildren<ParticleSystem>(true).Length, prefab.GetComponentsInChildren<MonoBehaviour>(true).Length);
		}

		private static string DescribePlumeViolation(GameObject prefab)
		{
			VisualEffect component = prefab.GetComponent<VisualEffect>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.visualEffectAsset == (Object)null)
			{
				return "needs a VisualEffect with its graph on the root";
			}
			int num = prefab.GetComponentsInChildren<MonoBehaviour>(true).Length;
			if (num <= 0)
			{
				return null;
			}
			return $"carries {num} script component(s); a plume is a graph only";
		}

		private static AssetBundle FindLoadedBundle(BloodFXManifest manifest)
		{
			string text = FirstName(manifest);
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles())
			{
				if ((Object)(object)allLoadedAssetBundle == (Object)null)
				{
					continue;
				}
				try
				{
					if (allLoadedAssetBundle.Contains(text) || BundleHasMatchingAsset(allLoadedAssetBundle, text))
					{
						return allLoadedAssetBundle;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static string FirstName(BloodFXManifest manifest)
		{
			BloodFXManifestCategories bloodFXManifestCategories = manifest?.categories;
			if (bloodFXManifestCategories == null)
			{
				return null;
			}
			string[][] array = new string[8][] { bloodFXManifestCategories.burst, bloodFXManifestCategories.chute, bloodFXManifestCategories.spray, bloodFXManifestCategories.wound, bloodFXManifestCategories.wall, bloodFXManifestCategories.spots, bloodFXManifestCategories.body, bloodFXManifestCategories.puddle };
			foreach (string[] array2 in array)
			{
				if (array2 != null && array2.Length != 0 && !string.IsNullOrWhiteSpace(array2[0]))
				{
					return array2[0];
				}
			}
			return null;
		}

		private static bool TryLoadManifest(out BloodFXManifest manifest, out string reason)
		{
			manifest = null;
			reason = string.Empty;
			try
			{
				string text = ResolveRuntimeAssetPath("y4ngz-bloodfx.manifest.json");
				if (string.IsNullOrWhiteSpace(text))
				{
					reason = "file='y4ngz-bloodfx.manifest.json' not found next to the plugin";
					return false;
				}
				manifest = JsonUtility.FromJson<BloodFXManifest>(File.ReadAllText(text));
				if (manifest == null)
				{
					reason = "manifest_json_returned_null";
					return false;
				}
				if (manifest.categories == null)
				{
					reason = "manifest_missing_categories";
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		private static string ResolveRuntimeAssetPath(string fileName)
		{
			if (string.IsNullOrWhiteSpace(fileName))
			{
				return null;
			}
			string text = null;
			try
			{
				string location = typeof(BloodFXAssets).Assembly.Location;
				if (!string.IsNullOrWhiteSpace(location))
				{
					text = Path.GetDirectoryName(location);
				}
			}
			catch
			{
				text = null;
			}
			string[] array = new string[8]
			{
				(!string.IsNullOrWhiteSpace(text)) ? Path.Combine(text, fileName) : null,
				Path.Combine(Paths.PluginPath, "y4ngz-BloodVFX", fileName),
				Path.Combine(Paths.PluginPath, "BloodVFX", fileName),
				Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZBloodFX", fileName),
				Path.Combine(Paths.PluginPath, "Y4NGZBloodFX", fileName),
				Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZCompany", fileName),
				Path.Combine(Paths.PluginPath, "Y4NGZCompany", fileName),
				Path.Combine(Paths.PluginPath, fileName)
			};
			foreach (string text2 in array)
			{
				if (!string.IsNullOrWhiteSpace(text2) && File.Exists(text2))
				{
					return text2;
				}
			}
			return null;
		}

		private static T LoadAssetByName<T>(AssetBundle bundle, string assetName) where T : Object
		{
			if ((Object)(object)bundle == (Object)null || string.IsNullOrWhiteSpace(assetName))
			{
				return default(T);
			}
			try
			{
				T val = bundle.LoadAsset<T>(assetName);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			catch
			{
			}
			string[] bundleAssetNames = GetBundleAssetNames(bundle);
			for (int i = 0; i < bundleAssetNames.Length; i++)
			{
				if (!AssetNameMatches(bundleAssetNames[i], assetName))
				{
					continue;
				}
				try
				{
					T val2 = bundle.LoadAsset<T>(bundleAssetNames[i]);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
				}
				catch
				{
				}
			}
			return default(T);
		}

		private static bool BundleHasMatchingAsset(AssetBundle bundle, string assetName)
		{
			string[] bundleAssetNames = GetBundleAssetNames(bundle);
			for (int i = 0; i < bundleAssetNames.Length; i++)
			{
				if (AssetNameMatches(bundleAssetNames[i], assetName))
				{
					return true;
				}
			}
			return false;
		}

		private static string[] GetBundleAssetNames(AssetBundle bundle)
		{
			try
			{
				return ((Object)(object)bundle != (Object)null) ? bundle.GetAllAssetNames() : Array.Empty<string>();
			}
			catch
			{
				return Array.Empty<string>();
			}
		}

		private static bool AssetNameMatches(string bundleAssetName, string requestedName)
		{
			if (string.IsNullOrWhiteSpace(bundleAssetName) || string.IsNullOrWhiteSpace(requestedName))
			{
				return false;
			}
			string a = Path.GetFileNameWithoutExtension(bundleAssetName).Replace('\\', '/');
			string text = Path.GetFileNameWithoutExtension(requestedName).Replace('\\', '/');
			if (!string.Equals(a, text, StringComparison.OrdinalIgnoreCase) && !bundleAssetName.EndsWith("/" + requestedName, StringComparison.OrdinalIgnoreCase))
			{
				return bundleAssetName.EndsWith("/" + text, StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
	}
	public static class BloodFXBodyDecals
	{
		public readonly struct Profile
		{
			public readonly float Radius;

			public readonly float MinWidth;

			public readonly float MaxWidth;

			public readonly float Depth;

			public readonly float FrontMargin;

			public readonly float HeavyScale;

			public Profile(float radius, float minWidth, float maxWidth, float depth, float frontMargin, float heavyScale)
			{
				Radius = radius;
				MinWidth = minWidth;
				MaxWidth = maxWidth;
				Depth = depth;
				FrontMargin = frontMargin;
				HeavyScale = heavyScale;
			}
		}

		public static GameObject PlaceAt(long channelKey, Transform anchor, Vector3 surface, Vector3 direction, in Profile profile, bool final, int cap, string label, GameObject prefabOverride = null)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			cap = Mathf.Max(0, cap);
			if (cap == 0 || (Object)(object)anchor == (Object)null)
			{
				return null;
			}
			GameObject val = (((Object)(object)prefabOverride != (Object)null) ? prefabOverride : BloodFXAssets.BodyPrefab);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			float size;
			GameObject val2 = SpawnAt(anchor, surface, direction, in profile, final, heavy: true, val, (DecalLayerEnum)255, out size);
			BloodFXDecalRegistry.RegisterBodyDecal(channelKey, val2, cap);
			BloodFXBootstrap.Trace("body decal " + label + " bone='" + ((Object)anchor).name + "' pos=" + BloodFXDirector.Format(surface) + " prefab='" + ((Object)val).name + "' " + $"size={size:0.###}x{size:0.###}x{profile.Depth:0.###} frontMargin={profile.FrontMargin:0.###} " + $"decalLayer={(object)(DecalLayerEnum)255} path=creature-contact " + $"count={BloodFXDecalRegistry.BodyCount(channelKey)}/{cap} " + BloodFXDirector.DescribeProjector(val2) + ".");
			return val2;
		}

		internal static GameObject SpawnAt(Transform anchor, Vector3 surface, Vector3 direction, in Profile profile, bool final, bool heavy, GameObject prefab, DecalLayerEnum layer, out float size)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0028: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((((Vector3)(ref direction)).sqrMagnitude > 0.0001f) ? (-((Vector3)(ref direction)).normalized) : (surface - anchor.position));
			if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
			{
				val = anchor.forward;
			}
			if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
			{
				val = Vector3.forward;
			}
			((Vector3)(ref val)).Normalize();
			Quaternion val2 = BloodFXDirector.ProjectAlong(-val) * Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward);
			GameObject obj = Object.Instantiate<GameObject>(prefab, surface, val2, anchor);
			size = (final ? (profile.MaxWidth * profile.HeavyScale) : (Random.Range(profile.MinWidth, profile.MaxWidth) * (heavy ? profile.HeavyScale : 1f)));
			BloodFXDirector.ApplyProjectorScale(obj, size, profile.Depth, profile.FrontMargin, layer);
			return obj;
		}

		internal static Vector3 SegmentSurface(Transform bone, Transform segmentEnd, float along, Vector3 hitDirection, float radius, out Vector3 inward)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_0088: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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)
			Vector3 position = bone.position;
			Vector3 val = (((Object)(object)segmentEnd != (Object)null) ? (segmentEnd.position - position) : Vector3.zero);
			float magnitude = ((Vector3)(ref val)).magnitude;
			Vector3 val2 = position;
			if (magnitude > 0.0001f)
			{
				val /= magnitude;
				val2 = position + val * (magnitude * Mathf.Clamp01(along));
			}
			else
			{
				val = Vector3.up;
			}
			Vector3 val3 = ((((Vector3)(ref hitDirection)).sqrMagnitude > 0.0001f) ? (-hitDirection) : Vector3.zero);
			val3 -= val * Vector3.Dot(val3, val);
			if (((Vector3)(ref val3)).sqrMagnitude <= 0.0001f)
			{
				val3 = RandomPerpendicular(val);
			}
			((Vector3)(ref val3)).Normalize();
			inward = -val3;
			return val2 + val3 * radius;
		}

		internal static void MoveToBone(Transform decal, Transform bone)
		{
			decal.SetParent(bone, false);
		}

		internal static GameObject MirrorOnto(GameObject decal, Transform bone)
		{
			return Object.Instantiate<GameObject>(decal, bone, false);
		}

		private static Vector3 RandomPerpendicular(Vector3 axis)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < 4; i++)
			{
				Vector3 val = Random.onUnitSphere;
				val -= axis * Vector3.Dot(val, axis);
				if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
				{
					return val;
				}
			}
			return Vector3.Cross(axis, (Mathf.Abs(axis.y) < 0.9f) ? Vector3.up : Vector3.forward);
		}
	}
	internal static class BloodFXBodyWounds
	{
		private sealed class PlayerRig
		{
			internal readonly PlayerControllerB Player;

			internal readonly Transform Root;

			internal readonly Transform[] Body = (Transform[])(object)new Transform[16];

			internal readonly Transform[] Arms = (Transform[])(object)new Transform[16];

			internal PlayerRig(PlayerControllerB player, Transform root)
			{
				Player = player;
				Root = root;
			}
		}

		private sealed class CorpseRig
		{
			internal readonly DeadBodyInfo Body;

			internal readonly Transform[] Bones = (Transform[])(object)new Transform[16];

			internal CorpseRig(DeadBodyInfo body)
			{
				Body = body;
			}
		}

		private static readonly BloodFXBodyDecals.Profile TorsoProfile = new BloodFXBodyDecals.Profile(0.26f, 0.28f, 0.42f, 0.24f, 0.04f, 1.15f);

		private static readonly BloodFXBodyDecals.Profile ArmProfile = new BloodFXBodyDecals.Profile(0.12f, 0.15f, 0.22f, 0.12f, 0.03f, 1.15f);

		private static readonly BloodFXBodyDecals.Profile LegProfile = new BloodFXBodyDecals.Profile(0.14f, 0.17f, 0.26f, 0.14f, 0.03f, 1.15f);

		private const string BodyRigRootName = "spine";

		private static readonly Dictionary<int, PlayerRig> PlayerRigs = new Dictionary<int, PlayerRig>(4);

		private static readonly Dictionary<int, CorpseRig> CorpseRigs = new Dictionary<int, CorpseRig>(4);

		private static readonly List<Transform> RigScratch = new List<Transform>(64);

		private static readonly List<GameObject> WoundScratch = new List<GameObject>(16);

		private static readonly List<int> StaleCorpses = new List<int>(4);

		internal static void AddHitWound(PlayerControllerB player, Vector3 direction, bool heavy, string path)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			int bodyWoundCap = BloodFXDecalRegistry.BodyWoundCap;
			if ((Object)(object)player == (Object)null || bodyWoundCap == 0 || !BloodFXAssets.HasBodyPrefabs)
			{
				return;
			}
			GameObject bodyPrefab = BloodFXAssets.BodyPrefab;
			if ((Object)(object)bodyPrefab == (Object)null)
			{
				return;
			}
			PlayerRig playerRig = ResolvePlayerRig(player);
			int num = BloodFXWoundBones.Select(Random.value, FacingSide(player.thisPlayerBody, direction));
			Transform val = playerRig.Body[num];
			if ((Object)(object)val == (Object)null)
			{
				num = 0;
				val = (((Object)(object)playerRig.Body[num] != (Object)null) ? playerRig.Body[num] : ResolveTorsoAnchor(player));
			}
			if ((Object)(object)val == (Object)null)
			{
				BloodFXBootstrap.Trace("wound ABORTED (no rig bone) player='" + BloodFXDecalSurfaces.SafeName(player) + "' path=" + path + ".");
				return;
			}
			BloodFXDecalSurfaces.AuditPlayerMaterials(player);
			BloodFXDecalSurfaces.EnsurePlayerDecalLayer(player);
			BloodFXDecalSurfaces.EnsurePlayerReceivesDecals(player);
			int instanceID = ((Object)player).GetInstanceID();
			bool flag = BloodFXDecalRegistry.BodyCount(instanceID) >= bodyWoundCap - 1;
			float size;
			Vector3 surface;
			GameObject val2 = Place(val, playerRig.Body[BloodFXWoundBones.SegmentEndOf(num)], num, direction, heavy, flag, bodyPrefab, PlayerDecalLayer(), out size, out surface);
			GameObject val3 = null;
			if (BloodFXWoundBones.IsArm(num) && IsLocalLivingOwner(player))
			{
				Transform val4 = playerRig.Arms[num];
				if ((Object)(object)val4 != (Object)null)
				{
					val3 = BloodFXBodyDecals.MirrorOnto(val2, val4);
				}
			}
			BloodFXDecalRegistry.RegisterBodyWound(instanceID, val2, val3);
			if (BloodFXBootstrap.Verbose)
			{
				BloodFXBootstrap.Trace("wound player='" + BloodFXDecalSurfaces.SafeName(player) + "' bone='" + ((Object)val).name + "' " + $"pos={BloodFXDirector.Format(surface)} prefab='{((Object)bodyPrefab).name}' size={size:0.###} " + "heavy=" + (heavy ? "y" : "n") + " final=" + (flag ? "y" : "n") + " mirror=" + (((Object)(object)val3 != (Object)null) ? "y" : "n") + " dirKnown=" + ((((Vector3)(ref direction)).sqrMagnitude > 0.0001f) ? "y" : "n") + " path=" + path + " decalPath=" + BloodFXDecalSurfaces.ActivePathName + " " + $"count={BloodFXDecalRegistry.BodyCount(instanceID)}/{bodyWoundCap} " + BloodFXDirector.DescribeProjector(val2) + ".");
			}
		}

		internal static void AddDeathWound(PlayerControllerB player, DeadBodyInfo body, Vector3 direction)
		{
			//IL_0046: 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)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			int bodyWoundCap = BloodFXDecalRegistry.BodyWoundCap;
			if ((Object)(object)player == (Object)null || (Object)(object)body == (Object)null || bodyWoundCap == 0 || !BloodFXAssets.HasBodyPrefabs)
			{
				return;
			}
			GameObject bodyPrefab = BloodFXAssets.BodyPrefab;
			if ((Object)(object)bodyPrefab == (Object)null)
			{
				return;
			}
			Transform[] array = ResolveCorpseRig(body);
			int num = BloodFXWoundBones.Select(Random.value, FacingSide(player.thisPlayerBody, direction));
			Transform val = array[num];
			if ((Object)(object)val == (Object)null)
			{
				num = 0;
				val = array[num];
			}
			if ((Object)(object)val == (Object)null)
			{
				BloodFXBootstrap.Trace("death wound ABORTED (corpse has no rig bone) player='" + BloodFXDecalSurfaces.SafeName(player) + "' corpse='" + ((Object)body).name + "'.");
				return;
			}
			int instanceID = ((Object)player).GetInstanceID();
			bool final = BloodFXDecalRegistry.BodyCount(instanceID) >= bodyWoundCap - 1;
			float size;
			Vector3 surface;
			GameObject val2 = Place(val, array[BloodFXWoundBones.SegmentEndOf(num)], num, direction, heavy: true, final, bodyPrefab, PlayerDecalLayer(), out size, out surface);
			BloodFXDecalRegistry.RegisterBodyWound(instanceID, val2, null);
			if (BloodFXBootstrap.Verbose)
			{
				BloodFXBootstrap.Trace("death wound player='" + BloodFXDecalSurfaces.SafeName(player) + "' corpse='" + ((Object)body).name + "' " + $"bone='{((Object)val).name}' pos={BloodFXDirector.Format(surface)} size={size:0.###} " + $"count={BloodFXDecalRegistry.BodyCount(instanceID)}/{bodyWoundCap} " + BloodFXDirector.DescribeProjector(val2) + ".");
			}
		}

		internal static void MoveToCorpse(PlayerControllerB player, DeadBodyInfo body)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)body == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)player).GetInstanceID();
			BloodFXDecalRegistry.DropBodyMirrors(instanceID);
			WoundScratch.Clear();
			BloodFXDecalRegistry.CopyBodyWounds(instanceID, WoundScratch);
			if (WoundScratch.Count == 0)
			{
				return;
			}
			Transform[] array = ResolveCorpseRig(body);
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < WoundScratch.Count; i++)
			{
				GameObject val = WoundScratch[i];
				Transform parent = val.transform.parent;
				int num3 = (((Object)(object)parent != (Object)null) ? BloodFXWoundBones.SlotOf(((Object)parent).name) : (-1));
				Transform val2 = ((num3 >= 0) ? array[num3] : null);
				if ((Object)(object)val2 == (Object)null)
				{
					BloodFXDecalRegistry.RemoveBodyWound(instanceID, val);
					num2++;
				}
				else
				{
					BloodFXBodyDecals.MoveToBone(val.transform, val2);
					num++;
				}
			}
			WoundScratch.Clear();
			if (BloodFXBootstrap.Verbose)
			{
				BloodFXBootstrap.Trace("corpse player='" + BloodFXDecalSurfaces.SafeName(player) + "' corpse='" + ((Object)body).name + "' " + $"woundsMoved={num} woundsDropped={num2}.");
			}
		}

		internal static void DropMirrors(PlayerControllerB player)
		{
			if ((Object)(object)player != (Object)null)
			{
				BloodFXDecalRegistry.DropBodyMirrors(((Object)player).GetInstanceID());
			}
		}

		internal static void ResetTransientState()
		{
			PlayerRigs.Clear();
			CorpseRigs.Clear();
			WoundScratch.Clear();
		}

		private static GameObject Place(Transform bone, Transform segmentEnd, int wound, Vector3 direction, bool heavy, bool final, GameObject prefab, DecalLayerEnum layer, out float size, out Vector3 surface)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			BloodFXBodyDecals.Profile profile = ProfileFor(BloodFXWoundBones.RegionOf(wound));
			BloodFXWoundBones.SegmentSpan(wound, out var from, out var to);
			surface = BloodFXBodyDecals.SegmentSurface(bone, segmentEnd, Random.Range(from, to), direction, profile.Radius, out var inward);
			return BloodFXBodyDecals.SpawnAt(bone, surface, inward, in profile, final, heavy, prefab, layer, out size);
		}

		private static BloodFXBodyDecals.Profile ProfileFor(BloodFXWoundRegion region)
		{
			return region switch
			{
				BloodFXWoundRegion.Arm => ArmProfile, 
				BloodFXWoundRegion.Leg => LegProfile, 
				_ => TorsoProfile, 
			};
		}

		private static DecalLayerEnum PlayerDecalLayer()
		{
			if (BloodFXDecalSurfaces.BodyDecalLayerIsolationActive)
			{
				return (DecalLayerEnum)128;
			}
			return (DecalLayerEnum)255;
		}

		private static BloodFXBodySide FacingSide(Transform body, Vector3 direction)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)body == (Object)null || ((Vector3)(ref direction)).sqrMagnitude <= 0.0001f)
			{
				return BloodFXWoundBones.FacingSide(directionKnown: false, 0.0);
			}
			Vector3 val = -direction;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
			{
				return BloodFXWoundBones.FacingSide(directionKnown: true, 0.0);
			}
			return BloodFXWoundBones.FacingSide(directionKnown: true, Vector3.Dot(((Vector3)(ref val)).normalized, body.right));
		}

		private static bool IsLocalLivingOwner(PlayerControllerB player)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.localPlayerController == (Object)(object)player && player.isPlayerControlled)
			{
				return !player.isPlayerDead;
			}
			return false;
		}

		private static Transform ResolveTorsoAnchor(PlayerControllerB player)
		{
			if ((Object)(object)player.upperSpine != (Object)null)
			{
				return player.upperSpine;
			}
			if ((Object)(object)player.lowerSpine != (Object)null)
			{
				return player.lowerSpine;
			}
			return null;
		}

		private static PlayerRig ResolvePlayerRig(PlayerControllerB player)
		{
			int instanceID = ((Object)player).GetInstanceID();
			if (PlayerRigs.TryGetValue(instanceID, out var value) && (Object)(object)value.Player == (Object)(object)player && (Object)(object)value.Root != (Object)null)
			{
				return value;
			}
			Transform root = ResolveBodyRigRoot(player);
			value = new PlayerRig(player, root);
			FillRig(root, value.Body);
			FillRig(player.playerModelArmsMetarig, value.Arms);
			PlayerRigs[instanceID] = value;
			return value;
		}

		internal static Transform ResolveBodyRigRoot(PlayerControllerB player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			Transform obj = (((Object)(object)player.lowerSpine != (Object)null) ? player.lowerSpine : player.upperSpine);
			Transform transform = ((Component)player).transform;
			Transform val = obj;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)transform)
			{
				if (((Object)val).name == "spine")
				{
					return val;
				}
				val = val.parent;
			}
			if (!((Object)(object)player.lowerSpine != (Object)null))
			{
				return player.upperSpine;
			}
			return player.lowerSpine.parent;
		}

		private static Transform[] ResolveCorpseRig(DeadBodyInfo body)
		{
			int instanceID = ((Object)body).GetInstanceID();
			if (CorpseRigs.TryGetValue(instanceID, out var value) && (Object)(object)value.Body == (Object)(object)body)
			{
				return value.Bones;
			}
			StaleCorpses.Clear();
			foreach (KeyValuePair<int, CorpseRig> corpseRig in CorpseRigs)
			{
				if ((Object)(object)corpseRig.Value.Body == (Object)null)
				{
					StaleCorpses.Add(corpseRig.Key);
				}
			}
			for (int i = 0; i < StaleCorpses.Count; i++)
			{
				CorpseRigs.Remove(StaleCorpses[i]);
			}
			StaleCorpses.Clear();
			value = new CorpseRig(body);
			FillRig(((Component)body).transform, value.Bones);
			CorpseRigs[instanceID] = value;
			return value.Bones;
		}

		private static void FillRig(Transform root, Transform[] slots)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			((Component)root).GetComponentsInChildren<Transform>(true, RigScratch);
			for (int i = 0; i < RigScratch.Count; i++)
			{
				Transform val = RigScratch[i];
				int num = BloodFXWoundBones.SlotOf(((Object)val).name);
				if (num >= 0 && (Object)(object)slots[num] == (Object)null)
				{
					slots[num] = val;
				}
			}
			RigScratch.Clear();
		}
	}
	public static class BloodFXBootstrap
	{
		private const string ConfigSection = "08 - Blood FX";

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<int> WorldDecalCap { get; private set; }

		internal static ConfigEntry<int> BodyDecalCap { get; private set; }

		internal static ConfigEntry<int> CreatureBodyDecalCap { get; private set; }

		internal static ConfigEntry<int> FinisherBodyDecalCap { get; private set; }

		internal static ConfigEntry<bool> EnableBundyDamageBlood { get; private set; }

		internal static ConfigEntry<float> IntensityMultiplier { get; private set; }

		internal static ConfigEntry<float> DecalFadeSeconds { get; private set; }

		internal static ConfigEntry<float> MaxEventsPerSecond { get; private set; }

		internal static ConfigEntry<float> MaxViewerDistance { get; private set; }

		internal static ConfigEntry<bool> SuppressVanillaBlood { get; private set; }

		internal static ConfigEntry<bool> DebugLogging { get; private set; }

		internal static ConfigEntry<bool> EnableDecalsOnRoomMaterials { get; private set; }

		internal static ConfigEntry<bool> IsolateBodyDecalsOnDecalLayer { get; private set; }

		internal static ConfigEntry<bool> GibDeathsEnabled { get; private set; }

		internal static ConfigEntry<string> GibDeathCauses { get; private set; }

		internal static ConfigEntry<float> GibPieceLifetimeSeconds { get; private set; }

		internal static ConfigEntry<int> GibSurvivorPieces { get; private set; }

		internal static ConfigEntry<int> GibChunkPieces { get; private set; }

		internal static ConfigEntry<float> GibAudioVolume { get; private set; }

		internal static ConfigEntry<bool> GibBloodPlume { get; private set; }

		internal static ConfigEntry<bool> BloodyFootprintsEnabled { get; private set; }

		internal static ConfigEntry<float> FootprintLifetimeSeconds { get; private set; }

		internal static ConfigEntry<int> FootprintBudget { get; private set; }

		internal static ConfigEntry<int> FootprintStepsFromWound { get; private set; }

		internal static ConfigEntry<int> FootprintStepsFromPool { get; private set; }

		public static bool BloodFXEnabled => Enabled?.Value ?? false;

		public static bool BundyDamageBloodEnabled => EnableBundyDamageBlood?.Value ?? true;

		public static int CreatureBodyDecalCapValue => CreatureBodyDecalCap?.Value ?? 10;

		public static bool Verbose => DebugLogging?.Value ?? false;

		public static void Trace(string message)
		{
			if (Verbose)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)("[Y4NGZCompany.BloodFX] " + message));
				}
			}
		}

		internal static void Initialize(ConfigFile config, ManualLogSource logger, Harmony harmony)
		{
			Log = logger;
			TryImportLegacyConfig(config);
			BindConfig(config);
			if (!Enabled.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"[Y4NGZCompany.BloodFX] Disabled via config.");
				}
				return;
			}
			harmony.PatchAll(typeof(BloodFXPatches));
			BloodFXRoundLifecyclePatches.Install(harmony);
			harmony.PatchAll(typeof(BloodFXDecalShieldPatches));
			TryPatchNetwork(harmony);
			BloodFXGibNetwork.Install(harmony);
			BloodFXHeadBodyHost.Install(harmony);
			BloodFXGibPrewarm.Install(harmony);
			BloodFXFootprints.Initialize(harmony);
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("[Y4NGZCompany.BloodFX] Ready: " + $"worldDecalCap={WorldDecalCap.Value}, bodyDecalCap={BodyDecalCap.Value}, " + $"creatureBodyDecalCap={CreatureBodyDecalCap.Value}, " + $"finisherBodyDecalCap={FinisherBodyDecalCap.Value}, " + $"bundyDamageBlood={EnableBundyDamageBlood.Value}, " + $"intensity={IntensityMultiplier.Value}, decalFadeSeconds={DecalFadeSeconds.Value}, " + $"maxEventsPerSecond={MaxEventsPerSecond.Value}, " + $"maxViewerDistance={MaxViewerDistance.Value}, " + $"suppressVanillaBlood={SuppressVanillaBlood.Value}, " + $"debugLogging={DebugLogging.Value}, " + $"gibDeaths={GibDeathsEnabled.Value}, gibDeathCauses='{GibDeathCauses.Value}', " + $"gibPieceLifetimeSeconds={GibPieceLifetimeSeconds.Value}, " + $"gibSurvivorPieces={GibSurvivorPieces.Value}, gibChunkPieces={GibChunkPieces.Value}, " + $"bloodyFootprints={BloodyFootprintsEnabled.Value}, " + $"footprintLifetimeSeconds={FootprintLifetimeSeconds.Value}, " + $"footprintBudget={FootprintBudget.Value}, " + $"footprintStepsFromWound={FootprintStepsFromWound.Value}, " + $"footprintStepsFromPool={FootprintStepsFromPool.Value}."));
			}
		}

		private static void TryPatchNetwork(Harmony harmony)
		{
			try
			{
				harmony.PatchAll(typeof(BloodFXNetPatches));
				BloodFXNet.Initialize();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("[Y4NGZCompany.BloodFX] Network patches failed (" + ex.GetType().Name + ": " + ex.Message + "); BloodVFX still registers its messages and announces itself to the host from its runtime driver, but as host it no longer follows BloodFX setting changes made in game."));
				}
			}
		}

		private static void TryImportLegacyConfig(ConfigFile config)
		{
			string text = "[08 - Blood FX]";
			try
			{
				string text2 = ((config != null) ? config.ConfigFilePath : null);
				if (string.IsNullOrEmpty(text2) || (File.Exists(text2) && File.ReadAllText(text2).IndexOf(text, StringComparison.Ordinal) >= 0))
				{
					return;
				}
				string path = Path.Combine(Paths.ConfigPath, "com.y4ngz.company.cfg");
				if (!File.Exists(path))
				{
					return;
				}
				List<string> list = new List<string>();
				bool flag = false;
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text3 = array[i].Trim();
					if (text3.StartsWith("[", StringComparison.Ordinal) && text3.EndsWith("]", StringComparison.Ordinal))
					{
						flag = string.Equals(text3, text, StringComparison.Ordinal);
					}
					else
					{
						if (!flag || text3.Length == 0 || text3.StartsWith("#", StringComparison.Ordinal))
						{
							continue;
						}
						int num = text3.IndexOf('=');
						if (num > 0)
						{
							string text4 = text3.Substring(0, num).Trim();
							string text5 = text3.Substring(num + 1).Trim();
							if (text4.Length != 0)
							{
								list.Add(text4 + " = " + text5);
							}
						}
					}
				}
				if (list.Count == 0)
				{
					return;
				}
				StringBuilder stringBuilder = new StringBuilder();
				if (File.Exists(text2))
				{
					stringBuilder.Append(File.ReadAllText(text2));
					stringBuilder.AppendLine();
				}
				else
				{
					string directoryName = Path.GetDirectoryName(text2);
					if (!string.IsNullOrEmpty(directoryName))
					{
						Directory.CreateDirectory(directoryName);
					}
				}
				stringBuilder.AppendLine(text);
				stringBuilder.AppendLine();
				for (int j = 0; j < list.Count; j++)
				{
					stringBuilder.AppendLine(list[j]);
				}
				File.WriteAllText(text2, stringBuilder.ToString());
				config.Reload();
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)($"[Y4NGZCompany.BloodFX] Imported {list.Count} setting(s) from " + "'com.y4ngz.company.cfg' into '" + Path.GetFileName(text2) + "': " + string.Join(", ", list.ToArray()) + "."));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[Y4NGZCompany.BloodFX] Legacy config import skipped (" + ex.GetType().Name + ": " + ex.Message + "); defaults are in force."));
				}
			}
		}

		private static void BindConfig(ConfigFile config)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Expected O, but got Unknown
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Expected O, but got Unknown
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Expected O, but got Unknown
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Expected O, but got Unknown
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Expected O, but got Unknown
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Expected O, but got Unknown
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Expected O, but got Unknown
			//IL_0418: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Expected O, but got Unknown
			//IL_0449: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Expected O, but got Unknown
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_0480: Expected O, but got Unknown
			//IL_04a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Expected O, but got Unknown
			Enabled = config.Bind<bool>("08 - Blood FX", "Enabled", true, "Master switch for the blood burst, splatter, and body-blood system.");
			WorldDecalCap = config.Bind<int>("08 - Blood FX", "World Decal Cap", 48, new ConfigDescription("Most wall and floor blood decals alive at once. Decals last the whole round; past this many the oldest is recycled. Set to 0 to place none.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 256), Array.Empty<object>()));
			BodyDecalCap = config.Bind<int>("08 - Blood FX", "Body Decal Cap Per Player", 8, new ConfigDescription("Most blood wounds carried on one player's body, spread across torso, arms and legs. Every distinct hit adds one; the oldest fades when the cap is reached. They survive healing, move onto the corpse, and clear on respawn or round end. Set to 0 to disable body blood.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>()));
			CreatureBodyDecalCap = config.Bind<int>("08 - Blood FX", "Creature Body Decal Cap", 10, new ConfigDescription("Most blood decals carried on one creature body. Creature blood has its own channel so it cannot evict player hit or finisher blood. Set to 0 to disable it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 24), Array.Empty<object>()));
			FinisherBodyDecalCap = config.Bind<int>("08 - Blood FX", "Finisher Body Decal Cap", 6, new ConfigDescription("Most blood decals carried in one player's finisher channel. This is separate from ordinary hit blood and clears with the other body channels on respawn or round end. Set to 0 to disable finisher body blood.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>()));
			EnableBundyDamageBlood = config.Bind<bool>("08 - Blood FX", "Enable Bundy Damage Blood", true, "Allow damage dealt to Bundy to place replicated blood decals on his body. Set off to keep the general Blood FX system while disabling Bundy damage blood.");
			IntensityMultiplier = config.Bind<float>("08 - Blood FX", "Intensity Multiplier", 1f, new ConfigDescription("Scales particle emission and the number of decals placed per hit. Damage amount still picks the small/medium/heavy tier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			DecalFadeSeconds = config.Bind<float>("08 - Blood FX", "Decal Fade Seconds", 1.2f, new ConfigDescription("How long a recycled blood decal takes to dissolve away. When a cap is reached the oldest decal fades out over this many seconds instead of vanishing mid-view. A fading decal has already left the cap queue, so this never costs you live blood. Set to 0 to remove recycled decals instantly, as before.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			MaxEventsPerSecond = config.Bind<float>("08 - Blood FX", "Max Blood Events Per Second", 2f, new ConfigDescription("Most blood bursts and splatter events one player can produce per second. Hits past the limit skip the burst and splatter but still leave a body wound. Set to 0 for no limit (duplicate-event suppression still applies).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			MaxViewerDistance = config.Bind<float>("08 - Blood FX", "Max Viewer Distance", 100f, new ConfigDescription("Metres from your camera past which a hit places no burst, spray, or wall/floor splatter. Damage runs on every client, so without this a hit across the map spends world decal slots on blood nobody will ever see — evicting the blood in the room you are standing in. Body blood and death puddles ignore this, because both persist and can be walked up to later. Set to 0 to place blood at any distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			SuppressVanillaBlood = config.Bind<bool>("08 - Blood FX", "Suppress Vanilla Blood Pools", true, "Stop the game's own pooled blood splats from spawning so only this system's blood shows. Snow footprints are unaffected.");
			DebugLogging = config.Bind<bool>("08 - Blood FX", "Debug Logging", false, "Write one diagnostic line per blood event (hit, environment splatter, body decal, death) plus a one-time material audit of each player's suit. Hits are rare, so the volume is low; turn it off once the system is dialled in.");
			EnableDecalsOnRoomMaterials = config.Bind<bool>("08 - Blood FX", "Enable Decals On Room Materials", true, "Interior wall and tile materials ship with HDRP's 'Receive Decals' turned off, so blood sprayed onto a wall was placed but never drawn (floors receive, walls did not). When on, the first blood decal to land on a surface flips that material to decal-receiving. Visual-only, applies to static room geometry, and each material is processed once.");
			IsolateBodyDecalsOnDecalLayer = config.Bind<bool>("08 - Blood FX", "Isolate Body Decals On Decal Layer", false, "ESCAPE HATCH, off by default. Puts body blood on HDRP DecalLayer7 and grants that layer only to the wounded player's renderers, so the projector physically cannot paint anything else. Verified broken in the shipped build: the HDRP asset reports supportDecalLayers=true but no shader in the game data carries the DECAL_LAYERS variant, so layered decals draw nothing at all. Held items are kept clean by the item material shield instead. Only turn this on if a future game or mod update ships the missing shader variants.");
			GibDeathsEnabled = config.Bind<bool>("08 - Blood FX", "Gib Deaths Enabled", true, "Fatal deaths from the selected causes burst the body into pieces and leave NO recoverable body (counts as an unrecovered casualty). The host's setting decides, and only when every player in the lobby runs a compatible BloodVFX with the gib assets.");
			GibDeathCauses = config.Bind<string>("08 - Blood FX", "Gib Death Causes", "Blast", "Comma-separated CauseOfDeath names whose fatal deaths burst the body. Only causes on the supported list take effect (supported: " + BloodFXGibCauses.SupportedNames + "); others are ignored with one warning. The host's setting decides.");
			GibPieceLifetimeSeconds = config.Bind<float>("08 - Blood FX", "Gib Piece Lifetime Seconds", 60f, new ConfigDescription("How long the body pieces of a gib death stay before they are removed. The pieces are cosmetic. The host's setting decides.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 300f), Array.Empty<object>()));
			GibSurvivorPieces = config.Bind<int>("08 - Blood FX", "Gib Survivor Pieces", 6, new ConfigDescription("Most recognizable body pieces (arms, legs, torso, pelvis) a gib death leaves. A blast death (a landmine or other explosion) leaves every one of them this allows; any other death draws between half of this, rounded up, and this many. Set to 0 for none. The host's setting decides.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			GibChunkPieces = config.Bind<int>("08 - Blood FX", "Gib Chunk Pieces", 14, new ConfigDescription("Most small flesh chunks a gib death leaves. A blast death (a landmine or other explosion) draws from the top half of the range, about three quarters of this up to this many; any other death draws between half of this, rounded up, and this many. Set to 0 for none. The host's setting decides.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>()));
			if (GibSurvivorPieces.Value < 6)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)("[Y4NGZCompany.BloodFX] Gib piece counts come from your config: Gib Survivor Pieces=" + $"{GibSurvivorPieces.Value}, Gib Chunk Pieces={GibChunkPieces.Value}. The defaults are now " + $"{6} and {14}; your " + "config values win. Set them to the defaults for more gib pieces."));
				}
			}
			GibAudioVolume = config.Bind<float>("08 - Blood FX", "Gib Audio Volume", 1f, new ConfigDescription("Volume of the burst and landing sounds of gib deaths on this machine. Set to 0 to mute them. Your own setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			GibBloodPlume = config.Bind<bool>("08 - Blood FX", "Gib Blood Plume", true, "Play the blood plume (a VFX Graph burst of blood splats) where a gibbed body bursts, on this machine. Turn it off if it looks wrong or costs frames. Your own setting.");
			BloodyFootprintsEnabled = config.Bind<bool>("08 - Blood FX", "Bloody Footprints Enabled", true, "Leave a short, fading trail of left and right bloody boot prints after a wound or after stepping in a fresh death pool. Low health alone never leaves prints. Shows other players' prints only when the host also runs BloodVFX.");
			FootprintLifetimeSeconds = config.Bind<float>("08 - Blood FX", "Footprint Lifetime Seconds", 45f, new ConfigDescription("How long a bloody footprint stays before it fades away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
			FootprintBudget = config.Bind<int>("08 - Blood FX", "Footprint Budget", 64, new ConfigDescription("Most bloody footprints shown at once (live and fading prints, all players). Set to 0 to show none.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 256), Array.Empty<object>()));
			FootprintStepsFromWound = config.Bind<int>("08 - Blood FX", "Footprint Steps From Wound", 8, new ConfigDescription("Bloody steps a player leaves after a bleeding wound. Set to 0 for no trail from wounds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 32), Array.Empty<object>()));
			FootprintStepsFromPool = config.Bind<int>("08 - Blood FX", "Footprint Steps From Pool", 14, new ConfigDescription("Bloody steps a player leaves after walking through a blood pool. Set to 0 for no trail from pools.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 32), Array.Empty<object>()));
		}
	}
	internal enum BloodFXAnnounceResult
	{
		Added,
		Changed,
		Unchanged
	}
	internal enum BloodFXPeerGap
	{
		None,
		Silent,
		ProtocolMismatch,
		MissingCapability
	}
	internal readonly struct BloodFXPeerInfo
	{
		internal readonly ulong ClientId;

		internal readonly byte Protocol;

		internal readonly BloodFXCapabilities Capabilities;

		internal readonly string PluginVersion;

		internal bool IsCompatible => Protocol == 1;

		internal BloodFXPeerInfo(ulong clientId, in BloodFXHello hello)
		{
			ClientId = clientId;
			Protocol = hello.Protocol;
			Capabilities = hello.Capabilities;
			PluginVersion = hello.PluginVersion ?? string.Empty;
		}

		internal bool Has(BloodFXCapabilities required)
		{
			if (IsCompatible)
			{
				return (Capabilities & required) == required;
			}
			return false;
		}
	}
	internal sealed class BloodFXCapabilityRegistry
	{
		private readonly List<ulong> _connected = new List<ulong>();

		private readonly Dictionary<ulong, BloodFXPeerInfo> _announced = new Dictionary<ulong, BloodFXPeerInfo>();

		private ulong _hostClientId;

		internal int ConnectedCount => _connected.Count;

		internal int AnnouncedCount => _announced.Count;

		internal void Reset(ulong hostClientId)
		{
			_hostClientId = hostClientId;
			_connected.Clear();
			_announced.Clear();
		}

		internal bool IsConnected(ulong clientId)
		{
			return _connected.Contains(clientId);
		}

		internal bool MarkConnected(ulong clientId)
		{
			if (clientId == _hostClientId || _connected.Contains(clientId))
			{
				return false;
			}
			_connected.Add(clientId);
			return true;
		}

		internal BloodFXAnnounceResult Announce(ulong clientId, in BloodFXHello hello)
		{
			if (clientId == _hostClientId)
			{
				return BloodFXAnnounceResult.Unchanged;
			}
			MarkConnected(clientId);
			BloodFXPeerInfo value = new BloodFXPeerInfo(clientId, in hello);
			if (!_announced.TryGetValue(clientId, out var value2))
			{
				_announced[clientId] = value;
				return BloodFXAnnounceResult.Added;
			}
			_announced[clientId] = value;
			if (value2.Protocol != value.Protocol || value2.Capabilities != value.Capabilities || !string.Equals(value2.PluginVersion, value.PluginVersion, StringComparison.Ordinal))
			{
				return BloodFXAnnounceResult.Changed;
			}
			return BloodFXAnnounceResult.Unchanged;
		}

		internal bool Remove(ulong clientId)
		{
			bool num = _connected.Remove(clientId);
			bool flag = _announced.Remove(clientId);
			return num || flag;
		}

		internal bool TryGet(ulong clientId, out BloodFXPeerInfo info)
		{
			return _announced.TryGetValue(clientId, out info);
		}

		internal BloodFXPeerGap FindGap(BloodFXCapabilities required, out ulong clientId)
		{
			for (int i = 0; i < _connected.Count; i++)
			{
				ulong key = (clientId = _connected[i]);
				if (!_announced.TryGetValue(key, out var value))
				{
					return BloodFXPeerGap.Silent;
				}
				if (!value.IsCompatible)
				{
					return BloodFXPeerGap.ProtocolMismatch;
				}
				if (!value.Has(required))
				{
					return BloodFXPeerGap.MissingCapability;
				}
			}
			clientId = 0uL;
			return BloodFXPeerGap.None;
		}

		internal void CollectAnnounced(List<ulong> into)
		{
			for (int i = 0; i < _connected.Count; i++)
			{
				if (_announced.ContainsKey(_connected[i]))
				{
					into.Add(_connected[i]);
				}
			}
		}

		internal void CollectCapable(BloodFXCapabilities required, ulong? except, List<ulong> into)
		{
			for (int i = 0; i < _connected.Count; i++)
			{
				ulong num = _connected[i];
				if ((!except.HasValue || except.Value != num) && _announced.TryGetValue(num, out var value) && value.Has(required))
				{
					into.Add(num);
				}
			}
		}
	}
	internal static class BloodFXCauseMask
	{
		internal const int MaxCauseValue = 31;

		internal static uint Parse(string list, IReadOnlyDictionary<string, int> supported, List<string> ignored)
		{
			if (string.IsNullOrWhiteSpace(list))
			{
				return 0u;
			}
			uint num = 0u;
			string[] array = list.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					if (TryFind(supported, text, out var value) && value >= 0 && value <= 31)
					{
						num |= (uint)(1 << value);
					}
					else if (ignored != null && !ContainsIgnoreCase(ignored, text))
					{
						ignored.Add(text);
					}
				}
			}
			return num;
		}

		internal static bool Contains(uint mask, int causeOfDeath)
		{
			if (causeOfDeath >= 0 && causeOfDeath <= 31)
			{
				return (mask & (uint)(1 << causeOfDeath)) != 0;
			}
			return false;
		}

		private static bool TryFind(IReadOnlyDictionary<string, int> supported, string name, out int value)
		{
			value = 0;
			if (supported == null)
			{
				return false;
			}
			foreach (KeyValuePair<string, int> item in supported)
			{
				if (string.Equals(item.Key, name, StringComparison.OrdinalIgnoreCase))
				{
					value = item.Value;
					return true;
				}
			}
			return false;
		}

		private static bool ContainsIgnoreCase(List<string> names, string name)
		{
			for (int i = 0; i < names.Count; i++)
			{
				if (string.Equals(names[i], name, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}
	}
	public static class BloodFXCreatureBlood
	{
		public static bool IsAvailable
		{
			get
			{
				ConfigEntry<bool> enabled = BloodFXBootstrap.Enabled;
				if (enabled != null && enabled.Value)
				{
					return BloodFXAssets.IsAvailable;
				}
				return false;
			}
		}

		public static bool PlayCreatureBleed(int creatureKey, Vector3 point, Vector3 direction, Color tint, int contacts, float scale)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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)
			return BloodFXDirector.OnCreatureBled(creatureKey, point, direction, tint, Mathf.Max(1, contacts), Mathf.Max(0.01f, scale));
		}
	}
	internal static class BloodFXDecalBank
	{
		private sealed class BankedDecal : MonoBehaviour
		{
			internal GameObject Prefab;

			internal bool InBank;
		}

		private const string RootName = "Y4NGZCompany_BloodFXDecalBank";

		private static readonly BloodFXInstanceBank<GameObject, BankedDecal> Bank = new BloodFXInstanceBank<GameObject, BankedDecal>(Create, IsAlive, Discard);

		private static readonly List<DecalProjector> InstanceProjectors = new List<DecalProjector>(2);

		private static readonly List<DecalProjector> PrefabProjectors = new List<DecalProjector>(2);

		private static Transform _root;

		private static bool _sized;

		internal static int BankedTotal
		{
			get
			{
				Bank.Prune();
				return Bank.BankedTotal;
			}
		}

		private static bool Usable
		{
			get
			{
				ConfigEntry<bool> enabled = BloodFXBootstrap.Enabled;
				if (enabled != null && enabled.Value)
				{
					return BloodFXAssets.IsAvailable;
				}
				return false;
			}
		}

		internal static int Prewarm()
		{
			if (!Usable)
			{
				return 0;
			}
			Bank.Prune();
			return Size(fill: true);
		}

		internal static GameObject Rent(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent)
		{
			//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_0065: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null)
			{
				return null;
			}
			if ((Object)(object)parent == (Object)null)
			{
				return Object.Instantiate<GameObject>(prefab, position, rotation);
			}
			if (!_sized)
			{
				Size(fill: false);
			}
			BankedDecal bankedDecal = Bank.Rent(prefab);
			if ((Object)(object)bankedDecal == (Object)null)
			{
				return Object.Instantiate<GameObject>(prefab, position, rotation, parent);
			}
			bankedDecal.InBank = false;
			GameObject gameObject = ((Component)bankedDecal).gameObject;
			Transform transform = gameObject.transform;
			transform.SetParent(parent, false);
			transform.SetPositionAndRotation(position, rotation);
			gameObject.SetActive(prefab.activeSelf);
			return gameObject;
		}

		internal static int TakeWarmDrawSet(List<GameObject> into)
		{
			if (into == null || !Usable)
			{
				return 0;
			}
			if (!_sized)
			{
				Size(fill: false);
			}
			return TakeOneEach(BloodFXAssets.SpotsPrefabs, into) + TakeOneEach(BloodFXAssets.WallPrefabs, into) + TakeOneEach(BloodFXAssets.PuddlePrefabs, into);
		}

		internal static void Return(GameObject instance)
		{
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			BankedDecal component = instance.GetComponent<BankedDecal>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)instance);
			}
			else if (!component.InBank)
			{
				GameObject prefab = component.Prefab;
				Transform val = (((Object)(object)prefab != (Object)null && Bank.HasRoomFor(prefab)) ? Root() : null);
				if ((Object)(object)val == (Object)null)
				{
					Object.Destroy((Object)(object)instance);
					return;
				}
				instance.SetActive(false);
				instance.transform.SetParent(val, false);
				RestorePrefabState(instance, prefab);
				component.InBank = true;
				Bank.Return(prefab, component);
			}
		}

		private static int Size(bool fill)
		{
			_sized = true;
			GameObject[] spotsPrefabs = BloodFXAssets.SpotsPrefabs;
			GameObject[] wallPrefabs = BloodFXAssets.WallPrefabs;
			GameObject[] puddlePrefabs = BloodFXAssets.PuddlePrefabs;
			BloodFXDirector.GibSplatterMix(out var sprayDecals, out var wallWeight);
			BloodFXDecalBankQuota bloodFXDecalBankQuota = BloodFXDecalBankPlan.Quotas(Mathf.Max(0, BloodFXBootstrap.WorldDecalCap?.Value ?? 48), sprayDecals, wallWeight, spotsPrefabs.Length, wallPrefabs.Length, puddlePrefabs.Length);
			return SizePool(spotsPrefabs, bloodFXDecalBankQuota.Spots, fill) + SizePool(wallPrefabs, bloodFXDecalBankQuota.Wall, fill) + SizePool(puddlePrefabs, bloodFXDecalBankQuota.Puddle, fill);
		}

		private static int SizePool(GameObject[] prefabs, int quota, bool fill)
		{
			int num = 0;
			for (int i = 0; i < prefabs.Length; i++)
			{
				GameObject val = prefabs[i];
				if (!((Object)(object)val == (Object)null))
				{
					int target = BloodFXDecalBankPlan.Spread(quota, prefabs.Length, i);
					if (fill)
					{
						num += Bank.Fill(val, target);
					}
					else
					{
						Bank.SetTarget(val, target);
					}
				}
			}
			return num;
		}

		private static int TakeOneEach(GameObject[] prefabs, List<GameObject> into)
		{
			int num = 0;
			for (int i = 0; i < prefabs.Length; i++)
			{
				BankedDecal bankedDecal = (((Object)(object)prefabs[i] != (Object)null) ? Bank.Rent(prefabs[i]) : null);
				if (!((Object)(object)bankedDecal == (Object)null))
				{
					bankedDecal.InBank = false;
					((Component)bankedDecal).transform.SetParent((Transform)null, false);
					into.Add(((Component)bankedDecal).gameObject);
					num++;
				}
			}
			return num;
		}

		private static void RestorePrefabState(GameObject instance, GameObject prefab)
		{
			//IL_000c: 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_00b0: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			instance.transform.localScale = prefab.transform.localScale;
			instance.GetComponentsInChildren<DecalProjector>(true, InstanceProjectors);
			prefab.GetComponentsInChildren<DecalProjector>(true, PrefabProjectors);
			int num = Mathf.Min(InstanceProjectors.Count, PrefabProjectors.Count);
			for (int i = 0; i < num; i++)
			{
				DecalProjector obj = InstanceProjectors[i];
				DecalProjector val = PrefabProjectors[i];
				obj.material = val.material;
				obj.drawDistance = val.drawDistance;
				obj.fadeScale = val.fadeScale;
				obj.startAngleFade = val.startAngleFade;
				obj.endAngleFade = val.endAngleFade;
				obj.uvScale = val.uvScale;
				obj.uvBias = val.uvBias;
				obj.decalLayerMask = val.decalLayerMask;
				obj.scaleMode = val.scaleMode;
				obj.pivot = val.pivot;
				obj.size = val.size;
				obj.fadeFactor = val.fadeFactor;
				((Behaviour)obj).enabled = ((Behaviour)val).enabled;
			}
			InstanceProjectors.Clear();
			PrefabProjectors.Clear();
		}

		private static Transform Root()
		{
			//IL_0018: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if ((Object)(object)_root != (Object)null)
			{
				return _root;
			}
			GameObject val = new GameObject("Y4NGZCompany_BloodFXDecalBank");
			val.SetActive(false);
			((Object)val).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)val);
			_root = val.transform;
			return _root;
		}

		private static BankedDecal Create(GameObject prefab)
		{
			Transform val = Root();
			if ((Object)(object)prefab == (Object)null || (Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(prefab, val, false);
			obj.SetActive(false);
			BankedDecal bankedDecal = obj.AddComponent<BankedDecal>();
			bankedDecal.Prefab = prefab;
			bankedDecal.InBank = true;
			return bankedDecal;
		}

		private static bool IsAlive(BankedDecal banked)
		{
			return (Object)(object)banked != (Object)null;
		}

		private static void Discard(BankedDecal banked)
		{
			if ((Object)(object)banked != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)banked).gameObject);
			}
		}
	}
	internal readonly struct BloodFXDecalBankQuota
	{
		internal readonly int Spots;

		internal readonly int Wall;

		internal readonly int Puddle;

		internal int Total => Spots + Wall + Puddle;

		internal BloodFXDecalBankQuota(int spots, int wall, int puddle)
		{
			Spots = spots;
			Wall = wall;
			Puddle = puddle;
		}
	}
	internal static class BloodFXDecalBankPlan
	{
		internal const int DeathDecals = 4;

		internal const int Puddles = 4;

		internal static BloodFXDecalBankQuota Quotas(int worldDecalCap, int gibSprayDecals, float wallWeight, int spotsPrefabs, int wallPrefabs, int puddlePrefabs)
		{
			if (worldDecalCap <= 0)
			{
				return default(BloodFXDecalBankQuota);
			}
			int num = BloodFXGibDecalShare.ShareFor(worldDecalCap);
			int num2 = num + 4;
			int puddle = ((puddlePrefabs > 0) ? 4 : 0);
			if (spotsPrefabs <= 0 && wallPrefabs <= 0)
			{
				return new BloodFXDecalBankQuota(0, 0, puddle);
			}
			if (wallPrefabs <= 0)
			{
				return new BloodFXDecalBankQuota(num2, 0, puddle);
			}
			if (spotsPrefabs <= 0)
			{
				return new BloodFXDecalBankQuota(0, num2, puddle);
			}
			int num3 = Math.Min(num, Math.Max(0, gibSprayDecals)) + 4;
			float num4 = Math.Min(1f, Math.Max(0f, wallWeight));
			int num5 = Math.Min(num2, (int)Math.Floor(num4 * (float)num3 + 0.5f));
			return new BloodFXDecalBankQuota(num2 - num5, num5, puddle);
		}

		internal static int Spread(int quota, int prefabCount, int index)
		{
			if (quota <= 0 || prefabCount <= 0 || index < 0 || index >= prefabCount)
			{
				return 0;
			}
			return quota / prefabCount + ((index < quota % prefabCount) ? 1 : 0);
		}
	}
	internal static class BloodFXDecalFade
	{
		private sealed class FadingDecal
		{
			internal readonly GameObject Decal;

			internal readonly Material[] Materials;

			internal float Elapsed;

			internal FadingDecal(GameObject decal, Material[] materials)
			{
				Decal = decal;
				Materials = materials;
				Elapsed = 0f;
			}
		}

		private sealed class Driver : MonoBehaviour
		{
			private void LateUpdate()
			{
				Tick();
			}
		}

		private const float MinDissolveSoftness = 0.25f;

		private static readonly int DissolveId = Shader.PropertyToID("_Dissolve");

		private static readonly int DissolveSoftnessId = Shader.PropertyToID("_DissolveSoftness")