Latest versions of MelonLoader are known to have issues with some games. Use version 0.5.4 until the issue has been fixed!
Decompiled source of ProcedualAnimation v3.4.4
Procudeal aimation.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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 HarmonyLib; using MelonLoader; using ModThatIsNotMod.BoneMenu; using ModThatIsNotMod.MonoBehaviours; using Procudeal_aimation; using PuppetMasta; using StressLevelZero.AI; using StressLevelZero.Props.Weapons; using UnhollowerBaseLib; using UnhollowerRuntimeLib; using UnityEngine; using UnityEngine.Audio; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Main), "Procudeal aimation", "5.6.3", "Jeb", null)] [assembly: MelonGame("Stress Level Zero", "BONEWORKS")] [assembly: AssemblyTitle("Procudeal aimation")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Procudeal aimation")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e02c3ff9-db33-4862-8ec0-1d1790eb4fef")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [HarmonyPatch(typeof(PuppetMaster), "Resurrect", new Type[] { })] public class PuppetMasterResurrectPatch { [HarmonyPrefix] public static bool Prefix(PuppetMaster __instance) { if (!Main.modEnabled) { return true; } AIBrain componentInParent = ((Component)__instance).GetComponentInParent<AIBrain>(); if ((Object)(object)componentInParent == (Object)null) { return true; } AgonyLogic component = ((Component)componentInParent).GetComponent<AgonyLogic>(); if ((Object)(object)component != (Object)null && component.isSuffering) { return false; } return true; } } namespace ModThatIsNotMod.MonoBehaviours { public class OriginalPuppetProperties : MonoBehaviour { public float damper; public float weight; public float animSpeed; public StateSettings stateSettings; public OriginalPuppetProperties(IntPtr ptr) : base(ptr) { } } public class AgonyLogic : MonoBehaviour { private AIBrain _brain; private BehaviourPowerLegs _legsBehaviour; private List<Rigidbody> _limbs = new List<Rigidbody>(); private List<Rigidbody> _legs = new List<Rigidbody>(); private Rigidbody _pelvis; private Rigidbody _chest; private Rigidbody _head; private Transform _pelvisTransform; private Transform _chestTransform; private List<Rigidbody> _knees = new List<Rigidbody>(); private List<Rigidbody> _feet = new List<Rigidbody>(); private Dictionary<string, Transform> poseTargets = new Dictionary<string, Transform>(); private Dictionary<string, Quaternion> initialPoseRots = new Dictionary<string, Quaternion>(); public Rigidbody lastHitBone; public bool isSuffering = false; public float sufferingStartTime = 0f; private bool _isSmallEnemy = false; private bool _isInitialized = false; private AudioSource _agonySource; private string[] legKeywords = new string[6] { "foot", "calf", "thigh", "knee", "toe", "leg" }; private string[] armKeywords = new string[5] { "hand", "arm", "shoulder", "wrist", "elbow" }; private string[] coreKeywords = new string[3] { "chest", "spine", "neck" }; private string[] pelvisKeywords = new string[3] { "pelvis", "hip", "root" }; private PhysicMaterial _slipperyMaterial; private Dictionary<Collider, PhysicMaterial> _originalMaterials = new Dictionary<Collider, PhysicMaterial>(); private bool _isVocal = false; private float _audioCooldown = 0f; private int _lastAudioIndex = -1; public AgonyLogic(IntPtr ptr) : base(ptr) { } private void Awake() { _brain = ((Component)this).GetComponent<AIBrain>(); } public void OnEnable() { isSuffering = false; _isInitialized = false; lastHitBone = null; RestoreOriginalFriction(); Il2CppArrayBase<AudioSource> componentsInChildren = ((Component)((Component)this).transform).GetComponentsInChildren<AudioSource>(true); foreach (AudioSource item in componentsInChildren) { if (!((Object)(object)item == (Object)null)) { string text = ((Object)((Component)item).gameObject).name.ToLower(); if (!text.Contains("ambient") && !text.Contains("music") && !text.Contains("system") && !text.Contains("master")) { item.mute = false; item.volume = 1f; ((Behaviour)item).enabled = true; } } } Il2CppArrayBase<MonoBehaviour> componentsInChildren2 = ((Component)((Component)this).transform).GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour item2 in componentsInChildren2) { if (!((Object)(object)item2 == (Object)null)) { string name = ((object)item2).GetType().Name; string text2 = ((object)item2).GetType().Namespace ?? ""; if ((text2 == "ModThatIsNotMod.MonoBehaviours" || text2.Contains("SLZ")) && (name.IndexOf("sfx", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("vocal", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("voice", StringComparison.OrdinalIgnoreCase) >= 0)) { ((Behaviour)item2).enabled = true; } } } } public void ProcessHit() { if ((Object)(object)_brain == (Object)null || isSuffering) { return; } string text = ((Object)((Component)_brain).gameObject).name.ToLower(); if (text.Contains("crab") || text.Contains("headcrab") || text.Contains("omni") || text.Contains("rat") || text.Contains("turret")) { isSuffering = false; return; } AudioSource[] array = Il2CppArrayBase<AudioSource>.op_Implicit(((Component)_brain).GetComponentsInChildren<AudioSource>()); AudioSource[] array2 = array; foreach (AudioSource val in array2) { if ((Object)(object)val != (Object)(object)_agonySource) { val.Stop(); val.volume = 0f; } } if (!_isInitialized) { InitializeBodyParts(); _isInitialized = true; } initialPoseRots.Clear(); isSuffering = true; sufferingStartTime = Time.time; MelonCoroutines.Start(Struggle()); } public void MercyKill() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_0093: Unknown result type (might be due to invalid IL or missing references) float value = Random.value; if (value > 0.1f && _knees != null && _knees.Count > 0) { Rigidbody val = _knees[Random.Range(0, _knees.Count)]; if ((Object)(object)val != (Object)null) { float num = 0.8f; Vector3 val2 = _pelvisTransform.up * 1f + Random.onUnitSphere * 0.3f; val.AddForce(val2 * (Main.pullForce * num), (ForceMode)1); } } isSuffering = false; if ((Object)(object)_agonySource != (Object)null && _agonySource.isPlaying) { _agonySource.Stop(); } } private void InitializeBodyParts() { _isSmallEnemy = false; _limbs.Clear(); _legs.Clear(); _knees.Clear(); _feet.Clear(); PuppetMaster puppetMaster = _brain.puppetMaster; if ((Object)(object)puppetMaster == (Object)null || puppetMaster.muscles == null || ((Il2CppArrayBase<Muscle>)(object)puppetMaster.muscles).Length == 0) { return; } _legsBehaviour = ((Component)_brain).gameObject.GetComponent<BehaviourPowerLegs>(); if ((Object)(object)_legsBehaviour != (Object)null) { ((Behaviour)_legsBehaviour).enabled = false; } string text = ((Object)((Component)_brain).gameObject).name.ToLower(); if (text.Contains("crab") || text.Contains("headcrab") || text.Contains("omni") || text.Contains("rat") || text.Contains("turret")) { _isSmallEnemy = true; return; } _pelvis = ((Il2CppArrayBase<Muscle>)(object)puppetMaster.muscles)[0].rigidbody; foreach (Muscle item in (Il2CppArrayBase<Muscle>)(object)puppetMaster.muscles) { if ((Object)(object)item.rigidbody == (Object)null) { continue; } string text2 = ((Object)item.rigidbody).name.ToLower(); if (text2.Contains("head")) { _head = item.rigidbody; } string[] array = pelvisKeywords; foreach (string value in array) { if (text2.Contains(value)) { _pelvis = item.rigidbody; break; } } string[] array2 = coreKeywords; foreach (string value2 in array2) { if (text2.Contains(value2)) { _chest = item.rigidbody; break; } } bool flag = false; string[] array3 = legKeywords; foreach (string value3 in array3) { if (text2.Contains(value3)) { _legs.Add(item.rigidbody); flag = true; break; } } if (flag) { continue; } string[] array4 = armKeywords; foreach (string value4 in array4) { if (text2.Contains(value4)) { _limbs.Add(item.rigidbody); break; } } } foreach (Muscle item2 in (Il2CppArrayBase<Muscle>)(object)puppetMaster.muscles) { if ((Object)(object)item2.rigidbody == (Object)null) { continue; } string text3 = ((Object)item2.rigidbody).name.ToLower(); if (!text3.Contains("twist") && !text3.Contains("toe")) { if (text3.Contains("calf") || text3.Contains("lowerleg")) { _knees.Add(item2.rigidbody); } if (text3.Contains("foot")) { _feet.Add(item2.rigidbody); } } } if (_knees.Count < 2) { foreach (Muscle item3 in (Il2CppArrayBase<Muscle>)(object)puppetMaster.muscles) { if (!((Object)(object)item3.rigidbody == (Object)null)) { string text4 = ((Object)item3.rigidbody).name.ToLower(); if (!text4.Contains("twist") && !text4.Contains("toe") && (text4.Contains("knee") || (text4.Contains("leg") && !text4.Contains("up"))) && !_knees.Contains(item3.rigidbody)) { _knees.Add(item3.rigidbody); } } } } if (_feet.Count < 2) { foreach (Muscle item4 in (Il2CppArrayBase<Muscle>)(object)puppetMaster.muscles) { if (!((Object)(object)item4.rigidbody == (Object)null)) { string text5 = ((Object)item4.rigidbody).name.ToLower(); if (text5.Contains("ankle") && !_feet.Contains(item4.rigidbody)) { _feet.Add(item4.rigidbody); } } } } if ((Object)(object)_pelvis != (Object)null) { _pelvisTransform = ((Component)_pelvis).transform; } if ((Object)(object)_chest != (Object)null) { _chestTransform = ((Component)_chest).transform; } } private void CreateSlipperyMaterial() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if ((Object)(object)_slipperyMaterial == (Object)null) { _slipperyMaterial = new PhysicMaterial(); ((Object)_slipperyMaterial).name = "SlipperyLegs"; _slipperyMaterial.dynamicFriction = 0.05f; _slipperyMaterial.staticFriction = 0.05f; _slipperyMaterial.frictionCombine = (PhysicMaterialCombine)2; _slipperyMaterial.bounciness = 0f; _slipperyMaterial.bounceCombine = (PhysicMaterialCombine)2; ((Object)_slipperyMaterial).hideFlags = (HideFlags)32; } } private void ApplyLowFriction() { CreateSlipperyMaterial(); _originalMaterials.Clear(); foreach (Rigidbody leg in _legs) { if ((Object)(object)leg == (Object)null) { continue; } Il2CppArrayBase<Collider> componentsInChildren = ((Component)leg).GetComponentsInChildren<Collider>(true); foreach (Collider item in componentsInChildren) { if (!((Object)(object)item == (Object)null)) { if (!_originalMaterials.ContainsKey(item)) { _originalMaterials[item] = item.sharedMaterial; } item.sharedMaterial = _slipperyMaterial; } } } } private void RestoreOriginalFriction() { foreach (KeyValuePair<Collider, PhysicMaterial> originalMaterial in _originalMaterials) { if ((Object)(object)originalMaterial.Key != (Object)null) { originalMaterial.Key.sharedMaterial = originalMaterial.Value; } } _originalMaterials.Clear(); } private void SetupAudioSource() { if (!((Object)(object)_chest != (Object)null) || !((Object)(object)_agonySource == (Object)null)) { return; } _agonySource = ((Component)_chest).gameObject.AddComponent<AudioSource>(); _agonySource.spatialBlend = 1f; _agonySource.minDistance = 2f; _agonySource.maxDistance = 40f; _agonySource.rolloffMode = (AudioRolloffMode)0; _agonySource.playOnAwake = false; AudioMixer val = ((IEnumerable<AudioMixer>)Resources.FindObjectsOfTypeAll<AudioMixer>()).FirstOrDefault((Func<AudioMixer, bool>)((AudioMixer x) => ((Object)x).name == "Master")); if ((Object)(object)val != (Object)null) { Il2CppReferenceArray<AudioMixerGroup> val2 = val.FindMatchingGroups("SFX"); if (((Il2CppArrayBase<AudioMixerGroup>)(object)val2).Length > 0) { _agonySource.outputAudioMixerGroup = ((Il2CppArrayBase<AudioMixerGroup>)(object)val2)[0]; } } } private IEnumerator FadeOutAudio() { if (!((Object)(object)_agonySource == (Object)null)) { float startVol = _agonySource.volume; float fadeTime = 1.2f; float t = 0f; while (t < fadeTime) { t += Time.deltaTime; _agonySource.volume = Mathf.Lerp(startVol, 0f, t / fadeTime); yield return null; } _agonySource.Stop(); _agonySource.volume = startVol; } } private IEnumerator Struggle() { if (_isSmallEnemy) { isSuffering = false; yield break; } Il2CppArrayBase<AudioSource> localSources = ((Component)((Component)this).transform).GetComponentsInChildren<AudioSource>(true); foreach (AudioSource src in localSources) { if ((Object)(object)src != (Object)null && (Object)(object)src.clip != (Object)null && (Object)(object)src != (Object)(object)_agonySource) { string clipName = ((Object)src.clip).name.ToLower(); if (clipName.Contains("die") || clipName.Contains("death") || clipName.Contains("pain") || clipName.Contains("vo_")) { src.mute = true; } } } Il2CppArrayBase<MonoBehaviour> allBehaviours = ((Component)((Component)this).transform).GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour mb in allBehaviours) { if (!((Object)(object)mb == (Object)null)) { string typeName = ((object)mb).GetType().Name; string nameSpace = ((object)mb).GetType().Namespace ?? ""; if ((nameSpace == "ModThatIsNotMod.MonoBehaviours" || nameSpace.Contains("SLZ")) && (typeName.IndexOf("sfx", StringComparison.OrdinalIgnoreCase) >= 0 || typeName.IndexOf("vocal", StringComparison.OrdinalIgnoreCase) >= 0 || typeName.IndexOf("voice", StringComparison.OrdinalIgnoreCase) >= 0)) { ((Behaviour)mb).enabled = false; } } } AudioSource[] allSources = Il2CppArrayBase<AudioSource>.op_Implicit(((Component)((Component)this).transform).GetComponentsInChildren<AudioSource>(true)); List<AudioSource> sourcesToCheck = allSources.Where((AudioSource s) => (Object)(object)s != (Object)(object)_agonySource).ToList(); _isVocal = Random.Range(0f, 100f) < 100f; yield return (object)new WaitForFixedUpdate(); float elapsed = 0f; float roll = Random.Range(0f, 100f); bool isBicycle = roll < 11f; bool isCrawling = roll >= 11f && roll < 21f; bool isFetalCurl = roll >= 21f && roll < 56f; if (isBicycle) { ApplyLowFriction(); } Vector3 lockedCrawlDir = Vector3.forward; if ((Object)(object)_chest != (Object)null && (Object)(object)_pelvis != (Object)null) { lockedCrawlDir = _chest.position - _pelvis.position; lockedCrawlDir.y = 0f; if (((Vector3)(ref lockedCrawlDir)).sqrMagnitude < 0.0001f) { lockedCrawlDir = Vector3.forward; } else { ((Vector3)(ref lockedCrawlDir)).Normalize(); } } float audioCooldown = 0f; int lastAudioIndex = -1; float dynamicStruggleTime = Main.struggleTime; while (isSuffering && elapsed < dynamicStruggleTime) { elapsed += Time.fixedDeltaTime; if (audioCooldown > 0f) { audioCooldown -= Time.fixedDeltaTime; } float fadeStartTime = Mathf.Max(0f, dynamicStruggleTime - 2.5f); if (elapsed > fadeStartTime) { float fadeProgress = (elapsed - fadeStartTime) / (dynamicStruggleTime - fadeStartTime); Mathf.Clamp01(1f - fadeProgress); } foreach (AudioSource src2 in sourcesToCheck) { if ((Object)(object)src2 != (Object)null && ((Behaviour)src2).enabled && (Object)(object)src2.clip != (Object)null) { string clipName2 = ((Object)src2.clip).name; if (clipName2.IndexOf("die", StringComparison.OrdinalIgnoreCase) >= 0 || clipName2.IndexOf("death", StringComparison.OrdinalIgnoreCase) >= 0 || clipName2.IndexOf("pain", StringComparison.OrdinalIgnoreCase) >= 0 || clipName2.IndexOf("hurt", StringComparison.OrdinalIgnoreCase) >= 0 || clipName2.IndexOf("vo_", StringComparison.OrdinalIgnoreCase) >= 0) { ((Behaviour)src2).enabled = false; src2.mute = true; } } } bool canPlayAgony = isBicycle && Main.agonyClips.Count > 0; bool canPlayNormal = !isBicycle && Main.normalDeathClips.Count > 0; if (_isVocal && (canPlayAgony || canPlayNormal)) { SetupAudioSource(); if ((Object)(object)_agonySource != (Object)null && audioCooldown <= 0f) { _agonySource.Stop(); _agonySource.loop = false; List<AudioClip> targetList = (isBicycle ? Main.agonyClips : Main.normalDeathClips); int index = 0; if (targetList.Count > 1) { do { index = Random.Range(0, targetList.Count); } while (index == lastAudioIndex); } lastAudioIndex = index; AudioClip chosenClip = targetList[index]; _agonySource.clip = chosenClip; if (isBicycle) { if (Random.value <= 0.1f) { _agonySource.volume = 0f; } else { _agonySource.volume = Random.Range(0.85f, 1f); MelonLogger.Msg(ConsoleColor.Cyan, " | playing sound " + ((Object)chosenClip).name); } } else { _agonySource.volume = Random.Range(0.35f, 0.45f); MelonLogger.Msg(ConsoleColor.Cyan, " | playing sound " + ((Object)chosenClip).name); } _agonySource.pitch = Random.Range(0.8f, 1.05f); _agonySource.Play(); float trueAudioLength = chosenClip.length / Mathf.Abs(_agonySource.pitch); audioCooldown = trueAudioLength; dynamicStruggleTime = Mathf.Max(0.1f, trueAudioLength - 1f); } } Vector3 val; if (isBicycle) { float force = Main.pullForce; if ((Object)(object)_pelvis == (Object)null || (Object)(object)_pelvisTransform == (Object)null) { yield break; } bool isOnBack = (Object)(object)_chestTransform != (Object)null && Vector3.Dot(_chestTransform.forward, Vector3.up) > 0.2f; bool isOnStomach = (Object)(object)_chestTransform != (Object)null && Vector3.Dot(_chestTransform.forward, Vector3.up) < -0.2f; float randomSeed = Mathf.Abs((float)((long)((Object)_pelvis).GetInstanceID() * 73856L)) % 100f / 100f; float cycleSpeed = Mathf.Lerp(4f, 8f, randomSeed); float chaos = Mathf.Sin(elapsed * 2.8f) * 0.6f; float pedalPhase = elapsed * cycleSpeed + chaos; float lateralSpring = 120f; float lateralDamp = 10f; Vector3 kickAxis = _pelvisTransform.forward; Vector3 liftAxis = _pelvisTransform.up; for (int i = 0; i < _knees.Count; i++) { Rigidbody knee = _knees[i]; if ((Object)(object)knee == (Object)null || (Object)(object)_pelvis == (Object)null || (Object)(object)_pelvisTransform == (Object)null) { continue; } if (elapsed < 2f) { knee.AddForce(-knee.velocity * 0.5f, (ForceMode)0); if (i < _feet.Count && (Object)(object)_feet[i] != (Object)null) { _feet[i].AddForce(-_feet[i].velocity * 0.5f, (ForceMode)0); } continue; } float targetSideX = ((i % 2 == 0) ? (-0.15f) : 0.15f); float offset = ((i % 2 == 0) ? 0f : 2.4f); float rawDrive = Mathf.Sin(pedalPhase + offset) + chaos * 0.5f; float forwardDrive = Mathf.Clamp(rawDrive * 1.5f, -1f, 1f); float verticalLift = Mathf.Clamp(Mathf.Cos(pedalPhase + offset), -0.2f, 1f); float kneeDist = Vector3.Distance(knee.position, _pelvis.position); if (kneeDist > 0.95f && forwardDrive > 0f) { forwardDrive *= 0.1f; } Vector3 pedalVector = kickAxis * forwardDrive * 1.5f + liftAxis * verticalLift * 0.5f; Vector3 kneeForce = pedalVector * 45f; knee.AddForce(kneeForce, (ForceMode)0); _pelvis.AddForce(-kneeForce, (ForceMode)0); knee.AddForce(-knee.velocity * 0.5f, (ForceMode)0); if (Vector3.Distance(knee.position, _pelvis.position) < 0.35f && forwardDrive > 0f) { Vector3 kneePush = _pelvisTransform.forward * 10f; knee.AddForce(kneePush, (ForceMode)0); _pelvis.AddForce(-kneePush, (ForceMode)0); } if (forwardDrive < 0f) { Vector3 kneeLift = _pelvisTransform.up * 10f; knee.AddForce(kneeLift, (ForceMode)0); _pelvis.AddForce(-kneeLift, (ForceMode)0); } float kneeXError = targetSideX - _pelvisTransform.InverseTransformPoint(knee.position).x; float kneeSidewaysVel = Vector3.Dot(knee.velocity, _pelvisTransform.right); Vector3 latForce = _pelvisTransform.right * (kneeXError * lateralSpring - kneeSidewaysVel * lateralDamp); knee.AddForce(latForce, (ForceMode)0); _pelvis.AddForce(-latForce, (ForceMode)0); Vector3 relativeKneeVel = knee.velocity - _pelvis.velocity; knee.velocity = _pelvis.velocity + Vector3.ClampMagnitude(relativeKneeVel, 4f); if (i >= _feet.Count || !((Object)(object)_feet[i] != (Object)null)) { continue; } Rigidbody foot = _feet[i]; float footDist = Vector3.Distance(foot.position, _pelvis.position); float maxLegLimit = 1.05f; val = foot.position - _pelvis.position; Vector3 pelvisToFoot = ((Vector3)(ref val)).normalized; if (footDist < maxLegLimit) { Vector3 finalFootForce; if (forwardDrive > 0f) { Vector3 val2; if (!isOnBack) { val2 = pelvisToFoot; } else { val = pelvisToFoot + Vector3.down * 0.2f; val2 = ((Vector3)(ref val)).normalized; } Vector3 kickDirection = val2; finalFootForce = kickDirection * forwardDrive * 85f; } else { finalFootForce = -pelvisToFoot * Mathf.Abs(forwardDrive) * 45f; } foot.AddForce(finalFootForce, (ForceMode)0); _pelvis.AddForce(-finalFootForce, (ForceMode)0); if (Vector3.Distance(foot.position, _pelvis.position) < 0.4f && forwardDrive > 0f) { Vector3 footPush = pelvisToFoot * 20f; foot.AddForce(footPush, (ForceMode)0); _pelvis.AddForce(-footPush, (ForceMode)0); } } foot.AddForce(-foot.velocity * 0.5f, (ForceMode)0); float footXError = targetSideX - _pelvisTransform.InverseTransformPoint(foot.position).x; float footSidewaysVel = Vector3.Dot(foot.velocity, _pelvisTransform.right); Vector3 latFootForce = _pelvisTransform.right * (footXError * lateralSpring - footSidewaysVel * lateralDamp); foot.AddForce(latFootForce, (ForceMode)0); _pelvis.AddForce(-latFootForce, (ForceMode)0); Vector3 relativeFootVel = foot.velocity - _pelvis.velocity; foot.velocity = _pelvis.velocity + Vector3.ClampMagnitude(relativeFootVel, 5.5f); } if ((Object)(object)_chest != (Object)null && (Object)(object)_pelvis != (Object)null && (Object)(object)_chestTransform != (Object)null) { if (isOnStomach) { _pelvis.angularVelocity = Vector3.zero; _chest.angularVelocity = Vector3.zero; _chest.AddForce(Vector3.down * 120f, (ForceMode)0); _pelvis.AddForce(Vector3.down * 120f, (ForceMode)0); } else if (isOnBack) { if (Physics.Raycast(_pelvis.position, Vector3.down, 1.2f)) { _chest.AddForce(Vector3.down * 150f, (ForceMode)0); _pelvis.AddForce(Vector3.down * 80f, (ForceMode)0); Rigidbody pelvis = _pelvis; pelvis.angularVelocity *= 0.3f; Rigidbody chest = _chest; chest.angularVelocity *= 0.3f; } else { Rigidbody pelvis2 = _pelvis; pelvis2.angularVelocity *= 0.9f; Rigidbody chest2 = _chest; chest2.angularVelocity *= 0.9f; } } else { Rigidbody pelvis3 = _pelvis; pelvis3.angularVelocity *= 0.8f; Rigidbody chest3 = _chest; chest3.angularVelocity *= 0.8f; } float thrashSpeedSeed = Mathf.Abs((float)((long)((Object)_pelvis).GetInstanceID() * 92341L)) % 100f / 100f; float baseThrashSpeed = Mathf.Lerp(2f, 4.5f, thrashSpeedSeed); float thrashChaos = Mathf.Sin(elapsed * 1.4f) * 0.8f; float thrashPhase = elapsed * baseThrashSpeed + thrashChaos; float chestThrashWave = Mathf.Sin(thrashPhase); float pelvisThrashWave = Mathf.Sin(thrashPhase - 0.75f); float thrashForce = 70f; _chest.AddForce(_chestTransform.right * (chestThrashWave * thrashForce), (ForceMode)0); _pelvis.AddForce(_pelvisTransform.right * (pelvisThrashWave * thrashForce), (ForceMode)0); val = _pelvis.position - _chest.position; Vector3 crunchDir = ((Vector3)(ref val)).normalized; float targetSpineLength = 0.42f; float currentDist = Vector3.Distance(_chest.position, _pelvis.position); if (currentDist > targetSpineLength) { Vector3 curlForce = crunchDir * ((currentDist - targetSpineLength) * 250f); _chest.AddForce(curlForce, (ForceMode)0); _pelvis.AddForce(-curlForce, (ForceMode)0); } Vector3 perfectStraightLinePos = _pelvis.position + _pelvisTransform.forward * targetSpineLength; Vector3 deviationVector = perfectStraightLinePos - _chest.position; Vector3 straightLineSpring = deviationVector * 300f; _chest.AddForce(straightLineSpring, (ForceMode)0); _pelvis.AddForce(-straightLineSpring, (ForceMode)0); _chest.AddForce(-_chest.velocity * 0.5f, (ForceMode)0); _pelvis.AddForce(-_pelvis.velocity * 0.5f, (ForceMode)0); Vector3 relativeChestVel = _chest.velocity - _pelvis.velocity; _chest.velocity = _pelvis.velocity + Vector3.ClampMagnitude(relativeChestVel, 3f); } foreach (Rigidbody hand in _limbs) { if (!((Object)(object)hand == (Object)null)) { if ((Object)(object)lastHitBone != (Object)null) { val = lastHitBone.position - hand.position; Vector3 woundDir = ((Vector3)(ref val)).normalized; hand.AddForce(woundDir * (force * 0.5f), (ForceMode)0); } else if ((Object)(object)_chest != (Object)null) { val = _chest.position - hand.position; Vector3 chestDir = ((Vector3)(ref val)).normalized; hand.AddForce(chestDir * (force * 0.4f), (ForceMode)0); } } } } else if (isFetalCurl && (Object)(object)_chest != (Object)null && (Object)(object)_pelvis != (Object)null) { float curlRamp = Mathf.Clamp01(elapsed / 0.5f); float force2 = 20f * curlRamp; val = _pelvis.position - _chest.position; Vector3 coreCurlDir = ((Vector3)(ref val)).normalized; _chest.AddForce(coreCurlDir * (force2 * 2.5f), (ForceMode)0); _pelvis.AddForce(-coreCurlDir * (force2 * 2.5f), (ForceMode)0); val = _chest.position - _pelvis.position; Vector3 spineAxis = ((Vector3)(ref val)).normalized; Vector3 rollTorque = Vector3.Cross(spineAxis, Vector3.up); _chest.AddTorque(rollTorque * (force2 * 0.8f), (ForceMode)0); _pelvis.AddTorque(rollTorque * (force2 * 0.8f), (ForceMode)0); Rigidbody chest4 = _chest; chest4.angularVelocity *= 0.8f; Rigidbody pelvis4 = _pelvis; pelvis4.angularVelocity *= 0.8f; foreach (Rigidbody hand2 in _limbs) { if (!((Object)(object)hand2 == (Object)null)) { val = _chest.position - hand2.position; Vector3 pullDir = ((Vector3)(ref val)).normalized; hand2.AddForce(pullDir * (force2 * 4f), (ForceMode)0); _chest.AddForce(-pullDir * (force2 * 4f), (ForceMode)0); Vector3 relVel = hand2.velocity - _chest.velocity; hand2.AddForce(-relVel * (force2 * 0.2f), (ForceMode)0); } } foreach (Rigidbody knee2 in _knees) { if (!((Object)(object)knee2 == (Object)null)) { val = _chest.position - knee2.position; Vector3 pullDir2 = ((Vector3)(ref val)).normalized; knee2.AddForce(pullDir2 * (force2 * 4f), (ForceMode)0); _chest.AddForce(-pullDir2 * (force2 * 4f), (ForceMode)0); Vector3 relVel2 = knee2.velocity - _chest.velocity; knee2.AddForce(-relVel2 * (force2 * 0.2f), (ForceMode)0); } } foreach (Rigidbody foot2 in _feet) { if (!((Object)(object)foot2 == (Object)null)) { val = _pelvis.position - foot2.position; Vector3 pullDir3 = ((Vector3)(ref val)).normalized; foot2.AddForce(pullDir3 * (force2 * 5f), (ForceMode)0); _pelvis.AddForce(-pullDir3 * (force2 * 5f), (ForceMode)0); Vector3 relVel3 = foot2.velocity - _pelvis.velocity; foot2.AddForce(-relVel3 * (force2 * 0.2f), (ForceMode)0); } } } else if (isCrawling && (Object)(object)_chest != (Object)null && (Object)(object)_pelvis != (Object)null) { float force3 = 45f; Vector3 crawlDir = lockedCrawlDir; float upDot = Vector3.Dot(_chestTransform.up, Vector3.up); if (upDot < -0.2f) { _chest.AddTorque(_chestTransform.forward * (force3 * 2f), (ForceMode)0); _pelvis.AddTorque(_pelvisTransform.forward * (force3 * 2f), (ForceMode)0); } else { Vector3 chestUpTorque = Vector3.Cross(_chestTransform.up, Vector3.up); _chest.AddTorque(chestUpTorque * (force3 * 2f), (ForceMode)0); Vector3 pelvisUpTorque = Vector3.Cross(_pelvisTransform.up, Vector3.up); _pelvis.AddTorque(pelvisUpTorque * (force3 * 2f), (ForceMode)0); } Vector3 currentSpineDir = _chest.position - _pelvis.position; currentSpineDir.y = 0f; if (((Vector3)(ref currentSpineDir)).sqrMagnitude > 0.0001f) { ((Vector3)(ref currentSpineDir)).Normalize(); Vector3 chestTorque = Vector3.Cross(currentSpineDir, crawlDir); _chest.AddTorque(chestTorque * (force3 * 4f), (ForceMode)0); Vector3 pelvisTorque = Vector3.Cross(currentSpineDir, crawlDir); _pelvis.AddTorque(pelvisTorque * (force3 * 4f), (ForceMode)0); } float crawlSpeed = 1.2f; float phase = Mathf.Sin(elapsed * (float)Math.PI * crawlSpeed); Vector3 rightDir = Vector3.Cross(crawlDir, Vector3.up); for (int i2 = 0; i2 < _limbs.Count; i2++) { Rigidbody hand3 = _limbs[i2]; if ((Object)(object)hand3 == (Object)null) { continue; } float sidePhase = ((i2 % 2 == 0) ? phase : (0f - phase)); float sideSign = ((i2 % 2 == 0) ? 1f : (-1f)); Vector3 armSpread = rightDir * sideSign; if (sidePhase > 0f) { float liftArc = Mathf.Sin(sidePhase * (float)Math.PI); Vector3 reachDir = crawlDir + armSpread * 0.4f + Vector3.up * (0.6f * liftArc); hand3.AddForce(((Vector3)(ref reachDir)).normalized * (force3 * 1.8f), (ForceMode)0); } else { Vector3 pullDir4 = -crawlDir + armSpread * 0.2f - Vector3.up * 1.5f; hand3.AddForce(((Vector3)(ref pullDir4)).normalized * (force3 * 1.5f), (ForceMode)0); Vector3 forwardVel = Vector3.Project(_chest.velocity, crawlDir); if (((Vector3)(ref forwardVel)).sqrMagnitude < 1.44f) { float pullIntensity = 0f - sidePhase; float pulseMultiplier = pullIntensity * pullIntensity; _chest.AddForce(crawlDir * (force3 * 8f * pulseMultiplier), (ForceMode)0); _chest.AddForce(Vector3.up * (force3 * 1.2f * pulseMultiplier), (ForceMode)0); } } hand3.velocity = Vector3.Lerp(hand3.velocity, Vector3.zero, Time.fixedDeltaTime * 4.5f); } } else { float force4 = Main.pullForce; if ((Object)(object)_chest != (Object)null && (Object)(object)_pelvis != (Object)null) { float dampingFactor = 12f; val = _chest.position - _pelvis.position; Vector3 bodyUp = ((Vector3)(ref val)).normalized; Vector3 bodyForward = _chestTransform.forward; val = Vector3.Cross(bodyUp, bodyForward); Vector3 bodyRight = ((Vector3)(ref val)).normalized; if (_limbs != null) { foreach (Rigidbody hand4 in _limbs) { if ((Object)(object)hand4 == (Object)null) { continue; } Rigidbody targetBone = (((Object)(object)lastHitBone != (Object)null) ? lastHitBone : (((Object)(object)_head != (Object)null) ? _head : _chest)); if ((Object)(object)targetBone != (Object)null) { val = targetBone.position - hand4.position; Vector3 directionToTarget = ((Vector3)(ref val)).normalized; Vector3 jitter = Random.onUnitSphere * (force4 * 0.5f); hand4.AddForce(directionToTarget * (force4 * 0.7f) + jitter, (ForceMode)0); Vector3 targetF = -directionToTarget * (force4 * 0.5f); if (targetF.y < 0f) { targetF.y = 0f; } targetBone.AddForce(targetF, (ForceMode)0); } hand4.AddForce(-hand4.velocity * (force4 * 0.1f), (ForceMode)0); } } if (_knees != null) { foreach (Rigidbody knee3 in _knees) { if (!((Object)(object)knee3 == (Object)null)) { float sideSign2 = ((_chestTransform.InverseTransformPoint(knee3.position).x < 0f) ? (-0.15f) : 0.15f); Vector3 kneeOffset = bodyForward * 0.4f + bodyUp * -0.25f + bodyRight * sideSign2; Vector3 kneeTargetPos = _pelvis.position + kneeOffset; Vector3 kneeDelta = kneeTargetPos - knee3.position; float kneeDist2 = ((Vector3)(ref kneeDelta)).magnitude; if (kneeDist2 > 0.02f) { Vector3 dirNormalized = kneeDelta / kneeDist2; Vector3 finalKneeForce = dirNormalized * 75f - knee3.velocity * dampingFactor; knee3.AddForce(finalKneeForce, (ForceMode)0); _pelvis.AddForce(-finalKneeForce * 0.5f, (ForceMode)0); } } } } if (_feet != null) { foreach (Rigidbody foot3 in _feet) { if (!((Object)(object)foot3 == (Object)null)) { float sideSign3 = ((_chestTransform.InverseTransformPoint(foot3.position).x < 0f) ? (-0.15f) : 0.15f); Vector3 footOffset = bodyForward * 0.15f + bodyUp * -0.55f + bodyRight * sideSign3; Vector3 footTargetPos = _pelvis.position + footOffset; Vector3 footDelta = footTargetPos - foot3.position; float footDist2 = ((Vector3)(ref footDelta)).magnitude; if (footDist2 > 0.02f) { Vector3 dirNormalized2 = footDelta / footDist2; Vector3 finalFootForce2 = dirNormalized2 * 100f - foot3.velocity * dampingFactor; foot3.AddForce(finalFootForce2, (ForceMode)0); _pelvis.AddForce(-finalFootForce2 * 0.5f, (ForceMode)0); } } } } } } yield return (object)new WaitForFixedUpdate(); } if (isSuffering && Random.Range(0f, 100f) <= 5f) { float spasmDuration = Random.Range(1f, 4f); float spasmTimer = 0f; float timeUntilNextTwitch = 0f; while (isSuffering && spasmTimer < spasmDuration) { spasmTimer += Time.fixedDeltaTime; timeUntilNextTwitch -= Time.fixedDeltaTime; if (timeUntilNextTwitch <= 0f) { timeUntilNextTwitch = Random.Range(0.5f, 1f); if (_knees != null && _knees.Count > 0 && (Object)(object)_pelvisTransform != (Object)null) { Rigidbody randomKnee = _knees[Random.Range(0, _knees.Count)]; if ((Object)(object)randomKnee != (Object)null) { float sideSign4 = ((Random.value > 0.5f) ? 1f : (-1f)); Vector3 twitchDir = _pelvisTransform.right * sideSign4 * 0.8f + _pelvisTransform.up * 0.4f; randomKnee.AddForce(((Vector3)(ref twitchDir)).normalized * (Main.pullForce * 1f), (ForceMode)1); } } } yield return (object)new WaitForFixedUpdate(); } } if (isSuffering && (Object)(object)_agonySource != (Object)null && _agonySource.isPlaying) { yield return FadeOutAudio(); } yield return ResetSequence(); isSuffering = false; } private IEnumerator ResetSequence() { RestoreOriginalFriction(); if ((Object)(object)_agonySource != (Object)null) { _agonySource.Stop(); } Il2CppArrayBase<AudioSource> allSources = ((Component)((Component)this).transform).GetComponentsInChildren<AudioSource>(true); foreach (AudioSource src in allSources) { if ((Object)(object)src != (Object)null && (Object)(object)src != (Object)(object)_agonySource) { src.mute = false; } } PuppetMaster pm = (((Object)(object)_brain != (Object)null) ? _brain.puppetMaster : null); OriginalPuppetProperties props = ((Component)this).GetComponent<OriginalPuppetProperties>(); if (!((Object)(object)pm == (Object)null) && !((Object)(object)props == (Object)null)) { if ((Object)(object)pm.targetAnimator != (Object)null) { ((Behaviour)pm.targetAnimator).enabled = true; pm.targetAnimator.speed = 0f; } pm.muscleDamper = props.damper; pm.muscleWeight = props.weight; pm.stateSettings = props.stateSettings; } yield break; } } } namespace Procudeal_aimation { public class Main : MelonMod { public static bool modEnabled = true; public static float pullForce = 35f; public static float struggleTime = 8f; private bool _isScannerRunning = false; public static List<AudioClip> agonyClips = new List<AudioClip>(); public static List<AudioClip> normalDeathClips = new List<AudioClip>(); public override void OnApplicationStart() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown MelonLogger.Msg(ConsoleColor.Cyan, "The Procedural Animation Mod IS ACTIVE"); ClassInjector.RegisterTypeInIl2Cpp<AgonyLogic>(); ClassInjector.RegisterTypeInIl2Cpp<OriginalPuppetProperties>(); Harmony val = new Harmony("com.jeb.proceduralanimation"); val.PatchAll(); SetupMenu(); MelonCoroutines.Start(LoadAudioFiles()); } private IEnumerator LoadAudioFiles() { string agonyPath = Path.Combine(MelonUtils.UserDataDirectory, "strugglesounds"); if (!Directory.Exists(agonyPath)) { Directory.CreateDirectory(agonyPath); } else { LoadClipsFromFolder(agonyPath, agonyClips); } string normalPath = Path.Combine(MelonUtils.UserDataDirectory, "normaldeathsounds"); if (!Directory.Exists(normalPath)) { Directory.CreateDirectory(normalPath); } else { LoadClipsFromFolder(normalPath, normalDeathClips); } yield break; } private void LoadClipsFromFolder(string folderPath, List<AudioClip> targetList) { string[] files = Directory.GetFiles(folderPath, "*.wav"); string[] array = files; foreach (string path in array) { try { AudioClip val = WavUtility.ToAudioClip(path); if ((Object)(object)val != (Object)null) { ((Object)val).name = Path.GetFileName(path); ((Object)val).hideFlags = (HideFlags)32; targetList.Add(val); MelonLogger.Msg(ConsoleColor.Green, $"Successfully loaded: {((Object)val).name} into {targetList.Count} clips"); } } catch (Exception ex) { MelonLogger.Error("Failed to load " + Path.GetFileName(path) + ": " + ex.Message); } } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if (!_isScannerRunning) { _isScannerRunning = true; MelonCoroutines.Start(OptimizedScanner()); } } private IEnumerator OptimizedScanner() { WaitForSeconds waitTime = new WaitForSeconds(1.5f); while (true) { if (modEnabled) { Il2CppArrayBase<AIBrain> allBrains = Object.FindObjectsOfType<AIBrain>(); foreach (AIBrain brain in allBrains) { if ((Object)(object)brain == (Object)null) { continue; } if ((Object)(object)((Component)brain).GetComponent<AgonyLogic>() == (Object)null) { ((Component)brain).gameObject.AddComponent<AgonyLogic>(); } PuppetMaster pm = brain.puppetMaster; if ((Object)(object)pm != (Object)null && (int)pm.state != 1) { Animator anim = pm.targetAnimator; if ((Object)(object)anim != (Object)null && anim.speed == 0f) { ((Behaviour)anim).enabled = true; anim.speed = 1f; } } } } yield return waitTime; } } private void SetupMenu() { //IL_0006: 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) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = MenuManager.CreateCategory("Procudeal aimation", Color.red); val.CreateBoolElement("Mod Active", Color.white, true, (Action<bool>)delegate(bool v) { modEnabled = v; }); val.CreateFloatElement("Force", Color.red, 35f, (Action<float>)delegate(float v) { pullForce = v; }, 5f, -2.1474836E+09f, 2.1474836E+09f, false); val.CreateFloatElement("Duration", Color.white, 8f, (Action<float>)delegate(float v) { struggleTime = v; }, 1f, -2.1474836E+09f, 2.1474836E+09f, false); } } public static class WavUtility { public static AudioClip ToAudioClip(string path) { byte[] array = File.ReadAllBytes(path); int num = 44; for (int i = 12; i < array.Length - 4; i++) { if (array[i] == 100 && array[i + 1] == 97 && array[i + 2] == 116 && array[i + 3] == 97) { num = i + 8; break; } } int num2 = BitConverter.ToInt16(array, 20); int num3 = BitConverter.ToInt16(array, 22); int num4 = BitConverter.ToInt32(array, 24); int num5 = BitConverter.ToInt16(array, 34); int num6; float[] array2; switch (num5) { case 32: { num6 = (array.Length - num) / 4; array2 = new float[num6]; for (int k = 0; k < num6; k++) { array2[k] = BitConverter.ToSingle(array, num + k * 4); } break; } case 24: { num6 = (array.Length - num) / 3; array2 = new float[num6]; for (int l = 0; l < num6; l++) { int num8 = num + l * 3; int num9 = (array[num8] << 8) | (array[num8 + 1] << 16) | (array[num8 + 2] << 24); array2[l] = (float)num9 / 2.1474836E+09f; } break; } case 16: { num6 = (array.Length - num) / 2; array2 = new float[num6]; for (int j = 0; j < num6; j++) { short num7 = BitConverter.ToInt16(array, num + j * 2); array2[j] = (float)num7 / 32768f; } break; } default: MelonLogger.Error($" bit depth aint supported ({num5}-bit) for da file {path}. convert to 16, 24, or 32-bit PLEASE"); return null; } AudioClip val = AudioClip.Create("LoadedAudio", num6 / num3, num3, num4, false); val.SetData(Il2CppStructArray<float>.op_Implicit(array2), 0); return val; } } [HarmonyPatch(typeof(Gun), "OnFire", new Type[] { })] public class GunFirePatch { [HarmonyPostfix] public static void Postfix(Gun __instance) { //IL_0028: 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_003d: Unknown result type (might be due to invalid IL or missing references) if (!Main.modEnabled || (Object)(object)__instance.firePointTransform == (Object)null) { return; } Ray val = default(Ray); ((Ray)(ref val))..ctor(__instance.firePointTransform.position, __instance.firePointTransform.forward); RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(val, ref val2, 500f)) { return; } AIBrain componentInParent = ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<AIBrain>(); if (!((Object)(object)componentInParent != (Object)null)) { return; } AgonyLogic component = ((Component)componentInParent).GetComponent<AgonyLogic>(); if ((Object)(object)component != (Object)null) { if (component.isSuffering && Time.time - component.sufferingStartTime >= 2f) { component.MercyKill(); } else { component.lastHitBone = ((Component)((RaycastHit)(ref val2)).collider).GetComponent<Rigidbody>(); } } } } [HarmonyPatch(typeof(PuppetMaster), "Kill", new Type[] { })] public class PuppetMasterKillPatch { [HarmonyPrefix] public static void Prefix(PuppetMaster __instance) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_00bb: 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) if (!Main.modEnabled) { return; } AIBrain componentInParent = ((Component)__instance).GetComponentInParent<AIBrain>(); if ((Object)(object)componentInParent == (Object)null) { return; } OriginalPuppetProperties component = ((Component)componentInParent).GetComponent<OriginalPuppetProperties>(); if ((Object)(object)component == (Object)null) { component = ((Component)componentInParent).gameObject.AddComponent<OriginalPuppetProperties>(); component.damper = __instance.muscleDamper; component.weight = __instance.muscleWeight; component.stateSettings = __instance.stateSettings; if ((Object)(object)__instance.targetAnimator != (Object)null) { component.animSpeed = __instance.targetAnimator.speed; } } if ((Object)(object)__instance.targetAnimator != (Object)null) { ((Behaviour)__instance.targetAnimator).enabled = false; } StateSettings stateSettings = __instance.stateSettings; stateSettings.deadMuscleWeight = 0f; stateSettings.deadMuscleDamper = 0f; __instance.stateSettings = stateSettings; __instance.muscleWeight = 0f; __instance.muscleDamper = 0f; AgonyLogic component2 = ((Component)componentInParent).GetComponent<AgonyLogic>(); if ((Object)(object)component2 != (Object)null) { component2.ProcessHit(); } } } }