Decompiled source of UltraBlood v0.2.1

UltraBlood.dll

Decompiled 18 minutes ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PluginConfig.API;
using PluginConfig.API.Decorators;
using PluginConfig.API.Fields;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UltraBlood")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Recolors ULTRAKILL blood splatters and puddles to a custom color.")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+85b68089e57ba10d8fea52167845b88a2d6c1c17")]
[assembly: AssemblyProduct("UltraBlood")]
[assembly: AssemblyTitle("UltraBlood")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UltraBlood
{
	internal static class BloodColorController
	{
		private const string BloodColorProperty = "_UltraBloodColor";

		private static Material _replacementComposite;

		private static Material _originalComposite;

		private static bool _activeLogged;

		private static bool _failureLogged;

		private static bool _renderPassLogged;

		private static bool _statusReported;

		private static string _lastDiagFingerprint;

		private const float RainbowCycleSpeed = 0.35f;

		public static bool PuddleRecolorActive { get; private set; }

		public static void Refresh()
		{
			if (TryGetManager(out var bsm))
			{
				ApplyToStainMaterials(bsm);
			}
		}

		public static void ApplyToStainMaterials(BloodsplatterManager bsm)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)bsm == (Object)null))
			{
				if (UltraBloodConfig.Enabled && UltraBloodConfig.RecolorPuddles)
				{
					ReplaceComposite(bsm, CurrentColor());
				}
				else
				{
					RestoreComposite(bsm);
				}
				if (UltraBloodConfig.DebugLogging)
				{
					LogStainMaterialsOnce(bsm);
				}
			}
		}

		public static void RestoreStainMaterials()
		{
			if (TryGetManager(out var bsm))
			{
				RestoreComposite(bsm);
			}
			if ((Object)(object)_replacementComposite != (Object)null)
			{
				Object.Destroy((Object)(object)_replacementComposite);
				_replacementComposite = null;
			}
		}

		public static void NoteCompositeRenderPass()
		{
			if (!_renderPassLogged && UltraBloodConfig.DebugLogging && TryGetManager(out var bsm) && !((Object)(object)bsm.bloodCompositeMaterial == (Object)null))
			{
				_renderPassLogged = true;
				Material bloodCompositeMaterial = bsm.bloodCompositeMaterial;
				bool flag = (Object)(object)_replacementComposite != (Object)null && bloodCompositeMaterial == _replacementComposite;
				string arg = (((Object)(object)bloodCompositeMaterial.shader != (Object)null) ? ((Object)bloodCompositeMaterial.shader).name : "<null>");
				Plugin.Log.LogInfo((object)$"[shader] composite render pass material id={((Object)bloodCompositeMaterial).GetInstanceID()} shader='{arg}' passIndex=1 isReplacement={flag}");
			}
		}

		private static Color CurrentColor()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			if (UltraBloodConfig.SolidCyanTest)
			{
				return Color.cyan;
			}
			if (UltraBloodConfig.EnemyBloodColorMatch)
			{
				return (Color)(((??)BloodParticles.LastEnemyColor) ?? UltraBloodConfig.BloodColor);
			}
			if (UltraBloodConfig.RainbowBlood)
			{
				return Color.HSVToRGB(Mathf.Repeat(Time.time * 0.35f, 1f), 0.9f, 1f);
			}
			return UltraBloodConfig.BloodColor;
		}

		public static void TickDynamicColor()
		{
			//IL_0015: 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_0025: 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_0031: 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)
			if (PuddleRecolorActive && !((Object)(object)_replacementComposite == (Object)null))
			{
				Color val = CurrentColor();
				_replacementComposite.SetColor("_UltraBloodColor", new Color(val.r, val.g, val.b, UltraBloodConfig.PuddleOpacity));
			}
		}

		private static void ReplaceComposite(BloodsplatterManager bsm, Color color)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			Shader compositeShader = ShaderBundle.CompositeShader;
			if ((Object)(object)compositeShader == (Object)null)
			{
				PuddleRecolorActive = false;
				if (!_failureLogged)
				{
					_failureLogged = true;
					Plugin.Log.LogError((object)($"[shader] puddle recoloring unavailable: {ShaderBundle.Status} ({ShaderBundle.StatusMessage}). " + "Running Unity " + Application.unityVersion + ". Leaving the vanilla composite in place."));
					ReportStatus($"Puddle recoloring unavailable ({ShaderBundle.Status}). See the log.", ok: false);
				}
				return;
			}
			if ((Object)(object)_replacementComposite == (Object)null)
			{
				_replacementComposite = new Material(compositeShader)
				{
					name = "UltraBlood_CompositeBlood"
				};
			}
			_replacementComposite.SetColor("_UltraBloodColor", new Color(color.r, color.g, color.b, UltraBloodConfig.PuddleOpacity));
			Material bloodCompositeMaterial = bsm.bloodCompositeMaterial;
			if ((Object)(object)bloodCompositeMaterial != (Object)(object)_replacementComposite)
			{
				if ((Object)(object)bloodCompositeMaterial != (Object)null)
				{
					_originalComposite = bloodCompositeMaterial;
				}
				bsm.bloodCompositeMaterial = _replacementComposite;
				if (!_activeLogged)
				{
					_activeLogged = true;
					Material bloodCompositeMaterial2 = bsm.bloodCompositeMaterial;
					Plugin.Log.LogInfo((object)$"[shader] manager id={((Object)bsm).GetInstanceID()}");
					Plugin.Log.LogInfo((object)string.Format("[shader] original composite id={0} name='{1}'", ((Object)(object)_originalComposite != (Object)null) ? ((Object)_originalComposite).GetInstanceID() : 0, ((Object)(object)_originalComposite != (Object)null) ? ((Object)_originalComposite).name : "null"));
					Plugin.Log.LogInfo((object)$"[shader] replacement composite id={((Object)_replacementComposite).GetInstanceID()} name='{((Object)_replacementComposite).name}' shader='{((Object)compositeShader).name}' passCount={compositeShader.passCount}");
					Plugin.Log.LogInfo((object)$"[shader] manager field material id={(((Object)(object)bloodCompositeMaterial2 != (Object)null) ? ((Object)bloodCompositeMaterial2).GetInstanceID() : 0)} referencesReplacement={bloodCompositeMaterial2 == _replacementComposite}");
				}
			}
			PuddleRecolorActive = true;
			ReportStatus("Puddle recoloring active.", ok: true);
		}

		private static void RestoreComposite(BloodsplatterManager bsm)
		{
			if ((Object)(object)_originalComposite != (Object)null && (Object)(object)bsm.bloodCompositeMaterial == (Object)(object)_replacementComposite)
			{
				bsm.bloodCompositeMaterial = _originalComposite;
			}
			PuddleRecolorActive = false;
		}

		private static void ReportStatus(string text, bool ok)
		{
			if (!_statusReported)
			{
				_statusReported = true;
				UltraBloodConfig.SetPuddleStatus(text, ok);
			}
		}

		private static void LogStainMaterialsOnce(BloodsplatterManager bsm)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			string[] obj = new string[8]
			{
				((Object)bsm).GetInstanceID().ToString(),
				MaterialId(bsm.stainMat),
				MaterialId(bsm.bloodCompositeMaterial),
				PuddleRecolorActive ? "active" : "inactive",
				UltraBloodConfig.Enabled ? "on" : "off",
				UltraBloodConfig.RecolorPuddles ? "puddles" : "nopuddles",
				UltraBloodConfig.SolidCyanTest ? "cyan" : "color",
				null
			};
			Color val = CurrentColor();
			obj[7] = ((Color)(ref val)).ToString("F2");
			string text = string.Join("|", obj);
			if (!(text == _lastDiagFingerprint))
			{
				_lastDiagFingerprint = text;
				LogMaterial("stainMat", bsm.stainMat);
				LogMaterial("bloodCompositeMaterial", bsm.bloodCompositeMaterial);
			}
		}

		private static string MaterialId(Material mat)
		{
			if (!((Object)(object)mat == (Object)null))
			{
				return ((Object)mat).GetInstanceID().ToString();
			}
			return "null";
		}

		private static void LogMaterial(string label, Material mat)
		{
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_012f: Expected I4, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: 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_01b8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mat == (Object)null)
			{
				Plugin.Log.LogInfo((object)("[diagnostics] " + label + ": <null material>"));
				return;
			}
			Shader shader = mat.shader;
			Plugin.Log.LogInfo((object)$"[diagnostics] === {label} === material='{((Object)mat).name}' id={((Object)mat).GetInstanceID()} renderQueue={mat.renderQueue}");
			if ((Object)(object)shader == (Object)null)
			{
				Plugin.Log.LogInfo((object)"[diagnostics] shader=<null>");
				return;
			}
			Plugin.Log.LogInfo((object)$"[diagnostics] shader='{((Object)shader).name}' id={((Object)shader).GetInstanceID()} passCount={shader.passCount}");
			string[] shaderKeywords = mat.shaderKeywords;
			Plugin.Log.LogInfo((object)("[diagnostics] enabledKeywords=[" + ((shaderKeywords != null && shaderKeywords.Length != 0) ? string.Join(", ", shaderKeywords) : "none") + "]"));
			int propertyCount = shader.GetPropertyCount();
			for (int i = 0; i < propertyCount; i++)
			{
				string propertyName = shader.GetPropertyName(i);
				ShaderPropertyType propertyType = shader.GetPropertyType(i);
				string arg;
				switch ((int)propertyType)
				{
				case 0:
				{
					Color color = mat.GetColor(propertyName);
					arg = ((Color)(ref color)).ToString("F3");
					break;
				}
				case 1:
				{
					Vector4 vector = mat.GetVector(propertyName);
					arg = ((Vector4)(ref vector)).ToString("F3");
					break;
				}
				case 2:
				case 3:
					arg = mat.GetFloat(propertyName).ToString("F3");
					break;
				case 4:
					arg = DescribeTexture(mat.GetTexture(propertyName));
					break;
				case 5:
					arg = mat.GetInt(propertyName).ToString();
					break;
				default:
					arg = "<unknown>";
					break;
				}
				Plugin.Log.LogInfo((object)$"[diagnostics]   {propertyName} <{propertyType}> = {arg}");
			}
		}

		private static string DescribeTexture(Texture texture)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)texture == (Object)null)
			{
				return "<none>";
			}
			Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
			string text = ((val != null) ? $" {val.format}" : string.Empty);
			return $"'{((Object)texture).name}' ({((object)texture).GetType().Name} {texture.width}x{texture.height}{text})";
		}

		private static bool TryGetManager(out BloodsplatterManager bsm)
		{
			bsm = null;
			try
			{
				return MonoSingleton<BloodsplatterManager>.TryGetInstance(ref bsm) && (Object)(object)bsm != (Object)null;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
	internal static class BloodParticles
	{
		private sealed class Snapshot
		{
			public ParticleSystem System;

			public MinMaxGradient StartColor;

			public bool LifetimeEnabled;

			public MinMaxGradient Lifetime;

			public bool SpeedEnabled;

			public MinMaxGradient Speed;

			public bool TrailsEnabled;

			public MinMaxGradient TrailLifetime;

			public MinMaxGradient TrailOverTrail;

			public ParticleSystemRenderer Renderer;

			public Material OriginalMaterial;

			public Material PrivateMaterial;

			public string TintProperty;

			public Color RainbowColor;

			public Color? EnemyColor;
		}

		private static readonly Dictionary<int, Snapshot> Snapshots = new Dictionary<int, Snapshot>();

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

		private static readonly Dictionary<int, EnemyIdentifier> BloodEnemies = new Dictionary<int, EnemyIdentifier>();

		private static readonly Dictionary<int, Color> EnemyColors = new Dictionary<int, Color>();

		private static readonly string[] TintProperties = new string[3] { "_Color", "_BaseColor", "_TintColor" };

		private static readonly Dictionary<EnemyType, Color> EnemyPalette = new Dictionary<EnemyType, Color>
		{
			{
				(EnemyType)3,
				new Color(0.72f, 0.58f, 0.42f)
			},
			{
				(EnemyType)13,
				new Color(0.8f, 0.64f, 0.6f)
			},
			{
				(EnemyType)14,
				new Color(0.76f, 0.32f, 0.34f)
			},
			{
				(EnemyType)15,
				new Color(0.44f, 0.52f, 0.3f)
			},
			{
				(EnemyType)36,
				new Color(0.7f, 0.66f, 0.62f)
			},
			{
				(EnemyType)5,
				new Color(0.82f, 0.84f, 0.9f)
			},
			{
				(EnemyType)1,
				new Color(0.55f, 0.58f, 0.62f)
			},
			{
				(EnemyType)6,
				new Color(0.9f, 0.55f, 0.15f)
			},
			{
				(EnemyType)12,
				new Color(0.8f, 0.45f, 0.15f)
			},
			{
				(EnemyType)7,
				new Color(0.75f, 0.16f, 0.16f)
			},
			{
				(EnemyType)8,
				new Color(0.22f, 0.46f, 0.85f)
			},
			{
				(EnemyType)22,
				new Color(0.22f, 0.46f, 0.85f)
			},
			{
				(EnemyType)20,
				new Color(0.72f, 0.5f, 0.24f)
			},
			{
				(EnemyType)31,
				new Color(0.88f, 0.86f, 0.82f)
			},
			{
				(EnemyType)33,
				new Color(0.46f, 0.49f, 0.53f)
			},
			{
				(EnemyType)34,
				new Color(0.4f, 0.43f, 0.47f)
			},
			{
				(EnemyType)35,
				new Color(0.5f, 0.53f, 0.57f)
			},
			{
				(EnemyType)0,
				new Color(0.85f, 0.84f, 0.8f)
			},
			{
				(EnemyType)2,
				new Color(0.66f, 0.26f, 0.26f)
			},
			{
				(EnemyType)4,
				new Color(0.8f, 0.68f, 0.42f)
			},
			{
				(EnemyType)9,
				new Color(0.96f, 0.82f, 0.32f)
			},
			{
				(EnemyType)10,
				new Color(0.26f, 0.26f, 0.3f)
			},
			{
				(EnemyType)23,
				new Color(0.55f, 0.4f, 0.28f)
			},
			{
				(EnemyType)24,
				new Color(0.5f, 0.36f, 0.25f)
			},
			{
				(EnemyType)21,
				new Color(0.9f, 0.75f, 0.3f)
			},
			{
				(EnemyType)11,
				new Color(0.6f, 0.58f, 0.55f)
			},
			{
				(EnemyType)18,
				new Color(0.82f, 0.8f, 0.76f)
			},
			{
				(EnemyType)16,
				new Color(0.92f, 0.86f, 0.62f)
			},
			{
				(EnemyType)28,
				new Color(0.8f, 0.22f, 0.22f)
			},
			{
				(EnemyType)19,
				new Color(0.7f, 0.46f, 0.42f)
			},
			{
				(EnemyType)29,
				new Color(0.8f, 0.76f, 0.72f)
			},
			{
				(EnemyType)17,
				new Color(0.78f, 0.66f, 0.6f)
			},
			{
				(EnemyType)30,
				new Color(0.78f, 0.66f, 0.6f)
			},
			{
				(EnemyType)26,
				new Color(0.7f, 0.74f, 0.78f)
			},
			{
				(EnemyType)27,
				new Color(0.35f, 0.45f, 0.55f)
			},
			{
				(EnemyType)32,
				new Color(0.5f, 0.5f, 0.52f)
			},
			{
				(EnemyType)38,
				new Color(0.9f, 0.78f, 0.35f)
			},
			{
				(EnemyType)41,
				new Color(0.22f, 0.22f, 0.26f)
			}
		};

		public static Color? LastEnemyColor { get; private set; }

		public static void NoteEnemy(Bloodsplatter splatter, EnemyIdentifier eid)
		{
			if (!((Object)(object)splatter == (Object)null))
			{
				int instanceID = ((Object)splatter).GetInstanceID();
				if ((Object)(object)eid != (Object)null)
				{
					BloodEnemies[instanceID] = eid;
				}
				else
				{
					BloodEnemies.Remove(instanceID);
				}
			}
		}

		public static void Register(Bloodsplatter splatter)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)splatter == (Object)null || !UltraBloodConfig.Enabled || !UltraBloodConfig.RecolorSplatters)
			{
				return;
			}
			GameObject gameObject = ((Component)splatter).gameObject;
			if ((Object)(object)gameObject == (Object)null || IsExcludedVariant(splatter.bloodSplatterType))
			{
				return;
			}
			Color? enemyColor = EnemyColorFor(splatter);
			HashSet<int> visited = new HashSet<int>();
			RegisterHierarchy(gameObject, visited, enemyColor);
			Plugin.RunDelayed(gameObject, delegate
			{
				if (!((Object)(object)splatter == (Object)null) && !((Object)(object)((Component)splatter).gameObject == (Object)null))
				{
					HashSet<int> visited2 = new HashSet<int>();
					RegisterHierarchy(((Component)splatter).gameObject, visited2, EnemyColorFor(splatter));
				}
			});
		}

		public static void ReapplyAll()
		{
			Prune();
			bool flag = UltraBloodConfig.Enabled && UltraBloodConfig.RecolorSplatters;
			foreach (Snapshot value in Snapshots.Values)
			{
				if (!((Object)(object)value.System == (Object)null))
				{
					if (flag)
					{
						ApplyColor(value);
					}
					else
					{
						Restore(value);
					}
				}
			}
		}

		public static void RestoreAll()
		{
			foreach (Snapshot value in Snapshots.Values)
			{
				if ((Object)(object)value.System != (Object)null)
				{
					Restore(value);
				}
			}
		}

		private static void RegisterHierarchy(GameObject root, HashSet<int> visited, Color? enemyColor)
		{
			ParticleSystem[] componentsInChildren = root.GetComponentsInChildren<ParticleSystem>(true);
			if (componentsInChildren != null)
			{
				ParticleSystem[] array = componentsInChildren;
				for (int i = 0; i < array.Length; i++)
				{
					RegisterSystem(array[i], visited, root, enemyColor);
				}
			}
		}

		private static void RegisterSystem(ParticleSystem ps, HashSet<int> visited, GameObject root, Color? enemyColor)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			if ((Object)(object)ps == (Object)null || !visited.Add(((Object)ps).GetInstanceID()))
			{
				return;
			}
			int instanceID = ((Object)ps).GetInstanceID();
			if (!Snapshots.TryGetValue(instanceID, out var value))
			{
				value = Capture(ps);
				Snapshots[instanceID] = value;
				LogOnce(ps, root);
			}
			value.EnemyColor = enemyColor;
			ApplyColor(value);
			SubEmittersModule subEmitters = ps.subEmitters;
			int num = SafeSubEmitterCount(subEmitters);
			for (int i = 0; i < num; i++)
			{
				ParticleSystem val = SafeGetSubEmitter(subEmitters, i);
				if ((Object)(object)val != (Object)null)
				{
					RegisterSystem(val, visited, root, enemyColor);
				}
			}
		}

		private static Snapshot Capture(ParticleSystem ps)
		{
			//IL_000d: 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_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_0022: 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_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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_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_006f: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			Snapshot snapshot = new Snapshot
			{
				System = ps,
				RainbowColor = RandomVividColor()
			};
			MainModule main = ps.main;
			snapshot.StartColor = ((MainModule)(ref main)).startColor;
			ColorOverLifetimeModule colorOverLifetime = ps.colorOverLifetime;
			snapshot.LifetimeEnabled = ((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled;
			snapshot.Lifetime = ((ColorOverLifetimeModule)(ref colorOverLifetime)).color;
			ColorBySpeedModule colorBySpeed = ps.colorBySpeed;
			snapshot.SpeedEnabled = ((ColorBySpeedModule)(ref colorBySpeed)).enabled;
			snapshot.Speed = ((ColorBySpeedModule)(ref colorBySpeed)).color;
			TrailModule trails = ps.trails;
			snapshot.TrailsEnabled = ((TrailModule)(ref trails)).enabled;
			if (((TrailModule)(ref trails)).enabled)
			{
				snapshot.TrailLifetime = ((TrailModule)(ref trails)).colorOverLifetime;
				snapshot.TrailOverTrail = ((TrailModule)(ref trails)).colorOverTrail;
			}
			ParticleSystemRenderer component = ((Component)ps).GetComponent<ParticleSystemRenderer>();
			if ((Object)(object)component != (Object)null)
			{
				snapshot.Renderer = component;
				snapshot.OriginalMaterial = ((Renderer)component).sharedMaterial;
				snapshot.TintProperty = FindRedDominantTintProperty(((Renderer)component).sharedMaterial);
			}
			return snapshot;
		}

		private static void ApplyColor(Snapshot snap)
		{
			//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_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_0022: 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_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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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_0067: 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_006d: 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_0085: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystem system = snap.System;
			if ((Object)(object)system == (Object)null)
			{
				return;
			}
			Color target = TargetFor(snap);
			MainModule main = system.main;
			((MainModule)(ref main)).startColor = Recolor(snap.StartColor, target);
			if (snap.LifetimeEnabled)
			{
				ColorOverLifetimeModule colorOverLifetime = system.colorOverLifetime;
				((ColorOverLifetimeModule)(ref colorOverLifetime)).color = Recolor(snap.Lifetime, target);
			}
			if (snap.SpeedEnabled)
			{
				ColorBySpeedModule colorBySpeed = system.colorBySpeed;
				((ColorBySpeedModule)(ref colorBySpeed)).color = Recolor(snap.Speed, target);
			}
			if (snap.TrailsEnabled)
			{
				TrailModule trails = system.trails;
				((TrailModule)(ref trails)).colorOverLifetime = Recolor(snap.TrailLifetime, target);
				((TrailModule)(ref trails)).colorOverTrail = Recolor(snap.TrailOverTrail, target);
			}
			if ((Object)(object)snap.Renderer != (Object)null && snap.TintProperty != null)
			{
				EnsurePrivateMaterial(snap);
				if ((Object)(object)snap.PrivateMaterial != (Object)null && snap.PrivateMaterial.HasProperty(snap.TintProperty))
				{
					Color val = (((Object)(object)snap.OriginalMaterial != (Object)null && snap.OriginalMaterial.HasProperty(snap.TintProperty)) ? snap.OriginalMaterial.GetColor(snap.TintProperty) : Color.white);
					snap.PrivateMaterial.SetColor(snap.TintProperty, new Color(1f, 1f, 1f, val.a));
				}
			}
		}

		private static void Restore(Snapshot snap)
		{
			//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_001b: 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_0033: 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_004a: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			ParticleSystem system = snap.System;
			if (!((Object)(object)system == (Object)null))
			{
				MainModule main = system.main;
				((MainModule)(ref main)).startColor = snap.StartColor;
				if (snap.LifetimeEnabled)
				{
					ColorOverLifetimeModule colorOverLifetime = system.colorOverLifetime;
					((ColorOverLifetimeModule)(ref colorOverLifetime)).color = snap.Lifetime;
				}
				if (snap.SpeedEnabled)
				{
					ColorBySpeedModule colorBySpeed = system.colorBySpeed;
					((ColorBySpeedModule)(ref colorBySpeed)).color = snap.Speed;
				}
				if (snap.TrailsEnabled)
				{
					TrailModule trails = system.trails;
					((TrailModule)(ref trails)).colorOverLifetime = snap.TrailLifetime;
					((TrailModule)(ref trails)).colorOverTrail = snap.TrailOverTrail;
				}
				if ((Object)(object)snap.Renderer != (Object)null && (Object)(object)snap.OriginalMaterial != (Object)null)
				{
					((Renderer)snap.Renderer).sharedMaterial = snap.OriginalMaterial;
				}
			}
		}

		private static void EnsurePrivateMaterial(Snapshot snap)
		{
			//IL_0032: 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_0057: Expected O, but got Unknown
			if (!((Object)(object)snap.PrivateMaterial != (Object)null) && !((Object)(object)snap.Renderer == (Object)null) && !((Object)(object)snap.OriginalMaterial == (Object)null))
			{
				snap.PrivateMaterial = new Material(snap.OriginalMaterial)
				{
					name = ((Object)snap.OriginalMaterial).name + " (UltraBlood)"
				};
				((Renderer)snap.Renderer).sharedMaterial = snap.PrivateMaterial;
			}
		}

		private static Color TargetFor(Snapshot snap)
		{
			//IL_0007: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (UltraBloodConfig.SolidCyanParticles)
			{
				return Color.cyan;
			}
			if (UltraBloodConfig.EnemyBloodColorMatch)
			{
				return (Color)(((??)snap.EnemyColor) ?? OpaqueBloodColor());
			}
			if (UltraBloodConfig.RainbowBlood)
			{
				return snap.RainbowColor;
			}
			return OpaqueBloodColor();
		}

		private static Color OpaqueBloodColor()
		{
			//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_000c: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			Color bloodColor = UltraBloodConfig.BloodColor;
			return new Color(bloodColor.r, bloodColor.g, bloodColor.b, 1f);
		}

		private static Color? EnemyColorFor(Bloodsplatter splatter)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (!UltraBloodConfig.EnemyBloodColorMatch)
			{
				return null;
			}
			if (BloodEnemies.TryGetValue(((Object)splatter).GetInstanceID(), out var value) && (Object)(object)value != (Object)null)
			{
				Color? val = SampleEnemyColor(value);
				LastEnemyColor = (Color)(((??)val) ?? OpaqueBloodColor());
				return val;
			}
			return null;
		}

		private static Color? SampleEnemyColor(EnemyIdentifier eid)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)eid).GetInstanceID();
			if (EnemyColors.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			Color? result = ComputeEnemyColor(eid);
			if (result.HasValue)
			{
				EnemyColors[instanceID] = result.Value;
			}
			return result;
		}

		private static Color? ComputeEnemyColor(EnemyIdentifier eid)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (EnemyPalette.TryGetValue(eid.enemyType, out var value))
				{
					return new Color(value.r, value.g, value.b, 1f);
				}
			}
			catch
			{
			}
			return null;
		}

		private static Color RandomVividColor()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			return Color.HSVToRGB(Random.value, Random.Range(0.7f, 1f), Random.Range(0.85f, 1f));
		}

		private static MinMaxGradient Recolor(MinMaxGradient original, Color target)
		{
			//IL_0002: 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_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected I4, but got Unknown
			//IL_0029: 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_002f: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_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_004e: 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_0054: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystemGradientMode mode = ((MinMaxGradient)(ref original)).mode;
			switch ((int)mode)
			{
			case 0:
				return new MinMaxGradient(RecolorSourceColor(((MinMaxGradient)(ref original)).color, target));
			case 2:
				return new MinMaxGradient(RecolorSourceColor(((MinMaxGradient)(ref original)).colorMin, target), RecolorSourceColor(((MinMaxGradient)(ref original)).colorMax, target));
			case 1:
				return new MinMaxGradient(RecolorGradientObject(((MinMaxGradient)(ref original)).gradient, target));
			case 3:
				return new MinMaxGradient(RecolorGradientObject(((MinMaxGradient)(ref original)).gradientMin, target), RecolorGradientObject(((MinMaxGradient)(ref original)).gradientMax, target));
			case 4:
			{
				MinMaxGradient result = default(MinMaxGradient);
				((MinMaxGradient)(ref result))..ctor(RecolorGradientObject(((MinMaxGradient)(ref original)).gradient, target));
				((MinMaxGradient)(ref result)).mode = (ParticleSystemGradientMode)4;
				return result;
			}
			default:
				return original;
			}
		}

		private static Gradient RecolorGradientObject(Gradient source, Color target)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			if (source == null)
			{
				return null;
			}
			GradientColorKey[] colorKeys = source.colorKeys;
			for (int i = 0; i < colorKeys.Length; i++)
			{
				colorKeys[i].color = RecolorSourceColor(colorKeys[i].color, target);
			}
			Gradient val = new Gradient();
			val.SetKeys(colorKeys, source.alphaKeys);
			val.mode = source.mode;
			return val;
		}

		private static Color RecolorSourceColor(Color source, Color target)
		{
			//IL_0000: 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_000c: 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_0025: 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_0035: 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)
			float num = Mathf.Max(source.r, Mathf.Max(source.g, source.b));
			return new Color(target.r * num, target.g * num, target.b * num, source.a);
		}

		private static bool IsExcludedVariant(BSType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			if ((int)type != 7 && (int)type != 8)
			{
				return (int)type == 6;
			}
			return true;
		}

		private static string FindRedDominantTintProperty(Material material)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0037: 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_004b: 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)
			if ((Object)(object)material == (Object)null)
			{
				return null;
			}
			string[] tintProperties = TintProperties;
			foreach (string text in tintProperties)
			{
				if (material.HasProperty(text))
				{
					Color color = material.GetColor(text);
					if (color.r > 0.25f && color.r > color.g * 1.5f && color.r > color.b * 1.5f)
					{
						return text;
					}
				}
			}
			return null;
		}

		private static void Prune()
		{
			if (Snapshots.Count == 0)
			{
				return;
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, Snapshot> snapshot in Snapshots)
			{
				if ((Object)(object)snapshot.Value.System == (Object)null)
				{
					list.Add(snapshot.Key);
				}
			}
			foreach (int item in list)
			{
				Snapshots.Remove(item);
			}
		}

		private static void LogOnce(ParticleSystem ps, GameObject root)
		{
			//IL_0036: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: 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_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			if (UltraBloodConfig.DebugLogging)
			{
				ParticleSystemRenderer component = ((Component)ps).GetComponent<ParticleSystemRenderer>();
				Material val = (((Object)(object)component != (Object)null) ? ((Renderer)component).sharedMaterial : null);
				Shader val2 = (((Object)(object)val != (Object)null) ? val.shader : null);
				MainModule main = ps.main;
				string text = HierarchyPath(((Component)ps).transform, root.transform);
				string item = string.Join("|", ((Object)root).name, text, ((Object)(object)val != (Object)null) ? ((Object)val).name : "no-mat", ((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "no-shader");
				if (LoggedFingerprints.Add(item))
				{
					ColorOverLifetimeModule colorOverLifetime = ps.colorOverLifetime;
					ColorBySpeedModule colorBySpeed = ps.colorBySpeed;
					TrailModule trails = ps.trails;
					ManualLogSource log = Plugin.Log;
					string[] obj = new string[11]
					{
						$"[particles] root='{((Object)root).name}' path='{text}' ps='{((Object)ps).name}' id={((Object)ps).GetInstanceID()} ",
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null
					};
					object[] array = new object[4];
					MinMaxGradient startColor = ((MainModule)(ref main)).startColor;
					array[0] = ((MinMaxGradient)(ref startColor)).mode;
					array[1] = ((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled;
					array[2] = ((ColorBySpeedModule)(ref colorBySpeed)).enabled;
					array[3] = ((TrailModule)(ref trails)).enabled;
					obj[1] = string.Format("startMode={0} colorOverLifetime={1} colorBySpeed={2} trails={3} ", array);
					obj[2] = "renderMode=";
					obj[3] = (((Object)(object)component != (Object)null) ? ((object)component.renderMode/*cast due to .constrained prefix*/).ToString() : "n/a");
					obj[4] = " material='";
					obj[5] = (((Object)(object)val != (Object)null) ? ((Object)val).name : "null");
					obj[6] = "' shader='";
					obj[7] = (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "null");
					obj[8] = "' redTintProperty='";
					obj[9] = FindRedDominantTintProperty(val) ?? "none";
					obj[10] = "'";
					log.LogInfo((object)string.Concat(obj));
				}
			}
		}

		private static string HierarchyPath(Transform node, Transform root)
		{
			if ((Object)(object)node == (Object)(object)root || (Object)(object)node == (Object)null)
			{
				if (!((Object)(object)node != (Object)null))
				{
					return "?";
				}
				return ((Object)node).name;
			}
			List<string> list = new List<string>();
			Transform val = node;
			int num = 0;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root && num++ < 32)
			{
				list.Insert(0, ((Object)val).name);
				val = val.parent;
			}
			list.Insert(0, ((Object)root).name);
			return string.Join("/", list);
		}

		private static int SafeSubEmitterCount(SubEmittersModule sub)
		{
			try
			{
				return ((SubEmittersModule)(ref sub)).enabled ? ((SubEmittersModule)(ref sub)).subEmittersCount : 0;
			}
			catch
			{
				return 0;
			}
		}

		private static ParticleSystem SafeGetSubEmitter(SubEmittersModule sub, int index)
		{
			try
			{
				return ((SubEmittersModule)(ref sub)).GetSubEmitterSystem(index);
			}
			catch
			{
				return null;
			}
		}
	}
	[HarmonyPatch]
	internal static class BloodPatches
	{
		[HarmonyPatch(typeof(BloodsplatterManager), "Start")]
		[HarmonyPostfix]
		private static void BloodsplatterManager_Start_Postfix(BloodsplatterManager __instance)
		{
			try
			{
				BloodColorController.ApplyToStainMaterials(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to recolor bloodstain materials: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(Bloodsplatter), "OnEnable")]
		[HarmonyPrefix]
		private static void Bloodsplatter_OnEnable_Prefix(Bloodsplatter __instance)
		{
			try
			{
				BloodParticles.Register(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to recolor blood splatter: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void Bloodsplatter_set_eid_Postfix(Bloodsplatter __instance, EnemyIdentifier value)
		{
			try
			{
				BloodParticles.NoteEnemy(__instance, value);
			}
			catch (Exception)
			{
			}
		}

		[HarmonyPatch(typeof(PostProcessV2_Handler), "OnPreRenderCallback")]
		[HarmonyPostfix]
		private static void PostProcessV2_Handler_OnPreRenderCallback_Postfix()
		{
			try
			{
				BloodColorController.NoteCompositeRenderPass();
			}
			catch (Exception)
			{
			}
		}
	}
	internal enum BundleCompatibilityResult
	{
		NotAttempted,
		FileMissing,
		Loaded,
		LoadReturnedNull,
		LoadThrew,
		AlreadyLoaded
	}
	internal static class CompatibilityTest
	{
		private struct BundleRow
		{
			public string Name;

			public string Path;

			public bool IsSbp;

			public bool IsGame;

			public bool FileExists;

			public long Length;

			public string Sha256;

			public BundleCompatibilityResult Result;

			public string[] AssetNames;

			public bool TextLoaded;

			public string ShaderName;

			public bool ShaderSupported;

			public int PassCount;

			public string UnityError;
		}

		private const string TestsFolderName = "CompatibilityTests";

		private const string CompositeShaderName = "UltraBlood/CompositeBloodTint";

		private static bool _ran;

		private static ManualLogSource _log;

		public static string Summary { get; private set; } = "not run";

		public static bool AnySbpLoaded { get; private set; }

		public static bool SbpCompositeShaderValid { get; private set; }

		public static void RunOnce(ManualLogSource logger, string pluginDirectory)
		{
			if (!_ran)
			{
				_ran = true;
				_log = logger ?? Plugin.Log;
				_log.LogInfo((object)"[compat] CompatibilityTest entry reached.");
				try
				{
					Run(pluginDirectory);
				}
				catch (Exception ex)
				{
					Summary = "error: " + ex.GetType().Name;
					_log.LogError((object)("[compat] compatibility test crashed: " + ex));
				}
				UltraBloodConfig.SetCompatStatus("Compatibility: " + Summary, AnySbpLoaded);
			}
		}

		private static void Run(string pluginDirectory)
		{
			string text = SafeAssemblyLocation();
			string text2 = ((!string.IsNullOrEmpty(pluginDirectory)) ? pluginDirectory : SafeDirectoryName(text));
			string text3 = (string.IsNullOrEmpty(text2) ? null : Path.Combine(text2, "CompatibilityTests"));
			_log.LogInfo((object)("[compat] current working directory: " + SafeCwd()));
			_log.LogInfo((object)("[compat] plugin assembly location: " + (text ?? "<unknown>")));
			_log.LogInfo((object)("[compat] plugin directory: " + (text2 ?? "<unknown>")));
			_log.LogInfo((object)("[compat] expected CompatibilityTests directory: " + (text3 ?? "<unknown>")));
			if (text3 == null)
			{
				Summary = "plugin directory could not be resolved";
				_log.LogWarning((object)"[compat] plugin directory could not be resolved; matrix not run");
				return;
			}
			bool flag = Directory.Exists(text3);
			_log.LogInfo((object)("[compat] directory exists: " + flag));
			if (!flag)
			{
				Summary = "CompatibilityTests directory missing";
				_log.LogWarning((object)("[compat] CompatibilityTests directory missing: " + text3));
				return;
			}
			string[] array = Directory.GetFiles(text3, "*.bundle", SearchOption.TopDirectoryOnly).OrderBy<string, string>(Path.GetFileName, StringComparer.OrdinalIgnoreCase).ToArray();
			_log.LogInfo((object)("[compat] number of files found: " + array.Length));
			string[] array2 = array;
			foreach (string path in array2)
			{
				_log.LogInfo((object)("[compat] discovered: " + Path.GetFileName(path)));
			}
			List<BundleRow> list = new List<BundleRow>();
			try
			{
				array2 = array;
				foreach (string path2 in array2)
				{
					list.Add(TestBundle(Path.GetFileName(path2), path2, isGame: false, unloadAllLoadedObjects: true));
				}
				BundleRow? bundleRow = TryGameControlRow();
				if (bundleRow.HasValue)
				{
					list.Add(bundleRow.Value);
				}
			}
			finally
			{
				PrintTable(list);
			}
			int legacyTotal = list.Count((BundleRow r) => !r.IsSbp && !r.IsGame);
			int legacyLoaded = list.Count((BundleRow r) => !r.IsSbp && !r.IsGame && r.Result == BundleCompatibilityResult.Loaded);
			int sbpTotal = list.Count((BundleRow r) => r.IsSbp);
			int num = list.Count((BundleRow r) => r.IsSbp && r.Result == BundleCompatibilityResult.Loaded);
			AnySbpLoaded = num > 0;
			SbpCompositeShaderValid = list.Any((BundleRow r) => r.IsSbp && r.ShaderName == "UltraBlood/CompositeBloodTint" && r.ShaderSupported && r.PassCount >= 2);
			LogResidentBundles();
			Summary = BuildSummary(legacyLoaded, legacyTotal, num, sbpTotal);
			_log.LogInfo((object)("[compat] summary: " + Summary));
		}

		private static string BuildSummary(int legacyLoaded, int legacyTotal, int sbpLoaded, int sbpTotal)
		{
			string text = $"legacy {legacyLoaded}/{legacyTotal} loaded";
			string text2 = $"SBP {sbpLoaded}/{sbpTotal} loaded";
			string text3 = (SbpCompositeShaderValid ? "SBP composite shader valid (passes>=2)" : (AnySbpLoaded ? "SBP composite shader NOT validated" : "no SBP bundle loaded"));
			return text + ", " + text2 + ", " + text3;
		}

		private static BundleRow? TryGameControlRow()
		{
			try
			{
				string path = Path.Combine(Application.dataPath, "StreamingAssets", "aa", "StandaloneWindows64");
				if (!Directory.Exists(path))
				{
					_log.LogInfo((object)"[compat] game bundle control: Addressables folder not found, skipped");
					return null;
				}
				string text = (from f in Directory.GetFiles(path, "*.bundle", SearchOption.AllDirectories)
					orderby new FileInfo(f).Length
					select f).FirstOrDefault();
				if (text == null)
				{
					_log.LogInfo((object)"[compat] game bundle control: no game *.bundle found, skipped");
					return null;
				}
				return TestBundle("GAME:" + Path.GetFileName(text), text, isGame: true, unloadAllLoadedObjects: false);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("[compat] game bundle control failed: " + ex.Message));
				return null;
			}
		}

		private static BundleRow TestBundle(string label, string path, bool isGame, bool unloadAllLoadedObjects)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			BundleRow bundleRow = new BundleRow
			{
				Name = label,
				Path = path,
				IsGame = isGame,
				IsSbp = label.StartsWith("sbp-", StringComparison.OrdinalIgnoreCase),
				Result = BundleCompatibilityResult.NotAttempted,
				Sha256 = "n/a",
				ShaderName = "-",
				PassCount = -1
			};
			bundleRow.FileExists = File.Exists(path);
			if (!bundleRow.FileExists)
			{
				bundleRow.Result = BundleCompatibilityResult.FileMissing;
				LogRowDetail(bundleRow);
				return bundleRow;
			}
			bundleRow.Length = new FileInfo(path).Length;
			if (!isGame)
			{
				bundleRow.Sha256 = Sha256File(path);
			}
			string captured = null;
			LogCallback val = (LogCallback)delegate(string condition, string stackTrace, LogType type)
			{
				//IL_0000: 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_0005: Invalid comparison between Unknown and I4
				if (((int)type == 0 || (int)type == 4) && condition != null && condition.IndexOf("AssetBundle", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					captured = condition.Trim();
				}
			};
			AssetBundle val2 = null;
			Application.logMessageReceived += val;
			try
			{
				val2 = AssetBundle.LoadFromFile(path);
			}
			catch (Exception ex)
			{
				bundleRow.UnityError = ex.Message.Trim();
				bundleRow.Result = ((ex.Message.IndexOf("already loaded", StringComparison.OrdinalIgnoreCase) >= 0) ? BundleCompatibilityResult.AlreadyLoaded : BundleCompatibilityResult.LoadThrew);
				LogRowDetail(bundleRow);
				return bundleRow;
			}
			finally
			{
				Application.logMessageReceived -= val;
			}
			if ((Object)(object)val2 == (Object)null)
			{
				bundleRow.Result = BundleCompatibilityResult.LoadReturnedNull;
				bundleRow.UnityError = captured;
				LogRowDetail(bundleRow);
				return bundleRow;
			}
			bundleRow.Result = BundleCompatibilityResult.Loaded;
			try
			{
				bundleRow.AssetNames = val2.GetAllAssetNames();
				TextAsset[] array = val2.LoadAllAssets<TextAsset>();
				bundleRow.TextLoaded = array != null && array.Length != 0;
				Shader val3 = val2.LoadAsset<Shader>("UltraBlood/CompositeBloodTint");
				if ((Object)(object)val3 == (Object)null)
				{
					Shader[] array2 = val2.LoadAllAssets<Shader>();
					val3 = ((array2 != null && array2.Length != 0) ? array2[0] : null);
				}
				if ((Object)(object)val3 != (Object)null)
				{
					bundleRow.ShaderName = ((Object)val3).name;
					bundleRow.ShaderSupported = val3.isSupported;
					bundleRow.PassCount = val3.passCount;
				}
			}
			catch (Exception ex2)
			{
				_log.LogWarning((object)("[compat] " + label + " inspection failed: " + ex2.GetType().Name + ": " + ex2.Message));
			}
			val2.Unload(unloadAllLoadedObjects);
			LogRowDetail(bundleRow);
			return bundleRow;
		}

		private static void LogRowDetail(BundleRow row)
		{
			string arg = ((row.AssetNames != null) ? string.Join(", ", row.AssetNames) : "-");
			_log.LogInfo((object)($"[compat]   {row.Name}: exists={row.FileExists} length={row.Length} sha256={row.Sha256} " + $"result={row.Result} assets=[{arg}] text={YesNo(row.TextLoaded, row.Result)} " + "shader='" + row.ShaderName + "' supported=" + SupportedText(row) + " passes=" + PassText(row) + (string.IsNullOrEmpty(row.UnityError) ? string.Empty : (" error='" + row.UnityError + "'"))));
		}

		private static void PrintTable(List<BundleRow> rows)
		{
			_log.LogInfo((object)"[compat] === matrix ===");
			_log.LogInfo((object)string.Format("[compat] {0,-38} {1,-17} {2,-5} {3,-32} {4,-9} {5}", "Bundle", "Load", "Text", "Shader", "Supported", "Passes"));
			foreach (BundleRow row in rows)
			{
				_log.LogInfo((object)$"[compat] {Trim(row.Name, 38),-38} {row.Result,-17} {YesNo(row.TextLoaded, row.Result),-5} {Trim(row.ShaderName, 32),-32} {SupportedText(row),-9} {PassText(row)}");
			}
			_log.LogInfo((object)"[compat] === end matrix ===");
		}

		private static void LogResidentBundles()
		{
			try
			{
				AssetBundle[] array = AssetBundle.GetAllLoadedAssetBundles().ToArray();
				_log.LogInfo((object)$"[compat] GetAllLoadedAssetBundles(): {array.Length} resident");
				foreach (AssetBundle item in array.Take(12))
				{
					_log.LogInfo((object)("[compat]   resident: " + SafeName(item)));
				}
				if (array.Length > 12)
				{
					_log.LogInfo((object)$"[compat]   ... and {array.Length - 12} more");
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("[compat] GetAllLoadedAssetBundles failed: " + ex.Message));
			}
		}

		private static string YesNo(bool value, BundleCompatibilityResult result)
		{
			if (result != BundleCompatibilityResult.Loaded)
			{
				return "-";
			}
			if (!value)
			{
				return "N";
			}
			return "Y";
		}

		private static string SupportedText(BundleRow row)
		{
			if (row.Result != BundleCompatibilityResult.Loaded || row.PassCount < 0)
			{
				return "-";
			}
			if (!row.ShaderSupported)
			{
				return "N";
			}
			return "Y";
		}

		private static string PassText(BundleRow row)
		{
			if (row.Result != BundleCompatibilityResult.Loaded || row.PassCount < 0)
			{
				return "-";
			}
			return row.PassCount.ToString();
		}

		private static string Trim(string value, int max)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			if (value.Length > max)
			{
				return value.Substring(0, max - 1) + "~";
			}
			return value;
		}

		private static string SafeName(AssetBundle bundle)
		{
			try
			{
				return ((Object)(object)bundle != (Object)null) ? ((Object)bundle).name : "<null>";
			}
			catch
			{
				return "<unavailable>";
			}
		}

		private static string SafeCwd()
		{
			try
			{
				return Directory.GetCurrentDirectory();
			}
			catch
			{
				return "<unavailable>";
			}
		}

		private static string SafeAssemblyLocation()
		{
			try
			{
				return Assembly.GetExecutingAssembly().Location;
			}
			catch
			{
				return null;
			}
		}

		private static string SafeDirectoryName(string path)
		{
			try
			{
				return string.IsNullOrEmpty(path) ? null : Path.GetDirectoryName(path);
			}
			catch
			{
				return null;
			}
		}

		private static string Sha256File(string path)
		{
			try
			{
				using FileStream inputStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
				using SHA256 sHA = SHA256.Create();
				byte[] array = sHA.ComputeHash(inputStream);
				StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
				byte[] array2 = array;
				foreach (byte b in array2)
				{
					stringBuilder.Append(b.ToString("X2"));
				}
				return stringBuilder.ToString();
			}
			catch
			{
				return "n/a";
			}
		}
	}
	[BepInPlugin("ultrablood.kiyroprower.ultrakill", "UltraBlood", "0.2.1")]
	[BepInProcess("ULTRAKILL.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "ultrablood.kiyroprower.ultrakill";

		public const string Version = "0.2.1";

		public const string PluginConfiguratorGuid = "com.eternalUnion.pluginConfigurator";

		internal static ManualLogSource Log;

		internal static Plugin Instance;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			LogRuntimeEnvironment();
			UltraBloodConfig.Initialize(((BaseUnityPlugin)this).Logger);
			string pluginDirectory = GetPluginDirectory();
			try
			{
				CompatibilityTest.RunOnce(((BaseUnityPlugin)this).Logger, pluginDirectory);
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[compat] Compatibility test crashed: {arg}");
			}
			_harmony = new Harmony("ultrablood.kiyroprower.ultrakill");
			_harmony.PatchAll(typeof(BloodPatches));
			Log.LogInfo((object)("UltraBlood loaded (ultrablood.kiyroprower.ultrakill). Recoloring is " + (UltraBloodConfig.Enabled ? "on" : "off") + "."));
		}

		private static void LogRuntimeEnvironment()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			Log.LogInfo((object)($"[runtime] Unity={Application.unityVersion}, Platform={Application.platform}, " + "Product=" + Application.productName + ", GameVersion=" + Application.version + ", " + $"ProcessBits={IntPtr.Size * 8}"));
			Log.LogInfo((object)($"[runtime] OS={SystemInfo.operatingSystem}, GraphicsAPI={SystemInfo.graphicsDeviceType}, " + "GPU=" + SystemInfo.graphicsDeviceName + ", GfxVersion=" + SystemInfo.graphicsDeviceVersion + ", " + $"ShaderLevel={SystemInfo.graphicsShaderLevel}, Instancing={SystemInfo.supportsInstancing}"));
			LogHostBinary();
		}

		private static void LogHostBinary()
		{
			try
			{
				string text = Directory.GetParent(Application.dataPath)?.FullName;
				if (text != null)
				{
					string text2 = Path.Combine(text, "UnityPlayer.dll");
					if (!File.Exists(text2))
					{
						Log.LogInfo((object)"[runtime] UnityPlayer.dll not found next to the game");
						return;
					}
					FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(text2);
					string text3 = Sha256File(text2);
					Log.LogInfo((object)("[runtime] UnityPlayer.dll ProductVersion=" + versionInfo.ProductVersion + ", FileVersion=" + versionInfo.FileVersion + ", sha256=" + text3));
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[runtime] UnityPlayer.dll inspection failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static string Sha256File(string path)
		{
			using FileStream inputStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(inputStream);
			StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("X2"));
			}
			return stringBuilder.ToString();
		}

		private static string GetPluginDirectory()
		{
			try
			{
				return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			}
			catch
			{
				return null;
			}
		}

		internal static void RunDelayed(GameObject target, Action action)
		{
			if (!((Object)(object)Instance == (Object)null) && !((Object)(object)target == (Object)null) && action != null)
			{
				((MonoBehaviour)Instance).StartCoroutine(DelayedRoutine(target, action));
			}
		}

		private static IEnumerator DelayedRoutine(GameObject target, Action action)
		{
			yield return null;
			yield return (object)new WaitForSeconds(0.1f);
			if ((Object)(object)target == (Object)null)
			{
				yield break;
			}
			try
			{
				action();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[particles] delayed rescan failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private void Update()
		{
			if (UltraBloodConfig.Enabled && UltraBloodConfig.RecolorPuddles && !UltraBloodConfig.SolidCyanTest && (UltraBloodConfig.RainbowBlood || UltraBloodConfig.EnemyBloodColorMatch))
			{
				BloodColorController.TickDynamicColor();
			}
		}

		private void OnDestroy()
		{
			BloodColorController.RestoreStainMaterials();
			BloodParticles.RestoreAll();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal enum BundleStatus
	{
		NotAttempted,
		Loaded,
		EmbeddedBundleResourceMissing,
		MultipleEmbeddedBundlesFound,
		BundleLoadFailed,
		ShaderNotFound,
		ShaderUnsupported,
		ShaderPassCountInvalid
	}
	internal static class ShaderBundle
	{
		private const string BundleResourceName = "UltraBlood.Resources.ultrablood-sbp.bundle";

		private const string ManifestResourceName = "UltraBlood.Resources.ultrablood-sbp.manifest.json";

		private const string ShaderName = "UltraBlood/CompositeBloodTint";

		private const string ProbeAssetName = "UltraBloodBundleProbe";

		private const string CompatibilityTestsFolder = "CompatibilityTests";

		private const string ExternalCompositeBundleName = "sbp-probe-composite.bundle";

		private const string RuntimeBundleFileName = "ultrablood-sbp.runtime.bundle";

		private static bool _attempted;

		private static AssetBundle _bundle;

		private static Shader _compositeShader;

		public static BundleStatus Status { get; private set; } = BundleStatus.NotAttempted;

		public static string StatusMessage { get; private set; } = "not attempted";

		public static string Source { get; private set; } = "none";

		public static Shader CompositeShader
		{
			get
			{
				if (!_attempted)
				{
					_attempted = true;
					try
					{
						Load();
					}
					catch (Exception ex)
					{
						Fail(BundleStatus.BundleLoadFailed, "exception: " + ex.GetType().Name + ": " + ex.Message);
						Plugin.Log.LogError((object)("[bundle] " + ex));
					}
				}
				return _compositeShader;
			}
		}

		private static void Load()
		{
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			Plugin.Log.LogInfo((object)("[bundle] assembly='" + executingAssembly.GetName().Name + "' location='" + executingAssembly.Location + "'"));
			string[] array = (from n in executingAssembly.GetManifestResourceNames()
				where n.IndexOf("ultrablood", StringComparison.OrdinalIgnoreCase) >= 0 && n.EndsWith(".bundle", StringComparison.OrdinalIgnoreCase)
				select n).ToArray();
			string[] array2 = array;
			foreach (string text in array2)
			{
				Plugin.Log.LogInfo((object)("[bundle] embedded bundle resource: " + text));
			}
			if (array.Length == 0)
			{
				Fail(BundleStatus.EmbeddedBundleResourceMissing, "no embedded *.bundle resource found");
				return;
			}
			if (array.Length > 1)
			{
				Fail(BundleStatus.MultipleEmbeddedBundlesFound, "multiple embedded bundle resources: " + string.Join(", ", array));
				return;
			}
			byte[] array3 = ReadResource(executingAssembly, "UltraBlood.Resources.ultrablood-sbp.bundle");
			if (array3 == null)
			{
				Fail(BundleStatus.EmbeddedBundleResourceMissing, "bundle resource stream missing: UltraBlood.Resources.ultrablood-sbp.bundle");
				return;
			}
			string text2 = Sha256Hex(array3);
			string arg = Encoding.ASCII.GetString(array3, 0, Math.Min(24, array3.Length)).Replace("\0", ".");
			Plugin.Log.LogInfo((object)$"[bundle] embedded bytes={array3.Length} sha256={text2} header='{arg}'");
			LogManifest(executingAssembly, array3.Length, text2);
			_bundle = LoadBundle(array3);
			if ((Object)(object)_bundle == (Object)null)
			{
				Fail(BundleStatus.BundleLoadFailed, "all load methods returned null");
				return;
			}
			string[] allAssetNames = _bundle.GetAllAssetNames();
			Plugin.Log.LogInfo((object)("[bundle] contains assets: " + string.Join(", ", allAssetNames)));
			TextAsset val = LoadFirst<TextAsset>(_bundle, "UltraBloodBundleProbe");
			Plugin.Log.LogInfo((object)(((Object)(object)val != (Object)null) ? ("[bundle] probe TextAsset loaded: \"" + val.text.Trim() + "\"") : "[bundle] probe TextAsset not found"));
			_compositeShader = LoadFirst<Shader>(_bundle, "UltraBlood/CompositeBloodTint");
			if ((Object)(object)_compositeShader == (Object)null)
			{
				Fail(BundleStatus.ShaderNotFound, "composite shader not found in bundle");
				return;
			}
			if (!_compositeShader.isSupported)
			{
				Fail(BundleStatus.ShaderUnsupported, $"shader '{((Object)_compositeShader).name}' is not supported on {SystemInfo.graphicsDeviceType}");
				return;
			}
			if (_compositeShader.passCount <= 1)
			{
				Fail(BundleStatus.ShaderPassCountInvalid, $"shader '{((Object)_compositeShader).name}' has passCount={_compositeShader.passCount}; the composite draws pass index 1");
				return;
			}
			Status = BundleStatus.Loaded;
			StatusMessage = "loaded from " + Source;
			Plugin.Log.LogInfo((object)$"[bundle] shader '{((Object)_compositeShader).name}' loaded from {Source}, supported=true, passCount={_compositeShader.passCount}");
		}

		private static AssetBundle LoadBundle(byte[] bytes)
		{
			string text = null;
			try
			{
				text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			}
			catch
			{
			}
			if (text != null)
			{
				string text2 = Path.Combine(text, "CompatibilityTests", "sbp-probe-composite.bundle");
				if (File.Exists(text2))
				{
					try
					{
						byte[] array = File.ReadAllBytes(text2);
						Plugin.Log.LogInfo((object)$"[bundle] external SBP bundle '{text2}' bytes={array.Length} sha256={Sha256Hex(array)}");
						AssetBundle val = AssetBundle.LoadFromFile(text2);
						Plugin.Log.LogInfo((object)("[bundle] external SBP LoadFromFile result: " + (((Object)(object)val != (Object)null) ? "loaded" : "null")));
						if ((Object)(object)val != (Object)null)
						{
							Source = "external:sbp-probe-composite.bundle";
							return val;
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("[bundle] external SBP load failed: " + ex.GetType().Name + ": " + ex.Message));
					}
				}
			}
			if (text != null)
			{
				try
				{
					string text3 = Path.Combine(text, "ultrablood-sbp.runtime.bundle");
					File.WriteAllBytes(text3, bytes);
					AssetBundle val2 = AssetBundle.LoadFromFile(text3);
					Plugin.Log.LogInfo((object)("[bundle] embedded SBP ->file LoadFromFile result: " + (((Object)(object)val2 != (Object)null) ? "loaded" : "null")));
					if ((Object)(object)val2 != (Object)null)
					{
						Source = "embedded-file";
						return val2;
					}
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("[bundle] embedded->file load failed: " + ex2.GetType().Name + ": " + ex2.Message));
				}
			}
			AssetBundle val3 = AssetBundle.LoadFromMemory(bytes);
			Plugin.Log.LogInfo((object)("[bundle] embedded SBP LoadFromMemory result: " + (((Object)(object)val3 != (Object)null) ? "loaded" : "null")));
			if ((Object)(object)val3 != (Object)null)
			{
				Source = "embedded-memory";
			}
			return val3;
		}

		private static T LoadFirst<T>(AssetBundle bundle, string preferredName) where T : Object
		{
			T val = bundle.LoadAsset<T>(preferredName);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			T[] array = bundle.LoadAllAssets<T>();
			if (array == null || array.Length == 0)
			{
				return default(T);
			}
			return array[0];
		}

		private static void LogManifest(Assembly assembly, int actualLength, string actualHash)
		{
			byte[] array = ReadResource(assembly, "UltraBlood.Resources.ultrablood-sbp.manifest.json");
			if (array == null)
			{
				Plugin.Log.LogWarning((object)"[bundle] embedded manifest missing: UltraBlood.Resources.ultrablood-sbp.manifest.json");
				return;
			}
			string text = Encoding.UTF8.GetString(array);
			Plugin.Log.LogInfo((object)("[bundle] build manifest: " + text.Replace("\r", string.Empty).Replace("\n", " ")));
			string text2 = ExtractJsonString(text, "sha256");
			if (!string.IsNullOrEmpty(text2))
			{
				bool flag = string.Equals(text2, actualHash, StringComparison.OrdinalIgnoreCase);
				Plugin.Log.LogInfo((object)("[bundle] manifest sha256 " + (flag ? "matches" : "DOES NOT MATCH") + " embedded bytes (manifest=" + text2 + ", actual=" + actualHash + ")"));
			}
			string text3 = ExtractJsonString(text, "unityEditorVersion");
			if (!string.IsNullOrEmpty(text3))
			{
				Plugin.Log.LogInfo((object)("[bundle] built with Unity " + text3 + "; runtime is Unity " + Application.unityVersion));
			}
		}

		private static string ExtractJsonString(string json, string key)
		{
			Match match = Regex.Match(json, "\"" + Regex.Escape(key) + "\"\\s*:\\s*\"([^\"]*)\"");
			if (!match.Success)
			{
				return null;
			}
			return match.Groups[1].Value;
		}

		private static byte[] ReadResource(Assembly assembly, string name)
		{
			using Stream stream = assembly.GetManifestResourceStream(name);
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static string Sha256Hex(byte[] data)
		{
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(data);
			StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("X2"));
			}
			return stringBuilder.ToString();
		}

		private static void Fail(BundleStatus status, string message)
		{
			Status = status;
			StatusMessage = message;
			Plugin.Log.LogError((object)$"[bundle] {status}: {message}");
		}
	}
	internal static class UltraBloodConfig
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static PostBoolValueChangeEvent <>9__39_0;

			public static PostColorValueChangeEvent <>9__39_1;

			public static PostBoolValueChangeEvent <>9__39_2;

			public static PostBoolValueChangeEvent <>9__39_3;

			public static PostBoolValueChangeEvent <>9__39_4;

			public static PostBoolValueChangeEvent <>9__39_5;

			public static PostFloatValueChangeEvent <>9__39_6;

			public static PostBoolValueChangeEvent <>9__39_7;

			public static PostBoolValueChangeEvent <>9__39_8;

			public static PostBoolValueChangeEvent <>9__39_9;

			internal void <CreateMenu>b__39_0(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_1(Color _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_2(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_3(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_4(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_5(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_6(float _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_7(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_8(bool _)
			{
				ScheduleReapply();
			}

			internal void <CreateMenu>b__39_9(bool _)
			{
				UpdateInteractivity();
			}
		}

		public static readonly Color DefaultBloodColor = new Color(0.5f, 0f, 0f);

		public const float DefaultPuddleOpacity = 0.65f;

		private static PluginConfigurator _configurator;

		private static ManualLogSource _log;

		private static BoolField _enabledField;

		private static ColorField _bloodColorField;

		private static BoolField _rainbowField;

		private static BoolField _enemyMatchField;

		private static BoolField _recolorSplattersField;

		private static BoolField _recolorPuddlesField;

		private static FloatField _puddleOpacityField;

		private static BoolField _debugLoggingField;

		private static BoolField _solidCyanTestField;

		private static BoolField _solidCyanParticlesField;

		private static ConfigHeader _puddleStatusHeader;

		private static ConfigHeader _compatStatusHeader;

		private static bool _reconciling;

		private static readonly Color TitleColor = new Color(1f, 0.3f, 0.3f);

		private static readonly Color EnabledColor = new Color(0.86f, 0.4f, 0.4f);

		public static bool Enabled
		{
			get
			{
				if (_enabledField != null)
				{
					return _enabledField.value;
				}
				return true;
			}
		}

		public static Color BloodColor
		{
			get
			{
				//IL_0012: 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)
				if (_bloodColorField != null)
				{
					return _bloodColorField.value;
				}
				return DefaultBloodColor;
			}
		}

		public static bool RainbowBlood
		{
			get
			{
				if (_rainbowField != null)
				{
					return _rainbowField.value;
				}
				return false;
			}
		}

		public static bool EnemyBloodColorMatch
		{
			get
			{
				if (_enemyMatchField != null)
				{
					return _enemyMatchField.value;
				}
				return false;
			}
		}

		public static bool RecolorSplatters
		{
			get
			{
				if (_recolorSplattersField != null)
				{
					return _recolorSplattersField.value;
				}
				return true;
			}
		}

		public static bool RecolorPuddles
		{
			get
			{
				if (_recolorPuddlesField != null)
				{
					return _recolorPuddlesField.value;
				}
				return true;
			}
		}

		public static float PuddleOpacity
		{
			get
			{
				if (_puddleOpacityField != null)
				{
					return Mathf.Clamp01(_puddleOpacityField.value);
				}
				return 0.65f;
			}
		}

		public static bool DebugLogging
		{
			get
			{
				if (_debugLoggingField != null)
				{
					return _debugLoggingField.value;
				}
				return false;
			}
		}

		public static bool SolidCyanTest
		{
			get
			{
				if (_solidCyanTestField != null)
				{
					return _solidCyanTestField.value;
				}
				return false;
			}
		}

		public static bool SolidCyanParticles
		{
			get
			{
				if (_solidCyanParticlesField != null)
				{
					return _solidCyanParticlesField.value;
				}
				return false;
			}
		}

		public static void SetPuddleStatus(string text, bool ok)
		{
			//IL_0040: 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)
			if (_puddleStatusHeader != null)
			{
				_puddleStatusHeader.text = text;
				_puddleStatusHeader.textColor = (ok ? new Color(0.56f, 0.86f, 0.56f) : new Color(1f, 0.55f, 0f));
			}
		}

		public static void SetCompatStatus(string text, bool ok)
		{
			//IL_0031: 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)
			if (_compatStatusHeader != null)
			{
				_compatStatusHeader.text = text;
				_compatStatusHeader.textColor = (Color)(ok ? new Color(0.56f, 0.86f, 0.56f) : Color.gray);
			}
		}

		public static void Initialize(ManualLogSource log)
		{
			_log = log;
			try
			{
				CreateMenu();
				_log.LogInfo((object)"Config menu initialized.");
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("PluginConfigurator menu could not be initialized: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void CreateMenu()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//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_00b5: Expected O, but got Unknown
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Expected O, but got Unknown
			//IL_014e: 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_0159: Expected O, but got Unknown
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Expected O, but got Unknown
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Expected O, but got Unknown
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Expected O, but got Unknown
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Expected O, but got Unknown
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Expected O, but got Unknown
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Expected O, but got Unknown
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Expected O, but got Unknown
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_036a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Expected O, but got Unknown
			if (_configurator != null)
			{
				return;
			}
			_configurator = PluginConfigurator.Create("UltraBlood", "ultrablood.kiyroprower.ultrakill");
			ConfigPanel rootPanel = _configurator.rootPanel;
			_enabledField = new BoolField(rootPanel, "Enabled", "ultrablood.enabled", true, true);
			BoolField enabledField = _enabledField;
			object obj = <>c.<>9__39_0;
			if (obj == null)
			{
				PostBoolValueChangeEvent val = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_0 = val;
				obj = (object)val;
			}
			enabledField.postValueChangeEvent += (PostBoolValueChangeEvent)obj;
			AddDescription(rootPanel, "Recolor blood using the color below. Turn off to restore vanilla blood.", EnabledColor);
			_bloodColorField = new ColorField(rootPanel, "Blood Color", "ultrablood.color", DefaultBloodColor);
			ColorField bloodColorField = _bloodColorField;
			object obj2 = <>c.<>9__39_1;
			if (obj2 == null)
			{
				PostColorValueChangeEvent val2 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_1 = val2;
				obj2 = (object)val2;
			}
			bloodColorField.postValueChangeEvent += (PostColorValueChangeEvent)obj2;
			AddDescription(rootPanel, "The color applied to blood. Its alpha is not used; puddle transparency is set by Puddle Opacity below.");
			AddTitle(rootPanel, "Color Mode");
			_rainbowField = new BoolField(rootPanel, "Rainbow Blood", "ultrablood.rainbow", false, true);
			BoolField rainbowField = _rainbowField;
			object obj3 = <>c.<>9__39_2;
			if (obj3 == null)
			{
				PostBoolValueChangeEvent val3 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_2 = val3;
				obj3 = (object)val3;
			}
			rainbowField.postValueChangeEvent += (PostBoolValueChangeEvent)obj3;
			AddDescription(rootPanel, "Give each blood particle system a random color, and cycle puddle color through the rainbow. Replaces the Blood Color. Cannot be used with Enemy Blood Color Match.");
			_enemyMatchField = new BoolField(rootPanel, "Enemy Blood Color Match", "ultrablood.enemyMatch", false, true);
			BoolField enemyMatchField = _enemyMatchField;
			object obj4 = <>c.<>9__39_3;
			if (obj4 == null)
			{
				PostBoolValueChangeEvent val4 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_3 = val4;
				obj4 = (object)val4;
			}
			enemyMatchField.postValueChangeEvent += (PostBoolValueChangeEvent)obj4;
			AddDescription(rootPanel, "Experimental. Recolor each enemy's blood to a curated color for that enemy. Puddles follow the last enemy that bled. Unmapped enemies use the Blood Color. Cannot be used with Rainbow Blood.");
			AddTitle(rootPanel, "What To Recolor");
			_recolorSplattersField = new BoolField(rootPanel, "Recolor Splatters", "ultrablood.splatters", true, true);
			BoolField recolorSplattersField = _recolorSplattersField;
			object obj5 = <>c.<>9__39_4;
			if (obj5 == null)
			{
				PostBoolValueChangeEvent val5 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_4 = val5;
				obj5 = (object)val5;
			}
			recolorSplattersField.postValueChangeEvent += (PostBoolValueChangeEvent)obj5;
			AddDescription(rootPanel, "Tint the blood spray particles that burst from enemies. Sand, gold, and underwater variants are left alone.");
			_recolorPuddlesField = new BoolField(rootPanel, "Recolor Puddles", "ultrablood.puddles", true, true);
			BoolField recolorPuddlesField = _recolorPuddlesField;
			object obj6 = <>c.<>9__39_5;
			if (obj6 == null)
			{
				PostBoolValueChangeEvent val6 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_5 = val6;
				obj6 = (object)val6;
			}
			recolorPuddlesField.postValueChangeEvent += (PostBoolValueChangeEvent)obj6;
			AddDescription(rootPanel, "Tint the bloodstains that pool on floors and walls.");
			_puddleOpacityField = new FloatField(rootPanel, "Puddle Opacity", "ultrablood.puddleOpacity", 0.65f, 0f, 1f);
			FloatField puddleOpacityField = _puddleOpacityField;
			object obj7 = <>c.<>9__39_6;
			if (obj7 == null)
			{
				PostFloatValueChangeEvent val7 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_6 = val7;
				obj7 = (object)val7;
			}
			puddleOpacityField.postValueChangeEvent += (PostFloatValueChangeEvent)obj7;
			AddDescription(rootPanel, "How opaque recolored puddles are. Lower is more see-through, closer to vanilla blood. 1 is fully opaque.");
			_puddleStatusHeader = AddDescription(rootPanel, "Puddle recoloring status is reported here once in a level.", Color.gray);
			AddTitle(rootPanel, "Diagnostics");
			_compatStatusHeader = AddDescription(rootPanel, "Bundle compatibility test: not run.", Color.gray);
			_solidCyanTestField = new BoolField(rootPanel, "Solid Cyan Test (Puddles)", "ultrablood.solidCyan", false, true);
			BoolField solidCyanTestField = _solidCyanTestField;
			object obj8 = <>c.<>9__39_7;
			if (obj8 == null)
			{
				PostBoolValueChangeEvent val8 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_7 = val8;
				obj8 = (object)val8;
			}
			solidCyanTestField.postValueChangeEvent += (PostBoolValueChangeEvent)obj8;
			AddDescription(rootPanel, "Force puddles to solid cyan, ignoring the color above. If puddles turn cyan the replacement shader is working. If they stay red it is not active.");
			_solidCyanParticlesField = new BoolField(rootPanel, "Solid Cyan Test (Impact Particles)", "ultrablood.solidCyanParticles", false, true);
			BoolField solidCyanParticlesField = _solidCyanParticlesField;
			object obj9 = <>c.<>9__39_8;
			if (obj9 == null)
			{
				PostBoolValueChangeEvent val9 = delegate
				{
					ScheduleReapply();
				};
				<>c.<>9__39_8 = val9;
				obj9 = (object)val9;
			}
			solidCyanParticlesField.postValueChangeEvent += (PostBoolValueChangeEvent)obj9;
			AddDescription(rootPanel, "Force blood impact particles to solid cyan. If the previously dark-red particles turn cyan they are being recolored. If they stay red another effect path remains.");
			_debugLoggingField = new BoolField(rootPanel, "Debug Logging", "ultrablood.debug", false, true);
			BoolField debugLoggingField = _debugLoggingField;
			object obj10 = <>c.<>9__39_9;
			if (obj10 == null)
			{
				PostBoolValueChangeEvent val10 = delegate
				{
					UpdateInteractivity();
				};
				<>c.<>9__39_9 = val10;
				obj10 = (object)val10;
			}
			debugLoggingField.postValueChangeEvent += (PostBoolValueChangeEvent)obj10;
			AddDescription(rootPanel, "Log the runtime environment, shader bundle loading, and material state to the BepInEx console.");
			ReconcileModes();
			UpdateInteractivity();
		}

		private static void UpdateInteractivity()
		{
			try
			{
				bool enabled = Enabled;
				bool flag = SolidCyanTest || SolidCyanParticles;
				bool enemyBloodColorMatch = EnemyBloodColorMatch;
				bool rainbowBlood = RainbowBlood;
				SetInteractable((ConfigField)(object)_bloodColorField, enabled && !flag && !enemyBloodColorMatch && !rainbowBlood);
				SetInteractable((ConfigField)(object)_rainbowField, enabled && !flag && !enemyBloodColorMatch);
				SetInteractable((ConfigField)(object)_enemyMatchField, enabled && !flag && !rainbowBlood);
				SetInteractable((ConfigField)(object)_solidCyanTestField, enabled);
				SetInteractable((ConfigField)(object)_solidCyanParticlesField, enabled);
				SetInteractable((ConfigField)(object)_recolorSplattersField, enabled);
				SetInteractable((ConfigField)(object)_recolorPuddlesField, enabled);
				SetInteractable((ConfigField)(object)_puddleOpacityField, enabled);
				SetInteractable((ConfigField)(object)_debugLoggingField, enabled);
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to update config interactivity: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		private static void SetInteractable(ConfigField field, bool value)
		{
			if (field != null && field.interactable != value)
			{
				field.interactable = value;
			}
		}

		private static void ScheduleReapply()
		{
			ReconcileModes();
			UpdateInteractivity();
			BloodColorController.Refresh();
			BloodParticles.ReapplyAll();
		}

		private static void ReconcileModes()
		{
			if (_reconciling || _rainbowField == null || _enemyMatchField == null || !_enemyMatchField.value || !_rainbowField.value)
			{
				return;
			}
			_reconciling = true;
			try
			{
				_rainbowField.value = false;
			}
			finally
			{
				_reconciling = false;
			}
		}

		private static ConfigHeader AddTitle(ConfigPanel panel, string text)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			return new ConfigHeader(panel, text, 18)
			{
				textColor = TitleColor
			};
		}

		private static ConfigHeader AddDescription(ConfigPanel panel, string text)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			return AddDescription(panel, text, Color.gray);
		}

		private static ConfigHeader AddDescription(ConfigPanel panel, string text, Color color)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			return new ConfigHeader(panel, text, 14)
			{
				textColor = color
			};
		}
	}
}