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;
}
}
}