Decompiled source of KillableSpitters v1.1.0

KillableSpitters.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using AIGraph;
using AK;
using Agents;
using AmorLib.Networking.StateReplicators;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using EWC.CustomWeapon.Enums;
using EWC.CustomWeapon.Properties.Effects;
using EWC.CustomWeapon.Properties.Effects.Hit.Explosion;
using EWC.CustomWeapon.Properties.Shared.Triggers;
using EWC.CustomWeapon.WeaponContext.Contexts;
using EWC.CustomWeapon.WeaponContext.Contexts.Base;
using Enemies;
using FX_EffectSystem;
using GTFO.API;
using GTFO.API.Resources;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppInterop.Runtime.Runtime;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo;
using Il2CppSystem.Collections.Generic;
using KillableSpitters.Events;
using KillableSpitters.Patches;
using KillableSpitters.Patches.Compat;
using KillableSpitters.Patches.Spitters;
using Microsoft.CodeAnalysis;
using Player;
using PlayerCoverage;
using SNetwork;
using SemanticVersioning;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("KillableSpitters")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+eb84f64f9a4fc43941ac49a3e4a7530eeb0bb19d")]
[assembly: AssemblyProduct("KillableSpitters")]
[assembly: AssemblyTitle("KillableSpitters")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace KillableSpitters
{
	[BepInPlugin("the_tavern-KillableSpitters", "KillableSpitters", "1.1.0")]
	[BepInProcess("GTFO.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BasePlugin
	{
		public const string Version = "1.1.0";

		public const string Name = "the_tavern-KillableSpitters";

		public static ManualLogSource Logger { get; private set; } = new ManualLogSource("KillableSpitters");

		public static float Config_SpitterHealth { get; set; }

		public static float Config_SpitterFreezeDuration { get; set; }

		public static bool Config_CfoamKillsSpitters { get; set; }

		public override void Load()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_003a: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_006a: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_0096: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			ConfigEntry<float> val = ((BasePlugin)this).Config.Bind<float>(new ConfigDefinition("General", "SpitterHealth"), 30f, new ConfigDescription("Health pool for killable spitters. Only the lobby host's value applies.", (AcceptableValueBase)null, Array.Empty<object>()));
			ConfigEntry<float> val2 = ((BasePlugin)this).Config.Bind<float>(new ConfigDefinition("C-Foam", "SpitterFreezeDuration"), 0.7f, new ConfigDescription("How many seconds a C-foamed spitter stays frozen before it dies if CfoamKillsSpitters is on, otherwise before it thaws back to normal. Only the lobby host's value applies to the kill timing.\nNote: Base GTFO freezes spitters for 240 seconds.", (AcceptableValueBase)null, Array.Empty<object>()));
			ConfigEntry<bool> obj = ((BasePlugin)this).Config.Bind<bool>(new ConfigDefinition("C-Foam", "CfoamKillsSpitters"), true, new ConfigDescription("Whether C-foam kills spitters. Off keeps the vanilla freeze-only behavior. Only the lobby host's value applies.", (AcceptableValueBase)null, Array.Empty<object>()));
			Config_SpitterHealth = val.Value;
			Config_SpitterFreezeDuration = val2.Value;
			Config_CfoamKillsSpitters = obj.Value;
			((BasePlugin)this).Config.Save();
			GameDataAPI.OnGameDataInitialized += SpitterKillManager.Setup;
			Patch_SpitterDamage.LogDamageForwarderFoldState();
			Fix_SpitterHealthRel.Install();
			Harmony val3 = new Harmony("the_tavern-KillableSpitters");
			val3.PatchAll();
			EWCCompat.Init(val3);
		}
	}
}
namespace KillableSpitters.Patches
{
	[HarmonyPatch]
	internal static class Fix_SpitterBotAggro
	{
		private static bool _broken;

		[HarmonyPatch(typeof(InfectionSpitter), "ManagerUpdate")]
		[HarmonyPrefix]
		public static bool Pre_ManagerUpdate(InfectionSpitter __instance, AIG_CourseNode courseNode, ref bool __result)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			if (SpitterKillManager.ShouldBlockManagerUpdate(__instance))
			{
				__result = false;
				return false;
			}
			if (_broken)
			{
				return true;
			}
			try
			{
				__result = ManagerUpdateNoBots(__instance, courseNode);
				return false;
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(74, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterBotAggro] Reimplementation failed, reverting to vanilla behavior: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
				return true;
			}
		}

		private static bool ManagerUpdateNoBots(InfectionSpitter s, AIG_CourseNode courseNode)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Invalid comparison between Unknown and I4
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Invalid comparison between Unknown and I4
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			float num = Clock.Time - s.m_lastUpdate;
			s.m_lastUpdate = Clock.Time;
			s.m_freezeTime = s.m_lastUpdate + 30f;
			bool result = false;
			bool flag = (int)s.m_currentState == 2;
			bool flag2 = false;
			float num2 = 8.5f;
			float num3 = 0f;
			Il2CppReferenceArray<PlayerCoverageData> coverageDatas = courseNode.m_playerCoverage.m_coverageDatas;
			Enumerator<PlayerAgent> enumerator = PlayerManager.PlayerAgentsInLevel.GetEnumerator();
			float num5 = default(float);
			while (enumerator.MoveNext())
			{
				PlayerAgent current = enumerator.Current;
				if ((Object)(object)((current != null) ? current.Owner : null) == (Object)null || current.Owner.IsBot)
				{
					continue;
				}
				int playerSlotIndex = current.PlayerSlotIndex;
				if (playerSlotIndex < 0 || playerSlotIndex >= ((Il2CppArrayBase<PlayerCoverageData>)(object)coverageDatas).Length || ((Il2CppArrayBase<PlayerCoverageData>)(object)coverageDatas)[playerSlotIndex].m_nodeDistanceUnblocked >= 2 || !((Agent)current).Alive)
				{
					continue;
				}
				result = true;
				Vector3 val = ((Agent)current).EyePosition - s.m_position;
				float magnitude = ((Vector3)(ref val)).magnitude;
				((Vector3)(ref val)).Normalize();
				float num4 = 0f;
				bool flag3 = false;
				if (((Agent)current).HasDetectionMod(ref num5) && magnitude < num5)
				{
					num4 = ((Agent)current).GetDetectionMod(val, magnitude);
					flag3 = num4 > 0.001f;
				}
				bool flag4 = !flag && magnitude < num2;
				if ((flag4 || flag3) && Vector3.Dot(s.m_fwd, val) >= -0.5f && !Physics.Linecast(s.m_position, ((Agent)current).EyePosition, LayerManager.MASK_WORLD))
				{
					if (flag4)
					{
						Vector3 horizontalVelocity = current.Locomotion.HorizontalVelocity;
						num3 = ((Vector3)(ref horizontalVelocity)).magnitude;
						num2 = magnitude;
						flag2 = true;
					}
					s.m_lightMeasure += num4 * num * 5f;
				}
			}
			if (s.m_lightMeasure > 0.5f)
			{
				if ((int)s.m_currentState != 2)
				{
					s.TryPlaySound(EVENTS.INFECTION_SPITTER_SCARED, false, 1f);
					s.SetTimedRetract(30f, s.m_lightRetractSpeed);
				}
				else if (s.m_stayInTimer < 30f)
				{
					s.m_stayInTimer = 30f;
				}
			}
			if ((int)s.m_currentState == 2)
			{
				s.m_timeToExplode = 3f;
				s.StopPurring();
				s.m_stayInTimer -= num;
				if (s.m_stayInTimer < 0f)
				{
					s.m_stayInTimer = 0f;
					s.m_currentState = (eSpitterState)1;
				}
				else if (s.m_stayInTimer < 1f)
				{
					s.TryPlaySound(EVENTS.INFECTION_SPITTER_PRIMED, true, 2f);
				}
			}
			else if (flag2)
			{
				if (!s.m_isSoundOut)
				{
					s.TryPlaySound(EVENTS.INFECTION_SPITTER_OUT, true, 1f);
					s.m_isSoundOut = true;
				}
				if (num2 < 6f)
				{
					float num6 = 1f - num2 / 6f;
					float num7 = ((num3 > 5f) ? 3f : 1f);
					s.m_timeToExplode -= num * (num6 * num6 * 6.5f) * num7;
					s.UpdateTargetingRetraction();
					if (!s.m_purrLoopPlaying)
					{
						s.TryPlaySound(EVENTS.INFECTION_SPITTER_PURR_LOOP, false, 1f);
						s.m_purrLoopPlaying = true;
					}
					if (SNet.IsMaster && !s.m_isExploding && (num2 < 1f || s.m_timeToExplode <= 0f))
					{
						s.SendExplode();
					}
				}
				else
				{
					s.RestoreTargetTimer(num);
				}
				s.m_currentState = (eSpitterState)1;
			}
			else
			{
				s.RestoreTargetTimer(num);
				if ((int)s.m_currentState != 0)
				{
					if (s.m_hasReachedTarget && s.m_targetRetract > 0.99f)
					{
						((Behaviour)s).enabled = false;
						s.m_currentState = (eSpitterState)0;
						s.CleanupSound();
					}
					else
					{
						if (s.m_isSoundOut)
						{
							s.m_isSoundOut = false;
							s.TryPlaySound(EVENTS.INFECTION_SPITTER_IN, true, 1f);
						}
						s.SetRetractTarget(0.5f, s.m_retractSpeed);
					}
				}
			}
			s.m_lightMeasure = Mathf.Clamp01(s.m_lightMeasure - num * 0.25f);
			return result;
		}
	}
	[HarmonyPatch]
	internal static class Fix_SpitterExplosionDamage
	{
		private static bool _broken;

		[HarmonyPatch(typeof(InfectionSpitter), "AssignCourseNode")]
		[HarmonyPostfix]
		public static void Post_AssignCourseNode(InfectionSpitter __instance)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (_broken)
			{
				return;
			}
			try
			{
				InfectionSpitterDamage damage = __instance.m_damage;
				if (!((Object)(object)damage == (Object)null))
				{
					damage.DamageTargetPos = ((Component)damage).transform.position;
				}
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(93, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterExplosionDamage] Failed to set DamageTargetPos; explosions will not ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("damage spitters: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class Fix_SpitterGlueCollisionCrash
	{
		private static bool _logged;

		private static bool _loggedProbeFailure;

		[HarmonyPatch(typeof(GlueGunProjectile), "CollisionCheck")]
		[HarmonyFinalizer]
		public static Exception? Finalize_CollisionCheck(GlueGunProjectile __instance, Exception? __exception)
		{
			if (!(__exception is NullReferenceException))
			{
				return __exception;
			}
			bool flag;
			try
			{
				flag = SpitterColliders.IsSpitter(((StickingProjectileBase)__instance).m_projLastRayHitCollider);
			}
			catch (Exception ex)
			{
				flag = true;
				if (!_loggedProbeFailure)
				{
					_loggedProbeFailure = true;
					Plugin.Logger.LogWarning((object)("[SpitterGlueCollisionCrash] Could not identify the glue collision target, suppressing the NullReferenceException conservatively: " + ex.Message));
				}
			}
			if (!flag)
			{
				return __exception;
			}
			if (!_logged)
			{
				_logged = true;
				Plugin.Logger.LogWarning((object)"[SpitterGlueCollisionCrash] Suppressed a downstream NullReferenceException from a GlueGunProjectile.CollisionCheck postfix on a glue-vs-spitter collision (e.g. DoorEnemyFixUpdated <= 1.1.3). That is the other mod's bug; KillableSpitters is neutralizing it so C-Foam / glue-mines keep working.");
			}
			return null;
		}
	}
	internal static class Fix_SpitterHealthRel
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate float HealthRelFn(IntPtr thisPtr, IntPtr methodInfoPtr);

		private static HealthRelFn? _thunk;

		private static bool _warnedThunkFailure;

		internal static bool Installed { get; private set; }

		internal unsafe static void Install()
		{
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Expected O, but got Unknown
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Expected O, but got Unknown
			if (Installed)
			{
				return;
			}
			bool flag = default(bool);
			try
			{
				IntPtr nativeClassPtr = Il2CppClassPointerStore<InfectionSpitterDamage>.NativeClassPtr;
				if (nativeClassPtr == IntPtr.Zero)
				{
					WarnAborted("class pointer unavailable");
					return;
				}
				IL2CPP.il2cpp_runtime_class_init(nativeClassPtr);
				IntPtr intPtr = IL2CPP.il2cpp_class_get_method_from_name(nativeClassPtr, "GetHealthRel", 0);
				if (intPtr == IntPtr.Zero)
				{
					WarnAborted("GetHealthRel not found on InfectionSpitterDamage");
					return;
				}
				INativeMethodInfoStruct val = UnityVersionHandler.Wrap((Il2CppMethodInfo*)(void*)intPtr);
				string text = Marshal.PtrToStringAnsi(val.Name);
				if (text != "GetHealthRel")
				{
					WarnAborted("resolved method is named '" + text + "'");
					return;
				}
				if (val.ParametersCount != 0)
				{
					WarnAborted($"unexpected parameter count {val.ParametersCount}");
					return;
				}
				IntPtr intPtr2 = IL2CPP.il2cpp_method_get_return_type(intPtr);
				string text2 = ((intPtr2 != IntPtr.Zero) ? Marshal.PtrToStringAnsi(IL2CPP.il2cpp_type_get_name(intPtr2)) : null);
				if (text2 != "System.Single")
				{
					WarnAborted("unexpected return type '" + text2 + "'");
					return;
				}
				INativeClassStruct obj = UnityVersionHandler.Wrap((Il2CppClass*)(void*)nativeClassPtr);
				VirtualInvokeData* ptr = (VirtualInvokeData*)(void*)obj.VTable;
				int vtableCount = obj.VtableCount;
				List<int> list = new List<int>();
				if (ptr != null)
				{
					for (int i = 0; i < vtableCount; i++)
					{
						if ((IntPtr)((VirtualInvokeData)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<VirtualInvokeData>())).method == intPtr)
						{
							list.Add(i);
						}
					}
				}
				IntPtr methodPointer = val.MethodPointer;
				_thunk = HealthRelThunk;
				IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(_thunk);
				foreach (int item in list)
				{
					((VirtualInvokeData)((byte*)ptr + (nint)item * (nint)Unsafe.SizeOf<VirtualInvokeData>())).methodPtr = functionPointerForDelegate;
				}
				val.MethodPointer = functionPointerForDelegate;
				Installed = true;
				ManualLogSource logger = Plugin.Logger;
				BepInExDebugLogInterpolatedStringHandler val2 = new BepInExDebugLogInterpolatedStringHandler(92, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterHealthRel] GetHealthRel redirected (original body 0x");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(methodPointer, "X");
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(list.Count);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" vtable slot(s) of ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(vtableCount);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" rewritten)");
				}
				logger.LogDebug(val2);
				if (list.Count == 0)
				{
					Plugin.Logger.LogWarning((object)"[SpitterHealthRel] No vtable slot referenced GetHealthRel's MethodInfo — native interface dispatch keeps the vanilla 0 (managed/interop callers are still redirected)");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(125, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[SpitterHealthRel] Install failed, spitters keep the vanilla GetHealthRel() = 0 ");
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("(EWC projectiles/DoTs will ignore spitters): ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				logger2.LogWarning(val3);
			}
		}

		private static void WarnAborted(string reason)
		{
			Plugin.Logger.LogWarning((object)("[SpitterHealthRel] Install aborted (" + reason + "), spitters keep the vanilla GetHealthRel() = 0 (EWC projectiles/DoTs will ignore spitters)"));
		}

		private static float HealthRelThunk(IntPtr thisPtr, IntPtr methodInfoPtr)
		{
			try
			{
				return SpitterKillManager.GetHealthRelForDamagePtr(thisPtr);
			}
			catch (Exception ex)
			{
				WarnThunkOnce(ex);
				return 0f;
			}
		}

		private static void WarnThunkOnce(Exception ex)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (_warnedThunkFailure)
			{
				return;
			}
			_warnedThunkFailure = true;
			try
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(62, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterHealthRel] Health lookup failed, reporting vanilla 0: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
			catch
			{
			}
		}

		internal static void LogSmokeCheck(InfectionSpitterDamage damage)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			if (!Installed)
			{
				return;
			}
			try
			{
				Plugin.Logger.LogDebug((object)($"[SpitterHealthRel] Smoke check: GetHealthRel() = {damage.GetHealthRel()} " + "(expected > 0 for a live spitter)"));
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterHealthRel] Smoke check failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class Fix_SpitterMineTrigger
	{
		private const float BeamRadius = 0.1f;

		private const int MaxLookPastSteps = 8;

		private static bool _broken;

		[HarmonyPatch(typeof(MineDeployerInstance_Detect_Laser), "UpdateDetection")]
		[HarmonyPrefix]
		public static bool Pre_UpdateDetection(MineDeployerInstance_Detect_Laser __instance)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			if (_broken)
			{
				return true;
			}
			try
			{
				eStickyMineMode mode = __instance.m_core.Mode;
				if ((int)mode != 2 && (int)mode != 1)
				{
					return true;
				}
				if (__instance.m_maxLineDistance <= 0f)
				{
					return true;
				}
				Transform lineRendererAlign = __instance.m_lineRendererAlign;
				Vector3 position = lineRendererAlign.position;
				Vector3 forward = lineRendererAlign.forward;
				float maxLineDistance = __instance.m_maxLineDistance;
				int scanMask = __instance.m_scanMask;
				LayerMask enemyMask = __instance.m_enemyMask;
				RaycastHit val = default(RaycastHit);
				if (!Physics.SphereCast(position, 0.1f, forward, ref val, maxLineDistance, scanMask))
				{
					return true;
				}
				if (!IsOnMask(((RaycastHit)(ref val)).collider, enemyMask))
				{
					return true;
				}
				if (!SpitterColliders.IsSpitter(((RaycastHit)(ref val)).collider))
				{
					return true;
				}
				if (HasNonSpitterEnemy(position, forward, maxLineDistance, scanMask, enemyMask))
				{
					return true;
				}
				if (((RaycastHit)(ref val)).distance != __instance.DetectionRange)
				{
					__instance.UpdateDetectionRange(((RaycastHit)(ref val)).distance);
				}
				return false;
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(76, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterMineTrigger] Detection patch failed, reverting to vanilla behavior: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val2);
				return true;
			}
		}

		private static bool HasNonSpitterEnemy(Vector3 origin, Vector3 dir, float remaining, int scanMask, LayerMask enemyMask)
		{
			//IL_0004: 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_001f: 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_0049: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			for (int i = 0; i < 8; i++)
			{
				if (!(remaining > 0.01f))
				{
					break;
				}
				if (!Physics.SphereCast(origin, 0.1f, dir, ref val, remaining, scanMask))
				{
					return false;
				}
				if (!IsOnMask(((RaycastHit)(ref val)).collider, enemyMask))
				{
					return false;
				}
				if (!SpitterColliders.IsSpitter(((RaycastHit)(ref val)).collider))
				{
					return true;
				}
				float num = ((RaycastHit)(ref val)).distance + 0.2f;
				origin += dir * num;
				remaining -= num;
			}
			return false;
		}

		private static bool IsOnMask(Collider collider, LayerMask mask)
		{
			if ((Object)(object)collider != (Object)null)
			{
				return (((LayerMask)(ref mask)).value & (1 << ((Component)collider).gameObject.layer)) != 0;
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class Fix_SpitterSentryTarget
	{
		private const float LosProbeOffset = 0.4f;

		private static bool _broken;

		[HarmonyPatch(typeof(SentryGunInstance_Detection), "CheckForTargetLegacy")]
		[HarmonyPrefix]
		public static bool Pre_CheckForTargetLegacy(ArchetypeDataBlock archetypeData, Transform detectionSource, ref EnemyAgent? __result)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (_broken)
			{
				return true;
			}
			try
			{
				__result = CheckForTargetSkippingSpitters(archetypeData, detectionSource);
				return false;
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(84, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterSentryTarget] Legacy detection patch failed, reverting to vanilla behavior: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
				return true;
			}
		}

		private static EnemyAgent? CheckForTargetSkippingSpitters(ArchetypeDataBlock archetypeData, Transform detectionSource)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			Vector3 forward = detectionSource.forward;
			Vector3 position = detectionSource.position;
			Il2CppReferenceArray<Collider> val = Physics.OverlapSphere(position, archetypeData.Sentry_DetectionMaxRange, LayerManager.MASK_SENTRYGUN_DETECTION_TARGETS);
			for (int i = 0; i < ((Il2CppArrayBase<Collider>)(object)val).Length; i++)
			{
				Vector3 position2 = ((Component)((Il2CppArrayBase<Collider>)(object)val)[i]).transform.position;
				if (Vector3.Angle(forward, position2 - position) >= archetypeData.Sentry_DetectionMaxAngle || Physics.Linecast(position, position2, LayerManager.MASK_SENTRYGUN_DETECTION_BLOCKERS) || Physics.Linecast(position, position2 + Vector3.up * 0.4f, LayerManager.MASK_SENTRYGUN_DETECTION_BLOCKERS) || Physics.Linecast(position, position2 + Vector3.left * 0.4f, LayerManager.MASK_SENTRYGUN_DETECTION_BLOCKERS))
				{
					continue;
				}
				IDamageable component = ((Component)((Il2CppArrayBase<Collider>)(object)val)[i]).GetComponent<IDamageable>();
				if (component != null && !(component.GetHealthRel() <= 0f))
				{
					Agent baseAgent = component.GetBaseAgent();
					EnemyAgent val2 = ((baseAgent != null) ? ((Il2CppObjectBase)baseAgent).TryCast<EnemyAgent>() : null);
					if (!((Object)(object)val2 == (Object)null) && ((!val2.RequireTagForDetection && !archetypeData.Sentry_FireTagOnly) || val2.IsTagged))
					{
						return val2;
					}
				}
			}
			return null;
		}
	}
	internal static class SpitterColliders
	{
		internal static bool TryGetDamage(Collider collider, out InfectionSpitterDamage damage)
		{
			damage = null;
			if ((Object)(object)collider == (Object)null)
			{
				return false;
			}
			damage = ((Component)collider).GetComponent<InfectionSpitterDamage>();
			if ((Object)(object)damage != (Object)null)
			{
				return true;
			}
			ColliderMaterial component = ((Component)collider).GetComponent<ColliderMaterial>();
			IDamageable val = ((component != null) ? component.Damageable : null);
			if (val != null)
			{
				damage = ((Il2CppObjectBase)val).TryCast<InfectionSpitterDamage>();
			}
			return (Object)(object)damage != (Object)null;
		}

		internal static bool IsSpitter(Collider collider)
		{
			InfectionSpitterDamage damage;
			return TryGetDamage(collider, out damage);
		}
	}
}
namespace KillableSpitters.Patches.Spitters
{
	[HarmonyPatch]
	internal static class Patch_SpitterDamage
	{
		[HarmonyPatch(typeof(InfectionSpitter), "OnIncomingDamage")]
		[HarmonyPrefix]
		public static bool Pre_OnIncomingDamage(InfectionSpitter __instance, float dam)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			try
			{
				if (SpitterKillManager.IsDeadOrDying(__instance.m_spitterIndex))
				{
					return false;
				}
				if (__instance.m_isExploding)
				{
					SpitterKillManager.ReportDamage(__instance, dam);
					return false;
				}
				if ((int)__instance.m_currentState == 2)
				{
					__instance.SendSlowExplode();
				}
				else
				{
					__instance.SendExplode();
				}
				SpitterKillManager.ReportDamage(__instance, dam);
				return false;
			}
			catch (Exception ex)
			{
				SpitterKillManager.Break(ex);
				return true;
			}
		}

		[HarmonyPatch(typeof(InfectionSpitter), "DoGetGlued")]
		[HarmonyPostfix]
		public static void Post_DoGetGlued(InfectionSpitter __instance)
		{
			SpitterKillManager.OnSpitterGlued(__instance);
		}

		[HarmonyPatch(typeof(InfectionSpitter), "OnIncomingGlue")]
		[HarmonyPrefix]
		public static bool Pre_OnIncomingGlue(InfectionSpitter __instance)
		{
			try
			{
				return !SpitterKillManager.IsDeadOrDying(__instance.m_spitterIndex);
			}
			catch (Exception ex)
			{
				SpitterKillManager.Break(ex);
				return true;
			}
		}

		[HarmonyPatch(typeof(InfectionSpitter), "ReceiveDamage")]
		[HarmonyPrefix]
		public static bool Pre_ReceiveDamage(InfectionSpitterDamage data)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return !SpitterKillManager.IsDeadOrDying(data.spitterIndex);
			}
			catch (Exception ex)
			{
				SpitterKillManager.Break(ex);
				return true;
			}
		}

		[HarmonyPatch(typeof(InfectionSpitter), "Update")]
		[HarmonyPostfix]
		public static void Post_Update(InfectionSpitter __instance)
		{
			SpitterKillManager.OnSpitterUpdatePostfix(__instance);
		}

		internal static void LogDamageForwarderFoldState()
		{
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			bool flag = default(bool);
			try
			{
				string[] array = new string[4] { "BulletDamage", "MeleeDamage", "FireDamage", "ExplosionDamage" };
				Dictionary<string, IntPtr> dictionary = new Dictionary<string, IntPtr>();
				FieldInfo[] fields = typeof(InfectionSpitterDamage).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo field in fields)
				{
					if (field.FieldType != typeof(IntPtr))
					{
						continue;
					}
					string text = Array.Find(array, (string f) => field.Name.StartsWith("NativeMethodInfoPtr_" + f + "_", StringComparison.Ordinal));
					if (text != null)
					{
						IntPtr intPtr = (IntPtr)field.GetValue(null);
						if (intPtr != IntPtr.Zero)
						{
							dictionary[text] = Marshal.ReadIntPtr(intPtr);
						}
					}
				}
				if (dictionary.Count < array.Length)
				{
					ManualLogSource logger = Plugin.Logger;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(65, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Damage-forwarder fold check incomplete (");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(dictionary.Count);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(array.Length);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" resolved)");
					}
					logger.LogWarning(val);
				}
				else if (dictionary.Values.Distinct().Count() == 1)
				{
					Plugin.Logger.LogDebug((object)("[SpitterKill] InfectionSpitterDamage damage forwarders share one native body " + $"(ICF-folded at 0x{dictionary["BulletDamage"]:X}, expected — none of them is patched)"));
				}
				else
				{
					Plugin.Logger.LogWarning((object)("[SpitterKill] Damage forwarders are NOT folded on this build — the ICF assumption that keeps them unpatchable no longer holds (the tap is on OnIncomingDamage, but the house rule against patching a forwarder still stands): " + string.Join(", ", dictionary.Select((KeyValuePair<string, IntPtr> kv) => $"{kv.Key}=0x{kv.Value:X}"))));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(68, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Damage-forwarder fold check failed (diagnostic only): ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger2.LogWarning(val);
			}
		}
	}
	public static class SpitterDeathFx
	{
		private const uint FlyerEnemyId = 42u;

		private const string DeathFxPrefabName = "FXC_FlyerDeath";

		private static bool _broken;

		private static bool _warnedNoPool;

		private static FX_Pool? _pool;

		public static void PlayAt(Vector3 position)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (_broken)
			{
				return;
			}
			try
			{
				CellSound.Post(EVENTS.FLYER_DIE, position);
				FX_Pool val = ResolvePool();
				if ((Object)(object)val == (Object)null)
				{
					WarnNoPoolOnce();
					return;
				}
				FX_EffectBase_Poolable val2 = val.AquireEffect();
				if (!((Object)(object)val2 == (Object)null))
				{
					((FX_EffectBase)val2).Play((FX_Trigger)null, position, Quaternion.identity);
				}
			}
			catch (ObjectCollectedException)
			{
				_pool = null;
			}
			catch (Exception ex2)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(41, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[SpitterKill] Spitter death FX disabled: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex2);
				}
				logger.LogWarning(val3);
			}
		}

		private static FX_Pool? ResolvePool()
		{
			if ((Object)(object)_pool != (Object)null)
			{
				return _pool;
			}
			_pool = FX_Manager.GetPreloadedEffectPool("FXC_FlyerDeath");
			if ((Object)(object)_pool != (Object)null)
			{
				return _pool;
			}
			GameObject val = GetDeathFxPrefab();
			if ((Object)(object)val == (Object)null)
			{
				val = Resources.Load<GameObject>("FXC_FlyerDeath");
			}
			if ((Object)(object)val != (Object)null)
			{
				_pool = FX_Manager.GetEffectPool(val);
			}
			return _pool;
		}

		private static GameObject? GetDeathFxPrefab()
		{
			GameObject enemyPrefab = EnemyPrefabManager.GetEnemyPrefab(42u);
			if ((Object)(object)enemyPrefab == (Object)null)
			{
				return null;
			}
			FlyerAnimationController componentInChildren = enemyPrefab.GetComponentInChildren<FlyerAnimationController>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return null;
			}
			return componentInChildren.m_deathFX;
		}

		private static void WarnNoPoolOnce()
		{
			if (!_warnedNoPool)
			{
				_warnedNoPool = true;
				Plugin.Logger.LogWarning((object)"[SpitterKill] Could not resolve the flyer death FX pool (FXC_FlyerDeath); spitter removals play sound only");
			}
		}
	}
	public struct SpitterDeathState
	{
		public const int MaskBytes = 240;

		public const int SpittersPerShard = 1920;

		public byte ShardIndex;

		private unsafe fixed byte _deadBits[240];

		public static (int shard, int local) MapIndex(ushort spitterIndex)
		{
			return (shard: spitterIndex / 1920, local: spitterIndex % 1920);
		}

		public unsafe bool IsDead(int localIndex)
		{
			if (localIndex < 0 || localIndex >= 1920)
			{
				return false;
			}
			fixed (byte* deadBits = _deadBits)
			{
				return (deadBits[localIndex >> 3] & (1 << (localIndex & 7))) != 0;
			}
		}

		public unsafe void SetDead(int localIndex)
		{
			if (localIndex >= 0 && localIndex < 1920)
			{
				fixed (byte* deadBits = _deadBits)
				{
					byte* num = deadBits + (localIndex >> 3);
					*num |= (byte)(1 << (localIndex & 7));
				}
			}
		}

		public unsafe void ClearDead(int localIndex)
		{
			if (localIndex >= 0 && localIndex < 1920)
			{
				fixed (byte* deadBits = _deadBits)
				{
					byte* num = deadBits + (localIndex >> 3);
					*num &= (byte)(~(1 << (localIndex & 7)));
				}
			}
		}

		public unsafe byte GetMaskByte(int byteIndex)
		{
			if (byteIndex < 0 || byteIndex >= 240)
			{
				return 0;
			}
			fixed (byte* deadBits = _deadBits)
			{
				return deadBits[byteIndex];
			}
		}
	}
	public static class SpitterKillManager
	{
		private const string DamageEventName = "killable_spitters_damage";

		private const string HealthEventName = "killable_spitters_health";

		private const uint REPLICATOR_BASE_ID = 1263751252u;

		private const int SHARD_COUNT = 4;

		public const int SpitterCapacity = 7680;

		private const float FinalizeGraceSeconds = 1.5f;

		private const float FinalizeDeadlineSeconds = 5f;

		private const float PopAdoptWindowSeconds = 1.5f;

		private const float MaxReportedDamagePerHit = 100f;

		private static bool _broken;

		private static bool _setupDone;

		private static bool _warnedCapacity;

		private static readonly Dictionary<ushort, float> _healthByIndex = new Dictionary<ushort, float>();

		private static readonly Dictionary<ushort, float> _healthRel = new Dictionary<ushort, float>();

		private static readonly HashSet<ushort> _dead = new HashSet<ushort>();

		private static readonly Dictionary<ushort, float> _dyingFinalizeAt = new Dictionary<ushort, float>();

		private static readonly Dictionary<ushort, float> _dyingDeadline = new Dictionary<ushort, float>();

		private static readonly HashSet<ushort> _finalized = new HashSet<ushort>();

		private static readonly HashSet<ushort> _explodingNow = new HashSet<ushort>();

		private static readonly Dictionary<ushort, float> _lastPopCompletedAt = new Dictionary<ushort, float>();

		private static readonly HashSet<ushort> _pendingDeathFx = new HashSet<ushort>();

		private static readonly HashSet<ushort> _modelHidden = new HashSet<ushort>();

		private static readonly Dictionary<ushort, float> _gluedAt = new Dictionary<ushort, float>();

		private static readonly Dictionary<IntPtr, ushort> _damagePtrToIndex = new Dictionary<IntPtr, ushort>();

		private static readonly StateReplicator<SpitterDeathState>?[] _replicators = new StateReplicator<SpitterDeathState>[4];

		private static readonly SpitterDeathState[] _currentStates = new SpitterDeathState[4];

		private static readonly Action<SpitterDeathState, SpitterDeathState, bool>?[] _handlers = new Action<SpitterDeathState, SpitterDeathState, bool>[4];

		public static void Setup()
		{
			if (!_setupDone)
			{
				_setupDone = true;
				NetworkAPI.RegisterEvent<SpitterDamageEvent>("killable_spitters_damage", (Action<ulong, SpitterDamageEvent>)OnDamageEventReceived);
				NetworkAPI.RegisterEvent<SpitterHealthEvent>("killable_spitters_health", (Action<ulong, SpitterHealthEvent>)OnHealthEventReceived);
				LevelAPI.OnBuildDone += OnBuildDone;
				LevelAPI.OnLevelCleanup += OnLevelCleanup;
				LevelAPI.OnEnterLevel += ReconcileDeadSpitters;
			}
		}

		public static bool IsDeadOrDying(ushort spitterIndex)
		{
			if (_dead.Count != 0)
			{
				return _dead.Contains(spitterIndex);
			}
			return false;
		}

		public static void ReportDamage(InfectionSpitter spitter, float dam)
		{
			if (_broken || (Object)(object)spitter == (Object)null || float.IsNaN(dam) || dam <= 0f)
			{
				return;
			}
			ushort spitterIndex = spitter.m_spitterIndex;
			if (!IsDeadOrDying(spitterIndex))
			{
				if (spitterIndex >= 7680)
				{
					WarnCapacityOnce(spitterIndex);
				}
				else if (SNet.IsMaster)
				{
					AccumulateDamage(spitterIndex, Math.Min(dam, 100f));
				}
				else if (SNet.HasMaster)
				{
					NetworkAPI.InvokeEvent<SpitterDamageEvent>("killable_spitters_damage", new SpitterDamageEvent
					{
						SpitterIndex = spitterIndex,
						Damage = dam
					}, SNet.Master, (SNet_ChannelType)2);
				}
			}
		}

		internal static float GetHealthRelForDamagePtr(IntPtr damagePtr)
		{
			if (_broken || _damagePtrToIndex.Count == 0)
			{
				return 0f;
			}
			if (!_damagePtrToIndex.TryGetValue(damagePtr, out var value))
			{
				return 0f;
			}
			if (value >= 7680 || IsDeadOrDying(value))
			{
				return 0f;
			}
			if (_healthByIndex.TryGetValue(value, out var value2))
			{
				return Math.Clamp(value2 / Math.Max(1f, Plugin.Config_SpitterHealth), 0.01f, 1f);
			}
			if (_healthRel.TryGetValue(value, out var value3))
			{
				return Math.Clamp(value3, 0.01f, 1f);
			}
			return 1f;
		}

		public static bool ShouldBlockManagerUpdate(InfectionSpitter spitter)
		{
			if (_broken)
			{
				return false;
			}
			try
			{
				if ((Object)(object)spitter == (Object)null)
				{
					return false;
				}
				ushort spitterIndex = spitter.m_spitterIndex;
				TickGlueKill(spitterIndex);
				if (_dead.Count == 0 || !_dead.Contains(spitterIndex))
				{
					return false;
				}
				if (_dyingFinalizeAt.TryGetValue(spitterIndex, out var value))
				{
					float value2;
					float num = (_dyingDeadline.TryGetValue(spitterIndex, out value2) ? value2 : float.MaxValue);
					if (Clock.Time >= num || (value != float.MaxValue && Clock.Time >= value))
					{
						FinalizeSpitter(spitterIndex, spitter);
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				Break(ex);
				return false;
			}
		}

		public static void OnSpitterUpdatePostfix(InfectionSpitter spitter)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			if (_broken || (Object)(object)spitter == (Object)null)
			{
				return;
			}
			try
			{
				ushort spitterIndex = spitter.m_spitterIndex;
				SpitterVisuals.TickRestores();
				if (spitter.m_isExploding)
				{
					_explodingNow.Add(spitterIndex);
					float value;
					if (_dyingFinalizeAt.ContainsKey(spitterIndex))
					{
						SpitterVisuals.ApplyDeathRamp(spitter);
					}
					else if (_healthRel.TryGetValue(spitterIndex, out value))
					{
						SpitterVisuals.ApplyDamageRamp(spitter, value);
					}
				}
				else if (_explodingNow.Remove(spitterIndex))
				{
					_lastPopCompletedAt[spitterIndex] = Clock.Time;
					float value2;
					if (_dyingFinalizeAt.ContainsKey(spitterIndex))
					{
						SpitterVisuals.TintPop(spitterIndex, spitter, SpitterVisuals.DeathPopColor, recolorLiveParticles: false, untilFinalize: true);
						if (_pendingDeathFx.Remove(spitterIndex))
						{
							SpitterDeathFx.PlayAt(spitter.m_position);
							HideSpitterModel(spitterIndex, spitter);
						}
					}
					else if (_healthRel.TryGetValue(spitterIndex, out value2))
					{
						SpitterVisuals.TintPop(spitterIndex, spitter, SpitterVisuals.GetPopColor(value2), recolorLiveParticles: false, untilFinalize: false);
						SpitterVisuals.ApplyDamageTint(spitter, value2);
					}
				}
				TickGlueKill(spitterIndex);
				if (_dyingFinalizeAt.Count == 0 || !_dyingFinalizeAt.TryGetValue(spitterIndex, out var value3))
				{
					return;
				}
				float value4;
				float num = (_dyingDeadline.TryGetValue(spitterIndex, out value4) ? value4 : float.MaxValue);
				if (value3 == float.MaxValue)
				{
					if (!spitter.m_isExploding)
					{
						_dyingFinalizeAt[spitterIndex] = Clock.Time + 1.5f;
					}
					else if (Clock.Time >= num)
					{
						FinalizeSpitter(spitterIndex, spitter);
					}
				}
				else if (Clock.Time >= value3 || Clock.Time >= num)
				{
					FinalizeSpitter(spitterIndex, spitter);
				}
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		public static void OnSpitterGlued(InfectionSpitter spitter)
		{
			if (_broken || (Object)(object)spitter == (Object)null)
			{
				return;
			}
			try
			{
				ushort spitterIndex = spitter.m_spitterIndex;
				if (spitterIndex >= 7680)
				{
					WarnCapacityOnce(spitterIndex);
				}
				else
				{
					if (IsDeadOrDying(spitterIndex))
					{
						return;
					}
					if (Plugin.Config_CfoamKillsSpitters)
					{
						if (SNet.IsMaster)
						{
							_gluedAt.TryAdd(spitterIndex, Clock.Time);
						}
					}
					else
					{
						spitter.m_stayInTimer = Math.Max(0f, Plugin.Config_SpitterFreezeDuration);
					}
				}
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		private static void TickGlueKill(ushort index)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			if (_gluedAt.Count == 0 || !SNet.IsMaster || !_gluedAt.TryGetValue(index, out var value))
			{
				return;
			}
			if (IsDeadOrDying(index))
			{
				_gluedAt.Remove(index);
			}
			else if (!(Clock.Time - value < Plugin.Config_SpitterFreezeDuration))
			{
				_gluedAt.Remove(index);
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(46, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Spitter ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ushort>(index);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" killed by C-foam (host)");
				}
				logger.LogDebug(val);
				HostMarkDead(index);
			}
		}

		public static void Break(Exception ex)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (_broken)
			{
				return;
			}
			_broken = true;
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(69, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Unexpected error, disabling killable spitters locally: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
			}
			logger.LogError(val);
			try
			{
				SpitterVisuals.RestoreAll();
			}
			catch (Exception ex2)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(46, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] FX restore during Break failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex2.Message);
				}
				logger2.LogWarning(val2);
			}
			try
			{
				foreach (ushort item in _modelHidden.ToList())
				{
					InfectionSpitter val3 = TryGetSpitter(item);
					if ((Object)(object)val3 != (Object)null)
					{
						ShowSpitterModel(item, val3);
					}
				}
				_modelHidden.Clear();
			}
			catch (Exception ex3)
			{
				ManualLogSource logger3 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(49, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] Model restore during Break failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex3.Message);
				}
				logger3.LogWarning(val2);
			}
		}

		private static void OnDamageEventReceived(ulong senderPlayer, SpitterDamageEvent data)
		{
			if (_broken || !SNet.IsMaster)
			{
				return;
			}
			try
			{
				if (!float.IsNaN(data.Damage) && !(data.Damage <= 0f))
				{
					if (data.SpitterIndex >= 7680)
					{
						WarnCapacityOnce(data.SpitterIndex);
					}
					else
					{
						AccumulateDamage(data.SpitterIndex, Math.Min(data.Damage, 100f));
					}
				}
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		private static void AccumulateDamage(ushort index, float dam)
		{
			if (!IsDeadOrDying(index))
			{
				if (!_healthByIndex.TryGetValue(index, out var value))
				{
					value = Math.Max(1f, Plugin.Config_SpitterHealth);
				}
				value -= dam;
				if (value > 0f)
				{
					_healthByIndex[index] = value;
					BroadcastHealth(index, value);
				}
				else
				{
					HostMarkDead(index);
				}
			}
		}

		private static void HostMarkDead(ushort index)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			_healthByIndex.Remove(index);
			_gluedAt.Remove(index);
			_dead.Add(index);
			var (num, dead) = SpitterDeathState.MapIndex(index);
			_currentStates[num].SetDead(dead);
			StateReplicator<SpitterDeathState> val = _replicators[num];
			bool flag = default(bool);
			if (val != null && val.IsValid)
			{
				val.SetState(_currentStates[num]);
			}
			else
			{
				ManualLogSource logger = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(73, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] Replicator for shard ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" missing, spitter ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<ushort>(index);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" death is local-only");
				}
				logger.LogWarning(val2);
			}
			ManualLogSource logger2 = Plugin.Logger;
			BepInExDebugLogInterpolatedStringHandler val3 = new BepInExDebugLogInterpolatedStringHandler(36, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[SpitterKill] Spitter ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<ushort>(index);
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" killed (host)");
			}
			logger2.LogDebug(val3);
			KillSpitter(index, silent: false);
		}

		private static void BroadcastHealth(ushort index, float health)
		{
			SpitterHealthEvent spitterHealthEvent = new SpitterHealthEvent
			{
				SpitterIndex = index,
				HealthRel = Math.Clamp(health / Math.Max(1f, Plugin.Config_SpitterHealth), 0f, 1f)
			};
			NetworkAPI.InvokeEvent<SpitterHealthEvent>("killable_spitters_health", spitterHealthEvent, (SNet_ChannelType)2);
			OnHealthEventReceived(0uL, spitterHealthEvent);
		}

		private static void OnHealthEventReceived(ulong senderPlayer, SpitterHealthEvent data)
		{
			if (_broken)
			{
				return;
			}
			try
			{
				if (float.IsNaN(data.HealthRel))
				{
					return;
				}
				if (data.SpitterIndex >= 7680)
				{
					WarnCapacityOnce(data.SpitterIndex);
				}
				else if (!IsDeadOrDying(data.SpitterIndex))
				{
					float num = Math.Clamp(data.HealthRel, 0f, 1f);
					_healthRel[data.SpitterIndex] = num;
					InfectionSpitter val = TryGetSpitter(data.SpitterIndex);
					if ((Object)(object)val != (Object)null)
					{
						SpitterVisuals.ApplyDamageTint(val, num);
					}
				}
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		private static void OnDeathStateChanged(int shard, SpitterDeathState oldState, SpitterDeathState newState, bool isRecall)
		{
			if (_broken)
			{
				return;
			}
			try
			{
				_currentStates[shard] = newState;
				for (int i = 0; i < 240; i++)
				{
					byte maskByte = oldState.GetMaskByte(i);
					byte maskByte2 = newState.GetMaskByte(i);
					if (maskByte == maskByte2)
					{
						continue;
					}
					byte b = (byte)(maskByte2 & ~maskByte);
					byte b2 = (byte)(maskByte & ~maskByte2);
					for (int j = 0; j < 8; j++)
					{
						ushort num = (ushort)(shard * 1920 + i * 8 + j);
						int num2 = 1 << j;
						if ((b & num2) != 0)
						{
							_dead.Add(num);
							KillSpitter(num, isRecall);
						}
						else if ((b2 & num2) != 0)
						{
							ReviveSpitter(num);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		private static void KillSpitter(ushort index, bool silent)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Invalid comparison between Unknown and I4
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if (_finalized.Contains(index) || (_dyingFinalizeAt.ContainsKey(index) && !silent))
			{
				return;
			}
			_dead.Add(index);
			InfectionSpitter val = TryGetSpitter(index);
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExDebugLogInterpolatedStringHandler val2 = new BepInExDebugLogInterpolatedStringHandler(60, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] Spitter ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<ushort>(index);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" not resolvable yet, deferring removal");
				}
				logger.LogDebug(val2);
				return;
			}
			if (silent)
			{
				FinalizeSpitter(index, val);
				return;
			}
			float value;
			if (val.m_isExploding)
			{
				_dyingFinalizeAt[index] = float.MaxValue;
				_pendingDeathFx.Add(index);
			}
			else if (_lastPopCompletedAt.TryGetValue(index, out value) && Clock.Time - value <= 1.5f)
			{
				_dyingFinalizeAt[index] = Clock.Time + 1.5f;
				SpitterVisuals.TintPop(index, val, SpitterVisuals.DeathPopColor, recolorLiveParticles: true, untilFinalize: true);
				SpitterDeathFx.PlayAt(val.m_position);
				HideSpitterModel(index, val);
			}
			else if (val.m_isGlued)
			{
				SpitterDeathFx.PlayAt(val.m_position);
				HideSpitterModel(index, val);
				_dyingFinalizeAt[index] = Clock.Time + 1.5f;
			}
			else
			{
				val.DoExplode(((int)val.m_currentState == 2) ? 0.5f : 1f);
				_dyingFinalizeAt[index] = float.MaxValue;
				_pendingDeathFx.Add(index);
			}
			_dyingDeadline[index] = Clock.Time + 5f;
		}

		private static void HideSpitterModel(ushort index, InfectionSpitter spitter)
		{
			_modelHidden.Add(index);
			if ((Object)(object)spitter.m_renderer != (Object)null)
			{
				spitter.m_renderer.enabled = false;
			}
			if ((Object)(object)spitter.m_light != (Object)null)
			{
				((Behaviour)spitter.m_light).enabled = false;
			}
			if ((Object)(object)spitter.m_damage != (Object)null && (Object)(object)((Component)spitter.m_damage).gameObject != (Object)(object)((Component)spitter).gameObject)
			{
				((Component)spitter.m_damage).gameObject.SetActive(false);
			}
		}

		private static void ShowSpitterModel(ushort index, InfectionSpitter spitter)
		{
			_modelHidden.Remove(index);
			if ((Object)(object)spitter.m_renderer != (Object)null)
			{
				spitter.m_renderer.enabled = true;
			}
			if ((Object)(object)spitter.m_light != (Object)null)
			{
				((Behaviour)spitter.m_light).enabled = true;
			}
			if ((Object)(object)spitter.m_damage != (Object)null && (Object)(object)((Component)spitter.m_damage).gameObject != (Object)(object)((Component)spitter).gameObject)
			{
				((Component)spitter.m_damage).gameObject.SetActive(true);
			}
		}

		private static void FinalizeSpitter(ushort index, InfectionSpitter spitter)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			if (_finalized.Add(index))
			{
				_dyingFinalizeAt.Remove(index);
				_dyingDeadline.Remove(index);
				_explodingNow.Remove(index);
				_lastPopCompletedAt.Remove(index);
				_modelHidden.Remove(index);
				_healthRel.Remove(index);
				SpitterVisuals.RestoreForSpitter(index);
				spitter.CleanupSound();
				if (_pendingDeathFx.Remove(index))
				{
					SpitterDeathFx.PlayAt(spitter.m_position);
				}
				((Behaviour)spitter).enabled = false;
				((Component)spitter).gameObject.SetActive(false);
				if ((Object)(object)spitter.m_damage != (Object)null && (Object)(object)((Component)spitter.m_damage).gameObject != (Object)(object)((Component)spitter).gameObject)
				{
					((Component)spitter.m_damage).gameObject.SetActive(false);
				}
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(30, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Spitter ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ushort>(index);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" removed");
				}
				logger.LogDebug(val);
			}
		}

		private static void ReviveSpitter(ushort index)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			_dead.Remove(index);
			_finalized.Remove(index);
			_dyingFinalizeAt.Remove(index);
			_dyingDeadline.Remove(index);
			_explodingNow.Remove(index);
			_lastPopCompletedAt.Remove(index);
			_pendingDeathFx.Remove(index);
			_healthByIndex.Remove(index);
			_healthRel.Remove(index);
			_gluedAt.Remove(index);
			SpitterVisuals.RestoreForSpitter(index);
			InfectionSpitter val = TryGetSpitter(index);
			if (!((Object)(object)val == (Object)null))
			{
				((Component)val).gameObject.SetActive(true);
				ShowSpitterModel(index, val);
				SpitterVisuals.ResetBodyGlow(val);
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExDebugLogInterpolatedStringHandler val2 = new BepInExDebugLogInterpolatedStringHandler(50, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] Spitter ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<ushort>(index);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" revived (checkpoint recall)");
				}
				logger.LogDebug(val2);
			}
		}

		private static void ReconcileDeadSpitters()
		{
			if (_broken || _dead.Count == 0)
			{
				return;
			}
			try
			{
				foreach (ushort item in _dead.ToList())
				{
					if (!_finalized.Contains(item) && !_dyingFinalizeAt.ContainsKey(item))
					{
						InfectionSpitter val = TryGetSpitter(item);
						if ((Object)(object)val != (Object)null)
						{
							FinalizeSpitter(item, val);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		private static InfectionSpitter? TryGetSpitter(ushort index)
		{
			List<InfectionSpitter> s_allSpitters = InfectionSpitter.s_allSpitters;
			if (s_allSpitters == null || index >= s_allSpitters.Count)
			{
				return null;
			}
			return s_allSpitters[(int)index];
		}

		private static void OnBuildDone()
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			if (_broken)
			{
				return;
			}
			try
			{
				UnloadReplicators();
				ClearRuntimeState();
				if (!APIStatus.Network.Ready)
				{
					Plugin.Logger.LogError((object)"[SpitterKill] NetworkAPI not ready during OnBuildDone, spitters stay vanilla");
					return;
				}
				bool flag = default(bool);
				for (int i = 0; i < 4; i++)
				{
					SpitterDeathState spitterDeathState = new SpitterDeathState
					{
						ShardIndex = (byte)i
					};
					StateReplicator<SpitterDeathState> val = StateReplicator<SpitterDeathState>.Create((uint)(1263751252 + i), spitterDeathState, (LifeTimeType)1, (IStateReplicatorHolder<SpitterDeathState>)null);
					if (val == null)
					{
						ManualLogSource logger = Plugin.Logger;
						BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(48, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] Failed to create replicator shard ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(i);
						}
						logger.LogError(val2);
					}
					else
					{
						_currentStates[i] = spitterDeathState;
						int shard = i;
						Action<SpitterDeathState, SpitterDeathState, bool> action = delegate(SpitterDeathState oldState, SpitterDeathState newState, bool isRecall)
						{
							OnDeathStateChanged(shard, oldState, newState, isRecall);
						};
						val.OnStateChanged += action;
						_handlers[i] = action;
						_replicators[i] = val;
					}
				}
				RegisterDamagePointers();
				ManualLogSource logger2 = Plugin.Logger;
				BepInExDebugLogInterpolatedStringHandler val3 = new BepInExDebugLogInterpolatedStringHandler(49, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[SpitterKill] Ready (");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(4);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" shards, ");
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("health=");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<float>(Plugin.Config_SpitterHealth);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(", IsMaster=");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<bool>(SNet.IsMaster);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(")");
				}
				logger2.LogDebug(val3);
			}
			catch (Exception ex)
			{
				Break(ex);
			}
		}

		private static void RegisterDamagePointers()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			List<InfectionSpitter> s_allSpitters = InfectionSpitter.s_allSpitters;
			if (s_allSpitters == null || s_allSpitters.Count == 0)
			{
				return;
			}
			for (int i = 0; i < s_allSpitters.Count; i++)
			{
				InfectionSpitter val = s_allSpitters[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.m_damage == (Object)null))
				{
					_damagePtrToIndex[((Il2CppObjectBase)val.m_damage).Pointer] = val.m_spitterIndex;
				}
			}
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExDebugLogInterpolatedStringHandler val2 = new BepInExDebugLogInterpolatedStringHandler(51, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[SpitterKill] Registered ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(_damagePtrToIndex.Count);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" spitter damage pointer(s)");
			}
			logger.LogDebug(val2);
			InfectionSpitter val3 = s_allSpitters[0];
			if ((Object)(object)val3 != (Object)null && (Object)(object)val3.m_damage != (Object)null)
			{
				Fix_SpitterHealthRel.LogSmokeCheck(val3.m_damage);
			}
		}

		private static void OnLevelCleanup()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			try
			{
				UnloadReplicators();
				ClearRuntimeState();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(29, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Cleanup error: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}

		private static void UnloadReplicators()
		{
			for (int i = 0; i < 4; i++)
			{
				StateReplicator<SpitterDeathState> val = _replicators[i];
				if (val != null)
				{
					if (_handlers[i] != null)
					{
						val.OnStateChanged -= _handlers[i];
					}
					val.Unload();
					_replicators[i] = null;
					_handlers[i] = null;
				}
			}
		}

		private static void ClearRuntimeState()
		{
			_healthByIndex.Clear();
			_healthRel.Clear();
			_dead.Clear();
			_dyingFinalizeAt.Clear();
			_dyingDeadline.Clear();
			_finalized.Clear();
			_explodingNow.Clear();
			_lastPopCompletedAt.Clear();
			_pendingDeathFx.Clear();
			_modelHidden.Clear();
			_gluedAt.Clear();
			_damagePtrToIndex.Clear();
			_warnedCapacity = false;
			SpitterVisuals.RestoreAll();
			for (int i = 0; i < 4; i++)
			{
				_currentStates[i] = new SpitterDeathState
				{
					ShardIndex = (byte)i
				};
			}
		}

		private static void WarnCapacityOnce(ushort index)
		{
			if (!_warnedCapacity)
			{
				_warnedCapacity = true;
				Plugin.Logger.LogWarning((object)($"[SpitterKill] Spitter index {index} exceeds capacity {7680}; " + "spitters beyond capacity stay vanilla (unkillable)"));
			}
		}
	}
	public static class SpitterVisuals
	{
		private readonly struct OriginalStartColor
		{
			public readonly ParticleSystem Ps;

			public readonly MainModule Main;

			public readonly ParticleSystemGradientMode Mode;

			public readonly Color ColorMin;

			public readonly Color ColorMax;

			public readonly Gradient? GradientMin;

			public readonly Gradient? GradientMax;

			public OriginalStartColor(ParticleSystem ps, MainModule main, MinMaxGradient captured)
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				Ps = ps;
				Main = main;
				Mode = captured.m_Mode;
				ColorMin = captured.m_ColorMin;
				ColorMax = captured.m_ColorMax;
				GradientMin = captured.m_GradientMin;
				GradientMax = captured.m_GradientMax;
			}
		}

		private const float FxRestoreSeconds = 3f;

		private const float SteadyGlowScale = 0.6f;

		private static readonly Color OrangeGlowBase = new Color(1f, 0.45f, 0f) * 0.5f;

		private static readonly Color RedGlowBase = new Color(1f, 0.06f, 0.04f) * 0.5f;

		private static readonly Color32 GreenPopColor = new Color32((byte)60, byte.MaxValue, (byte)200, byte.MaxValue);

		private static readonly Color32 OrangePopColor = new Color32(byte.MaxValue, (byte)150, (byte)30, byte.MaxValue);

		private static readonly Color32 RedPopColor = new Color32(byte.MaxValue, (byte)24, (byte)16, byte.MaxValue);

		private static bool _visualsBroken;

		private const int GcRaceAttempts = 3;

		private static MinMaxGradient? _sharedGradient;

		private static readonly Dictionary<int, OriginalStartColor> _tintedSystems = new Dictionary<int, OriginalStartColor>();

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

		private static readonly Dictionary<ushort, List<int>> _tintedBySpitter = new Dictionary<ushort, List<int>>();

		public static Color32 DeathPopColor => RedPopColor;

		public static Color DeathSteadyGlow => RedGlowBase * 0.6f;

		private static Color GetBaseGlow(float healthRel)
		{
			//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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			healthRel = Mathf.Clamp01(healthRel);
			if (!(healthRel >= 0.5f))
			{
				return Color.Lerp(OrangeGlowBase, RedGlowBase, (0.5f - healthRel) * 2f);
			}
			return Color.Lerp(InfectionSpitter.s_glowColor, OrangeGlowBase, (1f - healthRel) * 2f);
		}

		public static Color32 GetPopColor(float healthRel)
		{
			//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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			healthRel = Mathf.Clamp01(healthRel);
			if (!(healthRel >= 0.5f))
			{
				return Color32.Lerp(OrangePopColor, RedPopColor, (0.5f - healthRel) * 2f);
			}
			return Color32.Lerp(GreenPopColor, OrangePopColor, (1f - healthRel) * 2f);
		}

		public static void ApplyBodyGlow(InfectionSpitter spitter, Color color)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (_visualsBroken)
			{
				return;
			}
			try
			{
				if (!((Object)(object)spitter == (Object)null) && spitter.m_propBlock != null && !((Object)(object)spitter.m_renderer == (Object)null))
				{
					spitter.m_propBlock.SetColor(InfectionSpitter.s_glowColorID, color);
					spitter.m_renderer.SetPropertyBlock(spitter.m_propBlock, 0);
				}
			}
			catch (ObjectCollectedException)
			{
			}
			catch (Exception ex2)
			{
				BreakVisuals(ex2);
			}
		}

		public static void ApplyDamageTint(InfectionSpitter spitter, float healthRel)
		{
			//IL_0002: 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)
			ApplyBodyGlow(spitter, GetBaseGlow(healthRel) * 0.6f);
		}

		public static void ApplyDamageRamp(InfectionSpitter spitter, float healthRel)
		{
			//IL_000c: 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)
			if (!((Object)(object)spitter == (Object)null))
			{
				ApplyBodyGlow(spitter, GetBaseGlow(healthRel) * spitter.m_explodeProgression);
			}
		}

		public static void ApplyDeathRamp(InfectionSpitter spitter)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)spitter == (Object)null))
			{
				ApplyBodyGlow(spitter, RedGlowBase * spitter.m_explodeProgression);
			}
		}

		public static void ResetBodyGlow(InfectionSpitter spitter)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			ApplyBodyGlow(spitter, InfectionSpitter.s_startGlowColor);
		}

		public static void TintPop(ushort index, InfectionSpitter spitter, Color32 color, bool recolorLiveParticles, bool untilFinalize)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (_visualsBroken)
			{
				return;
			}
			for (int i = 1; i <= 3; i++)
			{
				try
				{
					TintPopCore(index, spitter, color, recolorLiveParticles, untilFinalize);
					return;
				}
				catch (ObjectCollectedException)
				{
				}
				catch (Exception ex2)
				{
					BreakVisuals(ex2);
					return;
				}
			}
			Plugin.Logger.LogDebug((object)"[SpitterKill] FX tint skipped (IL2CPP GC race persisted)");
		}

		private static void TintPopCore(ushort index, InfectionSpitter spitter, Color32 color, bool recolorLiveParticles, bool untilFinalize)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			FX_EffectBase_Poolable val = ((spitter != null) ? spitter.m_fx : null);
			if ((Object)(object)val == (Object)null || val.m_inPool)
			{
				return;
			}
			Il2CppArrayBase<ParticleSystem> componentsInChildren = ((Component)val).GetComponentsInChildren<ParticleSystem>(true);
			if (componentsInChildren == null)
			{
				return;
			}
			foreach (ParticleSystem item in componentsInChildren)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)item).GetInstanceID();
				if (!_tintedSystems.TryGetValue(instanceID, out var value))
				{
					MainModule main = item.main;
					value = new OriginalStartColor(item, main, main.startColor);
					_tintedSystems[instanceID] = value;
				}
				WriteStartColor(value.Main, Color32.op_Implicit(color));
				if (recolorLiveParticles)
				{
					RecolorLiveParticles(item, color);
				}
				if (untilFinalize)
				{
					_pendingRestores[instanceID] = float.MaxValue;
					if (!_tintedBySpitter.TryGetValue(index, out List<int> value2))
					{
						value2 = (_tintedBySpitter[index] = new List<int>());
					}
					if (!value2.Contains(instanceID))
					{
						value2.Add(instanceID);
					}
				}
				else
				{
					_pendingRestores[instanceID] = Clock.Time + 3f;
				}
			}
		}

		private static void WriteStartColor(MainModule main, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			WriteStartColor(main, (ParticleSystemGradientMode)0, color, color, null, null);
		}

		private static void WriteStartColor(MainModule main, ParticleSystemGradientMode mode, Color colorMin, Color colorMax, Gradient? gradientMin, Gradient? gradientMax)
		{
			//IL_001b: 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_0039: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			object obj = _sharedGradient;
			if (obj == null)
			{
				MinMaxGradient val = new MinMaxGradient(Color.white);
				_sharedGradient = val;
				obj = (object)val;
			}
			MinMaxGradient val2 = (MinMaxGradient)obj;
			val2.m_Mode = mode;
			val2.m_GradientMin = gradientMin;
			val2.m_GradientMax = gradientMax;
			val2.m_ColorMin = colorMin;
			val2.m_ColorMax = colorMax;
			main.startColor = val2;
		}

		private static void RecolorLiveParticles(ParticleSystem ps, Color32 color)
		{
			//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_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_0037: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			int particleCount = ps.particleCount;
			if (particleCount > 0)
			{
				Il2CppStructArray<Particle> val = new Il2CppStructArray<Particle>((long)particleCount);
				int particles = ps.GetParticles(val);
				for (int i = 0; i < particles; i++)
				{
					Particle val2 = ((Il2CppArrayBase<Particle>)(object)val)[i];
					((Particle)(ref val2)).startColor = new Color32(color.r, color.g, color.b, byte.MaxValue);
					((Il2CppArrayBase<Particle>)(object)val)[i] = val2;
				}
				ps.SetParticles(val, particles);
			}
		}

		public static void TickRestores()
		{
			if (_pendingRestores.Count == 0)
			{
				return;
			}
			List<int> list = null;
			foreach (var (item, num3) in _pendingRestores)
			{
				if (num3 != float.MaxValue && Clock.Time >= num3)
				{
					(list ?? (list = new List<int>())).Add(item);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item2 in list)
			{
				RestoreSystem(item2);
			}
		}

		public static void RestoreForSpitter(ushort index)
		{
			if (!_tintedBySpitter.TryGetValue(index, out List<int> value))
			{
				return;
			}
			foreach (int item in value)
			{
				RestoreSystem(item);
			}
			_tintedBySpitter.Remove(index);
		}

		public static void RestoreAll()
		{
			foreach (int item in _tintedSystems.Keys.ToList())
			{
				RestoreSystem(item);
			}
			_tintedSystems.Clear();
			_pendingRestores.Clear();
			_tintedBySpitter.Clear();
		}

		private static void RestoreSystem(int id)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_002f: 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)
			if (_tintedSystems.TryGetValue(id, out var value))
			{
				bool flag = default(bool);
				for (int i = 1; i <= 3; i++)
				{
					try
					{
						if ((Object)(object)value.Ps != (Object)null)
						{
							WriteStartColor(value.Main, value.Mode, value.ColorMin, value.ColorMax, value.GradientMin, value.GradientMax);
						}
					}
					catch (ObjectCollectedException)
					{
						if (i == 3)
						{
							Plugin.Logger.LogDebug((object)"[SpitterKill] FX tint restore skipped (IL2CPP GC race persisted)");
						}
						continue;
					}
					catch (Exception ex2)
					{
						ManualLogSource logger = Plugin.Logger;
						BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Failed to restore FX tint: ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex2.Message);
						}
						logger.LogWarning(val);
					}
					break;
				}
			}
			_tintedSystems.Remove(id);
			_pendingRestores.Remove(id);
		}

		private static void BreakVisuals(Exception ex)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!_visualsBroken)
			{
				_visualsBroken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(96, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Damage-state visuals failed, disabling visuals locally (kill feature unaffected): ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogWarning(val);
			}
		}
	}
}
namespace KillableSpitters.Patches.Compat
{
	internal static class EWCCompat
	{
		internal const string PluginGuid = "Dinorush.ExtraWeaponCustomization";

		internal static void Init(Harmony harmony)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			if (!((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("Dinorush.ExtraWeaponCustomization", out var value))
			{
				Plugin.Logger.LogDebug((object)"[EWCCompat] ExtraWeaponCustomization not present, compat patches skipped");
				return;
			}
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(71, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[EWCCompat] ExtraWeaponCustomization ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Version>(value.Metadata.Version);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" detected, applying compat patches");
			}
			logger.LogInfo(val);
			TryApply(harmony, "explosion", Fix_EWCExplosion.Apply);
			TryApply(harmony, "foam", Fix_EWCFoam.Apply);
		}

		private static void TryApply(Harmony harmony, string name, Action<Harmony> apply)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			bool flag = default(bool);
			try
			{
				ApplyIsolated(harmony, apply);
				ManualLogSource logger = Plugin.Logger;
				BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(33, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[EWCCompat] ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" compat patch applied");
				}
				logger.LogDebug(val);
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(103, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[EWCCompat] ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(name);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" compat patch failed (EWC version mismatch?), that feature stays ");
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("EWC-default for spitters: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				logger2.LogWarning(val2);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void ApplyIsolated(Harmony harmony, Action<Harmony> apply)
		{
			apply(harmony);
		}
	}
	internal static class Fix_EWCExplosion
	{
		private static bool _broken;

		private static bool _applied;

		internal static void Apply(Harmony harmony)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			if (!_applied)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(ExplosionManager), "DoExplosionDamage", (Type[])null, (Type[])null) ?? throw new MissingMethodException("EWC ExplosionManager.DoExplosionDamage not found");
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Fix_EWCExplosion), "Post_DoExplosionDamage", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_applied = true;
			}
		}

		public static void Post_DoExplosionDamage(Vector3 position, Explosive explosiveBase, float triggerAmt)
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			//IL_0040: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			if (_broken)
			{
				return;
			}
			try
			{
				float radius = explosiveBase.Radius;
				if (radius <= 0f)
				{
					return;
				}
				float maxDamage = explosiveBase.MaxDamage;
				float minDamage = explosiveBase.MinDamage;
				if (maxDamage == 0f && minDamage == 0f)
				{
					return;
				}
				Il2CppReferenceArray<Collider> val = Physics.OverlapSphere(position, radius, LayerManager.MASK_EXPLOSION_TARGETS);
				HashSet<IntPtr> hashSet = null;
				for (int i = 0; i < ((Il2CppArrayBase<Collider>)(object)val).Length; i++)
				{
					Collider val2 = ((Il2CppArrayBase<Collider>)(object)val)[i];
					if (!SpitterColliders.TryGetDamage(val2, out InfectionSpitterDamage damage))
					{
						continue;
					}
					if (hashSet == null)
					{
						hashSet = new HashSet<IntPtr>();
					}
					if (!hashSet.Add(((Il2CppObjectBase)damage).Pointer))
					{
						continue;
					}
					InfectionSpitter spitter = damage.m_spitter;
					if ((Object)(object)spitter == (Object)null || SpitterKillManager.IsDeadOrDying(spitter.m_spitterIndex))
					{
						continue;
					}
					Bounds bounds = val2.bounds;
					Vector3 val3 = ((Bounds)(ref bounds)).ClosestPoint(position);
					if (!Physics.Linecast(position, val3, LayerManager.MASK_EXPLOSION_BLOCKERS))
					{
						float num = MapInverted(Vector3.Distance(position, val3), explosiveBase.InnerRadius, radius, maxDamage, minDamage, explosiveBase.Exponent);
						num *= triggerAmt;
						if (!(num <= 0f))
						{
							damage.ExplosionDamage(num, position, Vector3.zero, 0u);
						}
					}
				}
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(81, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[EWCExplosion] Explosion sweep failed, EWC explosions no longer damage spitters: ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val4);
			}
		}

		private static float MapInverted(float distance, float innerRadius, float radius, float maxDamage, float minDamage, float exponent)
		{
			if (innerRadius == radius)
			{
				if (!(distance < innerRadius))
				{
					return minDamage;
				}
				return maxDamage;
			}
			float num = radius - innerRadius;
			float num2 = Math.Clamp(distance - innerRadius, 0f, num);
			if (exponent != 1f)
			{
				return (float)Math.Pow((num - num2) / num, exponent) * (maxDamage - minDamage) + minDamage;
			}
			return (num - num2) / num * (maxDamage - minDamage) + minDamage;
		}
	}
	internal static class Fix_EWCFoam
	{
		private static class Cache
		{
			internal static readonly FieldRef<Effect, DamageType> BlacklistType = AccessTools.FieldRefAccess<Effect, DamageType>("_blacklistType");
		}

		private static bool _broken;

		private static bool _applied;

		internal static void Apply(Harmony harmony)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			if (!_applied)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Foam), "TriggerApply", (Type[])null, (Type[])null) ?? throw new MissingMethodException("EWC Foam.TriggerApply not found");
				ConstructorInfo constructorInfo = AccessTools.Constructor(typeof(Foam), Type.EmptyTypes, false) ?? throw new MissingMethodException("EWC Foam constructor not found");
				_ = Cache.BlacklistType;
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Fix_EWCFoam), "Pre_TriggerApply", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)constructorInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Fix_EWCFoam), "Post_FoamCtor", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_applied = true;
			}
		}

		public static void Post_FoamCtor(Foam __instance)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_0051: 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)
			if (_broken)
			{
				return;
			}
			try
			{
				ref DamageType reference = ref Cache.BlacklistType.Invoke((Effect)(object)__instance);
				reference = (DamageType)((uint)reference & 0xFFFF7FFFu);
				TriggerCoordinator trigger = ((Effect)__instance).Trigger;
				if (trigger == null)
				{
					return;
				}
				foreach (ITrigger trigger2 in ((TriggerHolder)trigger.Activate).Triggers)
				{
					IDamageTypeTrigger val = (IDamageTypeTrigger)(object)((trigger2 is IDamageTypeTrigger) ? trigger2 : null);
					if (val != null)
					{
						val.BlacklistType = (DamageType)(val.BlacklistType & -32769);
					}
				}
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(71, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[EWCFoam] Blacklist relax failed, EWC foam no longer affects spitters: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val2);
			}
		}

		public static void Pre_TriggerApply(Foam __instance, List<TriggerContext> triggerList)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			if (_broken)
			{
				return;
			}
			try
			{
				foreach (TriggerContext trigger in triggerList)
				{
					WeaponTriggerContext context = trigger.context;
					WeaponHitDamageableContextBase val = (WeaponHitDamageableContextBase)(object)((context is WeaponHitDamageableContextBase) ? context : null);
					if (val == null || (((WeaponDamageTypeContext)val).DamageType & 0x8000) == 0)
					{
						continue;
					}
					IDamageable damageable = val.Damageable;
					InfectionSpitterDamage val2 = ((damageable != null) ? ((Il2CppObjectBase)damageable).TryCast<InfectionSpitterDamage>() : null);
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					InfectionSpitter spitter = val2.m_spitter;
					if (!((Object)(object)spitter == (Object)null) && !SpitterKillManager.IsDeadOrDying(spitter.m_spitterIndex))
					{
						float num = trigger.triggerAmt * (__instance.IgnoreFalloff ? 1f : ((WeaponHitContextBase)val).Falloff);
						if (!(Math.Max(__instance.Amount, __instance.BubbleAmount) * num <= 0f))
						{
							spitter.OnIncomingGlue();
						}
					}
				}
			}
			catch (Exception ex)
			{
				_broken = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(74, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[EWCFoam] Spitter foam apply failed, EWC foam no longer affects spitters: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val3);
			}
		}
	}
}
namespace KillableSpitters.Events
{
	public struct SpitterDamageEvent
	{
		public ushort SpitterIndex { get; set; }

		public float Damage { get; set; }
	}
	public struct SpitterHealthEvent
	{
		public ushort SpitterIndex { get; set; }

		public float HealthRel { get; set; }
	}
}