New mod: PlayerPickUp

This commit is contained in:
SDraw 2025-04-28 21:53:37 +03:00
parent e9ed898b9a
commit fd9809bd62
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
19 changed files with 793 additions and 23 deletions

View file

@ -6,11 +6,12 @@ Merged set of MelonLoader mods for ChilloutVR.
|[Avatar Motion Tweaker](/ml_amt/README.md)|1.5.1 [:arrow_down:](../../releases/latest/download/AvatarMotionTweaker.dll)|
|[Avatar Synced Look](/ml_asl/README.md)|1.1.1 [:arrow_down:](../../releases/latest/download/AvatarSyncedLook.dll)|
|[Better Fingers Tracking](/ml_bft/README.md)|1.1.2 [:arrow_down:](../../releases/latest/download/BetterFingersTracking.dll)|
|[Desktop Head Tracking](/ml_dht/README.md)|1.3.1 [:arrow_down:](../../releases/latest/download/DesktopHeadTracking.dll)|
|[Desktop Head Tracking](/ml_dht/README.md)|1.3.2 [:arrow_down:](../../releases/latest/download/DesktopHeadTracking.dll)|
|[Leap Motion Extension](/ml_lme/README.md)| 1.6.1 [:arrow_down:](../../releases/latest/download/LeapMotionExtension.dll)|
|[Pickup Arm Movement](/ml_pam/README.md)|1.2.2 [:arrow_down:](../../releases/latest/download/PickupArmMovement.dll)|
|[Players Instance Notifier](/ml_pin/README.md)|1.1.1 [:arrow_down:](../../releases/latest/download/PlayersInstanceNotifier.dll)|
|[Player Movement Copycat](/ml_pmc/README.md)|1.1.1 [:arrow_down:](../../releases/latest/download/PlayerMovementCopycat.dll)|
|[Player Pick Up](/ml_ppu/README.md)|1.0.0 [:arrow_down:](../../releases/latest/download/PlayerPickUp.dll)|
|[Player Ragdoll Mod](/ml_prm/README.md)|1.2.3 [:arrow_down:](../../releases/latest/download/PlayerRagdollMod.dll)|
|[Video Player Cookies](/ml_vpc/README.md)|1.0.1 [:arrow_down:](../../releases/latest/download/VideoPlayerCookies.dll)|
|[Vive Extended Input](/ml_vei/README.md)|1.1.1 [:arrow_down:](../../releases/latest/download/ViveExtendedInput.dll)|

View file

@ -2,6 +2,7 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Player.EyeMovement;
using ABI_RC.Systems.FaceTracking;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.VRModeSwitch;
using RootMotion.FinalIK;
using System;
@ -138,19 +139,23 @@ namespace ml_dht
// Game events
internal void OnAvatarSetup()
{
Utils.SetAvatarTPose();
m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent<CVRAvatar>();
m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head);
m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if(PlayerSetup.Instance._animator.isHuman)
{
IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose);
PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero;
PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity;
m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head);
if(m_headBone != null)
m_bindRotation = Quaternion.Inverse(m_avatarDescriptor.transform.rotation) * m_headBone.rotation;
m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if(m_lookIK != null)
m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate);
}
}
void OnAvatarClear()
{
@ -163,6 +168,7 @@ namespace ml_dht
void OnAvatarReuse()
{
m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if(m_lookIK != null)
m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate);
@ -190,7 +196,7 @@ namespace ml_dht
void UpdateFaceTracking(CVRFaceTracking p_component, GameEvents.EventResult p_result)
{
if(this.enabled && Settings.Enabled && Settings.FaceTracking && p_component.isLocal && p_component.UseFacialTracking )
if(this.enabled && Settings.Enabled && Settings.FaceTracking && p_component.isLocal && p_component.UseFacialTracking)
{
if(!m_lipDataSent)
{

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.3.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.3.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

@ -19,15 +19,5 @@ namespace ml_dht
public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => (ms_view?.GetValue(p_instance) as cohtml.Net.View)?.ExecuteScript(p_script);
public static void UpdateShapesLocal_Private(this CVRFaceTracking p_instance) => ms_updateShapesLocal?.Invoke(p_instance, ms_emptyArray);
public static void SetAvatarTPose()
{
if(PlayerSetup.Instance._animator.isHuman)
{
IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose);
PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero;
PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity;
}
}
}
}

View file

@ -6,7 +6,7 @@
<Authors>SDraw</Authors>
<Company>SDraw</Company>
<Product>DesktopHeadTracking</Product>
<Version>1.3.1</Version>
<Version>1.3.2</Version>
<Platforms>x64</Platforms>
<AssemblyName>DesktopHeadTracking</AssemblyName>
</PropertyGroup>

View file

@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_bft", "ml_bft\ml_bft.csp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_vpc", "ml_vpc\ml_vpc.csproj", "{7CF37B93-9341-422D-845C-9AB96DB4D0A1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_ppu", "ml_ppu\ml_ppu.csproj", "{F16DF16B-D127-4A2A-81FF-2FD80F320E64}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -71,6 +73,10 @@ Global
{7CF37B93-9341-422D-845C-9AB96DB4D0A1}.Debug|x64.Build.0 = Debug|x64
{7CF37B93-9341-422D-845C-9AB96DB4D0A1}.Release|x64.ActiveCfg = Release|x64
{7CF37B93-9341-422D-845C-9AB96DB4D0A1}.Release|x64.Build.0 = Release|x64
{F16DF16B-D127-4A2A-81FF-2FD80F320E64}.Debug|x64.ActiveCfg = Debug|x64
{F16DF16B-D127-4A2A-81FF-2FD80F320E64}.Debug|x64.Build.0 = Debug|x64
{F16DF16B-D127-4A2A-81FF-2FD80F320E64}.Release|x64.ActiveCfg = Release|x64
{F16DF16B-D127-4A2A-81FF-2FD80F320E64}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

132
ml_ppu/GameEvents.cs Normal file
View file

@ -0,0 +1,132 @@
using ABI_RC.Core;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using System;
using System.Reflection;
namespace ml_ppu
{
static class GameEvents
{
internal class GameEvent
{
event Action m_action;
public void AddListener(Action p_listener) => m_action += p_listener;
public void RemoveListener(Action p_listener) => m_action -= p_listener;
public void Invoke() => m_action?.Invoke();
}
internal class GameEvent<T1>
{
event Action<T1> m_action;
public void AddListener(Action<T1> p_listener) => m_action += p_listener;
public void RemoveListener(Action<T1> p_listener) => m_action -= p_listener;
public void Invoke(T1 p_obj) => m_action?.Invoke(p_obj);
}
public static readonly GameEvent OnAvatarSetup = new GameEvent();
public static readonly GameEvent OnAvatarClear = new GameEvent();
public static readonly GameEvent<float> OnIKScaling = new GameEvent<float>();
public static readonly GameEvent OnWorldPreSpawn = new GameEvent();
public static readonly GameEvent<CVRSeat> OnSeatPreSit = new GameEvent<CVRSeat>();
internal static void Init(HarmonyLib.Harmony p_instance)
{
try
{
p_instance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar), BindingFlags.Instance | BindingFlags.Public),
null,
new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static |BindingFlags.NonPublic))
);
p_instance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar), BindingFlags.Instance | BindingFlags.Public),
null,
new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
p_instance.Patch(
typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.Instance | BindingFlags.NonPublic),
null,
new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
p_instance.Patch(
typeof(RootLogic).GetMethod(nameof(RootLogic.SpawnOnWorldInstance), BindingFlags.Instance | BindingFlags.Public),
new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnWorldSpawn_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null
);
p_instance.Patch(
typeof(CVRSeat).GetMethod(nameof(CVRSeat.SitDown), BindingFlags.Instance | BindingFlags.Public),
new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnCVRSeatSitDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null
);
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnAvatarClear_Postfix()
{
try
{
OnAvatarClear.Invoke();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnSetupAvatar_Postfix()
{
try
{
OnAvatarSetup.Invoke();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnSetupIKScaling_Postfix(ref UnityEngine.Vector3 ___scaleDifference)
{
try
{
OnIKScaling.Invoke(1f + ___scaleDifference.y);
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnWorldSpawn_Prefix()
{
try
{
OnWorldPreSpawn.Invoke();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnCVRSeatSitDown_Prefix(ref CVRSeat __instance)
{
try
{
OnSeatPreSit.Invoke(__instance);
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
}
}

32
ml_ppu/GrabDetector.cs Normal file
View file

@ -0,0 +1,32 @@
using ABI.CCK.Components;
using ABI_RC.Core.Networking.IO.Social;
using ABI_RC.Core.Player;
using UnityEngine;
namespace ml_ppu
{
class GrabDetector : MonoBehaviour
{
void OnTriggerEnter(Collider p_collider)
{
if(!Settings.Enabled)
return;
CVRPointer l_pointer = p_collider.GetComponent<CVRPointer>();
if((l_pointer != null) && (l_pointer.type == "grab") && RestrictionsCheck(p_collider.transform.root))
PickUpManager.Instance?.OnGrabDetected(p_collider, l_pointer);
}
static bool RestrictionsCheck(Transform p_transform)
{
if(p_transform == PlayerSetup.Instance.transform)
return false;
PlayerDescriptor l_playerDescriptor = p_transform.GetComponent<PlayerDescriptor>();
if(l_playerDescriptor != null)
return (!Settings.FriendsOnly || Friends.FriendsWith(l_playerDescriptor.ownerId));
return false;
}
}
}

38
ml_ppu/Main.cs Normal file
View file

@ -0,0 +1,38 @@
using System;
using System.Collections;
using UnityEngine;
namespace ml_ppu
{
public class PlayerPickUp : MelonLoader.MelonMod
{
PickUpManager m_manager = null;
public override void OnInitializeMelon()
{
Settings.Init();
GameEvents.Init(HarmonyInstance);
ModUi.Init();
ModSupport.Init();
MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
}
IEnumerator WaitForRootLogic()
{
while(ABI_RC.Core.RootLogic.Instance == null)
yield return null;
m_manager = new GameObject("[PlayerPickUp]").AddComponent<PickUpManager>();
}
public override void OnDeinitializeMelon()
{
if(m_manager != null)
{
UnityEngine.Object.Destroy(m_manager.gameObject);
m_manager = null;
}
}
}
}

24
ml_ppu/ModSupport.cs Normal file
View file

@ -0,0 +1,24 @@
using System.Linq;
namespace ml_ppu
{
static class ModSupport
{
static bool ms_ragdollPresent = false;
internal static void Init()
{
ms_ragdollPresent = (MelonLoader.MelonBase.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerRagdollMod") != null);
}
public static bool IsRagdolled() => (ms_ragdollPresent && IsRagdollInternal());
static bool IsRagdollInternal() => ml_prm.RagdollController.Instance.IsRagdolled();
public static void TryToUnragdoll()
{
if(ms_ragdollPresent)
TryToUngradollInternal();
}
static void TryToUngradollInternal() => ml_prm.RagdollController.Instance.Unragdoll();
}
}

86
ml_ppu/ModUi.cs Normal file
View file

@ -0,0 +1,86 @@
using System;
using System.IO;
using System.Reflection;
using BTKUILib.UIObjects;
using BTKUILib.UIObjects.Components;
namespace ml_ppu
{
static class ModUi
{
enum UiIndex
{
Enabled = 0,
FriendsOnly,
VelocityMultiplier
}
readonly static string ms_namespace = typeof(ModUi).Namespace;
static Page ms_page = null;
static Category ms_category = null;
static ToggleButton ms_enabledToggle = null;
static ToggleButton ms_friendsOnlyToggle = null;
static SliderFloat ms_velocityMultiplierSlider = null;
internal static void Init()
{
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerPickUp", "PPU-Person", GetIconStream("person.png"));
ms_page = new Page("PlayerPickUp", "MainPage", true, "PPU-Person");
ms_page.MenuTitle = "Player Pick Up";
ms_page.MenuSubtitle = "Let people pick you up and carry you around";
ms_category = ms_page.AddCategory("Settings");
ms_enabledToggle = ms_category.AddToggle("Enabled", "Set mod's activity as enabled or disabled", Settings.Enabled);
ms_enabledToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Enabled, state);
ms_friendsOnlyToggle = ms_category.AddToggle("Friends only", "Allow only friends to pick you up", Settings.FriendsOnly);
ms_friendsOnlyToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.FriendsOnly, state);
ms_velocityMultiplierSlider = ms_category.AddSlider("Velocity multiplier", "Velocity multiplier upon drop", Settings.VelocityMultiplier, 0f, 50f);
ms_velocityMultiplierSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.VelocityMultiplier, value);
}
static void OnToggleUpdate(UiIndex p_index, bool p_state)
{
try
{
switch(p_index)
{
case UiIndex.Enabled:
Settings.SetSetting(Settings.ModSetting.Enabled, p_state);
break;
case UiIndex.FriendsOnly:
Settings.SetSetting(Settings.ModSetting.FriendsOnly, p_state);
break;
}
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnSliderUpdate(UiIndex p_index, float p_value)
{
try
{
switch(p_index)
{
case UiIndex.VelocityMultiplier:
Settings.SetSetting(Settings.ModSetting.VelocityMultiplier, p_value);
break;
}
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static Stream GetIconStream(string p_name) => Assembly.GetExecutingAssembly().GetManifestResourceStream(ms_namespace + ".resources." + p_name);
}
}

246
ml_ppu/PickUpManager.cs Normal file
View file

@ -0,0 +1,246 @@
using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.Movement;
using UnityEngine;
namespace ml_ppu
{
class PickUpManager : MonoBehaviour
{
public static PickUpManager Instance { get; private set; } = null;
Collider m_holderPointA = null;
CVRPointer m_holderPointerA = null;
Collider m_holderPointB = null;
CVRPointer m_holderPointerB = null;
CapsuleCollider m_collider = null;
Matrix4x4 m_colliderOffSet;
Matrix4x4 m_avatarOffSet;
Transform m_hips = null;
Transform m_armLeft = null;
Transform m_armRight = null;
bool m_ready = false;
bool m_held = false;
Vector3 m_lastPosition;
Vector3 m_velocity;
void Awake()
{
if(Instance != null)
{
DestroyImmediate(this);
return;
}
Instance = this;
DontDestroyOnLoad(this);
}
void Start()
{
GameEvents.OnAvatarSetup.AddListener(this.OnAvatarSetup);
GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear);
GameEvents.OnIKScaling.AddListener(this.OnIKScaling);
GameEvents.OnWorldPreSpawn.AddListener(this.OnWorldPreSpawn);
GameEvents.OnSeatPreSit.AddListener(this.OnSeatPreSit);
Settings.OnEnabledChanged.AddListener(this.OnEnabledChanged);
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
GameEvents.OnAvatarSetup.RemoveListener(this.OnAvatarSetup);
GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear);
GameEvents.OnIKScaling.RemoveListener(this.OnIKScaling);
GameEvents.OnWorldPreSpawn.RemoveListener(this.OnWorldPreSpawn);
GameEvents.OnSeatPreSit.RemoveListener(this.OnSeatPreSit);
Settings.OnEnabledChanged.RemoveListener(this.OnEnabledChanged);
}
void Update()
{
if(m_ready)
{
if(!m_held)
{
if((m_holderPointA != null) && !m_collider.bounds.Intersects(m_holderPointA.bounds))
{
m_holderPointA = null;
m_holderPointerA = null;
}
Vector3 l_armsMidPoint = (m_armLeft.position + m_armRight.position) * 0.5f;
Quaternion l_avatarRot = PlayerSetup.Instance._avatar.transform.rotation;
m_collider.transform.position = Vector3.zero;
m_collider.transform.rotation = Quaternion.identity;
m_collider.transform.up = Quaternion.Inverse(l_avatarRot) * (l_armsMidPoint - m_hips.position).normalized;
m_collider.transform.position = m_hips.position;
m_collider.transform.rotation = l_avatarRot * m_collider.transform.rotation;
}
else
{
// Check if our points are still valid
if((m_holderPointA != null) && m_holderPointerA.isActiveAndEnabled && (m_holderPointB != null) && m_holderPointerB.isActiveAndEnabled && !ModSupport.IsRagdolled())
{
Matrix4x4 l_midPoint = Matrix4x4.TRS(
Vector3.Lerp(m_holderPointA.transform.position, m_holderPointB.transform.position, 0.5f),
Quaternion.Slerp(m_holderPointA.transform.rotation, m_holderPointB.transform.rotation, 0.5f),
Vector3.one
);
Matrix4x4 l_colliderMat = l_midPoint * m_colliderOffSet;
m_collider.transform.position = l_colliderMat.GetPosition();
m_collider.transform.rotation = l_colliderMat.rotation;
Matrix4x4 l_avatarMat = l_colliderMat * m_avatarOffSet;
BetterBetterCharacterController.Instance.TeleportPlayerTo(l_avatarMat.GetPosition(), l_avatarMat.rotation.eulerAngles, true, false);
Vector3 l_position = l_avatarMat.GetPosition();
m_velocity = (l_position - m_lastPosition) / Time.deltaTime;
m_lastPosition = l_position;
}
else
{
m_holderPointA = null;
m_holderPointerA = null;
m_holderPointB = null;
m_holderPointerB = null;
m_held = false;
BetterBetterCharacterController.Instance.SetVelocity(m_velocity * Settings.VelocityMultiplier);
}
}
}
}
void OnAvatarSetup()
{
Animator l_animator = PlayerSetup.Instance._animator;
if((l_animator != null) && l_animator.isHuman)
{
IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose);
PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero;
PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity;
m_hips = l_animator.GetBoneTransform(HumanBodyBones.Hips);
m_armLeft = l_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm);
m_armRight = l_animator.GetBoneTransform(HumanBodyBones.RightUpperArm);
if((m_hips != null) && (m_armLeft != null) && (m_armRight != null))
{
Vector3 l_hipsPos = (PlayerSetup.Instance.transform.GetMatrix().inverse * m_hips.GetMatrix()).GetPosition();
Vector3 l_armPos = (PlayerSetup.Instance.transform.GetMatrix().inverse * m_armLeft.GetMatrix()).GetPosition();
m_collider = new GameObject("[Collider]").AddComponent<CapsuleCollider>();
m_collider.transform.parent = this.transform;
m_collider.isTrigger = true;
m_collider.height = Vector3.Distance(l_hipsPos, new Vector3(0f, l_armPos.y, l_armPos.z));
m_collider.radius = new Vector2(l_armPos.x, l_armPos.z).magnitude;
m_collider.center = new Vector3(0f, m_collider.height * 0.5f, 0f);
m_collider.gameObject.AddComponent<GrabDetector>();
m_ready = true;
}
}
}
void OnAvatarClear()
{
m_ready = false;
m_held = false;
if(m_collider != null)
{
UnityEngine.Object.Destroy(m_collider.gameObject);
m_collider = null;
}
m_holderPointA = null;
m_holderPointerA = null;
m_holderPointB = null;
m_holderPointerB = null;
}
void OnIKScaling(float p_scale)
{
if(m_ready)
m_collider.transform.localScale = Vector3.one * p_scale;
}
void OnWorldPreSpawn()
{
if(m_ready && m_held)
{
m_held = false;
m_holderPointA = null;
m_holderPointerA = null;
m_holderPointB = null;
m_holderPointerB = null;
}
}
void OnSeatPreSit(CVRSeat p_seat)
{
if(!p_seat.occupied && m_ready && m_held)
{
m_held = false;
m_holderPointA = null;
m_holderPointerA = null;
m_holderPointB = null;
m_holderPointerB = null;
}
}
void OnEnabledChanged(bool p_state)
{
if(!p_state && m_ready && m_held)
{
m_held = false;
m_holderPointA = null;
m_holderPointerA = null;
m_holderPointB = null;
m_holderPointerB = null;
}
}
internal void OnGrabDetected(Collider p_collider, CVRPointer p_pointer)
{
if(m_ready && !m_held && CVRWorld.Instance.allowFlying && !ModSupport.IsRagdolled())
{
if(m_holderPointA == null)
{
m_holderPointA = p_collider;
m_holderPointerA = p_pointer;
}
else
{
if((m_holderPointB == null) && (m_holderPointA != p_collider) && (m_holderPointA.transform.root == p_collider.transform.root))
{
m_holderPointB = p_collider;
m_holderPointerB = p_pointer;
// Remember offsets
Matrix4x4 l_midPoint = Matrix4x4.TRS(
Vector3.Lerp(m_holderPointA.transform.position, m_holderPointB.transform.position, 0.5f),
Quaternion.Slerp(m_holderPointA.transform.rotation, m_holderPointB.transform.rotation, 0.5f),
Vector3.one
);
m_colliderOffSet = l_midPoint.inverse * m_collider.transform.GetMatrix();
m_avatarOffSet = m_collider.transform.GetMatrix().inverse * PlayerSetup.Instance._avatar.transform.GetMatrix();
m_lastPosition = PlayerSetup.Instance._avatar.transform.position;
m_held = true;
}
}
}
}
}
}

View file

@ -0,0 +1,5 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_ppu.PlayerPickUp), "PlayerPickUp", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonOptionalDependencies("PlayerRagdollMod")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

21
ml_ppu/README.md Normal file
View file

@ -0,0 +1,21 @@
# Player Pick Up
This mod allow you to be picked up and carried around.
# Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Install [BTKUILib](https://github.com/BTK-Development/BTKUILib)
* Get [latest release DLL](../../../releases/latest):
* Put `PlayerPickUp.dll` in `Mods` folder of game
# Usage
Available mod's settings in BTKUILib's page:
* **Enabled:** sets mod's activity as enabled or disabled; `true` by default;
* **Friends only:** allow only friends to pick you up; `true` by default;
* **Velocity multiplier:** velocity multiplier upon drop/throw; `1.0` by default.
To pick you up remote player should:
* Make hands `grab` pointers to appear on your side (usually, press controller grip trigger button or fist gesture, depends on remote player controllers type);
* Touch your avatar's torso with both pointers;
# Notes
* Compatible with PlayerRagdolMod.

90
ml_ppu/Settings.cs Normal file
View file

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace ml_ppu
{
static class Settings
{
internal class SettingEvent<T>
{
event Action<T> m_action;
public void AddListener(Action<T> p_listener) => m_action += p_listener;
public void RemoveListener(Action<T> p_listener) => m_action -= p_listener;
public void Invoke(T p_value) => m_action?.Invoke(p_value);
}
public enum ModSetting
{
Enabled = 0,
FriendsOnly,
VelocityMultiplier
}
public static bool Enabled { get; private set; } = true;
public static bool FriendsOnly { get; private set; } = true;
public static float VelocityMultiplier { get; private set; } = 1f;
public static readonly SettingEvent<bool> OnEnabledChanged = new SettingEvent<bool>();
public static readonly SettingEvent<bool> OnFriendsOnlyChanged = new SettingEvent<bool>();
public static readonly SettingEvent<float> OnVelocityMultiplierChanged = new SettingEvent<float>();
static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("PPU", "Player Pick Up", true);
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled),
ms_category.CreateEntry(ModSetting.FriendsOnly.ToString(), FriendsOnly),
ms_category.CreateEntry(ModSetting.VelocityMultiplier.ToString(), VelocityMultiplier)
};
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
FriendsOnly = (bool)ms_entries[(int)ModSetting.FriendsOnly].BoxedValue;
VelocityMultiplier = Mathf.Clamp((float)ms_entries[(int)ModSetting.VelocityMultiplier].BoxedValue, 0f, 50f);
}
public static void SetSetting(ModSetting p_settings, object p_value)
{
try
{
switch(p_settings)
{
// Booleans
case ModSetting.Enabled:
{
Enabled = (bool)p_value;
OnEnabledChanged.Invoke(Enabled);
}
break;
case ModSetting.FriendsOnly:
{
FriendsOnly = (bool)p_value;
OnFriendsOnlyChanged.Invoke(FriendsOnly);
}
break;
// Floats
case ModSetting.VelocityMultiplier:
{
VelocityMultiplier = (float)p_value;
OnVelocityMultiplierChanged.Invoke(VelocityMultiplier);
}
break;
}
if(ms_entries != null)
ms_entries[(int)p_settings].BoxedValue = p_value;
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
}
}

12
ml_ppu/Utils.cs Normal file
View file

@ -0,0 +1,12 @@
using UnityEngine;
namespace ml_ppu
{
static class Utils
{
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
{
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.lossyScale : Vector3.one);
}
}
}

80
ml_ppu/ml_ppu.csproj Normal file
View file

@ -0,0 +1,80 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms>
<AssemblyName>PlayerPickUp</AssemblyName>
<Authors>SDraw</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<None Remove="resources\person.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="resources\person.png" />
</ItemGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="BTKUILib">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\Mods\BTKUILib.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
<Reference Include="ECM2">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ECM2.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="MelonLoader">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="PlayerRagdollMod">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\Mods\PlayerRagdollMod.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
</Target>
</Project>

BIN
ml_ppu/resources/person.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -3,6 +3,7 @@ This mod turns player's avatar into ragdoll puppet.
# Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Install [BTKUILib](https://github.com/BTK-Development/BTKUILib)
* Get [latest release DLL](../../../releases/latest):
* Put `PlayerRagdollMod.dll` in `Mods` folder of game