Decompiled source of NeckSnappingAndForceChoke v1.0.0

NeckSnappingAndForceChoke.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BrutalistickChoke;
using Il2Cpp;
using Il2CppActiveRagdoll;
using Il2CppHurricaneVR.Framework.Core;
using Il2CppHurricaneVR.Framework.Core.Grabbers;
using Il2CppHurricaneVR.Framework.Shared;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MelonLoader;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Plugin), "Neck Snapping and Force Choke", "1.0.0", "misha", null)]
[assembly: MelonGame("Kubunautilus", "BrutalistickVR")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("NeckSnappingAndForceChoke")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("NeckSnappingAndForceChoke")]
[assembly: AssemblyTitle("NeckSnappingAndForceChoke")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BrutalistickChoke
{
	public class Plugin : MelonMod
	{
		private class Choke
		{
			public StickHealth health;

			public ColliderBridge bridge;

			public Rigidbody[] twitch;

			public ConfigurableJoint[] knees;

			public Quaternion[] kneeBase;

			public Vector3[] kneeAxis;

			public ActRagJoint[] kneeScripts;

			public bool[] kneeScriptWas;

			public ConstantForce pelvis;

			public bool pelvisWasEnabled;

			public bool wasChoking;

			public float nextTick;

			public float nextBreath;

			public float nextLog;

			public float nextConvulse;

			public float nextTwitch;

			public float nextUnbalance;
		}

		private const string Tag = "Choke";

		private const float DamagePerSecond = 12f;

		private const float TickSeconds = 0.5f;

		private const float LogInterval = 2f;

		private const float ConvulseInterval = 0.7f;

		private const bool Unbalance = true;

		private const float UnbalanceInterval = 1.2f;

		private const bool Twitch = true;

		private const float TwitchInterval = 0.12f;

		private const float TwitchStrength = 2.2f;

		private const float LegBendPeriod = 1.1f;

		private const float LegBendAngle = 45f;

		private const bool KillPelvisForce = true;

		private const float BreathInterval = 1.3f;

		private const string NeckRegion = "neck";

		private readonly List<HVRHandGrabber> hands = new List<HVRHandGrabber>();

		private readonly Dictionary<int, Choke> active = new Dictionary<int, Choke>();

		private readonly HashSet<int> stillHeld = new HashSet<int>();

		private float nextScan;

		private bool secondaryWasDown;

		public override void OnInitializeMelon()
		{
			MelonLogger.Msg($"{"Choke"}: loaded, {12f} dmg/s, B breaks the neck");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			foreach (KeyValuePair<int, Choke> item in active)
			{
				EndChoke(item.Value);
			}
			hands.Clear();
			active.Clear();
			stillHeld.Clear();
			nextScan = 0f;
		}

		public override void OnUpdate()
		{
			EnsureHands();
			stillHeld.Clear();
			for (int i = 0; i < hands.Count; i++)
			{
				HVRHandGrabber val = hands[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				HVRGrabbable val2 = (((HVRGrabberBase)val).IsGrabbing ? ((HVRGrabberBase)val).HeldObject : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				ColliderBridge componentInParent = ((Component)val2).GetComponentInParent<ColliderBridge>();
				if ((Object)(object)componentInParent == (Object)null || !IsNeck(componentInParent.damageRegion))
				{
					continue;
				}
				StickHealth val3 = componentInParent.stickHealth;
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = ((Component)val2).GetComponentInParent<StickHealth>();
				}
				if (!((Object)(object)val3 == (Object)null) && !val3.isDead)
				{
					int instanceID = ((Object)val3).GetInstanceID();
					if (!active.TryGetValue(instanceID, out var value))
					{
						value = new Choke
						{
							health = val3,
							bridge = componentInParent
						};
						value.twitch = FindTwitchBodies(val3);
						FindKnees(value);
						active[instanceID] = value;
						BeginChoke(value);
					}
					stillHeld.Add(instanceID);
					Tick(value);
				}
			}
			DropReleased();
			CheckNeckSnap();
		}

		private void BeginChoke(Choke c)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			StickHealth health = c.health;
			c.wasChoking = health.isChoking;
			health.isChoking = true;
			c.pelvis = health.pelvisForce;
			if ((Object)(object)c.pelvis != (Object)null)
			{
				c.pelvisWasEnabled = ((Behaviour)c.pelvis).enabled;
			}
			for (int i = 0; i < c.kneeScripts.Length; i++)
			{
				ActRagJoint val = c.kneeScripts[i];
				if (!((Object)(object)val == (Object)null))
				{
					c.kneeScriptWas[i] = ((Behaviour)val).enabled;
					((Behaviour)val).enabled = false;
				}
			}
			int num;
			if (c.knees.Length == 0)
			{
				num = 0;
			}
			else
			{
				JointDrive slerpDrive = c.knees[0].slerpDrive;
				num = Mathf.RoundToInt(((JointDrive)(ref slerpDrive)).positionSpring);
			}
			int value = num;
			MelonLogger.Msg($"{"Choke"}: choking '{((Object)health).name}', hp={health.StickCurrentHealth:F1}, twitchRbs={c.twitch.Length}, kneeJoints={c.knees.Length}, kneeSpring={value}, pelvisForce={(Object)(object)c.pelvis != (Object)null}");
		}

		private void EndChoke(Choke c)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			StickHealth health = c.health;
			MelonLogger.Msg($"{"Choke"}: stop, hp={(((Object)(object)health == (Object)null) ? 0f : health.StickCurrentHealth):F1}");
			if ((Object)(object)health == (Object)null)
			{
				return;
			}
			health.isChoking = c.wasChoking;
			if ((Object)(object)c.pelvis != (Object)null)
			{
				((Behaviour)c.pelvis).enabled = c.pelvisWasEnabled;
			}
			for (int i = 0; i < c.knees.Length; i++)
			{
				if ((Object)(object)c.knees[i] != (Object)null)
				{
					c.knees[i].targetRotation = c.kneeBase[i];
				}
				ActRagJoint val = c.kneeScripts[i];
				if ((Object)(object)val != (Object)null)
				{
					((Behaviour)val).enabled = c.kneeScriptWas[i];
				}
			}
		}

		private void Tick(Choke c)
		{
			float time = Time.time;
			if (time >= c.nextTick)
			{
				c.nextTick = time + 0.5f;
				c.health.ReceivedGeneralDamage(6f, (DamageType)12, "neck", (ColliderBridge)null);
			}
			if (time >= c.nextConvulse)
			{
				c.nextConvulse = time + 0.7f;
				c.health.StartFlailingOrProtecting();
			}
			if ((Object)(object)c.pelvis != (Object)null)
			{
				((Behaviour)c.pelvis).enabled = false;
			}
			if (time >= c.nextUnbalance)
			{
				c.nextUnbalance = time + 1.2f;
				c.health.ForceUnbalance();
			}
			if (time >= c.nextTwitch)
			{
				c.nextTwitch = time + 0.12f;
				Spin(c.twitch, 2.2f);
			}
			if (time >= c.nextBreath)
			{
				c.nextBreath = time + 1.3f;
				Breath(c.health.enemyVoiceManager);
			}
			if (time >= c.nextLog)
			{
				c.nextLog = time + 2f;
				MelonLogger.Msg($"{"Choke"}: hp={c.health.StickCurrentHealth:F1} dead={c.health.isDead}");
			}
		}

		public override void OnLateUpdate()
		{
			ApplyBend();
		}

		public override void OnFixedUpdate()
		{
			ApplyBend();
		}

		private void ApplyBend()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (active.Count == 0)
			{
				return;
			}
			float num = 0.5f - 0.5f * Mathf.Cos(Time.time * (float)Math.PI * 2f / 1.1f);
			float num2 = 45f * num;
			foreach (KeyValuePair<int, Choke> item in active)
			{
				Choke value = item.Value;
				for (int i = 0; i < value.knees.Length; i++)
				{
					ConfigurableJoint val = value.knees[i];
					if (!((Object)(object)val == (Object)null))
					{
						val.targetRotation = value.kneeBase[i] * Quaternion.AngleAxis(num2, value.kneeAxis[i]);
					}
				}
			}
		}

		private static void Spin(Rigidbody[] bodies, float strength)
		{
			//IL_0046: 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)
			Vector3 val2 = default(Vector3);
			foreach (Rigidbody val in bodies)
			{
				if (!((Object)(object)val == (Object)null))
				{
					((Vector3)(ref val2))..ctor(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f));
					val.AddTorque(val2 * strength, (ForceMode)2);
				}
			}
		}

		private static void Breath(EnemyVoiceManager voice)
		{
			if ((Object)(object)voice == (Object)null)
			{
				return;
			}
			AudioSource voiceSource = voice.voiceSource;
			if ((Object)(object)voiceSource != (Object)null && voiceSource.isPlaying)
			{
				return;
			}
			voice.PlayChokingSound();
			if ((Object)(object)voiceSource != (Object)null && voiceSource.isPlaying)
			{
				return;
			}
			VoiceSoundBank activeSoundBank = voice.activeSoundBank;
			if (!((Object)(object)activeSoundBank == (Object)null))
			{
				Il2CppReferenceArray<AudioClip> val = activeSoundBank.chokingSounds;
				if (val == null || ((Il2CppArrayBase<AudioClip>)(object)val).Length == 0)
				{
					val = activeSoundBank.upperChestTraumaDeath;
				}
				if (val == null || ((Il2CppArrayBase<AudioClip>)(object)val).Length == 0)
				{
					val = activeSoundBank.slitThroatDeath;
				}
				if (val == null || ((Il2CppArrayBase<AudioClip>)(object)val).Length == 0)
				{
					val = activeSoundBank.majorHurt;
				}
				if (val != null && ((Il2CppArrayBase<AudioClip>)(object)val).Length > 0)
				{
					voice.PlayRandomClip(val, false);
				}
			}
		}

		private void DropReleased()
		{
			if (active.Count == 0)
			{
				return;
			}
			List<int> list = null;
			foreach (KeyValuePair<int, Choke> item in active)
			{
				if (!stillHeld.Contains(item.Key) || item.Value.health.isDead)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(item.Key);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					EndChoke(active[list[i]]);
					active.Remove(list[i]);
				}
			}
		}

		private void CheckNeckSnap()
		{
			//IL_0002: 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)
			bool flag = HVRController.GetButtonState((HVRHandSide)1, (HVRButtons)4).Active || HVRController.GetButtonState((HVRHandSide)0, (HVRButtons)4).Active;
			bool num = flag && !secondaryWasDown;
			secondaryWasDown = flag;
			if (!num)
			{
				return;
			}
			for (int i = 0; i < hands.Count; i++)
			{
				HVRHandGrabber val = hands[i];
				if ((Object)(object)val == (Object)null || !((HVRGrabberBase)val).IsGrabbing || (Object)(object)((HVRGrabberBase)val).HeldObject == (Object)null)
				{
					continue;
				}
				HVRGrabbable heldObject = ((HVRGrabberBase)val).HeldObject;
				ColliderBridge componentInParent = ((Component)heldObject).GetComponentInParent<ColliderBridge>();
				if ((Object)(object)componentInParent == (Object)null || !IsNeck(componentInParent.damageRegion))
				{
					continue;
				}
				StickJointBreaker val2 = FindNeckBreaker(heldObject);
				if ((Object)(object)val2 != (Object)null && !val2.isBroken)
				{
					val2.BreakJoint();
					StickHealth stickHealth = val2.stickHealth;
					MelonLogger.Msg("Choke: neck joint broken on '" + ((stickHealth != null) ? ((Object)stickHealth).name : null) + "'");
					continue;
				}
				StickHealth val3 = componentInParent.stickHealth;
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = ((Component)heldObject).GetComponentInParent<StickHealth>();
				}
				if ((Object)(object)val3 != (Object)null && !val3.isDead)
				{
					val3.ReceivedGeneralDamage(200f, (DamageType)6, "neck", componentInParent);
					MelonLogger.Msg("Choke: neck break damage on '" + ((Object)val3).name + "' (no joint found)");
				}
			}
		}

		private static Rigidbody[] FindTwitchBodies(StickHealth health)
		{
			Il2CppReferenceArray<GameObject> bodyParts = health.bodyParts;
			List<Rigidbody> list = new List<Rigidbody>();
			if (bodyParts != null)
			{
				for (int i = 0; i < ((Il2CppArrayBase<GameObject>)(object)bodyParts).Length; i++)
				{
					GameObject val = ((Il2CppArrayBase<GameObject>)(object)bodyParts)[i];
					if (!((Object)(object)val == (Object)null))
					{
						Rigidbody component = val.GetComponent<Rigidbody>();
						if ((Object)(object)component != (Object)null && !component.isKinematic)
						{
							list.Add(component);
						}
					}
				}
			}
			return list.ToArray();
		}

		private static void FindKnees(Choke c)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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)
			StickHealth health = c.health;
			List<ActRagJoint> list = new List<ActRagJoint>();
			List<ConfigurableJoint> list2 = new List<ConfigurableJoint>();
			List<Quaternion> list3 = new List<Quaternion>();
			List<Vector3> list4 = new List<Vector3>();
			Il2CppReferenceArray<ActRagJoint> actRagJoints = health.actRagJoints;
			Vector3 right = ((Component)health).transform.right;
			Vector3 val2;
			if (actRagJoints != null)
			{
				for (int i = 0; i < ((Il2CppArrayBase<ActRagJoint>)(object)actRagJoints).Length; i++)
				{
					ActRagJoint val = ((Il2CppArrayBase<ActRagJoint>)(object)actRagJoints)[i];
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					string text = RegionOf(val);
					if (text != null && text.IndexOf("knee", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						ConfigurableJoint target = val.target;
						if (!((Object)(object)target == (Object)null))
						{
							list.Add(val);
							list2.Add(target);
							list3.Add(target.targetRotation);
							val2 = ((Component)target).transform.InverseTransformDirection(right);
							list4.Add(((Vector3)(ref val2)).normalized);
						}
					}
				}
			}
			if (list2.Count == 0)
			{
				Il2CppReferenceArray<ConfigurableJoint> configurableJoints = health.configurableJoints;
				int[] array = new int[2] { 8, 11 };
				if (configurableJoints != null)
				{
					foreach (int num in array)
					{
						if (num < ((Il2CppArrayBase<ConfigurableJoint>)(object)configurableJoints).Length)
						{
							ConfigurableJoint val3 = ((Il2CppArrayBase<ConfigurableJoint>)(object)configurableJoints)[num];
							if (!((Object)(object)val3 == (Object)null))
							{
								list.Add(null);
								list2.Add(val3);
								list3.Add(val3.targetRotation);
								val2 = ((Component)val3).transform.InverseTransformDirection(right);
								list4.Add(((Vector3)(ref val2)).normalized);
							}
						}
					}
				}
			}
			c.knees = list2.ToArray();
			c.kneeBase = list3.ToArray();
			c.kneeAxis = list4.ToArray();
			c.kneeScripts = list.ToArray();
			c.kneeScriptWas = new bool[c.kneeScripts.Length];
		}

		private static string RegionOf(ActRagJoint aj)
		{
			ColliderBridge val = ((Component)aj).GetComponent<ColliderBridge>();
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)aj).GetComponentInParent<ColliderBridge>();
			}
			if ((Object)(object)val == (Object)null && (Object)(object)aj.target != (Object)null)
			{
				val = ((Component)aj.target).GetComponent<ColliderBridge>();
			}
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.damageRegion;
		}

		private static StickJointBreaker FindNeckBreaker(HVRGrabbable obj)
		{
			Transform val = ((Component)obj).transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 12)
			{
				StickJointBreaker component = ((Component)val).GetComponent<StickJointBreaker>();
				if ((Object)(object)component != (Object)null && IsNeck(component.dmgRegion))
				{
					return component;
				}
				num++;
				val = val.parent;
			}
			return null;
		}

		private void EnsureHands()
		{
			if (hands.Count > 0 || Time.unscaledTime < nextScan)
			{
				return;
			}
			nextScan = Time.unscaledTime + 2f;
			Il2CppArrayBase<HVRHandGrabber> val = Object.FindObjectsOfType<HVRHandGrabber>();
			hands.Clear();
			for (int i = 0; i < val.Length; i++)
			{
				if ((Object)(object)val[i] != (Object)null)
				{
					hands.Add(val[i]);
				}
			}
			if (hands.Count > 0)
			{
				MelonLogger.Msg($"{"Choke"}: {hands.Count} hand grabber(s)");
			}
		}

		private static bool IsNeck(string region)
		{
			if (region != null)
			{
				return region.IndexOf("neck", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}
	}
}