Decompiled source of KillableSpitters v1.2.0
KillableSpitters.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
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.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0+8baf7b6ec979d42048d99989761f21c74c781357")] [assembly: AssemblyProduct("KillableSpitters")] [assembly: AssemblyTitle("KillableSpitters")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.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.2.0")] [BepInProcess("GTFO.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency("Amor.AmorLib", ">=1.2.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BasePlugin { public const string Version = "1.2.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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0146: 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. Values below 1.0 are raised to 1.0. 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; if (Config_SpitterHealth < 1f) { Logger.LogWarning((object)($"SpitterHealth {Config_SpitterHealth} is below the minimum of 1.0 and will " + "be raised to 1.0")); } ((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; private static bool _foreignPatchSeen; [HarmonyPatch(typeof(GlueGunProjectile), "CollisionCheck")] [HarmonyFinalizer] public static Exception? Finalize_CollisionCheck(GlueGunProjectile __instance, Exception? __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!ForeignPatchPresent()) { return __exception; } bool flag; try { flag = SpitterColliders.IsSpitter(((StickingProjectileBase)__instance).m_projLastRayHitCollider); } catch (Exception value) { flag = true; if (!_loggedProbeFailure) { _loggedProbeFailure = true; Plugin.Logger.LogError((object)("[SpitterGlueCollisionCrash] Could not identify the glue collision target; suppressing this and every later NullReferenceException on GlueGunProjectile.CollisionCheck conservatively — a real crash may be hidden " + $"behind this until the probe works again: {value}")); } } 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; } private static bool ForeignPatchPresent() { if (_foreignPatchSeen) { return true; } bool flag; try { MethodInfo methodInfo = AccessTools.Method(typeof(GlueGunProjectile), "CollisionCheck", (Type[])null, (Type[])null); object obj; if (!(methodInfo != null)) { obj = null; } else { Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); obj = ((patchInfo != null) ? patchInfo.Owners : null); } List<string> list = ((IEnumerable<string>)obj)?.Where((string owner) => owner != "the_tavern-KillableSpitters").ToList() ?? new List<string>(); flag = list.Count > 0; Plugin.Logger.LogDebug((object)(flag ? ("[SpitterGlueCollisionCrash] Foreign CollisionCheck patch owner(s): " + string.Join(", ", list) + " — spitter NRE suppression armed") : "[SpitterGlueCollisionCrash] No foreign CollisionCheck patch — spitter NRE suppression disarmed")); } catch (Exception ex) { flag = true; Plugin.Logger.LogWarning((object)("[SpitterGlueCollisionCrash] Could not inspect CollisionCheck patches, arming the suppression conservatively: " + ex.Message)); } _foreignPatchSeen = flag; return flag; } } 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 try { if (SpitterKillManager.IsBroken || __instance.m_spitterIndex >= 7680) { return true; } 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), "DoGetGlued")] [HarmonyPrefix] public static bool Pre_DoGetGlued(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 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 byte GetMaskByte(int byteIndex) { if (byteIndex < 0 || byteIndex >= 240) { return 0; } fixed (byte* deadBits = _deadBits) { return deadBits[byteIndex]; } } } public struct SpitterHostConfigState { private byte _published; private byte _cfoamKills; public float FreezeDuration; public readonly bool IsPublished => _published != 0; public readonly bool CfoamKills => _cfoamKills != 0; public static SpitterHostConfigState FromHostConfig() { float config_SpitterFreezeDuration = Plugin.Config_SpitterFreezeDuration; return new SpitterHostConfigState { _published = 1, _cfoamKills = (Plugin.Config_CfoamKillsSpitters ? ((byte)1) : ((byte)0)), FreezeDuration = (float.IsFinite(config_SpitterFreezeDuration) ? Math.Max(0f, config_SpitterFreezeDuration) : 0f) }; } } 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 uint CONFIG_REPLICATOR_ID = 1263751256u; 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]; private static StateReplicator<SpitterHostConfigState>? _configReplicator; private static Action<SpitterHostConfigState, SpitterHostConfigState, bool>? _configHandler; private static SpitterHostConfigState _hostConfig; public static bool IsBroken => _broken; 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) || !_hostConfig.IsPublished) { return; } if (_hostConfig.CfoamKills) { if (SNet.IsMaster) { _gluedAt.TryAdd(spitterIndex, Clock.Time); } } else { spitter.m_stayInTimer = _hostConfig.FreezeDuration; } } } catch (Exception ex) { Break(ex); } } private static void TickGlueKill(ushort index) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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 < _hostConfig.FreezeDuration)) { _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)4); 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) { SetModelVisible(index, spitter, visible: false); } private static void ShowSpitterModel(ushort index, InfectionSpitter spitter) { SetModelVisible(index, spitter, visible: true); } private static void SetModelVisible(ushort index, InfectionSpitter spitter, bool visible) { if (visible) { _modelHidden.Remove(index); } else { _modelHidden.Add(index); } try { if ((Object)(object)spitter.m_renderer != (Object)null) { spitter.m_renderer.enabled = visible; } } catch (ObjectCollectedException) { } try { if ((Object)(object)spitter.m_light != (Object)null) { ((Behaviour)spitter.m_light).enabled = visible; } } catch (ObjectCollectedException) { } try { 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(visible); } } catch (ObjectCollectedException) { } } 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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; } } CreateConfigReplicator(); 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 CreateConfigReplicator() { SpitterHostConfigState spitterHostConfigState = SpitterHostConfigState.FromHostConfig(); if (SNet.IsMaster) { _hostConfig = spitterHostConfigState; } StateReplicator<SpitterHostConfigState> val = StateReplicator<SpitterHostConfigState>.Create(1263751256u, default(SpitterHostConfigState), (LifeTimeType)1, (IStateReplicatorHolder<SpitterHostConfigState>)null); if (val == null) { Plugin.Logger.LogError((object)"[SpitterKill] Failed to create the host-config replicator; clients keep vanilla glue"); return; } Action<SpitterHostConfigState, SpitterHostConfigState, bool> action = OnHostConfigChanged; val.OnStateChanged += action; _configHandler = action; _configReplicator = val; if (SNet.IsMaster) { val.SetState(spitterHostConfigState); } } private static void OnHostConfigChanged(SpitterHostConfigState oldState, SpitterHostConfigState newState, bool isRecall) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (_broken) { return; } try { if (newState.IsPublished) { _hostConfig = newState; ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(69, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[SpitterKill] Host C-foam config received (kills="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(newState.CfoamKills); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("freeze="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(newState.FreezeDuration); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("s, recall="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(isRecall); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } logger.LogDebug(val); } } 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; } } if (_configReplicator != null) { if (_configHandler != null) { _configReplicator.OnStateChanged -= _configHandler; } _configReplicator.Unload(); _configReplicator = null; _configHandler = 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(); _hostConfig = default(SpitterHostConfigState); _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; 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, float falloffMod, Explosive explosiveBase, float triggerAmt) { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013e: 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_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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 * falloffMod; 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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (_broken) { return; } try { HashSet<IntPtr> hashSet = null; 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; } if (hashSet == null) { hashSet = new HashSet<IntPtr>(); } if (!hashSet.Add(((Il2CppObj