mirror of
https://github.com/hanetzer/sdraw_mods_cvr.git
synced 2025-09-03 10:29:22 +00:00
New mod: PlayerPickUp
This commit is contained in:
parent
e9ed898b9a
commit
fd9809bd62
19 changed files with 793 additions and 23 deletions
|
@ -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)|
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)]
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
132
ml_ppu/GameEvents.cs
Normal 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
32
ml_ppu/GrabDetector.cs
Normal 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
38
ml_ppu/Main.cs
Normal 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
24
ml_ppu/ModSupport.cs
Normal 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
86
ml_ppu/ModUi.cs
Normal 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
246
ml_ppu/PickUpManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
ml_ppu/Properties/AssemblyInfo.cs
Normal file
5
ml_ppu/Properties/AssemblyInfo.cs
Normal 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
21
ml_ppu/README.md
Normal 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
90
ml_ppu/Settings.cs
Normal 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
12
ml_ppu/Utils.cs
Normal 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
80
ml_ppu/ml_ppu.csproj
Normal 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 "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
BIN
ml_ppu/resources/person.png
Normal file
BIN
ml_ppu/resources/person.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue