Decompiled source of CrimsonBlood v0.7.6

CrimsonBlood.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace CrimsonBlood;

[BepInPlugin("marc.valheim.crimsonblood", "Crimson Blood", "0.7.6.2")]
[BepInProcess("valheim.exe")]
public sealed class CrimsonBloodPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "marc.valheim.crimsonblood";

	public const string PluginName = "Crimson Blood";

	public const string PluginVersion = "0.7.6.2";

	private const string YoureInjuredGuid = "com.marccunningham.youreinjured";

	private const float MinimumMeaningfulHealthLoss = 0.01f;

	private const float MinimumDirectionMagnitude = 0.0001f;

	private const int MinimumPoolSize = 4;

	private const int MaximumPoolSize = 240;

	private const float MinimumVisualDistance = 5f;

	private const float MaximumVisualDistance = 150f;

	private const float MinimumImpactCooldown = 0.03f;

	private const float MaximumImpactCooldown = 0.75f;

	internal static CrimsonBloodPlugin Instance;

	private ConfigEntry<bool> enabledConfig;

	private ConfigEntry<KeyCode> diagnosticKey;

	private ConfigEntry<KeyCode> clearDiagnosticsKey;

	private ConfigEntry<float> visualDistance;

	private ConfigEntry<float> dynamicEffectRenderDistance;

	private ConfigEntry<float> surfaceSplatRenderDistance;

	private ConfigEntry<float> minimumHealthLossForVisual;

	private ConfigEntry<float> perTargetCooldown;

	private ConfigEntry<bool> useExternalAssetTextures;

	private ConfigEntry<string> assetRootFolder;

	private ConfigEntry<bool> useHighDefinitionGroundSplats;

	private ConfigEntry<int> maximumBurstTextures;

	private ConfigEntry<int> maximumTrailTextures;

	private ConfigEntry<int> maximumGroundSplatTextures;

	private ConfigEntry<int> burstPoolSize;

	private ConfigEntry<int> streakPoolSize;

	private ConfigEntry<int> dropletPoolSize;

	private ConfigEntry<int> splatPoolSize;

	private ConfigEntry<int> maximumDropletsPerHit;

	private ConfigEntry<int> maximumStreaksPerHeavyHit;

	private ConfigEntry<int> maximumBurstsPerHeavyHit;

	private ConfigEntry<float> dropletGravity;

	private ConfigEntry<float> dropletLifetime;

	private ConfigEntry<float> streakLifetime;

	private ConfigEntry<float> burstLifetime;

	private ConfigEntry<float> splatLifetime;

	private ConfigEntry<float> minimumSplatHealthLoss;

	private ConfigEntry<bool> createImmediateImpactSplats;

	private ConfigEntry<bool> allowWallMarks;

	private ConfigEntry<bool> groundOnlySurfaceMarks;

	private ConfigEntry<bool> createFallbackSplatsWhenAirborne;

	private ConfigEntry<float> surfaceOffset;

	private ConfigEntry<float> splatSettleSeconds;

	private ConfigEntry<bool> allowPlayerImpactVisuals;

	private ConfigEntry<bool> suppressPlayerImpactsWhenYoureInjuredLoaded;

	private ConfigEntry<bool> disableVanillaBloodEffects;

	private ConfigEntry<string> vanillaBloodEffectTokens;

	private ConfigEntry<bool> recordVanillaEffectRoots;

	private ConfigEntry<float> vanillaEffectWindowSeconds;

	private ConfigEntry<float> immediateWallProbeDistance;

	private ConfigEntry<bool> useGroundedLethalFinish;

	private ConfigEntry<bool> logVisualEvents;

	private ConfigEntry<float> bluntBloodMultiplier;

	private ConfigEntry<int> bluntMaximumDropletsPerHit;

	private ConfigEntry<float> bluntBurstSizeMultiplier;

	private ConfigEntry<float> bluntSplatSizeMultiplier;

	private ConfigEntry<float> bluntLifetimeMultiplier;

	private Harmony harmony;

	private Transform visualRoot;

	private string pluginDirectory;

	private string activeAssetDirectory;

	private Material quadMaterial;

	private Texture2D fallbackDropletTexture;

	private Texture2D fallbackBurstTexture;

	private Texture2D fallbackSplatTexture;

	private Texture2D fallbackTrailTexture;

	private readonly List<Texture2D> burstTextures = new List<Texture2D>();

	private readonly List<Texture2D> dropletTextures = new List<Texture2D>();

	private readonly List<Texture2D> splatTextures = new List<Texture2D>();

	private readonly List<Texture2D> trailTextures = new List<Texture2D>();

	private readonly List<Texture2D> ownedTextures = new List<Texture2D>();

	private readonly List<Material> ownedMaterials = new List<Material>();

	private readonly List<CrimsonBloodBurst> burstPool = new List<CrimsonBloodBurst>();

	private readonly List<CrimsonBloodStreak> streakPool = new List<CrimsonBloodStreak>();

	private readonly List<CrimsonBloodDroplet> dropletPool = new List<CrimsonBloodDroplet>();

	private readonly List<CrimsonBloodSplat> splatPool = new List<CrimsonBloodSplat>();

	private readonly Dictionary<int, float> nextAllowedImpactTime = new Dictionary<int, float>();

	private readonly Dictionary<Type, FieldInfo[]> damageFieldCache = new Dictionary<Type, FieldInfo[]>();

	private readonly Queue<string> recentEvents = new Queue<string>();

	private readonly Queue<string> recentVanillaEffectRoots = new Queue<string>();

	private readonly RaycastHit[] immediateSurfaceHits = (RaycastHit[])(object)new RaycastHit[16];

	private Camera cachedMainCamera;

	private float nextCameraSearchTime;

	private float nextCooldownTrimTime;

	private int applyDamageHookCalls;

	private int observedDamageCalls;

	private int realHealthLossEvents;

	private int zeroHealthLossEvents;

	private int visualEvents;

	private int burstsSpawned;

	private int streaksSpawned;

	private int dropletsSpawned;

	private int splatsSpawned;

	private int recycledBursts;

	private int recycledStreaks;

	private int recycledDroplets;

	private int recycledSplats;

	private int suppressedPlayerImpactEvents;

	private int cooldownSkippedEvents;

	private int distanceSkippedEvents;

	private int surfaceLandingEvents;

	private int fallbackLandingEvents;

	private int fallbackSplatsDiscarded;

	private int characterSurfaceSkips;

	private int rigidbodySurfaceSkips;

	private int invalidSurfaceSkips;

	private int suppressedVanillaBloodEffects;

	private int observedVanillaEffectRoots;

	private int unmatchedVanillaEffectRoots;

	private int mutedVanillaRenderers;

	private int groundedLethalFinishes;

	private int destroyedPoolEntriesRecovered;

	private float vanillaEffectWindowUntil;

	private Vector3 vanillaEffectWindowCenter;

	private bool externalAssetsLoaded;

	private string assetLoadSummary = "Not loaded yet";

	private string lastEvent = "none";

	internal float DropletGravityMultiplier
	{
		get
		{
			if (dropletGravity != null)
			{
				return Mathf.Clamp(dropletGravity.Value, 0.1f, 6f);
			}
			return 1f;
		}
	}

	internal float SurfaceOffset
	{
		get
		{
			if (surfaceOffset != null)
			{
				return Mathf.Clamp(surfaceOffset.Value, 0.001f, 0.05f);
			}
			return 0.006f;
		}
	}

	internal float SplatSettleSeconds
	{
		get
		{
			if (splatSettleSeconds != null)
			{
				return Mathf.Clamp(splatSettleSeconds.Value, 0.05f, 0.5f);
			}
			return 0.14f;
		}
	}

	internal float DynamicEffectRenderDistance
	{
		get
		{
			if (dynamicEffectRenderDistance != null)
			{
				return Mathf.Clamp(dynamicEffectRenderDistance.Value, 6f, 60f);
			}
			return 24f;
		}
	}

	internal float SurfaceSplatRenderDistance
	{
		get
		{
			if (surfaceSplatRenderDistance != null)
			{
				return Mathf.Clamp(surfaceSplatRenderDistance.Value, 4f, 50f);
			}
			return 18f;
		}
	}

	internal bool DisableVanillaBloodEffects
	{
		get
		{
			if (disableVanillaBloodEffects != null)
			{
				return disableVanillaBloodEffects.Value;
			}
			return false;
		}
	}

	internal bool UseGroundedLethalFinish
	{
		get
		{
			if (useGroundedLethalFinish != null)
			{
				return useGroundedLethalFinish.Value;
			}
			return true;
		}
	}

	internal bool CreateFallbackSplatsWhenAirborne
	{
		get
		{
			if (createFallbackSplatsWhenAirborne != null)
			{
				return createFallbackSplatsWhenAirborne.Value;
			}
			return false;
		}
	}

	internal bool AllowWallMarks
	{
		get
		{
			if (allowWallMarks != null)
			{
				return allowWallMarks.Value;
			}
			return false;
		}
	}

	internal Camera RenderCamera
	{
		get
		{
			if ((Object)(object)cachedMainCamera != (Object)null && ((Behaviour)cachedMainCamera).isActiveAndEnabled)
			{
				return cachedMainCamera;
			}
			if (Time.unscaledTime >= nextCameraSearchTime)
			{
				nextCameraSearchTime = Time.unscaledTime + 0.5f;
				cachedMainCamera = Camera.main;
			}
			return cachedMainCamera;
		}
	}

	internal bool IsCharacterSurface(Collider collider)
	{
		if ((Object)(object)collider != (Object)null)
		{
			return (Object)(object)((Component)collider).GetComponentInParent<Character>() != (Object)null;
		}
		return false;
	}

	internal bool IsRejectedBloodSurface(Collider collider)
	{
		if ((Object)(object)collider == (Object)null)
		{
			return true;
		}
		if (IsCharacterSurface(collider))
		{
			characterSurfaceSkips++;
			return true;
		}
		if ((Object)(object)collider.attachedRigidbody != (Object)null)
		{
			rigidbodySurfaceSkips++;
			return true;
		}
		Transform val = ((Component)collider).transform;
		int num = 0;
		while ((Object)(object)val != (Object)null && num < 8)
		{
			string text = ((Object)val).name ?? string.Empty;
			if (text.IndexOf("ragdoll", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("corpse", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("character", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				invalidSurfaceSkips++;
				return true;
			}
			num++;
			val = val.parent;
		}
		return false;
	}

	internal bool IsValidBloodSurfaceNormal(Vector3 normal)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		if (!AllowWallMarks)
		{
			return normal.y >= 0.55f;
		}
		return normal.y >= -0.35f;
	}

	internal void RecordCharacterSurfaceSkip()
	{
		characterSurfaceSkips++;
	}

	internal void RecordAirborneFallbackDiscarded()
	{
		fallbackSplatsDiscarded++;
	}

	internal void BeginVanillaEffectWindow(Character target)
	{
		//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)
		if (DisableVanillaBloodEffects && !((Object)(object)target == (Object)null))
		{
			vanillaEffectWindowCenter = target.GetCenterPoint();
			float num = ((vanillaEffectWindowSeconds == null) ? 1.25f : Mathf.Clamp(vanillaEffectWindowSeconds.Value, 0.25f, 3f));
			vanillaEffectWindowUntil = Mathf.Max(vanillaEffectWindowUntil, Time.time + num);
		}
	}

	internal void OnVanillaEffectsCreated(GameObject[] createdEffects)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: 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)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		if (!DisableVanillaBloodEffects || createdEffects == null || createdEffects.Length == 0 || Time.time > vanillaEffectWindowUntil)
		{
			return;
		}
		foreach (GameObject val in createdEffects)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Vector3 val2 = val.transform.position - vanillaEffectWindowCenter;
			if (((Vector3)(ref val2)).sqrMagnitude > 625f)
			{
				continue;
			}
			string text = BuildVanillaEffectSignature(val);
			observedVanillaEffectRoots++;
			if (EffectSignatureMatchesVanillaBlood(text))
			{
				MuteVanillaBloodVisuals(val);
				suppressedVanillaBloodEffects++;
				RememberVanillaEffectRoot("MUTED " + text);
				continue;
			}
			unmatchedVanillaEffectRoots++;
			if (recordVanillaEffectRoots != null && recordVanillaEffectRoots.Value)
			{
				RememberVanillaEffectRoot("OBSERVED " + text);
			}
		}
	}

	private void MuteVanillaBloodVisuals(GameObject root)
	{
		if ((Object)(object)root == (Object)null)
		{
			return;
		}
		Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if (!((Object)(object)val == (Object)null))
			{
				try
				{
					val.enabled = false;
					mutedVanillaRenderers++;
				}
				catch
				{
				}
			}
		}
	}

	private bool EffectSignatureMatchesVanillaBlood(string signature)
	{
		if (string.IsNullOrEmpty(signature))
		{
			return false;
		}
		string[] array = ((vanillaBloodEffectTokens != null) ? vanillaBloodEffectTokens.Value : "blood,bleed,gore,splatter").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
		string text = signature.ToLowerInvariant();
		for (int i = 0; i < array.Length; i++)
		{
			string value = array[i].Trim().ToLowerInvariant();
			if (!string.IsNullOrEmpty(value) && text.Contains(value))
			{
				return true;
			}
		}
		return false;
	}

	private static string BuildVanillaEffectSignature(GameObject root)
	{
		if ((Object)(object)root == (Object)null)
		{
			return "<destroyed>";
		}
		string text = ((Object)root).name ?? "unnamed";
		Transform transform = root.transform;
		for (int i = 0; i < transform.childCount && i < 24; i++)
		{
			Transform child = transform.GetChild(i);
			if ((Object)(object)child == (Object)null)
			{
				continue;
			}
			text = text + " / " + ((Object)child).name;
			for (int j = 0; j < child.childCount && j < 6; j++)
			{
				Transform child2 = child.GetChild(j);
				if ((Object)(object)child2 != (Object)null)
				{
					text = text + " / " + ((Object)child2).name;
				}
			}
		}
		return text;
	}

	private void RememberVanillaEffectRoot(string line)
	{
		while (recentVanillaEffectRoots.Count >= 24)
		{
			recentVanillaEffectRoots.Dequeue();
		}
		recentVanillaEffectRoots.Enqueue(line);
	}

	internal bool ShouldRenderEffectAt(Vector3 worldPosition, bool isSurfaceMark)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: 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_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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)
		Camera renderCamera = RenderCamera;
		Vector3 val = (((Object)(object)renderCamera != (Object)null) ? ((Component)renderCamera).transform.position : (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero));
		float num = (isSurfaceMark ? SurfaceSplatRenderDistance : DynamicEffectRenderDistance);
		Vector3 val2 = worldPosition - val;
		return ((Vector3)(ref val2)).sqrMagnitude <= num * num;
	}

	private void Awake()
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Expected O, but got Unknown
		Instance = this;
		pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		BindConfig();
		harmony = new Harmony("marc.valheim.crimsonblood");
		harmony.PatchAll(typeof(CrimsonBloodPlugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Crimson Blood 0.7.6.2 loaded. F11 writes diagnostics; F12 clears recent events.");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Asset-driven mode expects an assets folder beside CrimsonBlood.dll. If it is missing, safe procedural fallback textures are used.");
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
			harmony = null;
		}
		DestroyVisualSystem();
		nextAllowedImpactTime.Clear();
		damageFieldCache.Clear();
		recentEvents.Clear();
		recentVanillaEffectRoots.Clear();
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void Update()
	{
		//IL_0006: 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)
		if (Input.GetKeyDown(diagnosticKey.Value))
		{
			WriteDiagnostics();
		}
		if (Input.GetKeyDown(clearDiagnosticsKey.Value))
		{
			recentEvents.Clear();
			recentVanillaEffectRoots.Clear();
			lastEvent = "none";
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Crimson Blood: cleared recent event diagnostics.");
		}
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			EnsureVisualSystem();
		}
		if (Time.time >= nextCooldownTrimTime)
		{
			nextCooldownTrimTime = Time.time + 20f;
			TrimExpiredCooldowns();
		}
	}

	private void BindConfig()
	{
		enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Master", "Enabled", true, "Turns Crimson Blood's local visual layer on or off. It never changes combat balance.");
		diagnosticKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("0 - Master", "DiagnosticKey", (KeyCode)292, "Writes Crimson Blood diagnostic information to BepInEx/LogOutput.log.");
		clearDiagnosticsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("0 - Master", "ClearDiagnosticsKey", (KeyCode)293, "Clears Crimson Blood's recent diagnostic event list.");
		visualDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "VisualDistanceMetres", 80f, "Maximum distance from the local player at which combat blood visuals are created.");
		dynamicEffectRenderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "DynamicEffectRenderDistanceMetres", 24f, "Maximum distance from the camera at which airborne bursts, streaks, and droplets remain visible.");
		surfaceSplatRenderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "SurfaceSplatRenderDistanceMetres", 18f, "Maximum distance from the camera at which surface blood marks remain visible.");
		minimumHealthLossForVisual = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "MinimumActualHealthLoss", 0.1f, "Actual health lost after armour, blocking and resistances required before a visual may spawn.");
		perTargetCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "PerTargetImpactCooldownSeconds", 0.06f, "Stops rapid repeated damage ticks from producing excessive visual bursts.");
		useExternalAssetTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Asset Loading", "UseExternalAssetTextures", true, "Loads supplied transparent PNG effects from the assets folder beside CrimsonBlood.dll.");
		assetRootFolder = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Asset Loading", "AssetRootFolder", "assets", "Folder beside CrimsonBlood.dll that contains sprays, trails, and ground_splats.");
		useHighDefinitionGroundSplats = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Asset Loading", "UseHighDefinitionGroundSplats", false, "Uses assets/ground_splats/kenney_512 if you copied it beside the DLL. This uses more video memory than the default 256px set.");
		maximumBurstTextures = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Asset Loading", "MaximumBurstTextures", 6, "Maximum number of splash-frame textures loaded from assets/sprays/blood_splash_frames.");
		maximumTrailTextures = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Asset Loading", "MaximumTrailTextures", 8, "Maximum number of trail/droplet textures loaded from assets/trails.");
		maximumGroundSplatTextures = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Asset Loading", "MaximumGroundSplatTextures", 36, "Maximum number of ground-splat textures loaded. 36 default 256px textures are safe for this first asset build.");
		burstPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "ImpactBurstPoolSize", 20, "Pre-warmed maximum number of animated impact burst sprites.");
		streakPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "PressureStreakPoolSize", 22, "Pre-warmed maximum number of pressure spray streaks.");
		dropletPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "DropletPoolSize", 112, "Pre-warmed maximum number of moving droplets.");
		splatPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "SurfaceSplatPoolSize", 112, "Pre-warmed maximum number of temporary blood splats.");
		maximumDropletsPerHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumDropletsPerHit", 18, "Upper safety cap for liquid droplets created by one strong hit.");
		maximumStreaksPerHeavyHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumPressureStreaksPerHeavyHit", 4, "Upper safety cap for streaks on stronger hits.");
		maximumBurstsPerHeavyHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumAnimatedBurstsPerHeavyHit", 2, "Upper safety cap for animated splash bursts on stronger hits.");
		dropletGravity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "DropletGravityMultiplier", 1.85f, "Gravity multiplier for airborne droplets. Higher values make them fall and hit surfaces sooner.");
		dropletLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "DropletLifetimeSeconds", 1.2f, "Maximum airborne lifetime before a droplet fades or tries a final nearby splat.");
		streakLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "PressureStreakLifetimeSeconds", 0.18f, "Lifetime of directional pressure spray streaks.");
		burstLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "ImpactBurstLifetimeSeconds", 0.16f, "Lifetime of animated impact burst sprites.");
		splatLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "SurfaceSplatLifetimeSeconds", 120f, "How long temporary ground/structure blood remains before fading.");
		minimumSplatHealthLoss = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "MinimumHealthLossForImmediateImpactSplat", 0.85f, "Health loss required before an immediate blood mark may appear below/near the impact.");
		createImmediateImpactSplats = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "CreateImmediateImpactSplats", true, "Places one small wet mark near a meaningful hit where a suitable surface exists.");
		allowWallMarks = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "AllowWallAndSteepSurfaceBloodMarks", true, "Allows blood marks on static walls, wooden structures, steep rocks, floors, and terrain.");
		groundOnlySurfaceMarks = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "GroundOnlySurfaceMarks", false, "Legacy compatibility setting. v0.7.5 ignores this value so wall and steep-surface marks work when AllowWallAndSteepSurfaceBloodMarks is true.");
		immediateWallProbeDistance = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "ImmediateWallProbeDistanceMetres", 1.35f, "How far a fresh hit checks behind and around the wound for a nearby static wall or steep rock to receive blood.");
		createFallbackSplatsWhenAirborne = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "CreateFallbackSplatsWhenAirborne", false, "When false, droplets that never reach a valid ground surface simply fade instead of creating a late fallback blood mark in the air.");
		surfaceOffset = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "SurfaceOffset", 0.006f, "Small offset that keeps a blood mark just above its surface and avoids flickering.");
		splatSettleSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "SurfaceSplatSettleSeconds", 0.14f, "Real-time seconds a new blood mark takes to spread to its final size. This is independent of splat lifetime.");
		bluntBloodMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitBloodMultiplier", 1.85f, "Extra amount of blood created by strong blunt hits such as maces and clubs. Affects heavy hits only.");
		bluntMaximumDropletsPerHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumDropletsPerHeavyBluntHit", 30, "Safety cap used only for strong blunt hits. Lets mace and club impacts be fuller without raising every weapon type.");
		bluntBurstSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitBurstSizeMultiplier", 1.6f, "Size multiplier for animated impact bursts created by strong blunt hits.");
		bluntSplatSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitSplatSizeMultiplier", 1.45f, "Size multiplier for blood marks created by strong blunt hits.");
		bluntLifetimeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitLifetimeMultiplier", 1.38f, "Makes blunt-hit bursts, droplets, and streaks stay visible slightly longer.");
		allowPlayerImpactVisuals = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "AllowPlayerImpactVisuals", true, "Allows Crimson Blood to create impact visuals when the local player takes real damage.");
		suppressPlayerImpactsWhenYoureInjuredLoaded = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "PreventDuplicatePlayerHitBloodWhenYoureInjuredLoaded", true, "Prevents a duplicate Crimson Blood player-hit burst while You're Injured is installed. Creature blood remains enabled.");
		disableVanillaBloodEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "DisableVanillaBloodEffects", true, "Suppresses Valheim's default blood effect when Crimson Blood is handling damage visuals. This reduces visual clashing.");
		vanillaBloodEffectTokens = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Player Compatibility", "VanillaBloodEffectTokens", "blood,bleed,gore,splatter", "Name fragments used to identify actual vanilla effect GameObjects created during a hit. Unmatched roots appear in F11 diagnostics.");
		recordVanillaEffectRoots = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "RecordVanillaEffectRootsAroundHits", true, "Records EffectList-created root names near a real hit. Use F11 results to identify any remaining vanilla blood effect exactly.");
		vanillaEffectWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Player Compatibility", "VanillaEffectWindowSeconds", 1.25f, "Seconds after a damage event that newly created EffectList roots are inspected for vanilla blood suppression.");
		useGroundedLethalFinish = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "UseGroundedLethalFinish", true, "On a lethal hit, replaces the hanging animated impact burst with a grounded death spill and falling droplets.");
		logVisualEvents = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Debug", "LogVisualEvents", false, "Writes every spawned visual event to LogOutput.log. Leave false outside testing.");
	}

	internal void RecordHookCall()
	{
		applyDamageHookCalls++;
	}

	internal bool ShouldObserve(Character target)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		if (!enabledConfig.Value || (Object)(object)target == (Object)null)
		{
			return false;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)target == (Object)(object)localPlayer)
		{
			return true;
		}
		if ((Object)(object)localPlayer == (Object)null)
		{
			return false;
		}
		float num = Mathf.Clamp(visualDistance.Value, 5f, 150f);
		Vector3 val = ((Component)target).transform.position - ((Component)localPlayer).transform.position;
		return ((Vector3)(ref val)).sqrMagnitude <= num * num;
	}

	internal void RecordDamage(Character target, HitData hit, float healthBefore)
	{
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		if (!enabledConfig.Value || (Object)(object)target == (Object)null)
		{
			return;
		}
		observedDamageCalls++;
		float health = target.GetHealth();
		float num = Mathf.Max(0f, healthBefore - health);
		if (num < 0.01f)
		{
			zeroHealthLossEvents++;
			return;
		}
		realHealthLossEvents++;
		if (num < Mathf.Max(0.01f, minimumHealthLossForVisual.Value))
		{
			RememberEvent("SKIP threshold target=" + GetTargetName(target) + " loss=" + num.ToString("0.##"));
			return;
		}
		if (!IsWithinVisualRange(target))
		{
			distanceSkippedEvents++;
			return;
		}
		if ((Object)(object)target == (Object)(object)Player.m_localPlayer && !CanSpawnLocalPlayerImpact())
		{
			suppressedPlayerImpactEvents++;
			RememberEvent("SKIP player-compatibility loss=" + num.ToString("0.##"));
			return;
		}
		int instanceID = ((Object)target).GetInstanceID();
		if (nextAllowedImpactTime.TryGetValue(instanceID, out var value) && Time.time < value)
		{
			cooldownSkippedEvents++;
			return;
		}
		nextAllowedImpactTime[instanceID] = Time.time + Mathf.Clamp(perTargetCooldown.Value, 0.03f, 0.75f);
		EnsureVisualSystem();
		if ((Object)(object)visualRoot == (Object)null)
		{
			RememberEvent("SKIP visual-root-unavailable target=" + GetTargetName(target));
			return;
		}
		DamageCategory category = ResolveDamageCategory(hit);
		Vector3 impactPoint = ResolveImpactPoint(target, hit);
		Vector3 sprayDirection = ResolveOutwardSprayDirection(target, hit, impactPoint);
		Color bloodColor = ResolveBloodColor(target, category);
		float severity = Mathf.Clamp01((num - 0.35f) / 14f);
		bool lethalHit = health <= 0.01f;
		SpawnImpact(target, impactPoint, sprayDirection, num, severity, category, bloodColor, lethalHit);
	}

	private bool IsWithinVisualRange(Character target)
	{
		//IL_0032: 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_0042: 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)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return false;
		}
		float num = Mathf.Clamp(visualDistance.Value, 5f, 150f);
		Vector3 val = ((Component)target).transform.position - ((Component)localPlayer).transform.position;
		return ((Vector3)(ref val)).sqrMagnitude <= num * num;
	}

	private bool CanSpawnLocalPlayerImpact()
	{
		if (!allowPlayerImpactVisuals.Value)
		{
			return false;
		}
		if (suppressPlayerImpactsWhenYoureInjuredLoaded.Value)
		{
			return !IsYoureInjuredLoaded();
		}
		return true;
	}

	private static bool IsYoureInjuredLoaded()
	{
		try
		{
			return Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey("com.marccunningham.youreinjured");
		}
		catch
		{
			return false;
		}
	}

	private void EnsureVisualSystem()
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)visualRoot != (Object)null))
		{
			visualRoot = new GameObject("CrimsonBlood_LocalVisuals").transform;
			visualRoot.SetParent(((Component)this).transform, false);
			((Object)((Component)visualRoot).gameObject).hideFlags = (HideFlags)61;
			PrepareTexturesAndMaterials();
			PrewarmPools();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood: " + assetLoadSummary));
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood: visual pools ready: bursts=" + burstPool.Count + ", streaks=" + streakPool.Count + ", droplets=" + dropletPool.Count + ", splats=" + splatPool.Count + "."));
		}
	}

	private void PrepareTexturesAndMaterials()
	{
		ClearLoadedAssetLists();
		externalAssetsLoaded = false;
		activeAssetDirectory = string.Empty;
		bool flag = false;
		if (useExternalAssetTextures.Value)
		{
			string path = (string.IsNullOrWhiteSpace(assetRootFolder.Value) ? "assets" : assetRootFolder.Value.Trim());
			activeAssetDirectory = Path.Combine(pluginDirectory ?? string.Empty, path);
			flag = TryLoadExternalTextureSet(activeAssetDirectory);
		}
		int count = burstTextures.Count;
		int count2 = trailTextures.Count;
		int count3 = splatTextures.Count;
		externalAssetsLoaded = count > 0 || count2 > 0 || count3 > 0;
		CreateFallbackTexturesIfNeeded();
		if (burstTextures.Count == 0)
		{
			burstTextures.Add(fallbackBurstTexture);
		}
		if (dropletTextures.Count == 0)
		{
			dropletTextures.Add(fallbackDropletTexture);
		}
		if (splatTextures.Count == 0)
		{
			splatTextures.Add(fallbackSplatTexture);
		}
		quadMaterial = CreateTransparentMaterial("CrimsonBlood_AssetQuadMaterial", fallbackSplatTexture);
		ownedMaterials.Add(quadMaterial);
		if (trailTextures.Count == 0)
		{
			trailTextures.Add(fallbackTrailTexture);
		}
		string text = (flag ? "EXTERNAL" : (externalAssetsLoaded ? "PARTIAL" : "FALLBACK"));
		assetLoadSummary = "assetMode=" + text + " | root=" + (string.IsNullOrEmpty(activeAssetDirectory) ? "not-requested" : activeAssetDirectory) + " | externalBurst=" + count + " | externalTrail=" + count2 + " | externalSplat=" + count3 + " | activeBurst=" + burstTextures.Count + " | activeTrail=" + trailTextures.Count + " | activeSplat=" + splatTextures.Count;
	}

	private bool TryLoadExternalTextureSet(string assetRoot)
	{
		if (string.IsNullOrEmpty(assetRoot) || !Directory.Exists(assetRoot))
		{
			assetLoadSummary = "assets folder missing: " + assetRoot;
			return false;
		}
		string folder = Path.Combine(assetRoot, "sprays", "blood_splash_frames");
		string folder2 = Path.Combine(assetRoot, "trails");
		string folder3 = (useHighDefinitionGroundSplats.Value ? Path.Combine(assetRoot, "ground_splats", "kenney_512") : Path.Combine(assetRoot, "ground_splats", "kenney_256"));
		int num = LoadPngFolder(folder, Mathf.Clamp(maximumBurstTextures.Value, 1, 16), burstTextures, "burst");
		int num2 = LoadPngFolder(folder2, Mathf.Clamp(maximumTrailTextures.Value, 1, 16), trailTextures, "trail");
		int num3 = LoadPngFolder(folder3, Mathf.Clamp(maximumGroundSplatTextures.Value, 1, 48), splatTextures, "splat");
		for (int i = 0; i < trailTextures.Count; i++)
		{
			if ((Object)(object)trailTextures[i] != (Object)null)
			{
				dropletTextures.Add(trailTextures[i]);
			}
		}
		int num4;
		if (num > 0 && num2 > 0)
		{
			num4 = ((num3 > 0) ? 1 : 0);
			if (num4 != 0)
			{
				goto IL_014f;
			}
		}
		else
		{
			num4 = 0;
		}
		((BaseUnityPlugin)this).Logger.LogWarning((object)"Crimson Blood: asset folders were found but one or more required texture groups were empty. Falling back only for missing groups.");
		goto IL_014f;
		IL_014f:
		return (byte)num4 != 0;
	}

	private int LoadPngFolder(string folder, int maximumTextures, List<Texture2D> destination, string groupName)
	{
		if (!Directory.Exists(folder))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: missing " + groupName + " asset folder: " + folder));
			return 0;
		}
		string[] files;
		try
		{
			files = Directory.GetFiles(folder, "*.png", SearchOption.TopDirectoryOnly);
			Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: could not enumerate " + groupName + " textures. " + ex.Message));
			return 0;
		}
		int num = 0;
		for (int i = 0; i < files.Length; i++)
		{
			if (num >= maximumTextures)
			{
				break;
			}
			if (!CrimsonAssetLoader.TryLoadAlphaMaskPng(files[i], out var texture, out var error))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: skipped " + groupName + " texture '" + Path.GetFileName(files[i]) + "'. " + error));
			}
			else
			{
				((Object)texture).name = "CrimsonBlood_" + groupName + "_" + Path.GetFileNameWithoutExtension(files[i]);
				destination.Add(texture);
				ownedTextures.Add(texture);
				num++;
			}
		}
		return num;
	}

	private void CreateFallbackTexturesIfNeeded()
	{
		if ((Object)(object)fallbackDropletTexture == (Object)null)
		{
			fallbackDropletTexture = CrimsonAssetLoader.CreateSoftCircleTexture("CrimsonBlood_FallbackDroplet", 64, 0.88f);
			ownedTextures.Add(fallbackDropletTexture);
		}
		if ((Object)(object)fallbackBurstTexture == (Object)null)
		{
			fallbackBurstTexture = CrimsonAssetLoader.CreateBurstTexture("CrimsonBlood_FallbackBurst", 128);
			ownedTextures.Add(fallbackBurstTexture);
		}
		if ((Object)(object)fallbackSplatTexture == (Object)null)
		{
			fallbackSplatTexture = CrimsonAssetLoader.CreateIrregularSplatTexture("CrimsonBlood_FallbackSplat", 128);
			ownedTextures.Add(fallbackSplatTexture);
		}
		if ((Object)(object)fallbackTrailTexture == (Object)null)
		{
			fallbackTrailTexture = CrimsonAssetLoader.CreateTrailTexture("CrimsonBlood_FallbackTrail", 32, 128);
			ownedTextures.Add(fallbackTrailTexture);
		}
	}

	private Material CreateTransparentMaterial(string materialName, Texture2D texture)
	{
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Expected O, but got Unknown
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_0236: Unknown result type (might be due to invalid IL or missing references)
		Shader val = Shader.Find("Standard");
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Legacy Shaders/Transparent/Specular");
		}
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Particles/Alpha Blended");
		}
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Unlit/Transparent");
		}
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Sprites/Default");
		}
		Material val2 = new Material(val);
		((Object)val2).name = materialName;
		((Object)val2).hideFlags = (HideFlags)61;
		val2.color = Color.white;
		val2.renderQueue = 3000;
		if ((Object)(object)texture != (Object)null)
		{
			if (val2.HasProperty("_MainTex"))
			{
				val2.SetTexture("_MainTex", (Texture)(object)texture);
			}
			if (val2.HasProperty("_BaseMap"))
			{
				val2.SetTexture("_BaseMap", (Texture)(object)texture);
			}
		}
		if (val2.HasProperty("_Mode"))
		{
			val2.SetFloat("_Mode", 3f);
		}
		if (val2.HasProperty("_Surface"))
		{
			val2.SetFloat("_Surface", 1f);
		}
		if (val2.HasProperty("_Blend"))
		{
			val2.SetFloat("_Blend", 0f);
		}
		if (val2.HasProperty("_SrcBlend"))
		{
			val2.SetInt("_SrcBlend", 5);
		}
		if (val2.HasProperty("_DstBlend"))
		{
			val2.SetInt("_DstBlend", 10);
		}
		if (val2.HasProperty("_ZWrite"))
		{
			val2.SetInt("_ZWrite", 0);
		}
		if (val2.HasProperty("_Cull"))
		{
			val2.SetInt("_Cull", 0);
		}
		if (val2.HasProperty("_Glossiness"))
		{
			val2.SetFloat("_Glossiness", 0.82f);
		}
		if (val2.HasProperty("_Smoothness"))
		{
			val2.SetFloat("_Smoothness", 0.82f);
		}
		if (val2.HasProperty("_Metallic"))
		{
			val2.SetFloat("_Metallic", 0f);
		}
		if (val2.HasProperty("_SpecColor"))
		{
			val2.SetColor("_SpecColor", new Color(0.22f, 0.08f, 0.08f, 1f));
		}
		if (val2.HasProperty("_Color"))
		{
			val2.SetColor("_Color", Color.white);
		}
		if (val2.HasProperty("_BaseColor"))
		{
			val2.SetColor("_BaseColor", Color.white);
		}
		val2.SetOverrideTag("RenderType", "Transparent");
		val2.DisableKeyword("_ALPHATEST_ON");
		val2.EnableKeyword("_ALPHABLEND_ON");
		val2.DisableKeyword("_ALPHAPREMULTIPLY_ON");
		return val2;
	}

	private void PrewarmPools()
	{
		int num = Mathf.Clamp(burstPoolSize.Value, 4, 48);
		int num2 = Mathf.Clamp(streakPoolSize.Value, 4, 48);
		int num3 = Mathf.Clamp(dropletPoolSize.Value, 4, 240);
		int num4 = Mathf.Clamp(splatPoolSize.Value, 4, 240);
		for (int i = 0; i < num; i++)
		{
			CreateBurstPoolObject(i);
		}
		for (int j = 0; j < num2; j++)
		{
			CreateStreakPoolObject(j);
		}
		for (int k = 0; k < num3; k++)
		{
			CreateDropletPoolObject(k);
		}
		for (int l = 0; l < num4; l++)
		{
			CreateSplatPoolObject(l);
		}
	}

	private void CreateBurstPoolObject(int index)
	{
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5);
		((Object)obj).name = "CrimsonBlood_ImpactBurst_" + index;
		obj.transform.SetParent(visualRoot, false);
		((Object)obj).hideFlags = (HideFlags)61;
		RemoveCollider(obj);
		Renderer component = obj.GetComponent<Renderer>();
		ConfigureRenderer(component, quadMaterial);
		CrimsonBloodBurst crimsonBloodBurst = obj.AddComponent<CrimsonBloodBurst>();
		crimsonBloodBurst.Initialize(this, component);
		obj.SetActive(false);
		burstPool.Add(crimsonBloodBurst);
	}

	private void CreateDropletPoolObject(int index)
	{
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5);
		((Object)obj).name = "CrimsonBlood_Droplet_" + index;
		obj.transform.SetParent(visualRoot, false);
		((Object)obj).hideFlags = (HideFlags)61;
		RemoveCollider(obj);
		Renderer component = obj.GetComponent<Renderer>();
		ConfigureRenderer(component, quadMaterial);
		CrimsonBloodDroplet crimsonBloodDroplet = obj.AddComponent<CrimsonBloodDroplet>();
		crimsonBloodDroplet.Initialize(this, component);
		obj.SetActive(false);
		dropletPool.Add(crimsonBloodDroplet);
	}

	private void CreateStreakPoolObject(int index)
	{
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5);
		((Object)obj).name = "CrimsonBlood_Streak_" + index;
		obj.transform.SetParent(visualRoot, false);
		((Object)obj).hideFlags = (HideFlags)61;
		RemoveCollider(obj);
		Renderer component = obj.GetComponent<Renderer>();
		ConfigureRenderer(component, quadMaterial);
		CrimsonBloodStreak crimsonBloodStreak = obj.AddComponent<CrimsonBloodStreak>();
		crimsonBloodStreak.Initialize(this, component);
		obj.SetActive(false);
		streakPool.Add(crimsonBloodStreak);
	}

	private void CreateSplatPoolObject(int index)
	{
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5);
		((Object)obj).name = "CrimsonBlood_Splat_" + index;
		obj.transform.SetParent(visualRoot, false);
		((Object)obj).hideFlags = (HideFlags)61;
		RemoveCollider(obj);
		MeshRenderer component = obj.GetComponent<MeshRenderer>();
		ConfigureRenderer((Renderer)(object)component, quadMaterial);
		CrimsonBloodSplat crimsonBloodSplat = obj.AddComponent<CrimsonBloodSplat>();
		crimsonBloodSplat.Initialize(this, component);
		obj.SetActive(false);
		splatPool.Add(crimsonBloodSplat);
	}

	private static void RemoveCollider(GameObject gameObject)
	{
		Collider component = gameObject.GetComponent<Collider>();
		if ((Object)(object)component != (Object)null)
		{
			Object.Destroy((Object)(object)component);
		}
	}

	private static void ConfigureRenderer(Renderer renderer, Material material)
	{
		if (!((Object)(object)renderer == (Object)null))
		{
			renderer.sharedMaterial = material;
			renderer.shadowCastingMode = (ShadowCastingMode)0;
			renderer.receiveShadows = false;
		}
	}

	private void SpawnImpact(Character target, Vector3 impactPoint, Vector3 sprayDirection, float healthLoss, float severity, DamageCategory category, Color bloodColor, bool lethalHit)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: 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_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_039b: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0400: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0347: Unknown result type (might be due to invalid IL or missing references)
		//IL_0348: Unknown result type (might be due to invalid IL or missing references)
		//IL_034e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0353: Unknown result type (might be due to invalid IL or missing references)
		//IL_035a: Unknown result type (might be due to invalid IL or missing references)
		//IL_035c: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = impactPoint + sprayDirection * 0.035f + Vector3.up * 0.035f;
		bool flag = healthLoss >= 2.5f || severity >= 0.15f;
		bool flag2 = healthLoss >= 5.5f || severity >= 0.38f;
		bool flag3 = healthLoss >= 10f || severity >= 0.68f;
		if (category == DamageCategory.Fall)
		{
			TryCreateImmediateImpactSplat(((Component)target).transform, val, Mathf.Lerp(0.2f, 0.7f, severity), bloodColor, Vector3.zero);
			visualEvents++;
			RememberEvent("SPAWN target=" + GetTargetName(target) + " loss=" + healthLoss.ToString("0.##") + " category=Fall splat-only");
			return;
		}
		int num = Mathf.Clamp(maximumDropletsPerHit.Value, 2, 30);
		if (category == DamageCategory.Blunt && flag2)
		{
			num = Mathf.Clamp(bluntMaximumDropletsPerHit.Value, 4, 30);
		}
		int num2 = Mathf.Clamp(3 + Mathf.RoundToInt(Mathf.Lerp(2f, (float)num - 3f, severity)), 2, num);
		int num3 = (flag ? Mathf.Clamp(1 + Mathf.RoundToInt(Mathf.Lerp(0f, (float)maximumStreaksPerHeavyHit.Value - 1f, severity)), 1, Mathf.Max(1, maximumStreaksPerHeavyHit.Value)) : 0);
		int num4 = (flag ? 1 : 0);
		if (flag2)
		{
			num4 = Mathf.Clamp(maximumBurstsPerHeavyHit.Value, 1, 3);
		}
		switch (category)
		{
		case DamageCategory.Slash:
		case DamageCategory.Chop:
			num2 += 2;
			break;
		case DamageCategory.Pierce:
			num2++;
			num3 = Mathf.Max(num3, 1);
			break;
		case DamageCategory.Blunt:
			if (flag2)
			{
				float num5 = Mathf.Clamp(bluntBloodMultiplier.Value, 1f, 3f);
				num2 = Mathf.RoundToInt((float)num2 * num5) + 2;
				num3 = Mathf.Max(num3, 4);
				num4 = Mathf.Max(num4, 3);
			}
			else
			{
				num2++;
			}
			break;
		case DamageCategory.Fire:
			num2 = Mathf.Max(1, num2 - 6);
			num3 = 0;
			num4 = 0;
			break;
		case DamageCategory.Poison:
			num2 = Mathf.Max(1, num2 - 7);
			num3 = 0;
			num4 = 0;
			break;
		case DamageCategory.BiteOrMonsterHit:
			num2 += 2;
			break;
		}
		num2 = Mathf.Clamp(num2, 1, num);
		if (category != DamageCategory.Fire && category != DamageCategory.Poison && !(category == DamageCategory.Blunt && flag2))
		{
			num2 = Mathf.Min(num + 2, num2 + 2);
		}
		num3 = Mathf.Clamp(num3, 0, Mathf.Clamp(maximumStreaksPerHeavyHit.Value, 0, 8));
		num4 = Mathf.Clamp(num4, 0, Mathf.Clamp(maximumBurstsPerHeavyHit.Value + 1, 0, 4));
		if (createImmediateImpactSplats.Value && healthLoss >= Mathf.Max(0f, minimumSplatHealthLoss.Value))
		{
			float num6 = Mathf.Lerp(0.15f, flag3 ? 0.58f : 0.42f, severity);
			if (category == DamageCategory.Blunt && flag2)
			{
				num6 *= Mathf.Clamp(bluntSplatSizeMultiplier.Value, 1f, 2.5f);
			}
			TryCreateImmediateImpactSplat(((Component)target).transform, val + sprayDirection * 0.15f, num6, bloodColor, sprayDirection);
		}
		bool flag4 = lethalHit && UseGroundedLethalFinish;
		if (flag4)
		{
			groundedLethalFinishes++;
			num4 = 0;
			num3 = 0;
			num2 = Mathf.Min(num2, 12);
			TryCreateImmediateImpactSplat(((Component)target).transform, impactPoint, Mathf.Clamp(0.34f + severity * 0.48f, 0.34f, 0.78f), bloodColor, sprayDirection);
		}
		if (!flag4)
		{
			SpawnAnimatedBursts(((Component)target).transform, val, sprayDirection, num4, severity, category, bloodColor);
		}
		SpawnPressureStreaks(((Component)target).transform, val, sprayDirection, num3, severity, category, bloodColor);
		SpawnBallisticDroplets(((Component)target).transform, val, sprayDirection, num2, severity, category, bloodColor, flag2, flag4);
		visualEvents++;
		string text = "SPAWN target=" + GetTargetName(target) + " loss=" + healthLoss.ToString("0.##") + " category=" + category.ToString() + " lethal=" + lethalHit + " burst=" + num4 + " streaks=" + num3 + " droplets=" + num2 + " direction=" + FormatVector(sprayDirection);
		RememberEvent(text);
		if (logVisualEvents.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood " + text));
		}
	}

	private void SpawnAnimatedBursts(Transform owner, Vector3 source, Vector3 sprayDirection, int burstCount, float severity, DamageCategory category, Color color)
	{
		//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_0019: 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_002d: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: 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_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		if (burstCount <= 0 || burstPool.Count == 0)
		{
			return;
		}
		Vector3 val = Vector3.Cross(Vector3.up, sprayDirection);
		if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
		{
			val = Vector3.right;
		}
		((Vector3)(ref val)).Normalize();
		float num = Mathf.Lerp(0.52f, 1.2f, severity);
		for (int i = 0; i < burstCount; i++)
		{
			float num2 = Random.Range(-0.2f, 0.2f);
			float num3 = Random.Range(-0.06f, 0.16f);
			Vector3 val2 = sprayDirection + val * num2 + Vector3.up * num3;
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			float num4 = num * Random.Range(0.85f, 1.18f);
			float num5 = Mathf.Max(0.06f, burstLifetime.Value) * Random.Range(0.9f, 1.12f);
			Vector3 val3 = normalized * Random.Range(0.45f, 1.35f) + Vector3.up * Random.Range(0.05f, 0.45f);
			switch (category)
			{
			case DamageCategory.Pierce:
				num4 *= 0.82f;
				val3 *= 1.3f;
				break;
			case DamageCategory.Blunt:
			{
				float num6 = Mathf.Clamp(bluntBurstSizeMultiplier.Value, 1f, 2.5f);
				num4 *= num6;
				num5 *= Mathf.Clamp(bluntLifetimeMultiplier.Value, 1f, 2.5f);
				val3 *= 1.1f;
				break;
			}
			}
			GetBurst().Activate(owner, source + normalized * 0.02f, val3, num4, num5, color, burstTextures, Random.Range(0f, 360f));
			burstsSpawned++;
		}
	}

	private void SpawnPressureStreaks(Transform owner, Vector3 source, Vector3 sprayDirection, int streakCount, float severity, DamageCategory category, Color color)
	{
		//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_0019: 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_002d: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		if (streakCount <= 0 || streakPool.Count == 0)
		{
			return;
		}
		Vector3 val = Vector3.Cross(Vector3.up, sprayDirection);
		if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
		{
			val = Vector3.right;
		}
		((Vector3)(ref val)).Normalize();
		float num = Mathf.Lerp(5.5f, 12f, severity);
		float num2 = Mathf.Lerp(0.75f, 2.25f, severity);
		for (int i = 0; i < streakCount; i++)
		{
			float num3 = ((category == DamageCategory.Blunt) ? 0.78f : 0.44f);
			float num4 = Random.Range(0f - num3, num3);
			float num5 = Random.Range(-0.06f, 0.3f);
			Vector3 val2 = sprayDirection + val * num4 + Vector3.up * num5;
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			float num6 = num * Random.Range(0.84f, 1.24f);
			float num7 = Mathf.Lerp(0.028f, 0.078f, severity) * Random.Range(0.85f, 1.18f);
			float num8 = Mathf.Max(0.06f, streakLifetime.Value) * Random.Range(0.88f, 1.15f);
			Vector3 val3 = normalized * num6 + Vector3.up * num2;
			switch (category)
			{
			case DamageCategory.Pierce:
				val3 *= 1.16f;
				num7 *= 0.84f;
				break;
			case DamageCategory.Blunt:
				val3 *= 1.05f;
				num7 *= 1.65f;
				num8 *= Mathf.Clamp(bluntLifetimeMultiplier.Value, 1f, 2.5f);
				break;
			case DamageCategory.BiteOrMonsterHit:
				val3 += val * Random.Range(-1f, 1f);
				break;
			}
			GetStreak().Activate(owner, source, val3, num7, num8, color, GetRandomTrailTexture(), Random.Range(0f, 360f));
			streaksSpawned++;
		}
	}

	private void SpawnBallisticDroplets(Transform owner, Vector3 source, Vector3 sprayDirection, int dropletCount, float severity, DamageCategory category, Color color, bool heavyImpact, bool groundedLethal)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: 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_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: 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_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = Vector3.Cross(Vector3.up, sprayDirection);
		if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
		{
			val = Vector3.right;
		}
		((Vector3)(ref val)).Normalize();
		float num = Mathf.Lerp(3f, 9.8f, severity);
		float num2 = (heavyImpact ? 1.65f : 1.05f);
		if (groundedLethal)
		{
			num *= 0.68f;
			num2 = 0.15f;
		}
		for (int i = 0; i < dropletCount; i++)
		{
			float num3 = ((category == DamageCategory.Blunt) ? 1.55f : 1.1f);
			float num4 = Random.Range(0f - num3, num3);
			float num5 = Random.Range(-0.12f, 0.7f);
			Vector3 val2 = Random.insideUnitSphere * 0.13f;
			val2.y = Mathf.Abs(val2.y) * 0.35f;
			Vector3 val3 = sprayDirection + val * num4 * 0.5f + Vector3.up * num5 + val2;
			Vector3 normalized = ((Vector3)(ref val3)).normalized;
			float num6 = num * Random.Range(0.6f, 1.3f);
			Vector3 val4 = normalized * num6 + Vector3.up * (num2 * Random.Range(0.28f, 1.25f));
			if (groundedLethal)
			{
				val4.y = Mathf.Min(val4.y, 0.32f);
			}
			switch (category)
			{
			case DamageCategory.Pierce:
				val4 *= 1.15f;
				break;
			case DamageCategory.Blunt:
				val4 *= 1.1f;
				break;
			case DamageCategory.Fire:
			case DamageCategory.Poison:
				val4 *= 0.72f;
				break;
			}
			float num7 = Mathf.Lerp(0.03f, 0.105f, severity) * Random.Range(0.7f, 1.3f) * 1.12f;
			float num8 = Mathf.Max(0.22f, dropletLifetime.Value) * Random.Range(0.74f, 1.18f) * 1.1f;
			if (groundedLethal)
			{
				num8 = Mathf.Min(num8, 0.62f);
			}
			float num9 = Mathf.Lerp(0.75f, heavyImpact ? 1.55f : 1.18f, severity) * Random.Range(0.82f, 1.2f);
			if (category == DamageCategory.Blunt && heavyImpact)
			{
				float num10 = Mathf.Clamp(bluntBurstSizeMultiplier.Value, 1f, 2.5f);
				float num11 = Mathf.Clamp(bluntSplatSizeMultiplier.Value, 1f, 2.5f);
				num7 *= Mathf.Lerp(1.05f, num10, 0.52f);
				num8 *= Mathf.Clamp(bluntLifetimeMultiplier.Value, 1f, 2.5f);
				num9 *= num11;
			}
			GetDroplet().Activate(owner, source, val4, num7, num8, num9, color, GetRandomDropletTexture(), Random.Range(0f, 360f));
			dropletsSpawned++;
		}
	}

	private void TryCreateImmediateImpactSplat(Transform owner, Vector3 sourcePosition, float size, Color color, Vector3 preferredWallDirection)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0001: 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_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_0018: 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_002e: 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)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: 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_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		Vector3 origin = sourcePosition + Vector3.up * 1.15f;
		if (TryFindSurface(owner, origin, Vector3.down, 3.2f, out var bestHit) && ((RaycastHit)(ref bestHit)).normal.y >= 0.55f)
		{
			CreateSurfaceSplat(((RaycastHit)(ref bestHit)).point, ((RaycastHit)(ref bestHit)).normal, size, color, null);
		}
		if (!AllowWallMarks)
		{
			return;
		}
		float distance = ((immediateWallProbeDistance == null) ? 1.35f : Mathf.Clamp(immediateWallProbeDistance.Value, 0.25f, 3f));
		Vector3 val = preferredWallDirection;
		val.y = 0f;
		if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
		{
			val = (((Object)(object)owner != (Object)null) ? owner.forward : Vector3.forward);
			val.y = 0f;
		}
		((Vector3)(ref val)).Normalize();
		Vector3 val2 = Vector3.Cross(Vector3.up, val);
		Vector3 normalized = ((Vector3)(ref val2)).normalized;
		Vector3[] obj = new Vector3[4]
		{
			val,
			default(Vector3),
			default(Vector3),
			default(Vector3)
		};
		val2 = val + normalized * 0.38f;
		obj[1] = ((Vector3)(ref val2)).normalized;
		val2 = val - normalized * 0.38f;
		obj[2] = ((Vector3)(ref val2)).normalized;
		obj[3] = -val;
		Vector3[] array = (Vector3[])(object)obj;
		for (int i = 0; i < array.Length; i++)
		{
			if (TryFindSurface(owner, sourcePosition + Vector3.up * 0.06f, array[i], distance, out var bestHit2) && !(((RaycastHit)(ref bestHit2)).normal.y > 0.54f))
			{
				CreateSurfaceSplat(((RaycastHit)(ref bestHit2)).point, ((RaycastHit)(ref bestHit2)).normal, size * 0.82f, color, null);
				break;
			}
		}
	}

	internal void LandDropletOnSurface(Transform owner, Vector3 point, Vector3 normal, Transform parent, float dropletScale, float landingScale, Color color)
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		surfaceLandingEvents++;
		float size = Mathf.Clamp(dropletScale * 4.2f * landingScale, 0.07f, 0.64f);
		CreateSurfaceSplat(point, normal, size, color, parent);
	}

	internal void LandDropletFallback(Transform owner, Vector3 position, float dropletScale, float landingScale, Color color)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		fallbackLandingEvents++;
		if (!CreateFallbackSplatsWhenAirborne)
		{
			RecordAirborneFallbackDiscarded();
		}
		else
		{
			TryCreateImmediateImpactSplat(owner, position, Mathf.Clamp(dropletScale * 3.5f * landingScale, 0.07f, 0.46f), color, Vector3.zero);
		}
	}

	private void CreateSurfaceSplat(Vector3 worldPosition, Vector3 normal, float size, Color color, Transform surfaceParent)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: 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)
		if (!((Object)(object)visualRoot == (Object)null) && IsValidBloodSurfaceNormal(normal))
		{
			size = Mathf.Clamp(size, 0.04f, 0.58f);
			CrimsonBloodSplat splat = GetSplat();
			if (!((Object)(object)splat == (Object)null))
			{
				float lifetime = Mathf.Max(8f, splatLifetime.Value) * Random.Range(0.84f, 1.12f);
				splat.Activate(visualRoot, worldPosition, normal, size, lifetime, color, GetRandomSplatTexture(), Random.Range(0f, 360f));
				splatsSpawned++;
			}
		}
	}

	private bool TryFindSurface(Transform owner, Vector3 origin, Vector3 direction, float distance, out RaycastHit bestHit)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: 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_0075: 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)
		bestHit = default(RaycastHit);
		int num = Physics.RaycastNonAlloc(origin, direction, immediateSurfaceHits, distance, -1, (QueryTriggerInteraction)1);
		float num2 = float.MaxValue;
		bool result = false;
		for (int i = 0; i < num; i++)
		{
			Collider collider = ((RaycastHit)(ref immediateSurfaceHits[i])).collider;
			if (!((Object)(object)collider == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Component)collider).transform.IsChildOf(owner)) && !IsRejectedBloodSurface(collider) && IsValidBloodSurfaceNormal(((RaycastHit)(ref immediateSurfaceHits[i])).normal) && !(((RaycastHit)(ref immediateSurfaceHits[i])).distance >= num2))
			{
				num2 = ((RaycastHit)(ref immediateSurfaceHits[i])).distance;
				bestHit = immediateSurfaceHits[i];
				result = true;
			}
		}
		return result;
	}

	private CrimsonBloodBurst GetBurst()
	{
		RemoveDestroyedPoolEntries(burstPool);
		if (burstPool.Count == 0)
		{
			CreateBurstPoolObject(0);
		}
		for (int i = 0; i < burstPool.Count; i++)
		{
			CrimsonBloodBurst crimsonBloodBurst = burstPool[i];
			if ((Object)(object)crimsonBloodBurst != (Object)null && !((Component)crimsonBloodBurst).gameObject.activeSelf)
			{
				return crimsonBloodBurst;
			}
		}
		CrimsonBloodBurst crimsonBloodBurst2 = burstPool[0];
		for (int j = 1; j < burstPool.Count; j++)
		{
			if ((Object)(object)burstPool[j] != (Object)null && burstPool[j].ActivatedAt < crimsonBloodBurst2.ActivatedAt)
			{
				crimsonBloodBurst2 = burstPool[j];
			}
		}
		recycledBursts++;
		crimsonBloodBurst2.Deactivate();
		return crimsonBloodBurst2;
	}

	private CrimsonBloodStreak GetStreak()
	{
		RemoveDestroyedPoolEntries(streakPool);
		if (streakPool.Count == 0)
		{
			CreateStreakPoolObject(0);
		}
		for (int i = 0; i < streakPool.Count; i++)
		{
			CrimsonBloodStreak crimsonBloodStreak = streakPool[i];
			if ((Object)(object)crimsonBloodStreak != (Object)null && !((Component)crimsonBloodStreak).gameObject.activeSelf)
			{
				return crimsonBloodStreak;
			}
		}
		CrimsonBloodStreak crimsonBloodStreak2 = streakPool[0];
		for (int j = 1; j < streakPool.Count; j++)
		{
			if ((Object)(object)streakPool[j] != (Object)null && streakPool[j].ActivatedAt < crimsonBloodStreak2.ActivatedAt)
			{
				crimsonBloodStreak2 = streakPool[j];
			}
		}
		recycledStreaks++;
		crimsonBloodStreak2.Deactivate();
		return crimsonBloodStreak2;
	}

	private CrimsonBloodDroplet GetDroplet()
	{
		RemoveDestroyedPoolEntries(dropletPool);
		if (dropletPool.Count == 0)
		{
			CreateDropletPoolObject(0);
		}
		for (int i = 0; i < dropletPool.Count; i++)
		{
			CrimsonBloodDroplet crimsonBloodDroplet = dropletPool[i];
			if ((Object)(object)crimsonBloodDroplet != (Object)null && !((Component)crimsonBloodDroplet).gameObject.activeSelf)
			{
				return crimsonBloodDroplet;
			}
		}
		CrimsonBloodDroplet crimsonBloodDroplet2 = dropletPool[0];
		for (int j = 1; j < dropletPool.Count; j++)
		{
			if ((Object)(object)dropletPool[j] != (Object)null && dropletPool[j].ActivatedAt < crimsonBloodDroplet2.ActivatedAt)
			{
				crimsonBloodDroplet2 = dropletPool[j];
			}
		}
		recycledDroplets++;
		crimsonBloodDroplet2.Deactivate();
		return crimsonBloodDroplet2;
	}

	private CrimsonBloodSplat GetSplat()
	{
		RemoveDestroyedPoolEntries(splatPool);
		if (splatPool.Count == 0)
		{
			CreateSplatPoolObject(0);
		}
		for (int i = 0; i < splatPool.Count; i++)
		{
			CrimsonBloodSplat crimsonBloodSplat = splatPool[i];
			if ((Object)(object)crimsonBloodSplat != (Object)null && !((Component)crimsonBloodSplat).gameObject.activeSelf)
			{
				return crimsonBloodSplat;
			}
		}
		CrimsonBloodSplat crimsonBloodSplat2 = splatPool[0];
		for (int j = 1; j < splatPool.Count; j++)
		{
			if ((Object)(object)splatPool[j] != (Object)null && splatPool[j].ActivatedAt < crimsonBloodSplat2.ActivatedAt)
			{
				crimsonBloodSplat2 = splatPool[j];
			}
		}
		recycledSplats++;
		crimsonBloodSplat2.Deactivate();
		return crimsonBloodSplat2;
	}

	private void RemoveDestroyedPoolEntries<T>(List<T> pool) where T : MonoBehaviour
	{
		for (int num = pool.Count - 1; num >= 0; num--)
		{
			if (!((Object)(object)pool[num] != (Object)null))
			{
				pool.RemoveAt(num);
				destroyedPoolEntriesRecovered++;
			}
		}
	}

	private Texture2D GetRandomDropletTexture()
	{
		if (dropletTextures.Count != 0)
		{
			return dropletTextures[Random.Range(0, dropletTextures.Count)];
		}
		return fallbackDropletTexture;
	}

	private Texture2D GetRandomSplatTexture()
	{
		if (splatTextures.Count != 0)
		{
			return splatTextures[Random.Range(0, splatTextures.Count)];
		}
		return fallbackSplatTexture;
	}

	private Texture2D GetRandomTrailTexture()
	{
		if (trailTextures.Count != 0)
		{
			return trailTextures[Random.Range(0, trailTextures.Count)];
		}
		return fallbackTrailTexture;
	}

	private DamageCategory ResolveDamageCategory(HitData hit)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		if (hit == null)
		{
			return DamageCategory.Unknown;
		}
		if (string.Equals(((object)Unsafe.As<HitType, HitType>(ref hit.m_hitType)/*cast due to .constrained prefix*/).ToString(), "Fall", StringComparison.OrdinalIgnoreCase))
		{
			return DamageCategory.Fall;
		}
		object obj = hit.m_damage;
		if (obj == null)
		{
			return DamageCategory.Unknown;
		}
		float damageValue = GetDamageValue(obj, "m_fire");
		float damageValue2 = GetDamageValue(obj, "m_poison");
		float damageValue3 = GetDamageValue(obj, "m_slash");
		float damageValue4 = GetDamageValue(obj, "m_pierce");
		float damageValue5 = GetDamageValue(obj, "m_chop");
		float damageValue6 = GetDamageValue(obj, "m_blunt");
		if (damageValue > 0.001f && damageValue >= damageValue2 && damageValue >= damageValue3 && damageValue >= damageValue4 && damageValue >= damageValue5 && damageValue >= damageValue6)
		{
			return DamageCategory.Fire;
		}
		if (damageValue2 > 0.001f && damageValue2 >= damageValue3 && damageValue2 >= damageValue4 && damageValue2 >= damageValue5 && damageValue2 >= damageValue6)
		{
			return DamageCategory.Poison;
		}
		if (damageValue4 > 0.001f && damageValue4 >= damageValue3 && damageValue4 >= damageValue5 && damageValue4 >= damageValue6)
		{
			return DamageCategory.Pierce;
		}
		if (damageValue3 > 0.001f && damageValue3 >= damageValue5 && damageValue3 >= damageValue6)
		{
			return DamageCategory.Slash;
		}
		if (damageValue5 > 0.001f && damageValue5 >= damageValue6)
		{
			return DamageCategory.Chop;
		}
		if (damageValue6 > 0.001f)
		{
			if (!string.Equals(((object)Unsafe.As<HitType, HitType>(ref hit.m_hitType)/*cast due to .constrained prefix*/).ToString(), "EnemyHit", StringComparison.OrdinalIgnoreCase))
			{
				return DamageCategory.Blunt;
			}
			return DamageCategory.BiteOrMonsterHit;
		}
		return DamageCategory.Unknown;
	}

	private float GetDamageValue(object damage, string fieldName)
	{
		if (damage == null || string.IsNullOrEmpty(fieldName))
		{
			return 0f;
		}
		Type type = damage.GetType();
		if (!damageFieldCache.TryGetValue(type, out var value))
		{
			value = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
			damageFieldCache[type] = value;
		}
		for (int i = 0; i < value.Length; i++)
		{
			if (string.Equals(value[i].Name, fieldName, StringComparison.Ordinal))
			{
				try
				{
					object value2 = value[i].GetValue(damage);
					return (value2 is float) ? Mathf.Max(0f, (float)value2) : 0f;
				}
				catch
				{
					return 0f;
				}
			}
		}
		return 0f;
	}

	private static Vector3 ResolveImpactPoint(Character target, HitData hit)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: 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_0026: Unknown result type (might be due to invalid IL or missing references)
		//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_003e: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = hit?.m_point ?? Vector3.zero;
		Vector3 centerPoint = target.GetCenterPoint();
		if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
		{
			Vector3 val2 = val - centerPoint;
			if (!(((Vector3)(ref val2)).sqrMagnitude > 36f))
			{
				goto IL_003e;
			}
		}
		val = centerPoint;
		goto IL_003e;
		IL_003e:
		return val;
	}

	private static Vector3 ResolveOutwardSprayDirection(Character target, HitData hit, Vector3 impactPoint)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: 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_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//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_003b: 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_0041: 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_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: 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_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_007f: 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)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: 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)
		Vector3 val = hit?.m_dir ?? Vector3.zero;
		if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
		{
			val = ((Component)target).transform.forward;
		}
		val = ((Vector3)(ref val)).normalized;
		Vector3 centerPoint = target.GetCenterPoint();
		Vector3 val2 = impactPoint - centerPoint;
		val2.y *= 0.55f;
		if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f)
		{
			val2 = val;
		}
		else
		{
			((Vector3)(ref val2)).Normalize();
			if (Vector3.Dot(val2, val) < 0f)
			{
				val2 = -val2;
			}
		}
		Vector3 val3 = val * 0.78f + val2 * 0.32f + Vector3.up * 0.06f;
		if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f)
		{
			val3 = val;
		}
		val3.y = Mathf.Clamp(val3.y, -0.12f, 0.42f);
		return ((Vector3)(ref val3)).normalized;
	}

	private static Color ResolveBloodColor(Character target, DamageCategory category)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		string text = GetTargetName(target).ToLowerInvariant();
		if (text.Contains("skeleton") || text.Contains("ghost"))
		{
			return new Color(0.075f, 0.012f, 0.016f, 0.8f);
		}
		return new Color(0.34f, 0.02f, 0.025f, 0.88f);
	}

	private static string GetTargetName(Character target)
	{
		if ((Object)(object)target == (Object)null)
		{
			return "unknown";
		}
		try
		{
			string text = (((Object)(object)((Component)target).gameObject != (Object)null) ? ((Object)((Component)target).gameObject).name : string.Empty);
			if (!string.IsNullOrEmpty(text))
			{
				return text.Replace("(Clone)", string.Empty).Trim();
			}
		}
		catch
		{
		}
		return ((object)target).GetType().Name;
	}

	private void RememberEvent(string eventText)
	{
		lastEvent = eventText;
		while (recentEvents.Count >= 20)
		{
			recentEvents.Dequeue();
		}
		recentEvents.Enqueue(eventText);
	}

	private void TrimExpiredCooldowns()
	{
		if (nextAllowedImpactTime.Count == 0)
		{
			return;
		}
		List<int> list = null;
		foreach (KeyValuePair<int, float> item in nextAllowedImpactTime)
		{
			if (!(item.Value >= Time.time))
			{
				if (list == null)
				{
					list = new List<int>();
				}
				list.Add(item.Key);
			}
		}
		if (list != null)
		{
			for (int i = 0; i < list.Count; i++)
			{
				nextAllowedImpactTime.Remove(list[i]);
			}
		}
	}

	private void ClearLoadedAssetLists()
	{
		burstTextures.Clear();
		dropletTextures.Clear();
		splatTextures.Clear();
		trailTextures.Clear();
	}

	private void DestroyVisualSystem()
	{
		if ((Object)(object)visualRoot != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)visualRoot).gameObject);
			visualRoot = null;
		}
		for (int i = 0; i < ownedMaterials.Count; i++)
		{
			if ((Object)(object)ownedMaterials[i] != (Object)null)
			{
				Object.Destroy((Object)(object)ownedMaterials[i]);
			}
		}
		for (int j = 0; j < ownedTextures.Count; j++)
		{
			if ((Object)(object)ownedTextures[j] != (Object)null)
			{
				Object.Destroy((Object)(object)ownedTextures[j]);
			}
		}
		ownedMaterials.Clear();
		ownedTextures.Clear();
		burstPool.Clear();
		streakPool.Clear();
		dropletPool.Clear();
		splatPool.Clear();
		ClearLoadedAssetLists();
		quadMaterial = null;
		fallbackDropletTexture = null;
		fallbackBurstTexture = null;
		fallbackSplatTexture = null;
		fallbackTrailTexture = null;
	}

	private void WriteDiagnostics()
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Crimson Blood 0.7.6.2 Safe Vanilla Mute / Falling Blood status:");
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  Enabled=" + enabledConfig.Value + " | Local-only visuals | YoureInjuredLoaded=" + IsYoureInjuredLoaded()));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  AssetLoad=" + assetLoadSummary));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  ApplyDamageHookCalls=" + applyDamageHookCalls + " | ObservedDamageCalls=" + observedDamageCalls));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  RealHealthLossEvents=" + realHealthLossEvents + " | ZeroHealthLossEvents=" + zeroHealthLossEvents));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  VisualEvents=" + visualEvents + " | Bursts=" + burstsSpawned + " | Streaks=" + streaksSpawned + " | Droplets=" + dropletsSpawned + " | Splats=" + splatsSpawned));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  Recycled: bursts=" + recycledBursts + ", streaks=" + recycledStreaks + ", droplets=" + recycledDroplets + ", splats=" + recycledSplats + ", destroyedPoolEntriesRecovered=" + destroyedPoolEntriesRecovered));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  Skipped: playerCompatibility=" + suppressedPlayerImpactEvents + ", cooldown=" + cooldownSkippedEvents + ", distance=" + distanceSkippedEvents));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  RenderDistance: dynamic=" + DynamicEffectRenderDistance.ToString("0.##") + "m, splat=" + SurfaceSplatRenderDistance.ToString("0.##") + "m"));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  Landings: surfaceHit=" + surfaceLandingEvents + ", fallback=" + fallbackLandingEvents + ", discardedFallback=" + fallbackSplatsDiscarded + ", characterSurfaceSkipped=" + characterSurfaceSkips + ", rigidbodySurfaceSkipped=" + rigidbodySurfaceSkips + ", invalidSurfaceSkipped=" + invalidSurfaceSkips + ", groundedLethalFinishes=" + groundedLethalFinishes));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  StaticSurfaceMarks: wallsAndSteep=" + AllowWallMarks + ", immediateWallProbe=" + ((immediateWallProbeDistance == null) ? 1.35f : immediateWallProbeDistance.Value).ToString("0.##") + "m, airborneFallbackSplats=" + CreateFallbackSplatsWhenAirborne));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  VanillaEffects: observed=" + observedVanillaEffectRoots + ", muted=" + suppressedVanillaBloodEffects + ", unmatched=" + unmatchedVanillaEffectRoots + ", mutedRenderers=" + mutedVanillaRenderers + ", particleRenderersIncluded=true, tokens=" + ((vanillaBloodEffectTokens == null) ? "blood,bleed,gore,splatter" : vanillaBloodEffectTokens.Value)));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  Pool: bursts=" + burstPool.Count + ", streaks=" + streakPool.Count + ", droplets=" + dropletPool.Count + ", splats=" + splatPool.Count));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  HeavyBlunt: bloodMultiplier=" + bluntBloodMultiplier.Value.ToString("0.##") + ", maxDroplets=" + bluntMaximumDropletsPerHit.Value + ", burstSize=" + bluntBurstSizeMultiplier.Value.ToString("0.##") + ", splatSize=" + bluntSplatSizeMultiplier.Value.ToString("0.##") + ", lifetime=" + bluntLifetimeMultiplier.Value.ToString("0.##")));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  LastEvent=" + lastEvent));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  RecentEvents=" + recentEvents.Count));
		foreach (string recentEvent in recentEvents)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("    " + recentEvent));
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("  RecentVanillaEffectRoots=" + recentVanillaEffectRoots.Count));
		foreach (string recentVanillaEffectRoot in recentVanillaEffectRoots)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("    " + recentVanillaEffectRoot));
		}
	}

	private static string FormatVector(Vector3 value)
	{
		return "(" + value.x.ToString("0.##") + "," + value.y.ToString("0.##") + "," + value.z.ToString("0.##") + ")";
	}
}
internal enum DamageCategory
{
	Unknown,
	Slash,
	Pierce,
	Blunt,
	Chop,
	Fire,
	Poison,
	Fall,
	BiteOrMonsterHit
}
internal sealed class CrimsonDamageSnapshot
{
	public bool Observe;

	public float HealthBefore;
}
[HarmonyPatch(typeof(Character), "ApplyDamage")]
internal static class CharacterApplyDamageCrimsonPatch
{
	[HarmonyPrefix]
	private static void Prefix(Character __instance, out CrimsonDamageSnapshot __state)
	{
		__state = new CrimsonDamageSnapshot();
		CrimsonBloodPlugin instance = CrimsonBloodPlugin.Instance;
		if (!((Object)(object)instance == (Object)null))
		{
			instance.BeginVanillaEffectWindow(__instance);
			instance.RecordHookCall();
			if (instance.ShouldObserve(__instance))
			{
				__state.Observe = true;
				__state.HealthBefore = (((Object)(object)__instance != (Object)null) ? __instance.GetHealth() : 0f);
			}
		}
	}

	[HarmonyPostfix]
	private static void Postfix(Character __instance, HitData __0, CrimsonDamageSnapshot __state)
	{
		if (__state != null && __state.Observe && !((Object)(object)__instance == (Object)null))
		{
			CrimsonBloodPlugin instance = CrimsonBloodPlugin.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.RecordDamage(__instance, __0, __state.HealthBefore);
			}
		}
	}
}
[HarmonyPatch(typeof(EffectList), "Create", new Type[]
{
	typeof(Vector3),
	typeof(Quaternion),
	typeof(Transform),
	typeof(float),
	typeof(int)
})]
internal static class EffectListCreateCrimsonPatch
{
	[HarmonyPostfix]
	private static void Postfix(GameObject[] __result)
	{
		CrimsonBloodPlugin instance = CrimsonBloodPlugin.Instance;
		if ((Object)(object)instance != (Object)null)
		{
			instance.OnVanillaEffectsCreated(__result);
		}
	}
}
internal sealed class CrimsonBloodBurst : MonoBehaviour
{
	private CrimsonBloodPlugin plugin;

	private Renderer cachedRenderer;

	private MaterialPropertyBlock propertyBlock;

	private List<Texture2D> frames;

	private Vector3 velocity;

	private float remainingLifetime;

	private float startLifetime;

	private float baseSize;

	private float rotationDegrees;

	private Color color;

	private int lastFrameIndex;

	internal float ActivatedAt { get; private set; }

	internal void Initialize(CrimsonBloodPlugin sourcePlugin, Renderer renderer)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Expected O, but got Unknown
		plugin = sourcePlugin;
		cachedRenderer = renderer;
		propertyBlock = new MaterialPropertyBlock();
	}

	internal void Activate(Transform owner, Vector3 position, Vector3 startVelocity, float size, float lifetime, Color startColor, List<Texture2D> sourceFrames, float randomRotation)
	{
		//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_0040: 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)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		velocity = startVelocity;
		remainingLifetime = Mathf.Max(0.05f, lifetime);
		startLifetime = remainingLifetime;
		baseSize = Mathf.Max(0.05f, size);
		rotationDegrees = randomRotation;
		color = startColor;
		frames = ((sourceFrames != null && sourceFrames.Count > 0) ? sourceFrames : null);
		lastFrameIndex = -1;
		ActivatedAt = Time.time;
		((Component)this).transform.position = position;
		if ((Object)(object)cachedRenderer != (Object)null)
		{
			cachedRenderer.enabled = true;
		}
		ApplyFrame(0, color);
		UpdateBillboardAndScale(0f);
		((Component)this).gameObject.SetActive(true);
	}

	internal void Deactivate()
	{
		frames = null;
		if ((Object)(object)cachedRenderer != (Object)null)
		{
			cachedRenderer.enabled = true;
		}
		((Component)this).gameObject.SetActive(false);
	}

	private void Update()
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: 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_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_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: 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_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)plugin == (Object)null)
		{
			Deactivate();
			return;
		}
		float deltaTime = Time.deltaTime;
		remainingLifetime -= deltaTime;
		velocity += Physics.gravity * (plugin.DropletGravityMultiplier * 0.12f) * deltaTime;
		Transform transform = ((Component)this).transform;
		transform.position += velocity * deltaTime;
		float num = 1f - Mathf.Clamp01(remainingLifetime / Mathf.Max(0.01f, startLifetime));
		int frameIndex = ((frames != null && frames.Count != 0) ? Mathf.Min(frames.Count - 1, Mathf.FloorToInt(num * (float)frames.Count)) : 0);
		Color targetColor = color;
		float num2 = ((num > 0.55f) ? Mathf.Clamp01((1f - num) / 0.45f) : 1f);
		targetColor.a *= num2;
		ApplyFrame(frameIndex, targetColor);
		UpdateBillboardAndScale(num);
		if ((Object)(object)cachedRenderer != (Object)null)
		{
			cachedRenderer.enabled = plugin.ShouldRenderEffectAt(((Component)this).transform.position, isSurfaceMark: false);
		}
		if (remainingLifetime <= 0f)
		{
			Deactivate();
		}
	}

	private void UpdateBillboardAndScale(float age)
	{
		//IL_002c: 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_0041: 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_0064: 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_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		Camera val = (((Object)(object)plugin != (Object)null) ? plugin.RenderCamera : null);
		if ((Object)(object)val != (Object)null)
		{
			Vector3 val2 = ((Component)val).transform.position - ((Component)this).transform.position;
			if (((Vector3)(ref val2)).sqrMagnitude > 0.001f)
			{
				((Component)this).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val2)).normalized, ((Component)val).transform.up) * Quaternion.Euler(0f, 0f, rotationDegrees);
			}
		}
		Texture2D val3 = ((frames != null && frames.Count > 0) ? frames[Mathf.Clamp(lastFrameIndex, 0, frames.Count - 1)] : null);
		float num = (((Object)(object)val3 != (Object)null && ((Texture)val3).height > 0) ? ((float)((Texture)val3).width / (float)((Texture)val3).height) : 1f);
		float num2 = Mathf.Lerp(0.38f, 1.18f, Mathf.Clamp01(age / 0.68f));
		((Component)this).transform.localScale = new Vector3(baseSize * num * num2, baseSize * num2, 1f);
	}

	private void ApplyFrame(int frameIndex, Color targetColor)
	{
		//IL_007c: 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)
		if (!((Object)(object)cachedRenderer == (Object)null))
		{
			if (frames != null && frames.Count > 0)
			{
				frameIndex = Mathf.Clamp(frameIndex, 0, frames.Count - 1);
				lastFrameIndex = frameIndex;
				Texture2D val = frames[frameIndex];
				propertyBlock.SetTexture("_MainTex", (Texture)(object)val);
				propertyBlock.SetTexture("_BaseMap", (Texture)(object)val);
			}
			propertyBlock.SetColor("_Color", targetColor);
			propertyBlock.SetColor("_BaseColor", targetColor);
			cachedRenderer.SetPropertyBlock(propertyBlock);
		}
	}
}
internal sealed class CrimsonBloodStreak : MonoBehaviour
{
	private CrimsonBloodPlugin plugin;

	private Renderer cachedRenderer;

	private MaterialPropertyBlock propertyBlock;

	private Vector3 velocity;

	private float remainingLifetime;

	private float startLifetime;

	private float width;

	private float rotationDegrees;

	private Color color;

	private Texture2D texture;

	internal float ActivatedAt { get; private set; }

	internal void Initialize(CrimsonBloodPlugin sourcePlugin, Renderer renderer)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Expected O, but got Unknown
		plugin = sourcePlugin;
		cachedRenderer = renderer;
		propertyBlock = new MaterialPropertyBlock();
	}

	internal void Activate(Transform owner, Vector3 position, Vector3 startVelocity, float startWidth, float lifetime, Color startColor, Texture2D startTexture, float randomRotation)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to in