Decompiled source of DeflateOnKill v0.1.0

DeflateOnKill.dll

Decompiled 3 hours ago
using System.Collections;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Librarby;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("UKMod template")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UKMod template")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("3d018c2f-f5bc-47be-a844-3e9888579d1f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DeflateOnKill;

internal static class Dying
{
	public static bool Is(EnemyIdentifier e)
	{
		if ((Object)(object)e == (Object)null)
		{
			return false;
		}
		if (e.dead || e.health <= 0f)
		{
			return true;
		}
		return (Object.op_Implicit((Object)(object)e.zombie) && e.zombie.health <= 0f) || (Object.op_Implicit((Object)(object)e.machine) && e.machine.health <= 0f) || (Object.op_Implicit((Object)(object)e.statue) && e.statue.health <= 0f);
	}
}
[HarmonyPatch]
internal static class GetGorePatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		return (from m in AccessTools.GetDeclaredMethods(typeof(BloodsplatterManager))
			where m.Name == "GetGore" && typeof(GameObject).IsAssignableFrom(m.ReturnType)
			select m).Cast<MethodBase>();
	}

	private static void Postfix(object[] __args, GameObject __result)
	{
		if (!Plugin.Enabled.Value || !Plugin.DeflateAllGore.Value || (Object)(object)__result == (Object)null)
		{
			return;
		}
		EnemyIdentifier val = null;
		foreach (object obj in __args)
		{
			EnemyIdentifier val2 = (EnemyIdentifier)((obj is EnemyIdentifier) ? obj : null);
			if (val2 != null)
			{
				val = val2;
				break;
			}
		}
		if (!((Object)(object)val == (Object)null) && !val.puppet && Dying.Is(val))
		{
			Plugin.DeflateTarget(__result.transform, "gore spawn");
		}
	}
}
[HarmonyPatch(typeof(EnemyIdentifier), "ProcessDeath")]
internal static class GoreWatchPatch
{
	private static void Postfix(EnemyIdentifier __instance)
	{
		if (Plugin.Enabled.Value && Plugin.DeflateAllGore.Value && !__instance.puppet)
		{
			((MonoBehaviour)Plugin.Instance).StartCoroutine(Watch(__instance));
		}
	}

	private static IEnumerator Watch(EnemyIdentifier eid)
	{
		GoreZone gz = eid.GetGoreZone();
		if ((Object)(object)gz == (Object)null)
		{
			yield break;
		}
		Vector3 origin = ((Component)eid).transform.position;
		float radiusSqr = Plugin.GoreRadius.Value * Plugin.GoreRadius.Value;
		List<Transform> zones = new List<Transform>();
		if ((Object)(object)gz.goreZone != (Object)null)
		{
			zones.Add(gz.goreZone);
		}
		if ((Object)(object)gz.gibZone != (Object)null)
		{
			zones.Add(gz.gibZone);
		}
		HashSet<int> seen = new HashSet<int>();
		foreach (Transform zone in zones)
		{
			foreach (Transform item in zone)
			{
				Transform c = item;
				seen.Add(((Object)c).GetInstanceID());
			}
		}
		float end = Time.time + Plugin.WatchSeconds.Value;
		int count = 0;
		while (Time.time < end && count < Plugin.MaxPerEnemy.Value)
		{
			yield return null;
			foreach (Transform zone2 in zones)
			{
				if ((Object)(object)zone2 == (Object)null)
				{
					continue;
				}
				foreach (Transform item2 in zone2)
				{
					Transform c2 = item2;
					if (!seen.Add(((Object)c2).GetInstanceID()))
					{
						continue;
					}
					Vector3 val = c2.position - origin;
					if (!(((Vector3)(ref val)).sqrMagnitude > radiusSqr))
					{
						Plugin.DeflateTarget(c2, "gore watcher");
						int num = count + 1;
						count = num;
						if (num >= Plugin.MaxPerEnemy.Value)
						{
							break;
						}
					}
				}
			}
		}
	}
}
internal static class Tags
{
	public static bool IsExtremity(GameObject go)
	{
		return go.CompareTag("Limb") || go.CompareTag("Head") || go.CompareTag("EndLimb");
	}

	public static bool IsAnyBodyPart(GameObject go)
	{
		return go.CompareTag("Body") || IsExtremity(go);
	}
}
internal static class Parts
{
	public static Transform NearestPart(EnemyIdentifier eid, Transform from)
	{
		Transform val = from;
		while ((Object)(object)val != (Object)null)
		{
			EnemyIdentifierIdentifier component = ((Component)val).GetComponent<EnemyIdentifierIdentifier>();
			if ((Object)(object)component != (Object)null && (Object)(object)component.eid == (Object)(object)eid)
			{
				return val;
			}
			if ((Object)(object)val == (Object)(object)((Component)eid).transform)
			{
				break;
			}
			val = val.parent;
		}
		return null;
	}

	public static Transform FindRootTorso(EnemyIdentifier eid)
	{
		Transform result = null;
		int num = int.MaxValue;
		bool flag = false;
		EnemyIdentifierIdentifier[] componentsInChildren = ((Component)eid).GetComponentsInChildren<EnemyIdentifierIdentifier>(true);
		foreach (EnemyIdentifierIdentifier val in componentsInChildren)
		{
			if (!((Object)(object)val.eid != (Object)(object)eid))
			{
				int num2 = 0;
				Transform val2 = ((Component)val).transform;
				while ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)((Component)eid).transform)
				{
					num2++;
					val2 = val2.parent;
				}
				bool flag2 = ((Component)val).gameObject.CompareTag("Body");
				if (num2 < num || (num2 == num && flag2 && !flag))
				{
					result = ((Component)val).transform;
					num = num2;
					flag = flag2;
				}
			}
		}
		return result;
	}
}
internal static class DeferredDeflate
{
	private static readonly Dictionary<Transform, string> pending = new Dictionary<Transform, string>();

	private static bool scheduled;

	public static void Queue(Transform t, string reason)
	{
		if (!((Object)(object)t == (Object)null))
		{
			if (!pending.ContainsKey(t))
			{
				pending[t] = reason;
			}
			if (!scheduled)
			{
				scheduled = true;
				((MonoBehaviour)Plugin.Instance).StartCoroutine(Flush());
			}
		}
	}

	private static IEnumerator Flush()
	{
		yield return (object)new WaitForEndOfFrame();
		scheduled = false;
		List<KeyValuePair<Transform, string>> batch = new List<KeyValuePair<Transform, string>>(pending);
		pending.Clear();
		foreach (KeyValuePair<Transform, string> kv in batch)
		{
			Plugin.DeflateTarget(kv.Key, kv.Value);
		}
	}
}
[HarmonyPatch(typeof(EnemyIdentifier), "DestroyLimb")]
internal static class DestroyLimbPatch
{
	internal class State
	{
		public List<Transform> Pieces;

		public Transform Parent;
	}

	private static void Prefix(EnemyIdentifier __instance, Transform limb, LimbDestroyType type, out State __state)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Invalid comparison between Unknown and I4
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Invalid comparison between Unknown and I4
		__state = null;
		if ((Object)(object)limb == (Object)null || (int)type == 0)
		{
			return;
		}
		__state = new State
		{
			Parent = Parts.NearestPart(__instance, limb.parent)
		};
		if ((int)type == 2)
		{
			__state.Pieces = new List<Transform>();
			CharacterJoint[] componentsInChildren = ((Component)limb).GetComponentsInChildren<CharacterJoint>();
			foreach (CharacterJoint val in componentsInChildren)
			{
				__state.Pieces.Add(((Component)val).transform);
			}
		}
	}

	private static void Postfix(EnemyIdentifier __instance, Transform limb, LimbDestroyType type, bool __result, State __state)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Invalid comparison between Unknown and I4
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Invalid comparison between Unknown and I4
		if (!Plugin.Enabled.Value || !__result || __state == null || __instance.puppet)
		{
			return;
		}
		if ((int)type == 1)
		{
			Plugin.DeflateTarget(limb, "detached limb");
		}
		else if ((int)type == 2 && __state.Pieces != null)
		{
			foreach (Transform piece in __state.Pieces)
			{
				Plugin.DeflateTarget(piece, "limb gib");
			}
		}
		if (Plugin.DeflateParents.Value && (Object)(object)__state.Parent != (Object)null && (Object)(object)__state.Parent != (Object)(object)limb)
		{
			DeferredDeflate.Queue(__state.Parent, "limb parent");
		}
	}
}
[HarmonyPatch(typeof(EnemyIdentifier), "ProcessDeath")]
internal static class ProcessDeathPatch
{
	private static void Postfix(EnemyIdentifier __instance)
	{
		if (Plugin.Enabled.Value && !__instance.puppet && (Plugin.DeflateAttached.Value || Plugin.DeflateRootTorso.Value))
		{
			((MonoBehaviour)Plugin.Instance).StartCoroutine(HandleCorpse(__instance));
		}
	}

	private static IEnumerator HandleCorpse(EnemyIdentifier eid)
	{
		yield return null;
		if (!((Object)(object)eid == (Object)null))
		{
			if (Plugin.DeflateAttached.Value)
			{
				DeflateAttachedLimbs(eid);
			}
			if (Plugin.DeflateRootTorso.Value)
			{
				Transform torso = Parts.FindRootTorso(eid);
				DeferredDeflate.Queue(torso, "root torso");
			}
		}
	}

	private static void DeflateAttachedLimbs(EnemyIdentifier eid)
	{
		HashSet<Transform> hashSet = new HashSet<Transform>();
		EnemyIdentifierIdentifier[] componentsInChildren = ((Component)eid).GetComponentsInChildren<EnemyIdentifierIdentifier>(true);
		foreach (EnemyIdentifierIdentifier val in componentsInChildren)
		{
			if (!((Object)(object)val.eid != (Object)(object)eid) && !((Object)(object)((Component)val).transform == (Object)(object)((Component)eid).transform) && Tags.IsExtremity(((Component)val).gameObject))
			{
				hashSet.Add(((Component)val).transform);
			}
		}
		List<Transform> list = new List<Transform>();
		foreach (Transform item in hashSet)
		{
			bool flag = false;
			Transform parent = item.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)((Component)eid).transform)
			{
				if (hashSet.Contains(parent))
				{
					flag = true;
					break;
				}
				parent = parent.parent;
			}
			if (!flag)
			{
				list.Add(item);
			}
		}
		foreach (Transform item2 in list)
		{
			if (!((Object)(object)item2 == (Object)null) && !eid.DestroyLimb(item2, (LimbDestroyType)1))
			{
				Plugin.DeflateTarget(item2, "attached limb");
			}
		}
	}
}
[HarmonyPatch(typeof(EnemyIdentifier), "Splatter")]
internal static class SplatterPatch
{
	private static void Postfix(EnemyIdentifier __instance)
	{
		if (!Plugin.Enabled.Value || !Plugin.DeflateOnSplatter.Value || __instance.puppet)
		{
			return;
		}
		EnemyIdentifierIdentifier[] value = Traverse.Create((object)__instance).Field("limbs").GetValue<EnemyIdentifierIdentifier[]>();
		if (value == null)
		{
			return;
		}
		EnemyIdentifierIdentifier[] array = value;
		foreach (EnemyIdentifierIdentifier val in array)
		{
			if (!((Object)(object)val == (Object)null) && !((Component)val).transform.IsChildOf(((Component)__instance).transform) && Tags.IsAnyBodyPart(((Component)val).gameObject))
			{
				Plugin.DeflateTarget(((Component)val).transform, "splatter gib");
			}
		}
	}
}
[BepInPlugin("com.nico.deflateonkill", "Deflate On Kill", "0.2.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.nico.deflateonkill";

	public const string PluginName = "Deflate On Kill";

	public const string PluginVersion = "0.2.0";

	internal static ManualLogSource Log;

	internal static Plugin Instance;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> DeflateAttached;

	internal static ConfigEntry<bool> DeflateOnSplatter;

	internal static ConfigEntry<bool> DeflateParents;

	internal static ConfigEntry<bool> DeflateRootTorso;

	internal static ConfigEntry<bool> DeflateAllGore;

	internal static ConfigEntry<float> GoreRadius;

	internal static ConfigEntry<float> WatchSeconds;

	internal static ConfigEntry<int> MaxPerEnemy;

	internal static ConfigEntry<bool> Verbose;

	private void Awake()
	{
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "enabled", true, "Deflate the gibs, limbs and gore particles of enemies.");
		DeflateAttached = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "deflate_attached_limbs", true, "On death, also deflate limbs still attached to the corpse (only the part closest to the torso).");
		DeflateOnSplatter = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "deflate_splatter_gibs", true, "Deflate every piece when an enemy is splattered.");
		DeflateParents = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "deflate_limb_parents", true, "When a limb detaches, also deflate the body part it was attached to.");
		DeflateRootTorso = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "deflate_root_torso", true, "On death, also deflate the root torso (the top-most body part of the enemy).");
		DeflateAllGore = ((BaseUnityPlugin)this).Config.Bind<bool>("Gore", "deflate_all_gore", true, "Deflate every gore object (gibs, blood particles) that spawns because an enemy is dying.");
		GoreRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Gore", "radius", 25f, "Only objects that appear within this distance of the death position are caught by the post-death watcher.");
		WatchSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Gore", "watch_seconds", 2f, "How long after death the watcher keeps looking for newly spawned gore.");
		MaxPerEnemy = ((BaseUnityPlugin)this).Config.Bind<int>("Gore", "max_per_enemy", 80, "Safety cap on how many objects the post-death watcher deflates per enemy (each one is a physics object).");
		Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "verbose", false, "Log every deflate this plugin triggers.");
		Log.LogInfo((object)"Deflate On Kill loaded!");
		Harmony val = new Harmony("com.nico.deflateonkill");
		val.PatchAll();
	}

	internal static void DeflateTarget(Transform t, string reason)
	{
		if (!((Object)(object)t == (Object)null) && LibrarbyAPI.Deflate(((Component)t).gameObject) && Verbose.Value)
		{
			Log.LogInfo((object)("Deflated " + ((Object)t).name + " (" + reason + ")"));
		}
	}
}