();
+ m_soundManager = new SoundManager(m_controller.transform);
+ m_soundManager.LoadSounds();
}
System.Collections.IEnumerator WaitForWhitelist()
@@ -40,6 +44,8 @@ namespace ml_prm
{
WorldManager.DeInit();
+ m_soundManager = null;
+
if(m_controller != null)
UnityEngine.Object.Destroy(m_controller.gameObject);
m_controller = null;
diff --git a/ml_prm/ModUi.cs b/ml_prm/ModUi.cs
index 557a63e..52f7790 100644
--- a/ml_prm/ModUi.cs
+++ b/ml_prm/ModUi.cs
@@ -21,7 +21,6 @@ namespace ml_prm
Hotkey = 0,
Gravity,
PointersReaction,
- IgnoreLocal,
CombatReaction,
AutoRecover,
Slipperiness,
@@ -36,7 +35,8 @@ namespace ml_prm
RecoverDelay,
FallLimit,
GestureGrab,
- FriendsGrab
+ ImpactSounds,
+ ImpactVolume
}
const string c_ragdollKeyTooltip = "Switch ragdoll mode with '{0}' key";
@@ -52,7 +52,6 @@ namespace ml_prm
static ToggleButton ms_hotkeyToggle = null;
static ToggleButton ms_gravityToggle = null;
static ToggleButton ms_pointersToggle = null;
- static ToggleButton ms_ignoreLocalToggle = null;
static ToggleButton ms_combatToggle = null;
static ToggleButton ms_recoveryToggle = null;
static ToggleButton ms_slipperinessToggle = null;
@@ -62,12 +61,13 @@ namespace ml_prm
static ToggleButton ms_buoyancyToggle = null;
static ToggleButton ms_fallDamageToggle = null;
static ToggleButton ms_gestureGrabToggle = null;
- static ToggleButton ms_friendsGrabToggle = null;
+ static ToggleButton ms_impactSoundsToggle = null;
static SliderFloat ms_velocityMultiplierSlider = null;
static SliderFloat ms_movementDragSlider = null;
static SliderFloat ms_angularMovementDragSlider = null;
static SliderFloat ms_recoverDelaySlider = null;
static SliderFloat ms_fallLimitSlider = null;
+ static SliderFloat ms_impactVolumeSlider = null;
static Button ms_resetButton = null;
internal static void Init()
@@ -94,9 +94,6 @@ namespace ml_prm
ms_pointersToggle = ms_category.AddToggle("Pointers reaction", "React to trigger colliders with CVRPointer component of 'ragdoll' type", Settings.PointersReaction);
ms_pointersToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.PointersReaction, state);
- ms_ignoreLocalToggle = ms_category.AddToggle("Ignore local pointers", "Ignore local avatar's CVRPointer components of 'ragdoll' type", Settings.IgnoreLocal);
- ms_ignoreLocalToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.IgnoreLocal, state);
-
ms_combatToggle = ms_category.AddToggle("Combat reaction", "Ragdoll upon combat system death", Settings.CombatReaction);
ms_combatToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.CombatReaction, state);
@@ -121,11 +118,11 @@ namespace ml_prm
ms_fallDamageToggle = ms_category.AddToggle("Fall damage", "Enable ragdoll when falling from height", Settings.FallDamage);
ms_fallDamageToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.FallDamage, state);
- ms_gestureGrabToggle = ms_category.AddToggle("Gesture grab", "Enable grabbing of ragdolled body parts by remote players with trigger/grab gestureWarning: can lead to unpredictable physics behaviour in some cases", Settings.GestureGrab);
+ ms_gestureGrabToggle = ms_category.AddToggle("Grab attaching", "Enable attaching of ragdolled body parts to pointers of 'grab' type
Warning: can lead to unpredictable physics behaviour in some cases", Settings.GestureGrab);
ms_gestureGrabToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.GestureGrab, state);
- ms_friendsGrabToggle = ms_category.AddToggle("Friends grab only", " ", Settings.FriendsGrab);
- ms_friendsGrabToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.FriendsGrab, state);
+ ms_impactSoundsToggle = ms_category.AddToggle("Impact sounds", "Enable collision sound effects of ragdolled body parts", Settings.ImpactSounds);
+ ms_impactSoundsToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.ImpactSounds, state);
ms_velocityMultiplierSlider = ms_category.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Settings.VelocityMultiplier, 1f, 50f);
ms_velocityMultiplierSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.VelocityMultiplier, value);
@@ -143,6 +140,9 @@ namespace ml_prm
ms_fallLimitSlider.SliderTooltip = string.Format(c_fallLimitTooltip, GetDropHeight(Settings.FallLimit));
ms_fallLimitSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.FallLimit, value);
+ ms_impactVolumeSlider = ms_category.AddSlider("Impact volume", "Volume of collision of ragdolled body parts", Settings.ImpactVolume * 100f, 0f, 100f);
+ ms_impactVolumeSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.ImpactVolume, value);
+
ms_resetButton = ms_category.AddButton("Reset settings", "", "Reset mod settings to default");
ms_resetButton.OnPress += Reset;
}
@@ -177,10 +177,6 @@ namespace ml_prm
Settings.SetSetting(Settings.ModSetting.PointersReaction, p_state);
break;
- case UiIndex.IgnoreLocal:
- Settings.SetSetting(Settings.ModSetting.IgnoreLocal, p_state);
- break;
-
case UiIndex.CombatReaction:
Settings.SetSetting(Settings.ModSetting.CombatReaction, p_state);
break;
@@ -217,8 +213,8 @@ namespace ml_prm
Settings.SetSetting(Settings.ModSetting.GestureGrab, p_state);
break;
- case UiIndex.FriendsGrab:
- Settings.SetSetting(Settings.ModSetting.FriendsGrab, p_state);
+ case UiIndex.ImpactSounds:
+ Settings.SetSetting(Settings.ModSetting.ImpactSounds, p_state);
break;
}
}
@@ -256,6 +252,10 @@ namespace ml_prm
ms_fallLimitSlider.SliderTooltip = string.Format(c_fallLimitTooltip, GetDropHeight(p_value));
}
break;
+
+ case UiIndex.ImpactVolume:
+ Settings.SetSetting(Settings.ModSetting.ImpactVolume, p_value * 0.01f);
+ break;
}
}
catch(Exception e)
@@ -275,9 +275,6 @@ namespace ml_prm
OnToggleUpdate(UiIndex.PointersReaction, true);
ms_pointersToggle.ToggleValue = true;
- OnToggleUpdate(UiIndex.IgnoreLocal, true);
- ms_ignoreLocalToggle.ToggleValue = true;
-
OnToggleUpdate(UiIndex.CombatReaction, true);
ms_combatToggle.ToggleValue = true;
@@ -305,8 +302,8 @@ namespace ml_prm
OnToggleUpdate(UiIndex.GestureGrab, false);
ms_gestureGrabToggle.ToggleValue = false;
- OnToggleUpdate(UiIndex.FriendsGrab, true);
- ms_friendsGrabToggle.ToggleValue = true;
+ OnToggleUpdate(UiIndex.ImpactSounds, true);
+ ms_impactSoundsToggle.ToggleValue = true;
OnSliderUpdate(UiIndex.VelocityMultiplier, 2f);
ms_velocityMultiplierSlider.SetSliderValue(2f);
@@ -322,6 +319,9 @@ namespace ml_prm
OnSliderUpdate(UiIndex.FallLimit, 9.899494f);
ms_fallLimitSlider.SetSliderValue(9.899494f);
+
+ OnSliderUpdate(UiIndex.ImpactVolume, 100f);
+ ms_impactVolumeSlider.SetSliderValue(25f);
}
static void OnHotkeyKeyChanged(UnityEngine.KeyCode p_keyCode)
diff --git a/ml_prm/Properties/AssemblyInfo.cs b/ml_prm/Properties/AssemblyInfo.cs
index a692c71..e968e2d 100644
--- a/ml_prm/Properties/AssemblyInfo.cs
+++ b/ml_prm/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.2.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.2.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPriority(2)]
[assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")]
diff --git a/ml_prm/README.md b/ml_prm/README.md
index bfbaa98..ef09eb1 100644
--- a/ml_prm/README.md
+++ b/ml_prm/README.md
@@ -17,7 +17,6 @@ Optional mod's settings page with [BTKUILib](https://github.com/BTK-Development/
* **Use gravity:** enables/disables gravity for ragdoll; `true` by default.
* Note: Forcibly enabled in worlds that don't allow flight.
* **Pointers reaction:** enables ragdoll state when player collides with trigger colliders and particle systems with CVRPointer component of `ragdoll` type (avatars, props and world included); `true` by default.
-* **Ignore local pointers:** enables/disables ignoring of CVRPointer components of `ragdoll` type on local player's avatar; `true` by default.
* **Combat reaction:** enables ragdoll state upon death in worlds with combat system; `true` by default.
* **Auto recover:** enables automatic recovering after specific time delay; `false` by default.
* **Slipperiness:** enables/disable low friction of ragdoll; `false` by default.
@@ -30,9 +29,10 @@ Optional mod's settings page with [BTKUILib](https://github.com/BTK-Development/
* **Buoyancy:** enables floating in fluid volumes; `true` by default.
* Note: Forcibly enabled in worlds that don't allow flight.
* **Fall damage:** enables ragdoll when falling from specific height; `true` by default.
-* **Gesture grab:** enables grabbing of ragdolled body parts by remote players with trigger/grab gesture; `false` by default.
+* **Grab attaching:** enables attaching of ragdolled body parts to pointers with `grab` type of avatars, props and world(s); `false` by default.
* Note: Can lead to unpredictable physics behaviour in some cases.
-* **Friends grab only:** Allow only friends to be able to grab your radgolled body parts; `true` by default.
+* **Impact sounds:** enables collision sound effects of ragdolled body parts; `true` by default.
+ * Note: Sounds can be replaced in `(game_folder)/UserData/PlayerRagdollMod` folder.
* **Velocity multiplier:** velocity force multiplier based on player's movement direction; `2.0` by default.
* Note: Limited according to world's fly multiplier.
* Note: Forcibly set to `1.0` in worlds that don't allow flight.
@@ -41,6 +41,7 @@ Optional mod's settings page with [BTKUILib](https://github.com/BTK-Development/
* **Angular movement drag:** angular movement resistance; `2.0` by default.
* **Recover delay:** time delay for enabled `Auto recover` in seconds; `3.0` by default.
* **Fall limit:** height limit for fall damage; `5.0` by default.
+* **Impact volume:** collision sounds volume of ragdolled body parts; `100.0` by default.
* **Reset settings:** resets mod settings to default.
Optional mod's settings in [UIExpansionKit](https://github.com/ddakebono/ChilloutMods):
diff --git a/ml_prm/RagdollBodypartHandler.cs b/ml_prm/RagdollBodypartHandler.cs
index ac2aa93..d98f681 100644
--- a/ml_prm/RagdollBodypartHandler.cs
+++ b/ml_prm/RagdollBodypartHandler.cs
@@ -1,15 +1,15 @@
using ABI.CCK.Components;
using ABI_RC.Core;
-using ABI_RC.Core.Networking.IO.Social;
using ABI_RC.Core.Player;
-using ABI_RC.Core.Savior;
using ABI_RC.Systems.Movement;
+using NAK.Contacts;
+using System.Collections.Generic;
using UnityEngine;
namespace ml_prm
{
[DisallowMultipleComponent]
- class RagdollBodypartHandler : MonoBehaviour, CVRTriggerVolume
+ class RagdollBodypartHandler : MonoBehaviour
{
const string c_ragdollPointerType = "ragdoll";
const string c_grabPointerType = "grab";
@@ -18,14 +18,16 @@ namespace ml_prm
Rigidbody m_rigidBody = null;
public Collider collider { get; set; } = null;
+ ContactReceiver m_contactReciever = null;
PhysicsInfluencer m_physicsInfluencer = null;
public bool UseBuoyancy { get; set; } = false;
bool m_attached = false;
- CVRPointer m_attachedPointer = null;
Transform m_attachTransform = null;
+ ContactSender m_attachedSender = null;
FixedJoint m_attachJoint = null;
+ static List ms_attachedSenders = new List();
// Unity events
void Awake()
@@ -42,7 +44,28 @@ namespace ml_prm
}
if(collider != null)
+ {
RemoveGameCollision();
+
+ var l_constactShape = ContactConversion.FromCollider(collider, true);
+ m_contactReciever = this.gameObject.AddComponent();
+
+ m_contactReciever.shapeType = l_constactShape.shapeType;
+ m_contactReciever.localPosition = l_constactShape.localPosition;
+ m_contactReciever.localRotation = l_constactShape.localRotation;
+ m_contactReciever.radius = l_constactShape.radius;
+ m_contactReciever.height = l_constactShape.height;
+ m_contactReciever.boxSize = l_constactShape.boxSize;
+
+ m_contactReciever.collisionTags = new string[] { c_ragdollPointerType, c_grabPointerType };
+ m_contactReciever.receiverType = ReceiverType.Constant;
+ m_contactReciever.contentTypes = ContentType.World | ContentType.Avatar | ContentType.Prop;
+ m_contactReciever.SourceContentType = ContentType.Player;
+ m_contactReciever.contactValue = 1f;
+ m_contactReciever.drawGizmos = false;
+
+ m_contactReciever.OnContactEnter += this.OnContactEnter;
+ }
}
void Start()
@@ -64,46 +87,54 @@ namespace ml_prm
this.gameObject.name = string.Format("{0} [NoGizmo]", this.gameObject.name);
}
-
- if(collider != null)
- {
- CVRParticlePointerManager.volumes.Add(this);
- CVRParticlePointerManager.UpdateParticleSystems();
- }
}
void OnDestroy()
{
- if(collider != null)
- CVRParticlePointerManager.RemoveTrigger(collider);
-
Detach();
}
void Update()
{
- if(m_attached && ((m_attachedPointer == null) || !m_attachedPointer.isActiveAndEnabled))
+ if(m_attached && ((m_attachedSender == null) || !m_attachedSender.isActiveAndEnabled))
Detach();
}
- void OnTriggerEnter(Collider p_col)
+ void OnCollisionEnter(Collision p_col)
{
- if(m_ready && (RagdollController.Instance != null))
+ if(Settings.ImpactSounds && m_ready && !m_rigidBody.isKinematic && (p_col.gameObject.layer != CVRLayers.PlayerClone))
{
- CVRPointer l_pointer = p_col.GetComponent();
+ if(p_col.impulse.magnitude > 5f)
+ SoundManager.Instance.PlaySound(SoundManager.ImpactType.Soft);
+ }
+ }
- // Ragdolling
- if(Settings.PointersReaction && !RagdollController.Instance.IsRagdolled())
+ void OnContactEnter(ContactCollisionInfo p_col)
+ {
+ if(m_ready && (RagdollController.Instance != null) && ContactManager.Exists)
+ {
+ ContactSender l_sender = ContactManager.Instance.GetSenderById(p_col.senderContactId);
+ if((l_sender != null) && (l_sender.collisionTags != null))
{
- if((l_pointer != null) && (l_pointer.type == c_ragdollPointerType) && l_pointer.enabled && !IgnoreCheck(l_pointer.transform))
- RagdollController.Instance.Ragdoll();
- }
+ foreach(string l_tag in l_sender.collisionTags)
+ {
+ switch(l_tag)
+ {
+ case c_ragdollPointerType:
+ {
+ if(Settings.PointersReaction && !RagdollController.Instance.IsRagdolled() && RestrictionsCheck(l_sender.transform.root))
+ RagdollController.Instance.Ragdoll();
+ }
+ break;
- //Attachment
- if(!m_attached && RagdollController.Instance.IsRagdolled())
- {
- if((l_pointer != null) && (l_pointer.type == c_grabPointerType) && RestrictionsCheck(p_col.transform.root))
- Attach(l_pointer);
+ case c_grabPointerType:
+ {
+ if(!m_attached && RagdollController.Instance.IsRagdolled() && RestrictionsCheck(l_sender.transform.root))
+ Attach(l_sender);
+ }
+ break;
+ }
+ }
}
}
}
@@ -196,16 +227,16 @@ namespace ml_prm
RemoveGameCollision();
}
- void Attach(CVRPointer p_pointer)
+ void Attach(ContactSender p_sender)
{
- if(!m_attached && (collider != null) && (m_rigidBody != null))
+ if(!m_attached && (collider != null) && (m_rigidBody != null) && !ms_attachedSenders.Contains(p_sender))
{
- m_attachedPointer = p_pointer;
+ m_attachedSender = p_sender;
GameObject l_attachPoint = new GameObject("[AttachPoint]");
- l_attachPoint.layer = CVRLayers.PlayerNetwork;
+ l_attachPoint.layer = CVRLayers.Default;
m_attachTransform = l_attachPoint.transform;
- m_attachTransform.parent = p_pointer.transform;
+ m_attachTransform.parent = p_sender.transform;
Rigidbody l_body = l_attachPoint.AddComponent();
l_body.isKinematic = true;
@@ -216,6 +247,7 @@ namespace ml_prm
m_attachJoint.breakForce = Mathf.Infinity;
m_attachJoint.breakTorque = Mathf.Infinity;
+ ms_attachedSenders.Add(p_sender);
m_attached = true;
}
}
@@ -232,7 +264,10 @@ namespace ml_prm
Object.Destroy(m_attachJoint);
m_attachJoint = null;
- m_attachedPointer = null;
+ if(!ReferenceEquals(m_attachedSender, null))
+ ms_attachedSenders.Remove(m_attachedSender);
+ m_attachedSender = null;
+
m_attached = false;
}
}
@@ -246,32 +281,12 @@ namespace ml_prm
BetterBetterCharacterController.Instance.IgnoreCollision(collider);
}
- // CVRTriggerVolume
- public void TriggerEnter(CVRPointer pointer)
- {
- if(Settings.PointersReaction && (pointer != null) && pointer.enabled && (pointer.type == c_ragdollPointerType) && !IgnoreCheck(pointer.transform) && (RagdollController.Instance != null) && !RagdollController.Instance.IsRagdolled())
- RagdollController.Instance.Ragdoll();
- }
- public void TriggerExit(CVRPointer pointer)
+ internal void RestoreContact()
{
+ if((m_contactReciever != null) && ContactManager.Exists)
+ ContactManager.Instance.RestoreContact(m_contactReciever, m_contactReciever.isActiveAndEnabled);
}
- // Static utility
- static bool IgnoreCheck(Transform p_transform)
- {
- return (Settings.IgnoreLocal && (p_transform.root == PlayerSetup.Instance.transform));
- }
-
- static bool RestrictionsCheck(Transform p_transform)
- {
- if(p_transform == PlayerSetup.Instance.transform)
- return false;
-
- PlayerDescriptor l_playerDescriptor = p_transform.GetComponent();
- if(l_playerDescriptor != null)
- return (!Settings.FriendsGrab || Friends.FriendsWith(l_playerDescriptor.ownerId));
-
- return false;
- }
+ static bool RestrictionsCheck(Transform p_transform) => (p_transform != PlayerSetup.Instance.transform);
}
}
diff --git a/ml_prm/RagdollController.cs b/ml_prm/RagdollController.cs
index ac0f0fd..e0b073c 100644
--- a/ml_prm/RagdollController.cs
+++ b/ml_prm/RagdollController.cs
@@ -661,7 +661,7 @@ namespace ml_prm
IKSystem.Instance.applyOriginalHipRotation = true;
PlayerSetup.Instance.AnimatorManager.CancelEmote = true;
- m_ragdolledParameter.SetValue(true);
+ m_ragdolledParameter?.SetValue(true);
if(!WorldManager.IsSafeWorld())
{
@@ -677,6 +677,7 @@ namespace ml_prm
foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers)
{
+ l_handler.RestoreContact();
l_handler.SetVelocity(l_velocity);
l_handler.SetAngularVelocity(Vector3.zero);
}
@@ -689,6 +690,9 @@ namespace ml_prm
m_lastRagdollPosition = m_puppetReferences.hips.position;
m_downTime = 0f;
+ if(Settings.ImpactSounds)
+ SoundManager.Instance.PlaySound(SoundManager.ImpactType.Hard);
+
m_ragdolled = true;
}
}
@@ -706,7 +710,7 @@ namespace ml_prm
if(m_vrIK != null)
m_vrIK.solver.Reset();
- m_ragdolledParameter.SetValue(false);
+ m_ragdolledParameter?.SetValue(false);
m_puppet.localPosition = Vector3.zero;
m_puppet.localRotation = Quaternion.identity;
diff --git a/ml_prm/ResourcesHandler.cs b/ml_prm/ResourcesHandler.cs
new file mode 100644
index 0000000..ba44ef3
--- /dev/null
+++ b/ml_prm/ResourcesHandler.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+namespace ml_prm
+{
+ static class ResourcesHandler
+ {
+ const string c_modName = "PlayerRagdollMod";
+ readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
+
+ static readonly List ms_audioResources = new List()
+ {
+ "body_medium_impact_hard1.wav",
+ "body_medium_impact_hard2.wav",
+ "body_medium_impact_hard3.wav",
+ "body_medium_impact_hard4.wav",
+ "body_medium_impact_hard5.wav",
+ "body_medium_impact_hard6.wav",
+ "body_medium_impact_soft1.wav",
+ "body_medium_impact_soft2.wav",
+ "body_medium_impact_soft3.wav",
+ "body_medium_impact_soft4.wav",
+ "body_medium_impact_soft5.wav",
+ "body_medium_impact_soft6.wav",
+ "body_medium_impact_soft7.wav"
+ };
+
+ public static void ExtractResources()
+ {
+ string l_dirPath = MelonLoader.Utils.MelonEnvironment.UserDataDirectory;
+ if(!Directory.Exists(l_dirPath))
+ Directory.CreateDirectory(l_dirPath);
+
+ l_dirPath = Path.Combine(l_dirPath, c_modName);
+ if(!Directory.Exists(l_dirPath))
+ Directory.CreateDirectory(l_dirPath);
+
+ foreach(string l_name in ms_audioResources)
+ {
+ string l_filePath = Path.Combine(l_dirPath, l_name);
+ if(!File.Exists(l_filePath))
+ ExtractAudioFile(l_name, l_filePath);
+ }
+ }
+
+ static void ExtractAudioFile(string p_name, string p_path)
+ {
+ Assembly l_assembly = Assembly.GetExecutingAssembly();
+
+ try
+ {
+ Stream l_resourceStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources.sounds." + p_name);
+ Stream l_fileStream = File.Create(p_path);
+ l_resourceStream.CopyTo(l_fileStream);
+ l_fileStream.Flush();
+ l_fileStream.Close();
+ l_resourceStream.Close();
+ }
+ catch(Exception)
+ {
+ MelonLoader.MelonLogger.Warning("Unable to write '" + p_path + "' file, problems can occur.");
+ }
+ }
+ }
+}
diff --git a/ml_prm/Settings.cs b/ml_prm/Settings.cs
index 74869a4..f35a331 100644
--- a/ml_prm/Settings.cs
+++ b/ml_prm/Settings.cs
@@ -23,7 +23,6 @@ namespace ml_prm
AngularDrag,
Gravity,
PointersReaction,
- IgnoreLocal,
CombatReaction,
AutoRecover,
RecoverDelay,
@@ -35,7 +34,8 @@ namespace ml_prm
FallDamage,
FallLimit,
GestureGrab,
- FriendsGrab
+ ImpactSounds,
+ ImpactVolume
}
public static bool Hotkey { get; private set; } = true;
@@ -45,7 +45,6 @@ namespace ml_prm
public static float AngularDrag { get; private set; } = 2f;
public static bool Gravity { get; private set; } = true;
public static bool PointersReaction { get; private set; } = true;
- public static bool IgnoreLocal { get; private set; } = true;
public static bool CombatReaction { get; private set; } = true;
public static bool AutoRecover { get; private set; } = false;
public static float RecoverDelay { get; private set; } = 3f;
@@ -57,8 +56,8 @@ namespace ml_prm
public static bool FallDamage { get; private set; } = true;
public static float FallLimit { get; private set; } = 9.899494f;
public static bool GestureGrab { get; private set; } = false;
- public static bool FriendsGrab { get; private set; } = true;
- public static float GrabDistance { get; private set; } = 0.1f;
+ public static bool ImpactSounds { get; private set; } = true;
+ public static float ImpactVolume { get; private set; } = 0.25f;
public static readonly SettingEvent OnHotkeyChanged = new SettingEvent();
public static readonly SettingEvent OnHotkeyKeyChanged = new SettingEvent();
@@ -67,7 +66,6 @@ namespace ml_prm
public static readonly SettingEvent OnAngularDragChanged = new SettingEvent();
public static readonly SettingEvent OnGravityChanged = new SettingEvent();
public static readonly SettingEvent OnPointersReactionChanged = new SettingEvent();
- public static readonly SettingEvent OnIgnoreLocalChanged = new SettingEvent();
public static readonly SettingEvent OnCombatReactionChanged = new SettingEvent();
public static readonly SettingEvent OnAutoRecoverChanged = new SettingEvent();
public static readonly SettingEvent OnRecoverDelayChanged = new SettingEvent();
@@ -79,8 +77,8 @@ namespace ml_prm
public static readonly SettingEvent OnFallDamageChanged = new SettingEvent();
public static readonly SettingEvent OnFallLimitChanged = new SettingEvent();
public static readonly SettingEvent OnGestureGrabChanged = new SettingEvent();
- public static readonly SettingEvent OnFriendsGrabChanged = new SettingEvent();
- public static readonly SettingEvent OnGrabDistanceChanged = new SettingEvent();
+ public static readonly SettingEvent OnImpactSoundsChanged = new SettingEvent();
+ public static readonly SettingEvent OnImpactVolumeChanged = new SettingEvent();
static MelonLoader.MelonPreferences_Category ms_category = null;
static List ms_entries = null;
@@ -98,7 +96,6 @@ namespace ml_prm
ms_category.CreateEntry(ModSetting.AngularDrag.ToString(), AngularDrag, null, null, true),
ms_category.CreateEntry(ModSetting.Gravity.ToString(), Gravity, null, null, true),
ms_category.CreateEntry(ModSetting.PointersReaction.ToString(), PointersReaction, null, null, true),
- ms_category.CreateEntry(ModSetting.IgnoreLocal.ToString(), IgnoreLocal, null, null, true),
ms_category.CreateEntry(ModSetting.CombatReaction.ToString(), CombatReaction, null, null, true),
ms_category.CreateEntry(ModSetting.AutoRecover.ToString(), AutoRecover, null, null, true),
ms_category.CreateEntry(ModSetting.RecoverDelay.ToString(), RecoverDelay, null, null, true),
@@ -110,7 +107,8 @@ namespace ml_prm
ms_category.CreateEntry(ModSetting.FallDamage.ToString(), FallDamage, null, null, true),
ms_category.CreateEntry(ModSetting.FallLimit.ToString(), FallLimit, null, null, true),
ms_category.CreateEntry(ModSetting.GestureGrab.ToString(), GestureGrab, null, null, true),
- ms_category.CreateEntry(ModSetting.FriendsGrab.ToString(), FriendsGrab, null, null, true)
+ ms_category.CreateEntry(ModSetting.ImpactSounds.ToString(), ImpactSounds, null, null, true),
+ ms_category.CreateEntry(ModSetting.ImpactVolume.ToString(), ImpactVolume, null, null, true)
};
ms_entries[(int)ModSetting.HotkeyKey].OnEntryValueChangedUntyped.Subscribe(OnMelonSettingSave_HotkeyKey);
@@ -122,7 +120,6 @@ namespace ml_prm
AngularDrag = Mathf.Clamp((float)ms_entries[(int)ModSetting.AngularDrag].BoxedValue, 0f, 50f);
Gravity = (bool)ms_entries[(int)ModSetting.Gravity].BoxedValue;
PointersReaction = (bool)ms_entries[(int)ModSetting.PointersReaction].BoxedValue;
- IgnoreLocal = (bool)ms_entries[(int)ModSetting.IgnoreLocal].BoxedValue;
CombatReaction = (bool)ms_entries[(int)ModSetting.CombatReaction].BoxedValue;
AutoRecover = (bool)ms_entries[(int)ModSetting.AutoRecover].BoxedValue;
RecoverDelay = Mathf.Clamp((float)ms_entries[(int)ModSetting.RecoverDelay].BoxedValue, 1f, 10f);
@@ -134,7 +131,8 @@ namespace ml_prm
FallDamage = (bool)ms_entries[(int)ModSetting.FallDamage].BoxedValue;
FallLimit = Mathf.Clamp((float)ms_entries[(int)ModSetting.FallLimit].BoxedValue, 4.5f, 44.5f);
GestureGrab = (bool)ms_entries[(int)ModSetting.GestureGrab].BoxedValue;
- FriendsGrab = (bool)ms_entries[(int)ModSetting.FriendsGrab].BoxedValue;
+ ImpactSounds = (bool)ms_entries[(int)ModSetting.ImpactSounds].BoxedValue;
+ ImpactVolume = Mathf.Clamp((float)ms_entries[(int)ModSetting.ImpactVolume].BoxedValue, 0f, 1f);
}
static void OnMelonSettingSave_HotkeyKey(object p_oldValue, object p_newValue)
@@ -181,13 +179,6 @@ namespace ml_prm
}
break;
- case ModSetting.IgnoreLocal:
- {
- IgnoreLocal = (bool)p_value;
- OnIgnoreLocalChanged.Invoke(IgnoreLocal);
- }
- break;
-
case ModSetting.CombatReaction:
{
CombatReaction = (bool)p_value;
@@ -251,10 +242,10 @@ namespace ml_prm
}
break;
- case ModSetting.FriendsGrab:
+ case ModSetting.ImpactSounds:
{
- FriendsGrab = (bool)p_value;
- OnFriendsGrabChanged.Invoke(FriendsGrab);
+ ImpactSounds = (bool)p_value;
+ OnImpactSoundsChanged.Invoke(ImpactSounds);
}
break;
@@ -293,6 +284,13 @@ namespace ml_prm
OnFallLimitChanged.Invoke(FallLimit);
}
break;
+
+ case ModSetting.ImpactVolume:
+ {
+ ImpactVolume = (float)p_value;
+ OnImpactVolumeChanged.Invoke(ImpactVolume);
+ }
+ break;
}
if(ms_entries != null)
diff --git a/ml_prm/SoundManager.cs b/ml_prm/SoundManager.cs
new file mode 100644
index 0000000..40df8b1
--- /dev/null
+++ b/ml_prm/SoundManager.cs
@@ -0,0 +1,141 @@
+using ABI_RC.Core;
+using System.Collections;
+using System.IO;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace ml_prm
+{
+ public class SoundManager
+ {
+ enum SoundType
+ {
+ ImpactHard1 = 0,
+ ImpactHard2,
+ ImpactHard3,
+ ImpactHard4,
+ ImpactHard5,
+ ImpactHard6,
+ ImpactSoft1,
+ ImpactSoft2,
+ ImpactSoft3,
+ ImpactSoft4,
+ ImpactSoft5,
+ ImpactSoft6,
+ ImpactSoft7,
+
+ Count
+ }
+ public enum ImpactType
+ {
+ Hard = 0,
+ Soft
+ }
+
+ const string c_modName = "PlayerRagdollMod";
+
+ public static SoundManager Instance { get; private set; } = null;
+
+ bool m_loaded = false;
+ readonly AudioClip[] m_clips = null;
+ AudioSource m_audioSource = null;
+
+ public SoundManager(Transform p_root)
+ {
+ m_clips = new AudioClip[(int)SoundType.Count];
+ for(int i = 0; i < (int)SoundType.Count; i++)
+ m_clips[i] = null;
+
+ GameObject l_audioSource = new GameObject("[ImpactSource]");
+ l_audioSource.transform.parent = p_root;
+ l_audioSource.transform.localPosition = Vector3.zero;
+ l_audioSource.transform.localRotation = Quaternion.identity;
+
+ m_audioSource = l_audioSource.AddComponent();
+ m_audioSource.playOnAwake = false;
+ m_audioSource.loop = false;
+ m_audioSource.minDistance = 2f;
+ m_audioSource.maxDistance = 5f;
+ m_audioSource.dopplerLevel = 0f;
+ m_audioSource.panStereo = 0f;
+ m_audioSource.spatialBlend = 0f; // 2D
+ m_audioSource.spread = 0f;
+ m_audioSource.rolloffMode = AudioRolloffMode.Linear;
+ m_audioSource.outputAudioMixerGroup = RootLogic.Instance.mainSfx;
+
+ Instance = this;
+
+ }
+ ~SoundManager()
+ {
+ if(Instance == this)
+ Instance = null;
+
+ if(m_audioSource != null)
+ Object.Destroy(m_audioSource);
+ m_audioSource = null;
+ }
+
+ internal void LoadSounds()
+ {
+ if(!m_loaded)
+ {
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard1, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard1.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard2, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard2.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard3, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard3.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard4, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard4.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard5, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard5.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard6, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard6.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft1, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft1.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft2, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft2.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft3, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft3.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft4, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft4.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft5, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft5.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft6, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft6.wav")));
+ MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft7, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft7.wav")));
+ m_loaded = true;
+ }
+ }
+
+ IEnumerator LoadAudioClip(SoundType p_type, string p_path)
+ {
+ using UnityWebRequest l_uwr = UnityWebRequestMultimedia.GetAudioClip("file://" + p_path, AudioType.WAV);
+ ((DownloadHandlerAudioClip)l_uwr.downloadHandler).streamAudio = true;
+ yield return l_uwr.SendWebRequest();
+
+ if((l_uwr.result == UnityWebRequest.Result.ConnectionError) || (l_uwr.result == UnityWebRequest.Result.ProtocolError))
+ {
+ MelonLoader.MelonLogger.Warning(l_uwr.error);
+ yield break;
+ }
+
+ AudioClip l_content;
+ AudioClip l_clip = (l_content = DownloadHandlerAudioClip.GetContent(l_uwr));
+ yield return l_content;
+ if(!l_uwr.isDone || (l_clip == null))
+ yield break;
+
+ m_clips[(int)p_type] = l_clip;
+ }
+
+ public void PlaySound(ImpactType p_type)
+ {
+ if(m_loaded)
+ {
+ int l_index = -1;
+ switch(p_type)
+ {
+ case ImpactType.Hard:
+ l_index = (int)SoundType.ImpactHard1 + Random.Range(0, 6);
+ break;
+ case ImpactType.Soft:
+ l_index = (int)SoundType.ImpactSoft1 + Random.Range(0, 7);
+ break;
+ }
+
+ if((l_index != -1) && (m_clips[l_index] != null))
+ m_audioSource.PlayOneShot(m_clips[l_index], Settings.ImpactVolume);
+ }
+ }
+ }
+}
diff --git a/ml_prm/Utils.cs b/ml_prm/Utils.cs
index 809b910..6eeb601 100644
--- a/ml_prm/Utils.cs
+++ b/ml_prm/Utils.cs
@@ -1,13 +1,11 @@
using ABI.CCK.Components;
-using ABI_RC.Core.Player;
-using ABI_RC.Core.Savior;
-using ABI_RC.Systems.IK;
using ABI_RC.Systems.Movement;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using System.Linq;
using ABI_RC.Core.InteractionSystem;
+using NAK.Contacts;
namespace ml_prm
{
@@ -18,6 +16,8 @@ namespace ml_prm
static readonly FieldInfo ms_influencerTouchingVolumes = typeof(PhysicsInfluencer).GetField("_touchingVolumes", BindingFlags.Instance | BindingFlags.NonPublic);
static readonly FieldInfo ms_influencerSubmergedColliders = typeof(PhysicsInfluencer).GetField("_submergedColliders", BindingFlags.Instance | BindingFlags.NonPublic);
static readonly FieldInfo ms_lastCVRSeat = typeof(BetterBetterCharacterController).GetField("_lastCvrSeat", BindingFlags.Instance | BindingFlags.NonPublic);
+ static readonly FieldInfo ms_contactList = typeof(ContactManager).GetField("contactList", BindingFlags.Instance | BindingFlags.NonPublic);
+ static readonly FieldInfo ms_pendingRemove = typeof(ContactManager).GetField("pendingRemove", BindingFlags.Instance | BindingFlags.NonPublic);
public static void ClearFluidVolumes(this BetterBetterCharacterController p_instance) => (ms_touchingVolumes?.GetValue(p_instance) as List)?.Clear();
@@ -35,6 +35,18 @@ namespace ml_prm
public static CVRSeat GetCurrentSeat(this BetterBetterCharacterController p_instance) => (ms_lastCVRSeat?.GetValue(p_instance) as CVRSeat);
+ public static bool IsRegistered(this ContactManager p_instance, ContactBase p_contact) => (ms_contactList?.GetValue(p_instance) as List).Contains(p_contact);
+
+ public static void RestoreContact(this ContactManager p_instance, ContactBase p_contact, bool p_state)
+ {
+ if(p_instance.IsRegistered(p_contact))
+ (ms_pendingRemove?.GetValue(p_instance) as HashSet).Remove(p_contact.ContactId);
+ else
+ p_instance.Register(p_contact);
+
+ p_instance.SetEnabled(p_contact.ContactId, p_state);
+ }
+
// Unity specific
public static void CopyGlobal(this Transform p_source, Transform p_target)
{
diff --git a/ml_prm/ml_prm.csproj b/ml_prm/ml_prm.csproj
index 4059621..2969588 100644
--- a/ml_prm/ml_prm.csproj
+++ b/ml_prm/ml_prm.csproj
@@ -1,99 +1,127 @@
-
-
-
- netstandard2.1
- x64
- PlayerRagdollMod
- 1.2.4
- SDraw
- SDraw
- PlayerRagdollMod
- PlayerRagdollMod
-
-
+
+
+
+ netstandard2.1
+ x64
+ PlayerRagdollMod
+ 1.2.7
+ SDraw
+ SDraw
+ PlayerRagdollMod
+ PlayerRagdollMod
+
+
embedded
true
-
-
-
-
-
-
-
-
-
-
-
-
- $(CVRPath)/MelonLoader/net35/0Harmony.dll
- false
- false
-
-
- $(CVRPath)/ChilloutVR_Data/Managed/Assembly-CSharp.dll
- false
- false
-
-
- $(CVRPath)/ChilloutVR_Data/Managed/Assembly-CSharp-firstpass.dll
- false
- false
-
-
- $(CVRPath)/Mods/BTKUILib.dll
- false
- false
-
+