Check for props game settings and world restrictions

Combat system integration
Additional measure for unsafe worlds
This commit is contained in:
SDraw 2023-04-12 21:33:25 +03:00
parent 5185c00fb1
commit 912b37c87f
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
9 changed files with 186 additions and 57 deletions

View file

@ -7,7 +7,7 @@ Merged set of MelonLoader mods for ChilloutVR.
| Avatar Motion Tweaker | ml_amt | 1.2.5 | Yes, update review | Working |
| Desktop Head Tracking | ml_dht | 1.1.2 | Yes | Working |
| Desktop Reticle Switch | ml_drs | 1.0.0 | Yes | Working |
| Extended Game Notifications | ml_egn | 1.0.1 | Yes | Working
| Extended Game Notifications | ml_egn | 1.0.2 | Yes, update review | Working
| Four Point Tracking | ml_fpt | 1.0.9 | Retired | Deprecated | In-game feature since 2022r170 update
| Leap Motion Extension | ml_lme | 1.3.3 | Yes | Working |
| Pickup Arm Movement | ml_pam | 1.0.2 | Yes| Working |

View file

@ -1,5 +1,4 @@
using ABI_RC.Core.EventSystem;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.IO;
using ABI_RC.Core.Networking;
using ABI_RC.Core.Util;
@ -57,6 +56,7 @@ namespace ml_egn
Utils.ShowMenuNotification("Avatar changed", 1f);
else
Utils.ShowHUDNotification("(Synced) Client", "Avatar changed");
}
catch(System.Exception e)
{
@ -68,19 +68,37 @@ namespace ml_egn
{
try
{
if(Utils.IsConnected())
if(Utils.ArePropsEnabled())
{
if(Utils.IsMenuOpened())
Utils.ShowMenuNotification("Prop spawned", 1f);
if(Utils.ArePropsAllowed())
{
if(Utils.IsConnected())
{
if(Utils.IsMenuOpened())
Utils.ShowMenuNotification("Prop spawned", 1f);
else
Utils.ShowHUDNotification("(Synced) Client", "Prop spawned");
}
else
{
if(Utils.IsMenuOpened())
Utils.ShowMenuAlert("Prop Error", "Not connected to live instance");
else
Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Not connected to live instance");
}
}
else
Utils.ShowHUDNotification("(Synced) Client", "Prop spawned");
{
if(Utils.IsMenuOpened())
Utils.ShowMenuAlert("Prop Error", "Props are not allowed in this world");
}
}
else
{
if(Utils.IsMenuOpened())
Utils.ShowMenuAlert("Prop Error", "Not connected to live instance");
Utils.ShowMenuAlert("Prop Error", "Props are disabled in game settings");
else
Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Not connected to live instance");
Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Props are disabled in game settings");
}
}
catch(System.Exception e)

View file

@ -1,10 +1,10 @@
using System.Reflection;
[assembly: AssemblyTitle("ExtendedGameNotifications")]
[assembly: AssemblyVersion("1.0.1")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: AssemblyVersion("1.0.2")]
[assembly: AssemblyFileVersion("1.0.2")]
[assembly: MelonLoader.MelonInfo(typeof(ml_egn.ExtendedGameNotifications), "ExtendedGameNotifications", "1.0.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonInfo(typeof(ml_egn.ExtendedGameNotifications), "ExtendedGameNotifications", "1.0.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -1,5 +1,6 @@
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Networking;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
using DarkRift;
@ -42,5 +43,8 @@ namespace ml_egn
l_result = (NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected);
return l_result;
}
public static bool ArePropsAllowed() => ((MetaPort.Instance != null) && MetaPort.Instance.worldAllowProps);
public static bool ArePropsEnabled() => ((MetaPort.Instance != null) && MetaPort.Instance.settings.GetSettingsBool("ContentFilterPropsEnabled"));
}
}

View file

@ -6,6 +6,8 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using ABI_RC.Core.Util.AssetFiltering;
using ABI.CCK.Components;
using System.Linq;
namespace ml_prm
{
@ -47,6 +49,11 @@ namespace ml_prm
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnWorldSpawn_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null
);
HarmonyInstance.Patch(
typeof(CombatSystem).GetMethods().First(m => (!m.IsGenericMethod && m.Name == nameof(CombatSystem.Down))),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCombatDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null
);
// Whitelist the toggle script
(typeof(SharedFilter).GetField("_localComponentWhitelist", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null) as HashSet<Type>)?.Add(typeof(RagdollToggle));
@ -140,5 +147,22 @@ namespace ml_prm
}
}
static void OnCombatDown_Prefix(ref CombatSystem __instance)
{
if((__instance == CombatSystem.Instance) && !__instance.isDown)
ms_instance?.OnCombatDown();
}
void OnCombatDown()
{
try
{
if(m_localController != null)
m_localController.OnCombatDown();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
}
}

View file

@ -16,6 +16,7 @@ Optional mod's settings with [BTKUILib](https://github.com/BTK-Development/BTKUI
* **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 CVRPointer colliders of `ragdoll` type (avatars, props and world included); `true` by default.
* **Combat reaction:** enables ragdoll state upon death in worlds with combat system; `true` by default.
* **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.

View file

@ -1,4 +1,5 @@
using ABI_RC.Core.InteractionSystem;
using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem;
@ -33,6 +34,8 @@ namespace ml_prm
RagdollToggle m_avatarRagdollToggle = null;
RagdollTrigger m_customTrigger = null;
bool m_reachedGround = true;
internal RagdollController()
{
if(Instance == null)
@ -84,15 +87,18 @@ namespace ml_prm
m_velocity = (m_velocity + (l_pos - m_lastPosition) / Time.deltaTime) * 0.5f;
m_lastPosition = l_pos;
if(m_avatarReady && !m_reachedGround && MovementSystem.Instance.IsGrounded())
m_reachedGround = true;
if(m_enabled && m_avatarReady && BodySystem.isCalibratedAsFullBody)
BodySystem.TrackingPositionWeight = 0f;
if(Settings.Hotkey && Input.GetKeyDown(KeyCode.R) && !ViewManager.Instance.isGameMenuOpen())
SwitchRagdoll();
if((m_avatarRagdollToggle != null) && m_avatarRagdollToggle.isActiveAndEnabled && m_avatarRagdollToggle.shouldOverride && (m_enabled != m_avatarRagdollToggle.isOn))
SwitchRagdoll();
if(m_enabled && m_avatarReady && BodySystem.isCalibratedAsFullBody)
BodySystem.TrackingPositionWeight = 0f;
if((m_customTrigger != null) && m_customTrigger.GetStateWithReset() && !m_enabled && m_avatarReady && Settings.PointersReaction)
SwitchRagdoll();
}
@ -127,6 +133,7 @@ namespace ml_prm
m_colliders.Clear();
m_puppetReferences = new BipedRagdollReferences();
m_boneLinks.Clear();
m_reachedGround = true;
}
internal void OnAvatarSetup()
@ -253,6 +260,12 @@ namespace ml_prm
}
}
internal void OnCombatDown()
{
if(!m_enabled && m_avatarReady && Settings.CombatReaction)
SwitchRagdoll();
}
// IK updates
void OnIKPreUpdate()
{
@ -305,60 +318,88 @@ namespace ml_prm
// Arbitrary
public void SwitchRagdoll()
{
if(m_avatarReady && (MovementSystem.Instance.lastSeat == null) && !BodySystem.isCalibrating)
if(m_avatarReady)
{
m_enabled = !m_enabled;
MovementSystem.Instance.SetImmobilized(m_enabled);
if(m_enabled)
if(!m_enabled)
{
PlayerSetup.Instance.animatorManager.SetAnimatorParameterTrigger("CancelEmote");
if(BodySystem.isCalibratedAsFullBody)
BodySystem.TrackingPositionWeight = 0f;
// Copy before set to non-kinematic to reduce stacked forces
foreach(var l_link in m_boneLinks)
l_link.Item2.CopyGlobal(l_link.Item1);
foreach(Rigidbody l_body in m_rigidBodies)
l_body.isKinematic = false;
Vector3 l_velocity = m_velocity * Mathf.Clamp(Settings.VelocityMultiplier, 1f, (Utils.IsWorldSafe() ? Utils.GetWorldFlyMultiplier() : 1f));
foreach(Rigidbody l_body in m_rigidBodies)
if(IsSafeToRagdoll() && m_reachedGround)
{
l_body.velocity = l_velocity;
l_body.angularVelocity = Vector3.zero;
// Eject player from seat
if(MovementSystem.Instance.lastSeat != null)
{
Vector3 l_pos = PlayerSetup.Instance.transform.position;
Quaternion l_rot = PlayerSetup.Instance.transform.rotation;
MovementSystem.Instance.lastSeat.ExitSeat();
PlayerSetup.Instance.transform.position = l_pos;
PlayerSetup.Instance.transform.rotation = Quaternion.Euler(0f, l_rot.eulerAngles.y, 0f);
}
MovementSystem.Instance.SetImmobilized(true);
PlayerSetup.Instance.animatorManager.SetAnimatorParameterTrigger("CancelEmote");
if(BodySystem.isCalibratedAsFullBody)
BodySystem.TrackingPositionWeight = 0f;
if(!Utils.IsWorldSafe())
m_reachedGround = false; // Force player to unragdoll and reach ground first
// Copy before set to non-kinematic to reduce stacked forces
foreach(var l_link in m_boneLinks)
l_link.Item2.CopyGlobal(l_link.Item1);
foreach(Rigidbody l_body in m_rigidBodies)
l_body.isKinematic = false;
Vector3 l_velocity = m_velocity * Mathf.Clamp(Settings.VelocityMultiplier, 1f, (Utils.IsWorldSafe() ? Utils.GetWorldFlyMultiplier() : 1f));
foreach(Rigidbody l_body in m_rigidBodies)
{
l_body.velocity = l_velocity;
l_body.angularVelocity = Vector3.zero;
}
foreach(Collider l_collider in m_colliders)
l_collider.enabled = true;
m_enabled = true;
}
}
else
{
if(BodySystem.isCalibratedAsFullBody)
BodySystem.TrackingPositionWeight = 1f;
foreach(Rigidbody l_body in m_rigidBodies)
l_body.isKinematic = true;
if(m_puppetReferences.hips != null)
if(IsSafeToUnragdoll())
{
Vector3 l_hipsPos = m_puppetReferences.hips.position;
MovementSystem.Instance.SetImmobilized(false);
if(BodySystem.isCalibratedAsFullBody)
BodySystem.TrackingPositionWeight = 1f;
if(!Settings.RestorePosition)
foreach(Rigidbody l_body in m_rigidBodies)
l_body.isKinematic = true;
if(m_puppetReferences.hips != null)
{
if(Utils.IsInVR())
Vector3 l_hipsPos = m_puppetReferences.hips.position;
if(!Settings.RestorePosition)
{
Vector3 l_diff = l_hipsPos - PlayerSetup.Instance._avatar.transform.position;
Vector3 l_playerPos = PlayerSetup.Instance.transform.position;
PlayerSetup.Instance.transform.position = l_playerPos + l_diff;
if(Utils.IsInVR())
{
Vector3 l_diff = l_hipsPos - PlayerSetup.Instance._avatar.transform.position;
Vector3 l_playerPos = PlayerSetup.Instance.transform.position;
PlayerSetup.Instance.transform.position = l_playerPos + l_diff;
}
else
PlayerSetup.Instance.transform.position = l_hipsPos;
}
else
PlayerSetup.Instance.transform.position = l_hipsPos;
}
foreach(Collider l_collider in m_colliders)
l_collider.enabled = false;
m_lastPosition = PlayerSetup.Instance.transform.position;
m_velocity = Vector3.zero;
m_enabled = false;
}
}
foreach(Collider l_collider in m_colliders)
l_collider.enabled = m_enabled;
}
}
@ -371,5 +412,20 @@ namespace ml_prm
p_source.CopyGlobal(l_target);
return l_target;
}
static bool IsSafeToRagdoll()
{
bool l_result = true;
l_result &= !BodySystem.isCalibrating; // Not calibrating
l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); // Non-combat world or not dead
return l_result;
}
static bool IsSafeToUnragdoll()
{
bool l_result = true;
l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); // Non-combat world or not dead
return l_result;
}
}
}

View file

@ -14,7 +14,8 @@ namespace ml_prm
MovementDrag,
AngularDrag,
Gravity,
PointersReaction
PointersReaction,
CombatReaction
}
enum UiElementIndex
@ -23,6 +24,7 @@ namespace ml_prm
RestorePosition,
Gravity,
PointersReaction,
CombatReaction,
VelocityMultiplier,
MovementDrag,
AngularDrag
@ -35,6 +37,7 @@ 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 CombatReaction { get; private set; } = true;
static public event Action SwitchChange;
static public event Action<bool> HotkeyChange;
@ -44,6 +47,7 @@ namespace ml_prm
static public event Action<float> AngularDragChange;
static public event Action<bool> GravityChange;
static public event Action<bool> PointersReactionChange;
static public event Action<bool> CombatReactionChange;
static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
@ -61,7 +65,8 @@ namespace ml_prm
ms_category.CreateEntry(ModSetting.MovementDrag.ToString(), MovementDrag),
ms_category.CreateEntry(ModSetting.AngularDrag.ToString(), AngularDrag),
ms_category.CreateEntry(ModSetting.Gravity.ToString(), Gravity),
ms_category.CreateEntry(ModSetting.PointersReaction.ToString(), PointersReaction)
ms_category.CreateEntry(ModSetting.PointersReaction.ToString(), PointersReaction),
ms_category.CreateEntry(ModSetting.CombatReaction.ToString(), CombatReaction)
};
Hotkey = (bool)ms_entries[(int)ModSetting.Hotkey].BoxedValue;
@ -71,6 +76,7 @@ namespace ml_prm
AngularDrag = UnityEngine.Mathf.Clamp((float)ms_entries[(int)ModSetting.MovementDrag].BoxedValue, 0f, 50f);
Gravity = (bool)ms_entries[(int)ModSetting.Gravity].BoxedValue;
PointersReaction = (bool)ms_entries[(int)ModSetting.PointersReaction].BoxedValue;
CombatReaction = (bool)ms_entries[(int)ModSetting.CombatReaction].BoxedValue;
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "BTKUILib") != null)
{
@ -122,6 +128,14 @@ namespace ml_prm
PointersReactionChange?.Invoke(state);
};
ms_uiElements.Add(l_categoryMod.AddToggle("Combat reaction", "Ragdoll upon combat system death", CombatReaction));
(ms_uiElements[(int)UiElementIndex.CombatReaction] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) =>
{
CombatReaction = state;
ms_entries[(int)ModSetting.CombatReaction].BoxedValue = state;
CombatReactionChange?.Invoke(state);
};
ms_uiElements.Add(l_page.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", VelocityMultiplier, 1f, 50f));
(ms_uiElements[(int)UiElementIndex.VelocityMultiplier] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) =>
{
@ -168,6 +182,11 @@ namespace ml_prm
(ms_uiElements[(int)UiElementIndex.PointersReaction] as BTKUILib.UIObjects.Components.ToggleButton).ToggleValue = true;
PointersReactionChange?.Invoke(true);
CombatReaction = true;
ms_entries[(int)ModSetting.CombatReaction].BoxedValue = true;
(ms_uiElements[(int)UiElementIndex.CombatReaction] as BTKUILib.UIObjects.Components.ToggleButton).ToggleValue = true;
CombatReactionChange?.Invoke(true);
VelocityMultiplier = 2f;
ms_entries[(int)ModSetting.VelocityMultiplier].BoxedValue = 2f;
(ms_uiElements[(int)UiElementIndex.VelocityMultiplier] as BTKUILib.UIObjects.Components.SliderFloat).SetSliderValue(2f);

View file

@ -1,11 +1,16 @@
using ABI.CCK.Components;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.MovementSystem;
using System.Reflection;
using UnityEngine;
namespace ml_prm
{
static class Utils
{
public static bool IsInVR() => ((ABI_RC.Core.Savior.CheckVR.Instance != null) && ABI_RC.Core.Savior.CheckVR.Instance.hasVrDeviceLoaded);
static readonly FieldInfo ms_grounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
public static float GetWorldFlyMultiplier()
{
@ -15,6 +20,8 @@ namespace ml_prm
return l_result;
}
public static bool IsGrounded(this MovementSystem p_instance) => (bool)ms_grounded.GetValue(p_instance);
public static void CopyGlobal(this Transform p_source, Transform p_target)
{
p_target.position = p_source.position;