mirror of
https://github.com/hanetzer/sdraw_mods_cvr.git
synced 2025-09-03 18:39:23 +00:00
New mod: PlayerMovementCopycat
Collider radius upon avatar scaling
This commit is contained in:
parent
3dceb85037
commit
0f5e1484d1
16 changed files with 1122 additions and 7 deletions
130
ml_pmc/Main.cs
Normal file
130
ml_pmc/Main.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using ABI_RC.Core.Networking.IO.Social;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_pmc
|
||||
{
|
||||
public class PlayerMovementCopycat : MelonLoader.MelonMod
|
||||
{
|
||||
static PlayerMovementCopycat ms_instance = null;
|
||||
|
||||
PoseCopycat m_localCopycat = null;
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
if(ms_instance == null)
|
||||
ms_instance = this;
|
||||
|
||||
Settings.Init();
|
||||
ModUi.Init();
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
|
||||
}
|
||||
|
||||
public override void OnDeinitializeMelon()
|
||||
{
|
||||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
m_localCopycat = null;
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator WaitForLocalPlayer()
|
||||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
|
||||
m_localCopycat = PlayerSetup.Instance.gameObject.AddComponent<PoseCopycat>();
|
||||
ModUi.CopySwitch += this.OnTargetSelect;
|
||||
}
|
||||
|
||||
void OnTargetSelect(string p_id)
|
||||
{
|
||||
if(m_localCopycat != null)
|
||||
{
|
||||
if(m_localCopycat.IsActive())
|
||||
m_localCopycat.SetTarget(null);
|
||||
else
|
||||
{
|
||||
if(Friends.FriendsWith(p_id))
|
||||
{
|
||||
if(CVRPlayerManager.Instance.GetPlayerPuppetMaster(p_id, out PuppetMaster l_puppetMaster))
|
||||
{
|
||||
if(IsInSight(MovementSystem.Instance.proxyCollider, l_puppetMaster.GetComponent<CapsuleCollider>(), Utils.GetWorldMovementLimit()))
|
||||
m_localCopycat.SetTarget(l_puppetMaster.gameObject);
|
||||
else
|
||||
ModUi.ShowAlert("Selected player is too far away or obstructed");
|
||||
}
|
||||
else
|
||||
ModUi.ShowAlert("Selected player isn't connected or ready yet");
|
||||
}
|
||||
else
|
||||
ModUi.ShowAlert("Selected player isn't your friend");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsInSight(CapsuleCollider p_source, CapsuleCollider p_target, float p_limit)
|
||||
{
|
||||
bool l_result = false;
|
||||
if((p_source != null) && (p_target != null))
|
||||
{
|
||||
Ray l_ray = new Ray();
|
||||
l_ray.origin = (p_source.transform.position + p_source.transform.rotation * p_source.center);
|
||||
l_ray.direction = (p_target.transform.position + p_target.transform.rotation * p_target.center) - l_ray.origin;
|
||||
List<RaycastHit> l_hits = Physics.RaycastAll(l_ray, p_limit, LayerMask.NameToLayer("UI Internal")).ToList();
|
||||
if(l_hits.Count > 0)
|
||||
{
|
||||
l_hits.Sort((a, b) => ((a.distance < b.distance) ? -1 : 1));
|
||||
l_result = (l_hits.First().collider.gameObject.transform.root == p_target.transform.root);
|
||||
}
|
||||
}
|
||||
return l_result;
|
||||
}
|
||||
|
||||
// Patches
|
||||
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
|
||||
void OnAvatarClear()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localCopycat != null)
|
||||
m_localCopycat.OnAvatarClear();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
|
||||
void OnSetupAvatar()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localCopycat != null)
|
||||
m_localCopycat.OnAvatarSetup();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
136
ml_pmc/ModUi.cs
Normal file
136
ml_pmc/ModUi.cs
Normal file
|
@ -0,0 +1,136 @@
|
|||
using BTKUILib.UIObjects;
|
||||
using BTKUILib.UIObjects.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_pmc
|
||||
{
|
||||
static class ModUi
|
||||
{
|
||||
enum UiIndex
|
||||
{
|
||||
Toggle,
|
||||
Position,
|
||||
Rotation,
|
||||
Gestures,
|
||||
LookAtMix,
|
||||
MirrorPose,
|
||||
MirrorPosition,
|
||||
MirrorRotation,
|
||||
Reset
|
||||
}
|
||||
|
||||
internal static Action<string> CopySwitch;
|
||||
|
||||
static List<QMUIElement> ms_uiElements = null;
|
||||
static string ms_selectedPlayer;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ms_uiElements = new List<QMUIElement>();
|
||||
|
||||
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerMovementCopycat", "PMC-Dancing", GetIconStream("dancing.png"));
|
||||
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerMovementCopycat", "PMC-Dancing-On", GetIconStream("dancing_on.png"));
|
||||
|
||||
var l_category = BTKUILib.QuickMenuAPI.PlayerSelectPage.AddCategory("Player Movement Copycat", "PlayerMovementCopycat");
|
||||
|
||||
ms_uiElements.Add(l_category.AddButton("Copy movement", "PMC-Dancing", "Start/stop copy of player's movement"));
|
||||
(ms_uiElements[(int)UiIndex.Toggle] as Button).OnPress += OnCopySwitch;
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Apply position", "Apply local position change of target player", Settings.Position));
|
||||
(ms_uiElements[(int)UiIndex.Position] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.Position, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Apply rotation", "Apply local rotation change of target player", Settings.Rotation));
|
||||
(ms_uiElements[(int)UiIndex.Rotation] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.Rotation, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Copy gestures", "Copy gestures of target player", Settings.Gestures));
|
||||
(ms_uiElements[(int)UiIndex.Gestures] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.Gestures, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Apply LookAtIK", "Mix target player pose and camera view direction (desktop only)", Settings.LookAtMix));
|
||||
(ms_uiElements[(int)UiIndex.LookAtMix] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.LookAtMix, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Mirror pose", "Mirror target player pose", Settings.MirrorPose));
|
||||
(ms_uiElements[(int)UiIndex.MirrorPose] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.MirrorPose, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Mirror position", "Mirror target player movement against 0YZ plane", Settings.MirrorPosition));
|
||||
(ms_uiElements[(int)UiIndex.MirrorPosition] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.MirrorPosition, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddToggle("Mirror rotation", "Mirror target player rotation against 0YZ plane", Settings.MirrorRotation));
|
||||
(ms_uiElements[(int)UiIndex.MirrorRotation] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.MirrorRotation, value);
|
||||
|
||||
ms_uiElements.Add(l_category.AddButton("Reset settings", "", "Reset mod's settings to default"));
|
||||
(ms_uiElements[(int)UiIndex.Reset] as Button).OnPress += Reset;
|
||||
|
||||
BTKUILib.QuickMenuAPI.OnPlayerSelected += (_, id) => ms_selectedPlayer = id;
|
||||
PoseCopycat.OnActivityChange += UpdateToggleColor;
|
||||
}
|
||||
|
||||
static void OnCopySwitch() => CopySwitch?.Invoke(ms_selectedPlayer);
|
||||
|
||||
static void OnToggleUpdate(UiIndex p_index, bool p_value, bool p_force = false)
|
||||
{
|
||||
switch(p_index)
|
||||
{
|
||||
case UiIndex.Position:
|
||||
Settings.SetSetting(Settings.ModSetting.Position, p_value);
|
||||
break;
|
||||
|
||||
case UiIndex.Rotation:
|
||||
Settings.SetSetting(Settings.ModSetting.Rotation, p_value);
|
||||
break;
|
||||
|
||||
case UiIndex.Gestures:
|
||||
Settings.SetSetting(Settings.ModSetting.Gestures, p_value);
|
||||
break;
|
||||
|
||||
case UiIndex.LookAtMix:
|
||||
Settings.SetSetting(Settings.ModSetting.LookAtMix, p_value);
|
||||
break;
|
||||
|
||||
case UiIndex.MirrorPose:
|
||||
Settings.SetSetting(Settings.ModSetting.MirrorPose, p_value);
|
||||
break;
|
||||
|
||||
case UiIndex.MirrorPosition:
|
||||
Settings.SetSetting(Settings.ModSetting.MirrorPosition, p_value);
|
||||
break;
|
||||
|
||||
case UiIndex.MirrorRotation:
|
||||
Settings.SetSetting(Settings.ModSetting.MirrorRotation, p_value);
|
||||
break;
|
||||
}
|
||||
|
||||
if(p_force)
|
||||
(ms_uiElements[(int)p_index] as ToggleButton).ToggleValue = p_value;
|
||||
}
|
||||
|
||||
static void Reset()
|
||||
{
|
||||
OnToggleUpdate(UiIndex.Position, true, true);
|
||||
OnToggleUpdate(UiIndex.Rotation, true, true);
|
||||
OnToggleUpdate(UiIndex.Gestures, true, true);
|
||||
OnToggleUpdate(UiIndex.LookAtMix, true, true);
|
||||
OnToggleUpdate(UiIndex.MirrorPose, false, true);
|
||||
OnToggleUpdate(UiIndex.MirrorPosition, false, true);
|
||||
OnToggleUpdate(UiIndex.MirrorRotation, false, true);
|
||||
}
|
||||
|
||||
internal static void ShowAlert(string p_text) => BTKUILib.QuickMenuAPI.ShowAlertToast(p_text, 2);
|
||||
|
||||
// Currently broken in BTKUILib, waiting for fix
|
||||
static void UpdateToggleColor(bool p_state)
|
||||
{
|
||||
//(ms_uiElements[(int)UiIndex.Toggle] as Button).ButtonIcon = (p_state ? "PMC-Dancing-On" : "PMC-Dancing");
|
||||
//(ms_uiElements[(int)UiIndex.Toggle] as Button).ButtonText = (p_state ? "PMC-Dancing-On" : "PMC-Dancing");
|
||||
}
|
||||
|
||||
static Stream GetIconStream(string p_name)
|
||||
{
|
||||
Assembly l_assembly = Assembly.GetExecutingAssembly();
|
||||
string l_assemblyName = l_assembly.GetName().Name;
|
||||
return l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
|
||||
}
|
||||
}
|
||||
}
|
322
ml_pmc/PoseCopycat.cs
Normal file
322
ml_pmc/PoseCopycat.cs
Normal file
|
@ -0,0 +1,322 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.IK;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using RootMotion.FinalIK;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_pmc
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class PoseCopycat : MonoBehaviour
|
||||
{
|
||||
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
|
||||
|
||||
static public PoseCopycat Instance { get; private set; } = null;
|
||||
static internal System.Action<bool> OnActivityChange;
|
||||
|
||||
Animator m_animator = null;
|
||||
VRIK m_vrIk = null;
|
||||
float m_ikWeight = 1f;
|
||||
LookAtIK m_lookAtIk = null;
|
||||
float m_lookIkWeight = 1f;
|
||||
bool m_sitting = false;
|
||||
bool m_inVr = false;
|
||||
|
||||
bool m_active = false;
|
||||
float m_distanceLimit = float.MaxValue;
|
||||
bool m_fingerTracking = false;
|
||||
|
||||
HumanPoseHandler m_poseHandler = null;
|
||||
HumanPose m_pose;
|
||||
PuppetParser m_puppetParser = null;
|
||||
|
||||
internal PoseCopycat()
|
||||
{
|
||||
if(Instance == null)
|
||||
Instance = this;
|
||||
}
|
||||
~PoseCopycat()
|
||||
{
|
||||
if(Instance == this)
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
// Unity events
|
||||
void Update()
|
||||
{
|
||||
m_sitting = (MovementSystem.Instance.lastSeat != null);
|
||||
|
||||
if(m_active && (m_puppetParser != null))
|
||||
{
|
||||
OverrideIK();
|
||||
|
||||
if(m_puppetParser.HasAnimator())
|
||||
{
|
||||
bool l_mirror = Settings.MirrorPose;
|
||||
|
||||
if(Settings.Gestures)
|
||||
{
|
||||
CVRInputManager.Instance.gestureLeft = (l_mirror ? m_puppetParser.GetRightGesture() : m_puppetParser.GetLeftGesture());
|
||||
CVRInputManager.Instance.gestureRight = (l_mirror ? m_puppetParser.GetLeftGesture() : m_puppetParser.GetRightGesture());
|
||||
}
|
||||
|
||||
if(m_puppetParser.HasFingerTracking())
|
||||
{
|
||||
m_fingerTracking = true;
|
||||
|
||||
CVRInputManager.Instance.individualFingerTracking = true;
|
||||
IKSystem.Instance.FingerSystem.controlActive = true;
|
||||
|
||||
ref float[] l_curls = ref m_puppetParser.GetFingerCurls();
|
||||
|
||||
CVRInputManager.Instance.fingerCurlLeftThumb = l_curls[l_mirror ? 5 : 0];
|
||||
CVRInputManager.Instance.fingerCurlLeftIndex = l_curls[l_mirror ? 6 : 1];
|
||||
CVRInputManager.Instance.fingerCurlLeftMiddle = l_curls[l_mirror ? 7 : 2];
|
||||
CVRInputManager.Instance.fingerCurlLeftRing = l_curls[l_mirror ? 8 : 3];
|
||||
CVRInputManager.Instance.fingerCurlLeftPinky = l_curls[l_mirror ? 9 : 4];
|
||||
CVRInputManager.Instance.fingerCurlRightThumb = l_curls[l_mirror ? 0 : 5];
|
||||
CVRInputManager.Instance.fingerCurlRightIndex = l_curls[l_mirror ? 1 : 6];
|
||||
CVRInputManager.Instance.fingerCurlRightMiddle = l_curls[l_mirror ? 2 : 7];
|
||||
CVRInputManager.Instance.fingerCurlRightRing = l_curls[l_mirror ? 3 : 8];
|
||||
CVRInputManager.Instance.fingerCurlRightPinky = l_curls[l_mirror ? 4 : 9];
|
||||
|
||||
IKSystem.Instance.FingerSystem.leftThumbCurl = l_curls[l_mirror ? 5 : 0];
|
||||
IKSystem.Instance.FingerSystem.leftIndexCurl = l_curls[l_mirror ? 6 : 1];
|
||||
IKSystem.Instance.FingerSystem.leftMiddleCurl = l_curls[l_mirror ? 7 : 2];
|
||||
IKSystem.Instance.FingerSystem.leftRingCurl = l_curls[l_mirror ? 8 : 3];
|
||||
IKSystem.Instance.FingerSystem.leftPinkyCurl = l_curls[l_mirror ? 9 : 4];
|
||||
IKSystem.Instance.FingerSystem.rightThumbCurl = l_curls[l_mirror ? 0 : 5];
|
||||
IKSystem.Instance.FingerSystem.rightIndexCurl = l_curls[l_mirror ? 1 : 6];
|
||||
IKSystem.Instance.FingerSystem.rightMiddleCurl = l_curls[l_mirror ? 2 : 7];
|
||||
IKSystem.Instance.FingerSystem.rightRingCurl = l_curls[l_mirror ? 3 : 8];
|
||||
IKSystem.Instance.FingerSystem.rightPinkyCurl = l_curls[l_mirror ? 4 : 9];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_fingerTracking)
|
||||
{
|
||||
RestoreFingerTracking();
|
||||
m_fingerTracking = false;
|
||||
}
|
||||
}
|
||||
|
||||
Matrix4x4 l_offset = m_puppetParser.GetOffset();
|
||||
Vector3 l_pos = l_offset * ms_pointVector;
|
||||
Quaternion l_rot = l_offset.rotation;
|
||||
|
||||
l_pos.y = 0f;
|
||||
if(Settings.MirrorPosition)
|
||||
l_pos.x *= -1f;
|
||||
l_pos = Vector3.ClampMagnitude(l_pos, m_distanceLimit);
|
||||
|
||||
l_rot = Quaternion.Euler(0f, l_rot.eulerAngles.y * (Settings.MirrorRotation ? -1f : 1f), 0f);
|
||||
|
||||
Matrix4x4 l_result = PlayerSetup.Instance.transform.GetMatrix() * Matrix4x4.TRS(l_pos, l_rot, Vector3.one);
|
||||
|
||||
if(Settings.Position && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsWorldSafe() && Utils.IsCombatSafe())
|
||||
PlayerSetup.Instance.transform.position = l_result * ms_pointVector;
|
||||
|
||||
if(Settings.Rotation && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsCombatSafe())
|
||||
{
|
||||
if(m_inVr)
|
||||
{
|
||||
Vector3 l_avatarPos = PlayerSetup.Instance._avatar.transform.position;
|
||||
PlayerSetup.Instance.transform.rotation = l_result.rotation;
|
||||
Vector3 l_dif = l_avatarPos - PlayerSetup.Instance._avatar.transform.position;
|
||||
PlayerSetup.Instance.transform.position += l_dif;
|
||||
}
|
||||
else
|
||||
PlayerSetup.Instance.transform.rotation = l_result.rotation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!m_puppetParser.IsWaitingAnimator())
|
||||
SetTarget(null);
|
||||
}
|
||||
|
||||
if(Vector3.Distance(this.transform.position, m_puppetParser.transform.position) > m_distanceLimit)
|
||||
SetTarget(null);
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(m_active && (m_animator != null) && (m_puppetParser != null) && m_puppetParser.IsPoseParsed())
|
||||
{
|
||||
OverrideIK();
|
||||
|
||||
m_puppetParser.GetPose().CopyTo(ref m_pose);
|
||||
if(Settings.MirrorPose)
|
||||
Utils.MirrorPose(ref m_pose);
|
||||
m_poseHandler.SetHumanPose(ref m_pose);
|
||||
}
|
||||
}
|
||||
|
||||
// Patches
|
||||
internal void OnAvatarClear()
|
||||
{
|
||||
m_inVr = Utils.IsInVR();
|
||||
|
||||
if(m_puppetParser != null)
|
||||
Object.Destroy(m_puppetParser);
|
||||
m_puppetParser = null;
|
||||
|
||||
m_animator = null;
|
||||
m_vrIk = null;
|
||||
m_lookAtIk = null;
|
||||
|
||||
m_poseHandler?.Dispose();
|
||||
m_poseHandler = null;
|
||||
m_active = false;
|
||||
m_distanceLimit = float.MaxValue;
|
||||
m_fingerTracking = false;
|
||||
m_pose = new HumanPose();
|
||||
}
|
||||
internal void OnAvatarSetup()
|
||||
{
|
||||
m_animator = PlayerSetup.Instance._animator;
|
||||
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
|
||||
m_lookAtIk = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
|
||||
|
||||
if((m_animator != null) && m_animator.isHuman)
|
||||
{
|
||||
m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
|
||||
m_poseHandler.GetHumanPose(ref m_pose);
|
||||
|
||||
if(m_vrIk != null)
|
||||
{
|
||||
m_vrIk.onPreSolverUpdate.AddListener(this.OnVRIKPreUpdate);
|
||||
m_vrIk.onPostSolverUpdate.AddListener(this.OnVRIKPostUpdate);
|
||||
}
|
||||
|
||||
if(m_lookAtIk != null)
|
||||
{
|
||||
m_lookAtIk.onPreSolverUpdate.AddListener(this.OnLookAtIKPreUpdate);
|
||||
m_lookAtIk.onPostSolverUpdate.AddListener(this.OnLookAtIKPostUpdate);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_animator = null;
|
||||
}
|
||||
|
||||
// IK updates
|
||||
void OnVRIKPreUpdate()
|
||||
{
|
||||
if(m_active)
|
||||
{
|
||||
m_ikWeight = m_vrIk.solver.IKPositionWeight;
|
||||
m_vrIk.solver.IKPositionWeight = 0f;
|
||||
}
|
||||
}
|
||||
void OnVRIKPostUpdate()
|
||||
{
|
||||
if(m_active)
|
||||
m_vrIk.solver.IKPositionWeight = m_ikWeight;
|
||||
}
|
||||
|
||||
void OnLookAtIKPreUpdate()
|
||||
{
|
||||
if(m_active && !Settings.LookAtMix)
|
||||
{
|
||||
m_lookIkWeight = m_lookAtIk.solver.IKPositionWeight;
|
||||
m_lookAtIk.solver.IKPositionWeight = 0f;
|
||||
}
|
||||
}
|
||||
void OnLookAtIKPostUpdate()
|
||||
{
|
||||
if(m_active && !Settings.LookAtMix)
|
||||
m_lookAtIk.solver.IKPositionWeight = m_lookIkWeight;
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
public void SetTarget(GameObject p_target)
|
||||
{
|
||||
if(m_animator != null)
|
||||
{
|
||||
if(!m_active)
|
||||
{
|
||||
if(p_target != null)
|
||||
{
|
||||
m_puppetParser = p_target.AddComponent<PuppetParser>();
|
||||
m_distanceLimit = Utils.GetWorldMovementLimit();
|
||||
|
||||
m_active = true;
|
||||
OnActivityChange?.Invoke(m_active);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p_target == null)
|
||||
{
|
||||
if(m_puppetParser != null)
|
||||
Object.Destroy(m_puppetParser);
|
||||
m_puppetParser = null;
|
||||
|
||||
if(!m_sitting)
|
||||
{
|
||||
Quaternion l_rot = PlayerSetup.Instance.transform.rotation;
|
||||
PlayerSetup.Instance.transform.rotation = Quaternion.Euler(0f, l_rot.eulerAngles.y, 0f);
|
||||
}
|
||||
|
||||
RestoreIK();
|
||||
RestoreFingerTracking();
|
||||
m_fingerTracking = false;
|
||||
|
||||
m_active = false;
|
||||
OnActivityChange?.Invoke(m_active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsActive() => m_active;
|
||||
public bool IsFingerTrackingActive() => m_fingerTracking;
|
||||
|
||||
void OverrideIK()
|
||||
{
|
||||
if((m_vrIk != null) && !BodySystem.isCalibrating)
|
||||
BodySystem.TrackingPositionWeight = 0f;
|
||||
}
|
||||
void RestoreIK()
|
||||
{
|
||||
if((m_vrIk != null) && !BodySystem.isCalibrating)
|
||||
{
|
||||
BodySystem.TrackingPositionWeight = 1f;
|
||||
m_vrIk.solver.Reset();
|
||||
}
|
||||
}
|
||||
void RestoreFingerTracking()
|
||||
{
|
||||
CVRInputManager.Instance.individualFingerTracking = (m_inVr && Utils.AreKnucklesInUse() && !Utils.GetIndexGestureToggle());
|
||||
IKSystem.Instance.FingerSystem.controlActive = CVRInputManager.Instance.individualFingerTracking;
|
||||
|
||||
if(!CVRInputManager.Instance.individualFingerTracking)
|
||||
{
|
||||
CVRInputManager.Instance.fingerCurlLeftThumb = 0f;
|
||||
CVRInputManager.Instance.fingerCurlLeftIndex = 0f;
|
||||
CVRInputManager.Instance.fingerCurlLeftMiddle = 0f;
|
||||
CVRInputManager.Instance.fingerCurlLeftRing = 0f;
|
||||
CVRInputManager.Instance.fingerCurlLeftPinky = 0f;
|
||||
CVRInputManager.Instance.fingerCurlRightThumb = 0f;
|
||||
CVRInputManager.Instance.fingerCurlRightIndex = 0f;
|
||||
CVRInputManager.Instance.fingerCurlRightMiddle = 0f;
|
||||
CVRInputManager.Instance.fingerCurlRightRing = 0f;
|
||||
CVRInputManager.Instance.fingerCurlRightPinky = 0f;
|
||||
|
||||
IKSystem.Instance.FingerSystem.leftThumbCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.leftIndexCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.leftMiddleCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.leftRingCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.leftPinkyCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.rightThumbCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.rightIndexCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.rightMiddleCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.rightRingCurl = 0f;
|
||||
IKSystem.Instance.FingerSystem.rightPinkyCurl = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
ml_pmc/Properties/AssemblyInfo.cs
Normal file
12
ml_pmc/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyTitle("PlayerMovementCopycat")]
|
||||
[assembly: AssemblyVersion("1.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0")]
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonPriority(3)]
|
||||
[assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
156
ml_pmc/PuppetParser.cs
Normal file
156
ml_pmc/PuppetParser.cs
Normal file
|
@ -0,0 +1,156 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_pmc
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
class PuppetParser : MonoBehaviour
|
||||
{
|
||||
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
|
||||
|
||||
PuppetMaster m_puppetMaster = null;
|
||||
Animator m_animator = null;
|
||||
AnimatorCullingMode m_cullMode;
|
||||
float m_armatureScale = 1f;
|
||||
float m_armatureHeight = 0f;
|
||||
|
||||
bool m_waitAnimator = true;
|
||||
HumanPoseHandler m_poseHandler = null;
|
||||
HumanPose m_pose;
|
||||
bool m_poseParsed = false;
|
||||
|
||||
Matrix4x4 m_matrix = Matrix4x4.identity;
|
||||
Matrix4x4 m_offset = Matrix4x4.identity;
|
||||
|
||||
bool m_sitting = false;
|
||||
float m_leftGesture = 0f;
|
||||
float m_rightGesture = 0f;
|
||||
bool m_fingerTracking = false;
|
||||
float[] m_fingerCurls = null;
|
||||
|
||||
internal PuppetParser()
|
||||
{
|
||||
m_fingerCurls = new float[10];
|
||||
}
|
||||
|
||||
// Unity events
|
||||
void Start()
|
||||
{
|
||||
m_puppetMaster = this.GetComponent<PuppetMaster>();
|
||||
m_matrix = this.transform.GetMatrix();
|
||||
StartCoroutine(WaitForAnimator());
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if(m_animator != null)
|
||||
m_animator.cullingMode = m_cullMode;
|
||||
|
||||
m_poseHandler?.Dispose();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if(m_puppetMaster != null)
|
||||
{
|
||||
m_sitting = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorSitting;
|
||||
m_leftGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureLeft;
|
||||
m_rightGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureRight;
|
||||
m_fingerTracking = m_puppetMaster.PlayerAvatarMovementDataInput.IndexUseIndividualFingers;
|
||||
if(m_fingerTracking)
|
||||
{
|
||||
m_fingerCurls[0] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumbCurl;
|
||||
m_fingerCurls[1] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndexCurl;
|
||||
m_fingerCurls[2] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddleCurl;
|
||||
m_fingerCurls[3] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRingCurl;
|
||||
m_fingerCurls[4] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinkyCurl;
|
||||
m_fingerCurls[5] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumbCurl;
|
||||
m_fingerCurls[6] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndexCurl;
|
||||
m_fingerCurls[7] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddleCurl;
|
||||
m_fingerCurls[8] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRingCurl;
|
||||
m_fingerCurls[9] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinkyCurl;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ReferenceEquals(m_animator, null))
|
||||
{
|
||||
if(m_animator != null)
|
||||
{
|
||||
Matrix4x4 l_current = this.transform.GetMatrix();
|
||||
m_offset = m_matrix.inverse * l_current;
|
||||
m_matrix = l_current;
|
||||
}
|
||||
else
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(m_animator != null)
|
||||
{
|
||||
m_poseHandler.GetHumanPose(ref m_pose);
|
||||
m_pose.bodyPosition *= m_armatureScale;
|
||||
m_pose.bodyPosition.y += m_armatureHeight;
|
||||
m_poseParsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
System.Collections.IEnumerator WaitForAnimator()
|
||||
{
|
||||
while(m_puppetMaster.avatarObject == null)
|
||||
yield return null;
|
||||
|
||||
while(m_animator == null)
|
||||
{
|
||||
m_animator = m_puppetMaster.avatarObject.GetComponent<Animator>();
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if(m_animator.isHuman)
|
||||
{
|
||||
m_cullMode = m_animator.cullingMode;
|
||||
m_animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
|
||||
|
||||
Transform l_hips = m_animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
if((l_hips != null) && (l_hips.parent != null))
|
||||
{
|
||||
m_armatureScale = l_hips.parent.localScale.y;
|
||||
m_armatureHeight = ((m_puppetMaster.transform.GetMatrix().inverse * l_hips.parent.GetMatrix()) * ms_pointVector).y;
|
||||
}
|
||||
|
||||
m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
|
||||
m_matrix = this.transform.GetMatrix();
|
||||
}
|
||||
else
|
||||
Reset();
|
||||
|
||||
m_waitAnimator = false;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_animator = null;
|
||||
m_poseHandler?.Dispose();
|
||||
m_poseHandler = null;
|
||||
m_pose = new HumanPose();
|
||||
m_poseParsed = false;
|
||||
m_offset = Matrix4x4.identity;
|
||||
m_sitting = false;
|
||||
m_leftGesture = 0f;
|
||||
m_rightGesture = 0f;
|
||||
}
|
||||
|
||||
public bool IsWaitingAnimator() => m_waitAnimator;
|
||||
public bool HasAnimator() => !ReferenceEquals(m_animator, null);
|
||||
public ref HumanPose GetPose() => ref m_pose;
|
||||
public bool IsPoseParsed() => m_poseParsed;
|
||||
public ref Matrix4x4 GetOffset() => ref m_offset;
|
||||
public bool IsSitting() => m_sitting;
|
||||
public float GetLeftGesture() => m_leftGesture;
|
||||
public float GetRightGesture() => m_rightGesture;
|
||||
public bool HasFingerTracking() => m_fingerTracking;
|
||||
public ref float[] GetFingerCurls() => ref m_fingerCurls;
|
||||
}
|
||||
}
|
21
ml_pmc/README.md
Normal file
21
ml_pmc/README.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Player Movement Copycat
|
||||
Allows to copy pose, gestures and movement of your friends.
|
||||
|
||||
# Installation
|
||||
* Install [BTKUILib](https://github.com/BTK-Development/BTKUILib)
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_pmc.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
Available options in BTKUILib players list upon player selection:
|
||||
* **Copy movement:** starts/stops copycating of selected player.
|
||||
* Note: Selected player should be your friend, be in your view range and not obstructed by other players/world objects/props.
|
||||
* **Apply position:** enables/disables position changes of selected player; `true` by default.
|
||||
* Note: Forcibly disabled in worlds that don't allow flight.
|
||||
* **Apply rotation:** enables/disables rotation changes of selected player; `true` by default.
|
||||
* **Copy gestures:** enables/disables gestures copy of selected player; `true` by default.
|
||||
* **Apply LookAtIK:** enables/disables additional head rotation based on camera view in desktop mode; `true` by default.
|
||||
* **Mirror pose:** enables/disables pose and gestures mirroring; `false` by default.
|
||||
* **Mirror position:** enables/disables mirroring of position changes of selected player along 0XZ plane; `false` by default.
|
||||
* **Mirror rotation:** enables/disables mirroring of rotation changes of selected player along 0XZ plane; `false` by default.
|
120
ml_pmc/Settings.cs
Normal file
120
ml_pmc/Settings.cs
Normal file
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ml_pmc
|
||||
{
|
||||
static class Settings
|
||||
{
|
||||
public enum ModSetting
|
||||
{
|
||||
Position,
|
||||
Rotation,
|
||||
Gestures,
|
||||
LookAtMix,
|
||||
MirrorPose,
|
||||
MirrorPosition,
|
||||
MirrorRotation
|
||||
}
|
||||
|
||||
public static bool Position { get; private set; } = true;
|
||||
public static bool Rotation { get; private set; } = true;
|
||||
public static bool Gestures { get; private set; } = true;
|
||||
public static bool LookAtMix { get; private set; } = true;
|
||||
public static bool MirrorPose { get; private set; } = false;
|
||||
public static bool MirrorPosition { get; private set; } = false;
|
||||
public static bool MirrorRotation { get; private set; } = false;
|
||||
|
||||
public static Action<bool> PositionChange;
|
||||
public static Action<bool> RotationChange;
|
||||
public static Action<bool> GesturesChange;
|
||||
public static Action<bool> LookAtMixChange;
|
||||
public static Action<bool> MirrorPoseChange;
|
||||
public static Action<bool> MirrorPositionChange;
|
||||
public static Action<bool> MirrorRotationChange;
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ms_category = MelonLoader.MelonPreferences.CreateCategory("PMC", null, true);
|
||||
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
|
||||
{
|
||||
ms_category.CreateEntry(ModSetting.Position.ToString(), Position),
|
||||
ms_category.CreateEntry(ModSetting.Rotation.ToString(), Rotation),
|
||||
ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures),
|
||||
ms_category.CreateEntry(ModSetting.LookAtMix.ToString(), LookAtMix),
|
||||
ms_category.CreateEntry(ModSetting.MirrorPose.ToString(), MirrorPose),
|
||||
ms_category.CreateEntry(ModSetting.MirrorPosition.ToString(), MirrorPosition),
|
||||
ms_category.CreateEntry(ModSetting.MirrorRotation.ToString(), MirrorRotation),
|
||||
};
|
||||
|
||||
Position = (bool)ms_entries[(int)ModSetting.Position].BoxedValue;
|
||||
Rotation = (bool)ms_entries[(int)ModSetting.Rotation].BoxedValue;
|
||||
Gestures = (bool)ms_entries[(int)ModSetting.Gestures].BoxedValue;
|
||||
LookAtMix = (bool)ms_entries[(int)ModSetting.LookAtMix].BoxedValue;
|
||||
MirrorPose = (bool)ms_entries[(int)ModSetting.MirrorPose].BoxedValue;
|
||||
MirrorPosition = (bool)ms_entries[(int)ModSetting.MirrorPosition].BoxedValue;
|
||||
MirrorRotation = (bool)ms_entries[(int)ModSetting.MirrorRotation].BoxedValue;
|
||||
}
|
||||
|
||||
public static void SetSetting(ModSetting p_setting, object p_value)
|
||||
{
|
||||
switch(p_setting)
|
||||
{
|
||||
case ModSetting.Position:
|
||||
{
|
||||
Position = (bool)p_value;
|
||||
PositionChange?.Invoke((bool)p_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.Rotation:
|
||||
{
|
||||
Rotation = (bool)p_value;
|
||||
RotationChange?.Invoke((bool)p_value);
|
||||
break;
|
||||
}
|
||||
|
||||
case ModSetting.Gestures:
|
||||
{
|
||||
Gestures = (bool)p_value;
|
||||
GesturesChange?.Invoke((bool)p_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.LookAtMix:
|
||||
{
|
||||
LookAtMix = (bool)p_value;
|
||||
LookAtMixChange?.Invoke((bool)p_value);
|
||||
}
|
||||
break;
|
||||
|
||||
//
|
||||
case ModSetting.MirrorPose:
|
||||
{
|
||||
MirrorPose = (bool)p_value;
|
||||
MirrorPoseChange?.Invoke((bool)p_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.MirrorPosition:
|
||||
{
|
||||
MirrorPosition = (bool)p_value;
|
||||
MirrorPositionChange?.Invoke((bool)p_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.MirrorRotation:
|
||||
{
|
||||
MirrorRotation = (bool)p_value;
|
||||
MirrorRotationChange?.Invoke((bool)p_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(ms_entries != null)
|
||||
ms_entries[(int)p_setting].BoxedValue = p_value;
|
||||
}
|
||||
}
|
||||
}
|
81
ml_pmc/Utils.cs
Normal file
81
ml_pmc/Utils.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_pmc
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
static readonly FieldInfo ms_indexGestureToggle = typeof(InputModuleSteamVR).GetField("_steamVrIndexGestureToggleValue", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
static readonly (int, int)[] ms_sideMuscles = new (int, int)[]
|
||||
{
|
||||
(29,21), (30,22), (31,23), (32,24), (33,25), (34,26), (35,27), (36,28),
|
||||
(46,37), (47,38), (48,39), (49,40), (50,41), (51,42), (52,43), (53,44), (54,45),
|
||||
(75,55), (76,56), (77,57), (78,58), (79,59), (80,60), (81,61), (82,62), (83,63), (84,64),
|
||||
(85,65), (86,66), (87,67), (88,68), (89, 69), (90,70), (91,71), (92,72), (93,73), (94,74)
|
||||
};
|
||||
static readonly int[] ms_centralMuscles = new int[] { 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 18, 20 };
|
||||
|
||||
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
|
||||
public static bool AreKnucklesInUse() => PlayerSetup.Instance._trackerManager.trackerNames.Contains("knuckles");
|
||||
public static bool GetIndexGestureToggle() => (bool)ms_indexGestureToggle.GetValue(CVRInputManager.Instance.GetComponent<InputModuleSteamVR>());
|
||||
|
||||
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
|
||||
public static bool IsCombatSafe() => ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown);
|
||||
|
||||
public static float GetWorldMovementLimit()
|
||||
{
|
||||
float l_result = 1f;
|
||||
if(CVRWorld.Instance != null)
|
||||
{
|
||||
l_result = CVRWorld.Instance.baseMovementSpeed;
|
||||
l_result *= CVRWorld.Instance.sprintMultiplier;
|
||||
l_result *= CVRWorld.Instance.inAirMovementMultiplier;
|
||||
l_result *= CVRWorld.Instance.flyMultiplier;
|
||||
}
|
||||
return l_result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void CopyTo(this HumanPose p_source, ref HumanPose p_target)
|
||||
{
|
||||
p_target.bodyPosition = p_source.bodyPosition;
|
||||
p_target.bodyRotation = p_source.bodyRotation;
|
||||
|
||||
int l_count = Mathf.Min(p_source.muscles.Length, p_target.muscles.Length);
|
||||
for(int i = 0; i < l_count; i++)
|
||||
p_target.muscles[i] = p_source.muscles[i];
|
||||
}
|
||||
|
||||
public static void MirrorPose(ref HumanPose p_pose)
|
||||
{
|
||||
int l_count = p_pose.muscles.Length;
|
||||
foreach(var l_pair in ms_sideMuscles)
|
||||
{
|
||||
if((l_count > l_pair.Item1) && (l_count > l_pair.Item2))
|
||||
{
|
||||
float l_temp = p_pose.muscles[l_pair.Item1];
|
||||
p_pose.muscles[l_pair.Item1] = p_pose.muscles[l_pair.Item2];
|
||||
p_pose.muscles[l_pair.Item2] = l_temp;
|
||||
}
|
||||
}
|
||||
foreach(int l_index in ms_centralMuscles)
|
||||
{
|
||||
if(l_count > l_index)
|
||||
p_pose.muscles[l_index] *= -1f;
|
||||
}
|
||||
|
||||
p_pose.bodyRotation.x *= -1f;
|
||||
p_pose.bodyRotation.w *= -1f;
|
||||
p_pose.bodyPosition.x *= -1f;
|
||||
}
|
||||
}
|
||||
}
|
101
ml_pmc/ml_pmc.csproj
Normal file
101
ml_pmc/ml_pmc.csproj
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{758514D3-6E1A-4E05-A156-B4E5C74AB5C4}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ml_pmc</RootNamespace>
|
||||
<AssemblyName>ml_pmc</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony, Version=2.10.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="BTKUILib, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\Mods\BTKUILib.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader, Version=0.6.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine.AnimationModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.PhysicsModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ModUi.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="PoseCopycat.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PuppetParser.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\dancing.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\dancing_on.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
6
ml_pmc/ml_pmc.csproj.user
Normal file
6
ml_pmc/ml_pmc.csproj.user
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ReferencePath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\</ReferencePath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
BIN
ml_pmc/resources/dancing.png
Normal file
BIN
ml_pmc/resources/dancing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
ml_pmc/resources/dancing_on.png
Normal file
BIN
ml_pmc/resources/dancing_on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Loading…
Add table
Add a link
Reference in a new issue