diff --git a/README.md b/README.md deleted file mode 100644 index a0078dc..0000000 --- a/README.md +++ /dev/null @@ -1,16 +0,0 @@ -Merged set of MelonLoader mods for ChilloutVR. - -**Table for nightly game build 2025r180:** -| Full name | Latest version | -|:---------:|:--------------:| -|[Avatar Motion Tweaker](/ml_amt/README.md)|1.5.2 [:arrow_down:](../../releases/latest/download/r180n/AvatarMotionTweaker.dll)| -|[Avatar Synced Look](/ml_asl/README.md)|1.1.2 [:arrow_down:](../../releases/latest/download/r180n/AvatarSyncedLook.dll)| -|[Better Fingers Tracking](/ml_bft/README.md)|1.1.3 [:arrow_down:](../../releases/latest/download/r180n/BetterFingersTracking.dll)| -|[Leap Motion Extension](/ml_lme/README.md)| 1.6.2 [:arrow_down:](../../releases/latest/download/r180n/LeapMotionExtension.dll)| -|[Pickup Arm Movement](/ml_pam/README.md)|1.2.3 [:arrow_down:](../../releases/latest/download/r180n/PickupArmMovement.dll)| -|[Players Instance Notifier](/ml_pin/README.md)|1.1.2 [:arrow_down:](../../releases/latest/download/r180n/PlayersInstanceNotifier.dll)| -|[Player Movement Copycat](/ml_pmc/README.md)|1.1.2 [:arrow_down:](../../releases/latest/download/r180n/PlayerMovementCopycat.dll)| -|[Player Pick Up](/ml_ppu/README.md)|1.0.1 [:arrow_down:](../../releases/latest/download/r180n/PlayerPickUp.dll)| -|[Player Ragdoll Mod](/ml_prm/README.md)|1.2.4 [:arrow_down:](../../releases/latest/download/r180n/PlayerRagdollMod.dll)| -|[Vive Extended Input](/ml_vei/README.md)|1.1.2 [:arrow_down:](../../releases/latest/download/r180n/ViveExtendedInput.dll)| -|[Video Player Cookies](/ml_vpc/README.md)|1.0.2 [:arrow_down:](../../releases/latest/download/r180n/VideoPlayerCookies.dll)| diff --git a/ml_amt/Properties/AssemblyInfo.cs b/ml_amt/Properties/AssemblyInfo.cs index 003591e..8da9421 100644 --- a/ml_amt/Properties/AssemblyInfo.cs +++ b/ml_amt/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.5.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.5.3", "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)] diff --git a/ml_amt/ml_amt.csproj b/ml_amt/ml_amt.csproj index 13765dd..88f5e34 100644 --- a/ml_amt/ml_amt.csproj +++ b/ml_amt/ml_amt.csproj @@ -6,7 +6,7 @@ SDraw AvatarMotionTweaker AvatarMotionTweaker - 1.5.2 + 1.5.3 x64 AvatarMotionTweaker diff --git a/ml_asl/Main.cs b/ml_asl/Main.cs index 75f5c06..599c8f0 100644 --- a/ml_asl/Main.cs +++ b/ml_asl/Main.cs @@ -1,4 +1,6 @@ using ABI_RC.Core.Player; +using ABI_RC.Systems.FaceTracking; +using System; using System.Reflection; using UnityEngine; @@ -10,25 +12,35 @@ namespace ml_asl public override void OnInitializeMelon() { - Settings.Init(); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.Instance | BindingFlags.NonPublic ), + typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.Instance | BindingFlags.NonPublic), null, - new HarmonyLib.HarmonyMethod(typeof(AvatarSyncedLook).GetMethod(nameof(OnPlayerAvatarMovementDataUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic )) + new HarmonyLib.HarmonyMethod(typeof(AvatarSyncedLook).GetMethod(nameof(OnPlayerAvatarMovementDataUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) ); } + public override void OnLateInitializeMelon() + { + Settings.Init(); + } + static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData) { - if(Settings.Enabled && (__instance.EyeMovementController != null)) + try { - ____playerAvatarMovementData.EyeTrackingOverride = true; + if(Settings.Enabled && (__instance.EyeMovementController != null) && !FaceTrackingManager.Instance.IsEyeDataAvailable()) + { + ____playerAvatarMovementData.EyeTrackingOverride = true; - if(__instance.EyeMovementController.CurrentTarget != null) - ____playerAvatarMovementData.EyeTrackingPosition = __instance.EyeMovementController.CurrentTarget.GetPosition(); - else - ____playerAvatarMovementData.EyeTrackingPosition = (__instance.transform.GetMatrix() * ms_back).GetPosition(); + if(__instance.EyeMovementController.CurrentTarget != null) + ____playerAvatarMovementData.EyeTrackingPosition = __instance.EyeMovementController.CurrentTarget.GetPosition(); + else + ____playerAvatarMovementData.EyeTrackingPosition = (__instance.transform.GetMatrix() * ms_back).GetPosition(); + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); } } } diff --git a/ml_asl/Properties/AssemblyInfo.cs b/ml_asl/Properties/AssemblyInfo.cs index 578b743..c62cdb2 100644 --- a/ml_asl/Properties/AssemblyInfo.cs +++ b/ml_asl/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.1.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.1.4", "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)] diff --git a/ml_asl/ml_asl.csproj b/ml_asl/ml_asl.csproj index bb73298..6352e14 100644 --- a/ml_asl/ml_asl.csproj +++ b/ml_asl/ml_asl.csproj @@ -7,7 +7,7 @@ SDraw SDraw AvatarSyncedLook - 1.1.2 + 1.1.4 AvatarSyncedLook diff --git a/ml_bft/Properties/AssemblyInfo.cs b/ml_bft/Properties/AssemblyInfo.cs index 3a8006f..f0ac630 100644 --- a/ml_bft/Properties/AssemblyInfo.cs +++ b/ml_bft/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.1.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.1.4", "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)] diff --git a/ml_bft/ml_bft.csproj b/ml_bft/ml_bft.csproj index 99dd28d..ae19a0d 100644 --- a/ml_bft/ml_bft.csproj +++ b/ml_bft/ml_bft.csproj @@ -7,7 +7,7 @@ SDraw SDraw BetterFingersTracking - 1.1.3 + 1.1.4 BetterFingersTracking diff --git a/ml_lme/LeapInput.cs b/ml_lme/LeapInput.cs index d760245..a97e86d 100644 --- a/ml_lme/LeapInput.cs +++ b/ml_lme/LeapInput.cs @@ -29,7 +29,7 @@ namespace ml_lme public override void ModuleAdded() { base.ModuleAdded(); - base.InputEnabled = Settings.Enabled; + base.InputModuleEnabled = Settings.Enabled; base.HapticFeedback = false; m_inVR = Utils.IsInVR(); @@ -159,7 +159,7 @@ namespace ml_lme public override void UpdateInput() { - if(base.InputEnabled) + if(base.InputModuleEnabled) { LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData(); @@ -360,7 +360,7 @@ namespace ml_lme // Settings changes void OnEnableChanged(bool p_state) { - base.InputEnabled = p_state; + base.InputModuleEnabled = p_state; m_handVisibleLeft &= p_state; m_handVisibleRight &= p_state; diff --git a/ml_lme/Properties/AssemblyInfo.cs b/ml_lme/Properties/AssemblyInfo.cs index de7fe58..e383520 100644 --- a/ml_lme/Properties/AssemblyInfo.cs +++ b/ml_lme/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.6.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.6.4", "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)] diff --git a/ml_lme/ml_lme.csproj b/ml_lme/ml_lme.csproj index 432064c..f4ee57b 100644 --- a/ml_lme/ml_lme.csproj +++ b/ml_lme/ml_lme.csproj @@ -4,7 +4,7 @@ netstandard2.1 x64 LeapMotionExtension - 1.6.2 + 1.6.4 SDraw SDraw LeapMotionExtension diff --git a/ml_mods_cvr.sln b/ml_mods_cvr.sln index 7094403..2731878 100644 --- a/ml_mods_cvr.sln +++ b/ml_mods_cvr.sln @@ -27,6 +27,9 @@ 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}" + ProjectSection(ProjectDependencies) = postProject + {C4C3F080-379F-49DB-ADC6-6328BE884AE3} = {C4C3F080-379F-49DB-ADC6-6328BE884AE3} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_pah", "ml_pah\ml_pah.csproj", "{C4659F60-3FED-4F43-88E4-969907D4C7A6}" EndProject diff --git a/ml_pah/HistoryManager.cs b/ml_pah/HistoryManager.cs index 235dbe5..69760c8 100644 --- a/ml_pah/HistoryManager.cs +++ b/ml_pah/HistoryManager.cs @@ -1,5 +1,6 @@ using ABI_RC.Core.Networking.API; using ABI_RC.Core.Networking.API.Responses; +using ABI_RC.Core.Networking.API.Responses.DetailsV2; using Newtonsoft.Json; using System; using System.Collections; @@ -178,12 +179,12 @@ namespace ml_pah static async Task RequestAvatarInfoTask(AvatarEntry p_entry) { - BaseResponse l_baseResponse = await ApiConnection.MakeRequest(ApiConnection.ApiOperation.AvatarDetail, new { avatarID = p_entry.m_id }); + BaseResponse l_baseResponse = await ApiConnection.MakeRequest(ApiConnection.ApiOperation.AvatarDetail, new { avatarID = p_entry.m_id }, "2"); if(l_baseResponse != null) { if(!l_baseResponse.IsSuccessStatusCode) return; p_entry.m_name = l_baseResponse.Data.Name; - p_entry.m_imageUrl = l_baseResponse.Data.ImageUrl; + p_entry.m_imageUrl = l_baseResponse.Data.Image.AbsoluteUri; p_entry.m_cached = true; } } diff --git a/ml_pah/Properties/AssemblyInfo.cs b/ml_pah/Properties/AssemblyInfo.cs index f311837..2ae21cb 100644 --- a/ml_pah/Properties/AssemblyInfo.cs +++ b/ml_pah/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pah.PlayerAvatarHistory), "PlayerAvatarHistory", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pah.PlayerAvatarHistory), "PlayerAvatarHistory", "1.0.1", "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)] diff --git a/ml_pah/ml_pah.csproj b/ml_pah/ml_pah.csproj index 2d1f70f..cd5c23c 100644 --- a/ml_pah/ml_pah.csproj +++ b/ml_pah/ml_pah.csproj @@ -6,6 +6,7 @@ PlayerAvatarHistory PlayerAvatarHistory SDraw + 1.0.1 diff --git a/ml_pam/Properties/AssemblyInfo.cs b/ml_pam/Properties/AssemblyInfo.cs index 07543c2..553607d 100644 --- a/ml_pam/Properties/AssemblyInfo.cs +++ b/ml_pam/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.2.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.2.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(1)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] diff --git a/ml_pam/ml_pam.csproj b/ml_pam/ml_pam.csproj index fa8ddc0..cc099e6 100644 --- a/ml_pam/ml_pam.csproj +++ b/ml_pam/ml_pam.csproj @@ -4,7 +4,7 @@ netstandard2.1 x64 PickupArmMovement - 1.2.3 + 1.2.4 SDraw SDraw PickupArmMovement diff --git a/ml_pin/Main.cs b/ml_pin/Main.cs index 82e42a4..ea572ff 100644 --- a/ml_pin/Main.cs +++ b/ml_pin/Main.cs @@ -1,8 +1,6 @@ using ABI_RC.Core.AudioEffects; using ABI_RC.Core.Networking.IO.Social; using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Core.Networking.IO.Instancing; using ABI_RC.Systems.GameEventSystem; using System; using System.Collections; @@ -111,9 +109,9 @@ namespace ml_pin bool ShouldNotifyInCurrentInstance() { - bool l_isInPublic = ((MetaPort.Instance.CurrentInstancePrivacyType == Instances.InstancePrivacyType.Public) && Settings.NotifyInPublic); - bool l_isInFriends = (((MetaPort.Instance.CurrentInstancePrivacyType == Instances.InstancePrivacyType.Friends) || (MetaPort.Instance.CurrentInstancePrivacyType == Instances.InstancePrivacyType.FriendsOfFriends)) && Settings.NotifyInFriends); - bool l_isInPrivate = (((MetaPort.Instance.CurrentInstancePrivacyType == Instances.InstancePrivacyType.EveryoneCanInvite) || (MetaPort.Instance.CurrentInstancePrivacyType == Instances.InstancePrivacyType.OwnerMustInvite)) && Settings.NotifyInPrivate); + bool l_isInPublic = Utils.IsInPublicInstance() && Settings.NotifyInPublic; + bool l_isInFriends = Utils.IsInFriendsInstance() && Settings.NotifyInFriends; + bool l_isInPrivate = Utils.IsInPrivateInstance() && Settings.NotifyInPrivate; return (l_isInPublic || l_isInFriends || l_isInPrivate); } } diff --git a/ml_pin/Properties/AssemblyInfo.cs b/ml_pin/Properties/AssemblyInfo.cs index 3447171..ab61d02 100644 --- a/ml_pin/Properties/AssemblyInfo.cs +++ b/ml_pin/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.1.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.1.3", "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)] diff --git a/ml_pin/Utils.cs b/ml_pin/Utils.cs index df46c70..846b0eb 100644 --- a/ml_pin/Utils.cs +++ b/ml_pin/Utils.cs @@ -1,3 +1,4 @@ +using ABI_RC.Core.Networking.IO.Instancing; using ABI_RC.Core.UI; using System.Reflection; @@ -8,5 +9,47 @@ namespace ml_pin static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic); public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => (ms_view?.GetValue(p_instance) as cohtml.Net.View)?.ExecuteScript(p_script); + + // Instance info + public static bool IsInPublicInstance() + { + bool l_result = false; + switch(Instances.CurrentInstancePrivacyType) + { + case Instances.InstancePrivacyType.Public: + case Instances.InstancePrivacyType.GroupPublic: + l_result = true; + break; + } + return l_result; + } + + public static bool IsInFriendsInstance() + { + bool l_result = false; + switch(Instances.CurrentInstancePrivacyType) + { + case Instances.InstancePrivacyType.Friends: + case Instances.InstancePrivacyType.FriendsOfFriends: + case Instances.InstancePrivacyType.GroupPlus: + l_result = true; + break; + } + return l_result; + } + + public static bool IsInPrivateInstance() + { + bool l_result = false; + switch(Instances.CurrentInstancePrivacyType) + { + case Instances.InstancePrivacyType.EveryoneCanInvite: + case Instances.InstancePrivacyType.OwnerMustInvite: + case Instances.InstancePrivacyType.Group: + l_result = true; + break; + } + return l_result; + } } } diff --git a/ml_pin/ml_pin.csproj b/ml_pin/ml_pin.csproj index c53bcaf..9f28bde 100644 --- a/ml_pin/ml_pin.csproj +++ b/ml_pin/ml_pin.csproj @@ -7,7 +7,7 @@ SDraw SDraw PlayersInstanceNotifier - 1.1.2 + 1.1.3 PlayersInstanceNotifier diff --git a/ml_pmc/Properties/AssemblyInfo.cs b/ml_pmc/Properties/AssemblyInfo.cs index ff06217..50bd068 100644 --- a/ml_pmc/Properties/AssemblyInfo.cs +++ b/ml_pmc/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.1.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.1.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(3)] [assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")] diff --git a/ml_pmc/ml_pmc.csproj b/ml_pmc/ml_pmc.csproj index 65f409f..3d443bf 100644 --- a/ml_pmc/ml_pmc.csproj +++ b/ml_pmc/ml_pmc.csproj @@ -7,7 +7,7 @@ SDraw SDraw PlayerMovementCopycat - 1.1.2 + 1.1.3 PlayerMovementCopycat diff --git a/ml_ppu/AvatarParameter.cs b/ml_ppu/AvatarParameter.cs new file mode 100644 index 0000000..c88b55e --- /dev/null +++ b/ml_ppu/AvatarParameter.cs @@ -0,0 +1,109 @@ +using ABI_RC.Core.Util.AnimatorManager; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace ml_ppu +{ + class AvatarParameter + { + public readonly string m_name; + public readonly int m_hash = 0; + public readonly bool m_sync; + public readonly AnimatorControllerParameterType m_type; + readonly AvatarAnimatorManager m_manager = null; + + public AvatarParameter(string p_name, AvatarAnimatorManager p_manager) + { + m_name = p_name; + m_manager = p_manager; + + Regex l_regex = new Regex("^#?" + p_name + '$'); + foreach(var l_param in m_manager.Animator.parameters) + { + if(l_regex.IsMatch(l_param.name)) + { + m_name = l_param.name; + m_sync = !l_param.name.StartsWith('#'); + m_hash = l_param.nameHash; + m_type = l_param.type; + break; + } + } + } + + public void SetValue(bool p_value) + { + if(m_hash != 0) + { + if(m_sync) + m_manager.SetParameter(m_name, p_value); + else + { + switch(m_type) + { + case AnimatorControllerParameterType.Bool: + case AnimatorControllerParameterType.Trigger: + m_manager.Animator.SetBool(m_hash, p_value); + break; + case AnimatorControllerParameterType.Int: + m_manager.Animator.SetInteger(m_hash, p_value ? 1 : 0); + break; + case AnimatorControllerParameterType.Float: + m_manager.Animator.SetFloat(m_hash, p_value ? 1f : 0f); + break; + } + } + } + } + + public void SetValue(int p_value) + { + if(m_hash != 0) + { + if(m_sync) + m_manager.SetParameter(m_name, p_value); + else + { + switch(m_type) + { + case AnimatorControllerParameterType.Bool: + case AnimatorControllerParameterType.Trigger: + m_manager.Animator.SetBool(m_hash, p_value > 0); + break; + case AnimatorControllerParameterType.Int: + m_manager.Animator.SetInteger(m_hash, p_value); + break; + case AnimatorControllerParameterType.Float: + m_manager.Animator.SetFloat(m_hash, p_value); + break; + } + } + } + } + + public void SetValue(float p_value) + { + if(m_hash != 0) + { + if(m_sync) + m_manager.SetParameter(m_name, p_value); + else + { + switch(m_type) + { + case AnimatorControllerParameterType.Bool: + case AnimatorControllerParameterType.Trigger: + m_manager.Animator.SetBool(m_hash, p_value > 0f); + break; + case AnimatorControllerParameterType.Int: + m_manager.Animator.SetInteger(m_hash, (int)p_value); + break; + case AnimatorControllerParameterType.Float: + m_manager.Animator.SetFloat(m_hash, p_value); + break; + } + } + } + } + } +} diff --git a/ml_ppu/PickUpManager.cs b/ml_ppu/PickUpManager.cs index 68a1432..5673f90 100644 --- a/ml_ppu/PickUpManager.cs +++ b/ml_ppu/PickUpManager.cs @@ -33,6 +33,8 @@ namespace ml_ppu Vector3 m_lastPosition = Vector3.zero; Vector3 m_velocity = Vector3.zero; + AvatarParameter m_avatarParameter = null; + void Awake() { if(Instance != null) @@ -122,6 +124,7 @@ namespace ml_ppu m_held = false; BetterBetterCharacterController.Instance.SetVelocity(m_velocity * Settings.VelocityMultiplier); + m_avatarParameter?.SetValue(false); } } } @@ -134,6 +137,8 @@ namespace ml_ppu Animator l_animator = PlayerSetup.Instance.Animator; if((l_animator != null) && l_animator.isHuman) { + m_avatarParameter = new AvatarParameter("PickedUp", PlayerSetup.Instance.AnimatorManager); + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); PlayerSetup.Instance.AvatarTransform.localPosition = Vector3.zero; PlayerSetup.Instance.AvatarTransform.localRotation = Quaternion.identity; @@ -171,6 +176,8 @@ namespace ml_ppu { try { + m_avatarParameter = null; + m_ready = false; m_held = false; @@ -264,6 +271,8 @@ namespace ml_ppu m_lastPosition = l_playerPos; m_velocity = Vector3.zero; m_held = true; + + m_avatarParameter?.SetValue(true); } } } diff --git a/ml_ppu/Properties/AssemblyInfo.cs b/ml_ppu/Properties/AssemblyInfo.cs index d5a3c9b..b5eb944 100644 --- a/ml_ppu/Properties/AssemblyInfo.cs +++ b/ml_ppu/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_ppu.PlayerPickUp), "PlayerPickUp", "1.0.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_ppu.PlayerPickUp), "PlayerPickUp", "1.0.2", "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)] diff --git a/ml_ppu/README.md b/ml_ppu/README.md index 95c0f50..ac03b0e 100644 --- a/ml_ppu/README.md +++ b/ml_ppu/README.md @@ -13,9 +13,14 @@ Available mod's settings in BTKUILib's page: * **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; +Available additional parameters for AAS animator: +* **`PickedUp`:** defines current picked up state; boolean. + * Note: Can be set as local-only (not synced) if starts with `#` character. + # Notes * Compatible with PlayerRagdollMod. diff --git a/ml_ppu/ml_ppu.csproj b/ml_ppu/ml_ppu.csproj index 6d6ed75..3026bdf 100644 --- a/ml_ppu/ml_ppu.csproj +++ b/ml_ppu/ml_ppu.csproj @@ -5,7 +5,7 @@ x64 PlayerPickUp SDraw - 1.0.1 + 1.0.2 diff --git a/ml_prm/GameEvents.cs b/ml_prm/GameEvents.cs index 880c0aa..27837d5 100644 --- a/ml_prm/GameEvents.cs +++ b/ml_prm/GameEvents.cs @@ -55,7 +55,7 @@ namespace ml_prm ); p_instance.Patch( - typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.Instance |BindingFlags.NonPublic), + typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.Instance | BindingFlags.NonPublic), null, new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) ); @@ -73,7 +73,7 @@ namespace ml_prm ); p_instance.Patch( - typeof(RootLogic).GetMethod(nameof(RootLogic.SpawnOnWorldInstance),BindingFlags.Instance | BindingFlags.Public), + typeof(RootLogic).GetMethod(nameof(RootLogic.SpawnOnWorldInstance), BindingFlags.Instance | BindingFlags.Public), new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnWorldSpawn_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), null ); diff --git a/ml_prm/Main.cs b/ml_prm/Main.cs index 8a7bc12..75709ac 100644 --- a/ml_prm/Main.cs +++ b/ml_prm/Main.cs @@ -5,12 +5,14 @@ namespace ml_prm public class PlayerRagdollMod : MelonLoader.MelonMod { RagdollController m_controller = null; + SoundManager m_soundManager = null; public override void OnInitializeMelon() { Settings.Init(); GameEvents.Init(HarmonyInstance); WorldManager.Init(); + ResourcesHandler.ExtractResources(); } public override void OnLateInitializeMelon() @@ -26,6 +28,8 @@ namespace ml_prm yield return null; m_controller = new UnityEngine.GameObject("[PlayerRagdollMod]").AddComponent(); + m_soundManager = new SoundManager(m_controller.transform); + m_soundManager.LoadSounds(); } System.Collections.IEnumerator WaitForWhitelist() @@ -40,6 +44,8 @@ namespace ml_prm { WorldManager.DeInit(); + m_soundManager = null; + if(m_controller != null) UnityEngine.Object.Destroy(m_controller.gameObject); m_controller = null; diff --git a/ml_prm/ModUi.cs b/ml_prm/ModUi.cs index 557a63e..52f7790 100644 --- a/ml_prm/ModUi.cs +++ b/ml_prm/ModUi.cs @@ -21,7 +21,6 @@ namespace ml_prm Hotkey = 0, Gravity, PointersReaction, - IgnoreLocal, CombatReaction, AutoRecover, Slipperiness, @@ -36,7 +35,8 @@ namespace ml_prm RecoverDelay, FallLimit, GestureGrab, - FriendsGrab + ImpactSounds, + ImpactVolume } const string c_ragdollKeyTooltip = "Switch ragdoll mode with '{0}' key"; @@ -52,7 +52,6 @@ namespace ml_prm static ToggleButton ms_hotkeyToggle = null; static ToggleButton ms_gravityToggle = null; static ToggleButton ms_pointersToggle = null; - static ToggleButton ms_ignoreLocalToggle = null; static ToggleButton ms_combatToggle = null; static ToggleButton ms_recoveryToggle = null; static ToggleButton ms_slipperinessToggle = null; @@ -62,12 +61,13 @@ namespace ml_prm static ToggleButton ms_buoyancyToggle = null; static ToggleButton ms_fallDamageToggle = null; static ToggleButton ms_gestureGrabToggle = null; - static ToggleButton ms_friendsGrabToggle = null; + static ToggleButton ms_impactSoundsToggle = null; static SliderFloat ms_velocityMultiplierSlider = null; static SliderFloat ms_movementDragSlider = null; static SliderFloat ms_angularMovementDragSlider = null; static SliderFloat ms_recoverDelaySlider = null; static SliderFloat ms_fallLimitSlider = null; + static SliderFloat ms_impactVolumeSlider = null; static Button ms_resetButton = null; internal static void Init() @@ -94,9 +94,6 @@ namespace ml_prm ms_pointersToggle = ms_category.AddToggle("Pointers reaction", "React to trigger colliders with CVRPointer component of 'ragdoll' type", Settings.PointersReaction); ms_pointersToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.PointersReaction, state); - ms_ignoreLocalToggle = ms_category.AddToggle("Ignore local pointers", "Ignore local avatar's CVRPointer components of 'ragdoll' type", Settings.IgnoreLocal); - ms_ignoreLocalToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.IgnoreLocal, state); - ms_combatToggle = ms_category.AddToggle("Combat reaction", "Ragdoll upon combat system death", Settings.CombatReaction); ms_combatToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.CombatReaction, state); @@ -121,11 +118,11 @@ namespace ml_prm ms_fallDamageToggle = ms_category.AddToggle("Fall damage", "Enable ragdoll when falling from height", Settings.FallDamage); ms_fallDamageToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.FallDamage, state); - ms_gestureGrabToggle = ms_category.AddToggle("Gesture grab", "Enable grabbing of ragdolled body parts by remote players with trigger/grab gesture

Warning: can lead to unpredictable physics behaviour in some cases", Settings.GestureGrab); + ms_gestureGrabToggle = ms_category.AddToggle("Grab attaching", "Enable attaching of ragdolled body parts to pointers of 'grab' type

Warning: can lead to unpredictable physics behaviour in some cases", Settings.GestureGrab); ms_gestureGrabToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.GestureGrab, state); - ms_friendsGrabToggle = ms_category.AddToggle("Friends grab only", " ", Settings.FriendsGrab); - ms_friendsGrabToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.FriendsGrab, state); + ms_impactSoundsToggle = ms_category.AddToggle("Impact sounds", "Enable collision sound effects of ragdolled body parts", Settings.ImpactSounds); + ms_impactSoundsToggle.OnValueUpdated += (state) => OnToggleUpdate(UiIndex.ImpactSounds, state); ms_velocityMultiplierSlider = ms_category.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Settings.VelocityMultiplier, 1f, 50f); ms_velocityMultiplierSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.VelocityMultiplier, value); @@ -143,6 +140,9 @@ namespace ml_prm ms_fallLimitSlider.SliderTooltip = string.Format(c_fallLimitTooltip, GetDropHeight(Settings.FallLimit)); ms_fallLimitSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.FallLimit, value); + ms_impactVolumeSlider = ms_category.AddSlider("Impact volume", "Volume of collision of ragdolled body parts", Settings.ImpactVolume * 100f, 0f, 100f); + ms_impactVolumeSlider.OnValueUpdated += (value) => OnSliderUpdate(UiIndex.ImpactVolume, value); + ms_resetButton = ms_category.AddButton("Reset settings", "", "Reset mod settings to default"); ms_resetButton.OnPress += Reset; } @@ -177,10 +177,6 @@ namespace ml_prm Settings.SetSetting(Settings.ModSetting.PointersReaction, p_state); break; - case UiIndex.IgnoreLocal: - Settings.SetSetting(Settings.ModSetting.IgnoreLocal, p_state); - break; - case UiIndex.CombatReaction: Settings.SetSetting(Settings.ModSetting.CombatReaction, p_state); break; @@ -217,8 +213,8 @@ namespace ml_prm Settings.SetSetting(Settings.ModSetting.GestureGrab, p_state); break; - case UiIndex.FriendsGrab: - Settings.SetSetting(Settings.ModSetting.FriendsGrab, p_state); + case UiIndex.ImpactSounds: + Settings.SetSetting(Settings.ModSetting.ImpactSounds, p_state); break; } } @@ -256,6 +252,10 @@ namespace ml_prm ms_fallLimitSlider.SliderTooltip = string.Format(c_fallLimitTooltip, GetDropHeight(p_value)); } break; + + case UiIndex.ImpactVolume: + Settings.SetSetting(Settings.ModSetting.ImpactVolume, p_value * 0.01f); + break; } } catch(Exception e) @@ -275,9 +275,6 @@ namespace ml_prm OnToggleUpdate(UiIndex.PointersReaction, true); ms_pointersToggle.ToggleValue = true; - OnToggleUpdate(UiIndex.IgnoreLocal, true); - ms_ignoreLocalToggle.ToggleValue = true; - OnToggleUpdate(UiIndex.CombatReaction, true); ms_combatToggle.ToggleValue = true; @@ -305,8 +302,8 @@ namespace ml_prm OnToggleUpdate(UiIndex.GestureGrab, false); ms_gestureGrabToggle.ToggleValue = false; - OnToggleUpdate(UiIndex.FriendsGrab, true); - ms_friendsGrabToggle.ToggleValue = true; + OnToggleUpdate(UiIndex.ImpactSounds, true); + ms_impactSoundsToggle.ToggleValue = true; OnSliderUpdate(UiIndex.VelocityMultiplier, 2f); ms_velocityMultiplierSlider.SetSliderValue(2f); @@ -322,6 +319,9 @@ namespace ml_prm OnSliderUpdate(UiIndex.FallLimit, 9.899494f); ms_fallLimitSlider.SetSliderValue(9.899494f); + + OnSliderUpdate(UiIndex.ImpactVolume, 100f); + ms_impactVolumeSlider.SetSliderValue(25f); } static void OnHotkeyKeyChanged(UnityEngine.KeyCode p_keyCode) diff --git a/ml_prm/Properties/AssemblyInfo.cs b/ml_prm/Properties/AssemblyInfo.cs index a692c71..e968e2d 100644 --- a/ml_prm/Properties/AssemblyInfo.cs +++ b/ml_prm/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.2.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.2.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(2)] [assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")] diff --git a/ml_prm/README.md b/ml_prm/README.md index bfbaa98..ef09eb1 100644 --- a/ml_prm/README.md +++ b/ml_prm/README.md @@ -17,7 +17,6 @@ Optional mod's settings page with [BTKUILib](https://github.com/BTK-Development/ * **Use gravity:** enables/disables gravity for ragdoll; `true` by default. * Note: Forcibly enabled in worlds that don't allow flight. * **Pointers reaction:** enables ragdoll state when player collides with trigger colliders and particle systems with CVRPointer component of `ragdoll` type (avatars, props and world included); `true` by default. -* **Ignore local pointers:** enables/disables ignoring of CVRPointer components of `ragdoll` type on local player's avatar; `true` by default. * **Combat reaction:** enables ragdoll state upon death in worlds with combat system; `true` by default. * **Auto recover:** enables automatic recovering after specific time delay; `false` by default. * **Slipperiness:** enables/disable low friction of ragdoll; `false` by default. @@ -30,9 +29,10 @@ Optional mod's settings page with [BTKUILib](https://github.com/BTK-Development/ * **Buoyancy:** enables floating in fluid volumes; `true` by default. * Note: Forcibly enabled in worlds that don't allow flight. * **Fall damage:** enables ragdoll when falling from specific height; `true` by default. -* **Gesture grab:** enables grabbing of ragdolled body parts by remote players with trigger/grab gesture; `false` by default. +* **Grab attaching:** enables attaching of ragdolled body parts to pointers with `grab` type of avatars, props and world(s); `false` by default. * Note: Can lead to unpredictable physics behaviour in some cases. -* **Friends grab only:** Allow only friends to be able to grab your radgolled body parts; `true` by default. +* **Impact sounds:** enables collision sound effects of ragdolled body parts; `true` by default. + * Note: Sounds can be replaced in `(game_folder)/UserData/PlayerRagdollMod` folder. * **Velocity multiplier:** velocity force multiplier based on player's movement direction; `2.0` by default. * Note: Limited according to world's fly multiplier. * Note: Forcibly set to `1.0` in worlds that don't allow flight. @@ -41,6 +41,7 @@ Optional mod's settings page with [BTKUILib](https://github.com/BTK-Development/ * **Angular movement drag:** angular movement resistance; `2.0` by default. * **Recover delay:** time delay for enabled `Auto recover` in seconds; `3.0` by default. * **Fall limit:** height limit for fall damage; `5.0` by default. +* **Impact volume:** collision sounds volume of ragdolled body parts; `100.0` by default. * **Reset settings:** resets mod settings to default. Optional mod's settings in [UIExpansionKit](https://github.com/ddakebono/ChilloutMods): diff --git a/ml_prm/RagdollBodypartHandler.cs b/ml_prm/RagdollBodypartHandler.cs index ac2aa93..d98f681 100644 --- a/ml_prm/RagdollBodypartHandler.cs +++ b/ml_prm/RagdollBodypartHandler.cs @@ -1,15 +1,15 @@ using ABI.CCK.Components; using ABI_RC.Core; -using ABI_RC.Core.Networking.IO.Social; using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; using ABI_RC.Systems.Movement; +using NAK.Contacts; +using System.Collections.Generic; using UnityEngine; namespace ml_prm { [DisallowMultipleComponent] - class RagdollBodypartHandler : MonoBehaviour, CVRTriggerVolume + class RagdollBodypartHandler : MonoBehaviour { const string c_ragdollPointerType = "ragdoll"; const string c_grabPointerType = "grab"; @@ -18,14 +18,16 @@ namespace ml_prm Rigidbody m_rigidBody = null; public Collider collider { get; set; } = null; + ContactReceiver m_contactReciever = null; PhysicsInfluencer m_physicsInfluencer = null; public bool UseBuoyancy { get; set; } = false; bool m_attached = false; - CVRPointer m_attachedPointer = null; Transform m_attachTransform = null; + ContactSender m_attachedSender = null; FixedJoint m_attachJoint = null; + static List ms_attachedSenders = new List(); // Unity events void Awake() @@ -42,7 +44,28 @@ namespace ml_prm } if(collider != null) + { RemoveGameCollision(); + + var l_constactShape = ContactConversion.FromCollider(collider, true); + m_contactReciever = this.gameObject.AddComponent(); + + m_contactReciever.shapeType = l_constactShape.shapeType; + m_contactReciever.localPosition = l_constactShape.localPosition; + m_contactReciever.localRotation = l_constactShape.localRotation; + m_contactReciever.radius = l_constactShape.radius; + m_contactReciever.height = l_constactShape.height; + m_contactReciever.boxSize = l_constactShape.boxSize; + + m_contactReciever.collisionTags = new string[] { c_ragdollPointerType, c_grabPointerType }; + m_contactReciever.receiverType = ReceiverType.Constant; + m_contactReciever.contentTypes = ContentType.World | ContentType.Avatar | ContentType.Prop; + m_contactReciever.SourceContentType = ContentType.Player; + m_contactReciever.contactValue = 1f; + m_contactReciever.drawGizmos = false; + + m_contactReciever.OnContactEnter += this.OnContactEnter; + } } void Start() @@ -64,46 +87,54 @@ namespace ml_prm this.gameObject.name = string.Format("{0} [NoGizmo]", this.gameObject.name); } - - if(collider != null) - { - CVRParticlePointerManager.volumes.Add(this); - CVRParticlePointerManager.UpdateParticleSystems(); - } } void OnDestroy() { - if(collider != null) - CVRParticlePointerManager.RemoveTrigger(collider); - Detach(); } void Update() { - if(m_attached && ((m_attachedPointer == null) || !m_attachedPointer.isActiveAndEnabled)) + if(m_attached && ((m_attachedSender == null) || !m_attachedSender.isActiveAndEnabled)) Detach(); } - void OnTriggerEnter(Collider p_col) + void OnCollisionEnter(Collision p_col) { - if(m_ready && (RagdollController.Instance != null)) + if(Settings.ImpactSounds && m_ready && !m_rigidBody.isKinematic && (p_col.gameObject.layer != CVRLayers.PlayerClone)) { - CVRPointer l_pointer = p_col.GetComponent(); + if(p_col.impulse.magnitude > 5f) + SoundManager.Instance.PlaySound(SoundManager.ImpactType.Soft); + } + } - // Ragdolling - if(Settings.PointersReaction && !RagdollController.Instance.IsRagdolled()) + void OnContactEnter(ContactCollisionInfo p_col) + { + if(m_ready && (RagdollController.Instance != null) && ContactManager.Exists) + { + ContactSender l_sender = ContactManager.Instance.GetSenderById(p_col.senderContactId); + if((l_sender != null) && (l_sender.collisionTags != null)) { - if((l_pointer != null) && (l_pointer.type == c_ragdollPointerType) && l_pointer.enabled && !IgnoreCheck(l_pointer.transform)) - RagdollController.Instance.Ragdoll(); - } + foreach(string l_tag in l_sender.collisionTags) + { + switch(l_tag) + { + case c_ragdollPointerType: + { + if(Settings.PointersReaction && !RagdollController.Instance.IsRagdolled() && RestrictionsCheck(l_sender.transform.root)) + RagdollController.Instance.Ragdoll(); + } + break; - //Attachment - if(!m_attached && RagdollController.Instance.IsRagdolled()) - { - if((l_pointer != null) && (l_pointer.type == c_grabPointerType) && RestrictionsCheck(p_col.transform.root)) - Attach(l_pointer); + case c_grabPointerType: + { + if(!m_attached && RagdollController.Instance.IsRagdolled() && RestrictionsCheck(l_sender.transform.root)) + Attach(l_sender); + } + break; + } + } } } } @@ -196,16 +227,16 @@ namespace ml_prm RemoveGameCollision(); } - void Attach(CVRPointer p_pointer) + void Attach(ContactSender p_sender) { - if(!m_attached && (collider != null) && (m_rigidBody != null)) + if(!m_attached && (collider != null) && (m_rigidBody != null) && !ms_attachedSenders.Contains(p_sender)) { - m_attachedPointer = p_pointer; + m_attachedSender = p_sender; GameObject l_attachPoint = new GameObject("[AttachPoint]"); - l_attachPoint.layer = CVRLayers.PlayerNetwork; + l_attachPoint.layer = CVRLayers.Default; m_attachTransform = l_attachPoint.transform; - m_attachTransform.parent = p_pointer.transform; + m_attachTransform.parent = p_sender.transform; Rigidbody l_body = l_attachPoint.AddComponent(); l_body.isKinematic = true; @@ -216,6 +247,7 @@ namespace ml_prm m_attachJoint.breakForce = Mathf.Infinity; m_attachJoint.breakTorque = Mathf.Infinity; + ms_attachedSenders.Add(p_sender); m_attached = true; } } @@ -232,7 +264,10 @@ namespace ml_prm Object.Destroy(m_attachJoint); m_attachJoint = null; - m_attachedPointer = null; + if(!ReferenceEquals(m_attachedSender, null)) + ms_attachedSenders.Remove(m_attachedSender); + m_attachedSender = null; + m_attached = false; } } @@ -246,32 +281,12 @@ namespace ml_prm BetterBetterCharacterController.Instance.IgnoreCollision(collider); } - // CVRTriggerVolume - public void TriggerEnter(CVRPointer pointer) - { - if(Settings.PointersReaction && (pointer != null) && pointer.enabled && (pointer.type == c_ragdollPointerType) && !IgnoreCheck(pointer.transform) && (RagdollController.Instance != null) && !RagdollController.Instance.IsRagdolled()) - RagdollController.Instance.Ragdoll(); - } - public void TriggerExit(CVRPointer pointer) + internal void RestoreContact() { + if((m_contactReciever != null) && ContactManager.Exists) + ContactManager.Instance.RestoreContact(m_contactReciever, m_contactReciever.isActiveAndEnabled); } - // Static utility - static bool IgnoreCheck(Transform p_transform) - { - return (Settings.IgnoreLocal && (p_transform.root == PlayerSetup.Instance.transform)); - } - - static bool RestrictionsCheck(Transform p_transform) - { - if(p_transform == PlayerSetup.Instance.transform) - return false; - - PlayerDescriptor l_playerDescriptor = p_transform.GetComponent(); - if(l_playerDescriptor != null) - return (!Settings.FriendsGrab || Friends.FriendsWith(l_playerDescriptor.ownerId)); - - return false; - } + static bool RestrictionsCheck(Transform p_transform) => (p_transform != PlayerSetup.Instance.transform); } } diff --git a/ml_prm/RagdollController.cs b/ml_prm/RagdollController.cs index ac0f0fd..e0b073c 100644 --- a/ml_prm/RagdollController.cs +++ b/ml_prm/RagdollController.cs @@ -661,7 +661,7 @@ namespace ml_prm IKSystem.Instance.applyOriginalHipRotation = true; PlayerSetup.Instance.AnimatorManager.CancelEmote = true; - m_ragdolledParameter.SetValue(true); + m_ragdolledParameter?.SetValue(true); if(!WorldManager.IsSafeWorld()) { @@ -677,6 +677,7 @@ namespace ml_prm foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) { + l_handler.RestoreContact(); l_handler.SetVelocity(l_velocity); l_handler.SetAngularVelocity(Vector3.zero); } @@ -689,6 +690,9 @@ namespace ml_prm m_lastRagdollPosition = m_puppetReferences.hips.position; m_downTime = 0f; + if(Settings.ImpactSounds) + SoundManager.Instance.PlaySound(SoundManager.ImpactType.Hard); + m_ragdolled = true; } } @@ -706,7 +710,7 @@ namespace ml_prm if(m_vrIK != null) m_vrIK.solver.Reset(); - m_ragdolledParameter.SetValue(false); + m_ragdolledParameter?.SetValue(false); m_puppet.localPosition = Vector3.zero; m_puppet.localRotation = Quaternion.identity; diff --git a/ml_prm/ResourcesHandler.cs b/ml_prm/ResourcesHandler.cs new file mode 100644 index 0000000..ba44ef3 --- /dev/null +++ b/ml_prm/ResourcesHandler.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace ml_prm +{ + static class ResourcesHandler + { + const string c_modName = "PlayerRagdollMod"; + readonly static string ms_namespace = typeof(ResourcesHandler).Namespace; + + static readonly List ms_audioResources = new List() + { + "body_medium_impact_hard1.wav", + "body_medium_impact_hard2.wav", + "body_medium_impact_hard3.wav", + "body_medium_impact_hard4.wav", + "body_medium_impact_hard5.wav", + "body_medium_impact_hard6.wav", + "body_medium_impact_soft1.wav", + "body_medium_impact_soft2.wav", + "body_medium_impact_soft3.wav", + "body_medium_impact_soft4.wav", + "body_medium_impact_soft5.wav", + "body_medium_impact_soft6.wav", + "body_medium_impact_soft7.wav" + }; + + public static void ExtractResources() + { + string l_dirPath = MelonLoader.Utils.MelonEnvironment.UserDataDirectory; + if(!Directory.Exists(l_dirPath)) + Directory.CreateDirectory(l_dirPath); + + l_dirPath = Path.Combine(l_dirPath, c_modName); + if(!Directory.Exists(l_dirPath)) + Directory.CreateDirectory(l_dirPath); + + foreach(string l_name in ms_audioResources) + { + string l_filePath = Path.Combine(l_dirPath, l_name); + if(!File.Exists(l_filePath)) + ExtractAudioFile(l_name, l_filePath); + } + } + + static void ExtractAudioFile(string p_name, string p_path) + { + Assembly l_assembly = Assembly.GetExecutingAssembly(); + + try + { + Stream l_resourceStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources.sounds." + p_name); + Stream l_fileStream = File.Create(p_path); + l_resourceStream.CopyTo(l_fileStream); + l_fileStream.Flush(); + l_fileStream.Close(); + l_resourceStream.Close(); + } + catch(Exception) + { + MelonLoader.MelonLogger.Warning("Unable to write '" + p_path + "' file, problems can occur."); + } + } + } +} diff --git a/ml_prm/Settings.cs b/ml_prm/Settings.cs index 74869a4..f35a331 100644 --- a/ml_prm/Settings.cs +++ b/ml_prm/Settings.cs @@ -23,7 +23,6 @@ namespace ml_prm AngularDrag, Gravity, PointersReaction, - IgnoreLocal, CombatReaction, AutoRecover, RecoverDelay, @@ -35,7 +34,8 @@ namespace ml_prm FallDamage, FallLimit, GestureGrab, - FriendsGrab + ImpactSounds, + ImpactVolume } public static bool Hotkey { get; private set; } = true; @@ -45,7 +45,6 @@ namespace ml_prm public static float AngularDrag { get; private set; } = 2f; public static bool Gravity { get; private set; } = true; public static bool PointersReaction { get; private set; } = true; - public static bool IgnoreLocal { get; private set; } = true; public static bool CombatReaction { get; private set; } = true; public static bool AutoRecover { get; private set; } = false; public static float RecoverDelay { get; private set; } = 3f; @@ -57,8 +56,8 @@ namespace ml_prm public static bool FallDamage { get; private set; } = true; public static float FallLimit { get; private set; } = 9.899494f; public static bool GestureGrab { get; private set; } = false; - public static bool FriendsGrab { get; private set; } = true; - public static float GrabDistance { get; private set; } = 0.1f; + public static bool ImpactSounds { get; private set; } = true; + public static float ImpactVolume { get; private set; } = 0.25f; public static readonly SettingEvent OnHotkeyChanged = new SettingEvent(); public static readonly SettingEvent OnHotkeyKeyChanged = new SettingEvent(); @@ -67,7 +66,6 @@ namespace ml_prm public static readonly SettingEvent OnAngularDragChanged = new SettingEvent(); public static readonly SettingEvent OnGravityChanged = new SettingEvent(); public static readonly SettingEvent OnPointersReactionChanged = new SettingEvent(); - public static readonly SettingEvent OnIgnoreLocalChanged = new SettingEvent(); public static readonly SettingEvent OnCombatReactionChanged = new SettingEvent(); public static readonly SettingEvent OnAutoRecoverChanged = new SettingEvent(); public static readonly SettingEvent OnRecoverDelayChanged = new SettingEvent(); @@ -79,8 +77,8 @@ namespace ml_prm public static readonly SettingEvent OnFallDamageChanged = new SettingEvent(); public static readonly SettingEvent OnFallLimitChanged = new SettingEvent(); public static readonly SettingEvent OnGestureGrabChanged = new SettingEvent(); - public static readonly SettingEvent OnFriendsGrabChanged = new SettingEvent(); - public static readonly SettingEvent OnGrabDistanceChanged = new SettingEvent(); + public static readonly SettingEvent OnImpactSoundsChanged = new SettingEvent(); + public static readonly SettingEvent OnImpactVolumeChanged = new SettingEvent(); static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; @@ -98,7 +96,6 @@ namespace ml_prm ms_category.CreateEntry(ModSetting.AngularDrag.ToString(), AngularDrag, null, null, true), ms_category.CreateEntry(ModSetting.Gravity.ToString(), Gravity, null, null, true), ms_category.CreateEntry(ModSetting.PointersReaction.ToString(), PointersReaction, null, null, true), - ms_category.CreateEntry(ModSetting.IgnoreLocal.ToString(), IgnoreLocal, null, null, true), ms_category.CreateEntry(ModSetting.CombatReaction.ToString(), CombatReaction, null, null, true), ms_category.CreateEntry(ModSetting.AutoRecover.ToString(), AutoRecover, null, null, true), ms_category.CreateEntry(ModSetting.RecoverDelay.ToString(), RecoverDelay, null, null, true), @@ -110,7 +107,8 @@ namespace ml_prm ms_category.CreateEntry(ModSetting.FallDamage.ToString(), FallDamage, null, null, true), ms_category.CreateEntry(ModSetting.FallLimit.ToString(), FallLimit, null, null, true), ms_category.CreateEntry(ModSetting.GestureGrab.ToString(), GestureGrab, null, null, true), - ms_category.CreateEntry(ModSetting.FriendsGrab.ToString(), FriendsGrab, null, null, true) + ms_category.CreateEntry(ModSetting.ImpactSounds.ToString(), ImpactSounds, null, null, true), + ms_category.CreateEntry(ModSetting.ImpactVolume.ToString(), ImpactVolume, null, null, true) }; ms_entries[(int)ModSetting.HotkeyKey].OnEntryValueChangedUntyped.Subscribe(OnMelonSettingSave_HotkeyKey); @@ -122,7 +120,6 @@ namespace ml_prm AngularDrag = Mathf.Clamp((float)ms_entries[(int)ModSetting.AngularDrag].BoxedValue, 0f, 50f); Gravity = (bool)ms_entries[(int)ModSetting.Gravity].BoxedValue; PointersReaction = (bool)ms_entries[(int)ModSetting.PointersReaction].BoxedValue; - IgnoreLocal = (bool)ms_entries[(int)ModSetting.IgnoreLocal].BoxedValue; CombatReaction = (bool)ms_entries[(int)ModSetting.CombatReaction].BoxedValue; AutoRecover = (bool)ms_entries[(int)ModSetting.AutoRecover].BoxedValue; RecoverDelay = Mathf.Clamp((float)ms_entries[(int)ModSetting.RecoverDelay].BoxedValue, 1f, 10f); @@ -134,7 +131,8 @@ namespace ml_prm FallDamage = (bool)ms_entries[(int)ModSetting.FallDamage].BoxedValue; FallLimit = Mathf.Clamp((float)ms_entries[(int)ModSetting.FallLimit].BoxedValue, 4.5f, 44.5f); GestureGrab = (bool)ms_entries[(int)ModSetting.GestureGrab].BoxedValue; - FriendsGrab = (bool)ms_entries[(int)ModSetting.FriendsGrab].BoxedValue; + ImpactSounds = (bool)ms_entries[(int)ModSetting.ImpactSounds].BoxedValue; + ImpactVolume = Mathf.Clamp((float)ms_entries[(int)ModSetting.ImpactVolume].BoxedValue, 0f, 1f); } static void OnMelonSettingSave_HotkeyKey(object p_oldValue, object p_newValue) @@ -181,13 +179,6 @@ namespace ml_prm } break; - case ModSetting.IgnoreLocal: - { - IgnoreLocal = (bool)p_value; - OnIgnoreLocalChanged.Invoke(IgnoreLocal); - } - break; - case ModSetting.CombatReaction: { CombatReaction = (bool)p_value; @@ -251,10 +242,10 @@ namespace ml_prm } break; - case ModSetting.FriendsGrab: + case ModSetting.ImpactSounds: { - FriendsGrab = (bool)p_value; - OnFriendsGrabChanged.Invoke(FriendsGrab); + ImpactSounds = (bool)p_value; + OnImpactSoundsChanged.Invoke(ImpactSounds); } break; @@ -293,6 +284,13 @@ namespace ml_prm OnFallLimitChanged.Invoke(FallLimit); } break; + + case ModSetting.ImpactVolume: + { + ImpactVolume = (float)p_value; + OnImpactVolumeChanged.Invoke(ImpactVolume); + } + break; } if(ms_entries != null) diff --git a/ml_prm/SoundManager.cs b/ml_prm/SoundManager.cs new file mode 100644 index 0000000..40df8b1 --- /dev/null +++ b/ml_prm/SoundManager.cs @@ -0,0 +1,141 @@ +using ABI_RC.Core; +using System.Collections; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace ml_prm +{ + public class SoundManager + { + enum SoundType + { + ImpactHard1 = 0, + ImpactHard2, + ImpactHard3, + ImpactHard4, + ImpactHard5, + ImpactHard6, + ImpactSoft1, + ImpactSoft2, + ImpactSoft3, + ImpactSoft4, + ImpactSoft5, + ImpactSoft6, + ImpactSoft7, + + Count + } + public enum ImpactType + { + Hard = 0, + Soft + } + + const string c_modName = "PlayerRagdollMod"; + + public static SoundManager Instance { get; private set; } = null; + + bool m_loaded = false; + readonly AudioClip[] m_clips = null; + AudioSource m_audioSource = null; + + public SoundManager(Transform p_root) + { + m_clips = new AudioClip[(int)SoundType.Count]; + for(int i = 0; i < (int)SoundType.Count; i++) + m_clips[i] = null; + + GameObject l_audioSource = new GameObject("[ImpactSource]"); + l_audioSource.transform.parent = p_root; + l_audioSource.transform.localPosition = Vector3.zero; + l_audioSource.transform.localRotation = Quaternion.identity; + + m_audioSource = l_audioSource.AddComponent(); + m_audioSource.playOnAwake = false; + m_audioSource.loop = false; + m_audioSource.minDistance = 2f; + m_audioSource.maxDistance = 5f; + m_audioSource.dopplerLevel = 0f; + m_audioSource.panStereo = 0f; + m_audioSource.spatialBlend = 0f; // 2D + m_audioSource.spread = 0f; + m_audioSource.rolloffMode = AudioRolloffMode.Linear; + m_audioSource.outputAudioMixerGroup = RootLogic.Instance.mainSfx; + + Instance = this; + + } + ~SoundManager() + { + if(Instance == this) + Instance = null; + + if(m_audioSource != null) + Object.Destroy(m_audioSource); + m_audioSource = null; + } + + internal void LoadSounds() + { + if(!m_loaded) + { + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard1, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard1.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard2, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard2.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard3, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard3.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard4, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard4.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard5, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard5.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactHard6, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_hard6.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft1, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft1.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft2, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft2.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft3, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft3.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft4, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft4.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft5, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft5.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft6, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft6.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.ImpactSoft7, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "body_medium_impact_soft7.wav"))); + m_loaded = true; + } + } + + IEnumerator LoadAudioClip(SoundType p_type, string p_path) + { + using UnityWebRequest l_uwr = UnityWebRequestMultimedia.GetAudioClip("file://" + p_path, AudioType.WAV); + ((DownloadHandlerAudioClip)l_uwr.downloadHandler).streamAudio = true; + yield return l_uwr.SendWebRequest(); + + if((l_uwr.result == UnityWebRequest.Result.ConnectionError) || (l_uwr.result == UnityWebRequest.Result.ProtocolError)) + { + MelonLoader.MelonLogger.Warning(l_uwr.error); + yield break; + } + + AudioClip l_content; + AudioClip l_clip = (l_content = DownloadHandlerAudioClip.GetContent(l_uwr)); + yield return l_content; + if(!l_uwr.isDone || (l_clip == null)) + yield break; + + m_clips[(int)p_type] = l_clip; + } + + public void PlaySound(ImpactType p_type) + { + if(m_loaded) + { + int l_index = -1; + switch(p_type) + { + case ImpactType.Hard: + l_index = (int)SoundType.ImpactHard1 + Random.Range(0, 6); + break; + case ImpactType.Soft: + l_index = (int)SoundType.ImpactSoft1 + Random.Range(0, 7); + break; + } + + if((l_index != -1) && (m_clips[l_index] != null)) + m_audioSource.PlayOneShot(m_clips[l_index], Settings.ImpactVolume); + } + } + } +} diff --git a/ml_prm/Utils.cs b/ml_prm/Utils.cs index 809b910..6eeb601 100644 --- a/ml_prm/Utils.cs +++ b/ml_prm/Utils.cs @@ -1,13 +1,11 @@ using ABI.CCK.Components; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.IK; using ABI_RC.Systems.Movement; using System.Collections.Generic; using System.Reflection; using UnityEngine; using System.Linq; using ABI_RC.Core.InteractionSystem; +using NAK.Contacts; namespace ml_prm { @@ -18,6 +16,8 @@ namespace ml_prm static readonly FieldInfo ms_influencerTouchingVolumes = typeof(PhysicsInfluencer).GetField("_touchingVolumes", BindingFlags.Instance | BindingFlags.NonPublic); static readonly FieldInfo ms_influencerSubmergedColliders = typeof(PhysicsInfluencer).GetField("_submergedColliders", BindingFlags.Instance | BindingFlags.NonPublic); static readonly FieldInfo ms_lastCVRSeat = typeof(BetterBetterCharacterController).GetField("_lastCvrSeat", BindingFlags.Instance | BindingFlags.NonPublic); + static readonly FieldInfo ms_contactList = typeof(ContactManager).GetField("contactList", BindingFlags.Instance | BindingFlags.NonPublic); + static readonly FieldInfo ms_pendingRemove = typeof(ContactManager).GetField("pendingRemove", BindingFlags.Instance | BindingFlags.NonPublic); public static void ClearFluidVolumes(this BetterBetterCharacterController p_instance) => (ms_touchingVolumes?.GetValue(p_instance) as List)?.Clear(); @@ -35,6 +35,18 @@ namespace ml_prm public static CVRSeat GetCurrentSeat(this BetterBetterCharacterController p_instance) => (ms_lastCVRSeat?.GetValue(p_instance) as CVRSeat); + public static bool IsRegistered(this ContactManager p_instance, ContactBase p_contact) => (ms_contactList?.GetValue(p_instance) as List).Contains(p_contact); + + public static void RestoreContact(this ContactManager p_instance, ContactBase p_contact, bool p_state) + { + if(p_instance.IsRegistered(p_contact)) + (ms_pendingRemove?.GetValue(p_instance) as HashSet).Remove(p_contact.ContactId); + else + p_instance.Register(p_contact); + + p_instance.SetEnabled(p_contact.ContactId, p_state); + } + // Unity specific public static void CopyGlobal(this Transform p_source, Transform p_target) { diff --git a/ml_prm/ml_prm.csproj b/ml_prm/ml_prm.csproj index 4059621..2969588 100644 --- a/ml_prm/ml_prm.csproj +++ b/ml_prm/ml_prm.csproj @@ -1,99 +1,127 @@ - - - - netstandard2.1 - x64 - PlayerRagdollMod - 1.2.4 - SDraw - SDraw - PlayerRagdollMod - PlayerRagdollMod - - + + + + netstandard2.1 + x64 + PlayerRagdollMod + 1.2.7 + SDraw + SDraw + PlayerRagdollMod + PlayerRagdollMod + + embedded true - - - - - - - - - - - - - $(CVRPath)/MelonLoader/net35/0Harmony.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/Assembly-CSharp.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/Assembly-CSharp-firstpass.dll - false - false - - - $(CVRPath)/Mods/BTKUILib.dll - false - false - + + + + + + + + + + + + + + + + + + + + + + + + + + $(CVRPath)/MelonLoader/net35/0Harmony.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/Assembly-CSharp.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/Assembly-CSharp-firstpass.dll + false + false + + + $(CVRPath)/Mods/BTKUILib.dll + false + false + $(CVRPath)/ChilloutVR_Data/Managed/ECM2.dll false false - - - $(CVRPath)/MelonLoader/net35/MelonLoader.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.AnimationModule.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.ClothModule.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.CoreModule.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.InputLegacyModule.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.PhysicsModule.dll - false - false - - - $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.ParticleSystemModule.dll - false - false - - - - - - - - + + + $(CVRPath)/MelonLoader/net35/MelonLoader.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.AnimationModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AudioModule.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.ClothModule.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.CoreModule.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.InputLegacyModule.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.PhysicsModule.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/UnityEngine.ParticleSystemModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestModule.dll + false + false + + + + + + + + diff --git a/ml_prm/resources/sounds/body_medium_impact_hard1.wav b/ml_prm/resources/sounds/body_medium_impact_hard1.wav new file mode 100644 index 0000000..adebd1e Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_hard1.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_hard2.wav b/ml_prm/resources/sounds/body_medium_impact_hard2.wav new file mode 100644 index 0000000..85d0840 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_hard2.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_hard3.wav b/ml_prm/resources/sounds/body_medium_impact_hard3.wav new file mode 100644 index 0000000..5cb0d89 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_hard3.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_hard4.wav b/ml_prm/resources/sounds/body_medium_impact_hard4.wav new file mode 100644 index 0000000..9c5a4e0 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_hard4.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_hard5.wav b/ml_prm/resources/sounds/body_medium_impact_hard5.wav new file mode 100644 index 0000000..a51837e Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_hard5.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_hard6.wav b/ml_prm/resources/sounds/body_medium_impact_hard6.wav new file mode 100644 index 0000000..1ab9f78 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_hard6.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft1.wav b/ml_prm/resources/sounds/body_medium_impact_soft1.wav new file mode 100644 index 0000000..130a77e Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft1.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft2.wav b/ml_prm/resources/sounds/body_medium_impact_soft2.wav new file mode 100644 index 0000000..25b2004 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft2.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft3.wav b/ml_prm/resources/sounds/body_medium_impact_soft3.wav new file mode 100644 index 0000000..3b0b03c Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft3.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft4.wav b/ml_prm/resources/sounds/body_medium_impact_soft4.wav new file mode 100644 index 0000000..b0b0ff6 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft4.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft5.wav b/ml_prm/resources/sounds/body_medium_impact_soft5.wav new file mode 100644 index 0000000..3e8a152 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft5.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft6.wav b/ml_prm/resources/sounds/body_medium_impact_soft6.wav new file mode 100644 index 0000000..d5ebcac Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft6.wav differ diff --git a/ml_prm/resources/sounds/body_medium_impact_soft7.wav b/ml_prm/resources/sounds/body_medium_impact_soft7.wav new file mode 100644 index 0000000..fdebe11 Binary files /dev/null and b/ml_prm/resources/sounds/body_medium_impact_soft7.wav differ diff --git a/ml_vei/Properties/AssemblyInfo.cs b/ml_vei/Properties/AssemblyInfo.cs index 6c140ba..c3e96d1 100644 --- a/ml_vei/Properties/AssemblyInfo.cs +++ b/ml_vei/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.1.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.1.3", "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)] diff --git a/ml_vei/ml_vei.csproj b/ml_vei/ml_vei.csproj index d8d19d6..f26a419 100644 --- a/ml_vei/ml_vei.csproj +++ b/ml_vei/ml_vei.csproj @@ -4,7 +4,7 @@ netstandard2.1 x64 ViveExtendedInput - 1.1.2 + 1.1.3 SDraw SDraw ViveExtendedInput diff --git a/ml_vpc/Main.cs b/ml_vpc/Main.cs index 548e53d..38ebdd3 100644 --- a/ml_vpc/Main.cs +++ b/ml_vpc/Main.cs @@ -1,3 +1,4 @@ +using System; using ABI_RC.VideoPlayer; using System.Reflection; using System.IO; @@ -10,17 +11,66 @@ namespace ml_vpc public override void OnInitializeMelon() { - HarmonyInstance.Patch(typeof(YoutubeDl).GetMethod("GetVideoMetaDataAsync", BindingFlags.NonPublic | BindingFlags.Static), + HarmonyInstance.Patch(typeof(YoutubeDl).GetMethod("GetVideoMetaDataAsync", BindingFlags.Public | BindingFlags.Static), new HarmonyLib.HarmonyMethod(typeof(VideoPlayerCookies).GetMethod(nameof(OnGetYoutubeVideoMetaData_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) ); ms_cookiesPath = Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, "cookies.txt"); } - static void OnGetYoutubeVideoMetaData_Prefix(ref string parameter) + public override void OnLateInitializeMelon() { - if(File.Exists(ms_cookiesPath)) - parameter += string.Format(" --cookies \"{0}\"", ms_cookiesPath); + Settings.Init(); + } + + static void OnGetYoutubeVideoMetaData_Prefix(ref string youtubeUrl, ref string existingParameters) + { + try + { + if (!Settings.Enabled) + return; + + switch (Settings.Mode) + { + case Settings.CookieMode.File: + if (File.Exists(ms_cookiesPath)) + existingParameters += string.Format("--cookies \"{0}\"", ms_cookiesPath); + else + MelonLoader.MelonLogger.Warning("Cookies file not found in: '" + ms_cookiesPath + "'"); + break; + case Settings.CookieMode.BrowserFirefox: + existingParameters += "--cookies-from-browser firefox"; + break; + case Settings.CookieMode.BrowserBrave: + existingParameters += "--cookies-from-browser brave"; + break; + case Settings.CookieMode.BrowserChrome: + existingParameters += "--cookies-from-browser chrome"; + break; + case Settings.CookieMode.BrowserChromium: + existingParameters += "--cookies-from-browser chromium"; + break; + case Settings.CookieMode.BrowserEdge: + existingParameters += "--cookies-from-browser edge"; + break; + case Settings.CookieMode.BrowserOpera: + existingParameters += "--cookies-from-browser opera"; + break; + case Settings.CookieMode.BrowserSafari: + existingParameters += "--cookies-from-browser safari"; + break; + case Settings.CookieMode.BrowserVivaldi: + existingParameters += "--cookies-from-browser vivaldi"; + break; + case Settings.CookieMode.BrowserWhale: + existingParameters += "--cookies-from-browser whale"; + break; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Warning(e); + } } } } diff --git a/ml_vpc/Properties/AssemblyInfo.cs b/ml_vpc/Properties/AssemblyInfo.cs index eeac241..335c9a9 100644 --- a/ml_vpc/Properties/AssemblyInfo.cs +++ b/ml_vpc/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_vpc.VideoPlayerCookies), "VideoPlayerCookies", "1.0.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_vpc.VideoPlayerCookies), "VideoPlayerCookies", "1.0.5", "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)] +[assembly: MelonLoader.MelonAdditionalCredits("kafeijao, Slime")] diff --git a/ml_vpc/README.md b/ml_vpc/README.md index db042cc..ac01628 100644 --- a/ml_vpc/README.md +++ b/ml_vpc/README.md @@ -5,13 +5,41 @@ This mod allows yt-dlp to use cookies for playing YouTube videos. * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Get [latest release DLL](../../../releases/latest): * Put `VideoPlayerCookies.dll` in `Mods` folder of game - + # Usage +Available mod's settings in `Settings - General - Video Player Cookies`: +* **Enabled:** Whether this mod adds cookie parameters or not; `true` by default. +* **Cookie fetch mode:** cookies fetch method; `Cookie text file` by default. + * **Cookie text file** *(default)* fetches cookies from your `cookies.txt` file, check [How to create cookies.txt](#how-to-create-cookiestxt) + * **Browser Firefox** fetches cookies directly from FireFox browser. *requires to be logged-in on YouTube in FireFox* + * **Browser Brave** fetches cookies directly from Brave browser. *requires to be logged-in on YouTube in Brave* + * **Browser Chrome** fetches cookies directly from Chrome browser. *requires to be logged-in on YouTube in Chrome* + * **Browser Chromium** fetches cookies directly from Chromium browser. *requires to be logged-in on YouTube in Chromium* + * **Browser Edge** fetches cookies directly from Edge browser. *requires to be logged-in on YouTube in Edge* + * **Browser Opera** fetches cookies directly from Opera browser. *requires to be logged-in on YouTube in Opera* + * **Browser Safari** fetches cookies directly from Safari browser. *requires to be logged-in on YouTube in Safari* + * **Browser Vivaldi** fetches cookies directly from Vivaldi browser. *requires to be logged-in on YouTube in Vivaldi* + * **Browser Whale** fetches cookies directly from Whale browser. *requires to be logged-in on YouTube in Whale* + +# How to create cookies.txt * Acquire cookies for YouTube from your browser: * Chromium-based browsers: [Get cookies.txt LOCALLY](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc) extension * Firefox-based browsers: [cookies.txt](https://addons.mozilla.org/en-US/firefox/addon/cookies-txt) extension * Save result as file named `cookies.txt` in `/UserData` folder +# How to make it work on Linux (Tested on CachyOS / Arch with Firefox) +Because of Linux / Proton, the cookies.txt doesn't seem to work well, instead it's better to use browser mode, the following tutorial explains how to set it up (tested using Firefox) + +* Go to the mod settings and select your browser +* Put a video and wait for it to fail, then look at the logs + * It should tell you an error because it can't find your cookies in a path, for Firefox it looks like `AppData/Local/Packages/Mozilla.Firefox_n80bbvh6b1yt2/LocalCache/Roaming/Mozilla/Firefox/Profiles/` +* Go to AppData in the drive_c of the game, on Arch this is usually located at `/home/[USER]/.local/share/Steam/steamapps/compatdata/661130/pfx/drive_c/users/steamuser/Appdata` +* Create all the folders that it requires until the last one, for Firefox this means the folders `Packages`, `Mozilla.Firefox_n80bbvh6b1yt2`, `LocalCache`, `Roaming`, `Mozilla` and `Firefox` but don't create the last one (`Profiles`) yet unless you don't intend to use your real browser +* Because your cookies could change or expire, instead of giving it the files once, we'll use a symlink to the real firefox on your machine. This is completely up to you and you could just copy the files or get them from somewhere else +* Now you can create a link that points to the folder of your browser on Linux. For Firefox, this is the `Profiles` folder that should point to `/home/[USER]/.mozilla/firefox/` +* If you followed all the steps, the next video you put should successfully get the cookies from your browser + + # Notes * After first use yt-dlp will remove unnecessary cookies from file automatically. * Cookies contain private information and access to your YouTube account, **do not share them to anyone**. diff --git a/ml_vpc/ResourcesHandler.cs b/ml_vpc/ResourcesHandler.cs new file mode 100644 index 0000000..9d26199 --- /dev/null +++ b/ml_vpc/ResourcesHandler.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using System.Reflection; + +namespace ml_vpc +{ + static class ResourcesHandler + { + readonly static string ms_namespace = typeof(ResourcesHandler).Namespace; + + public static string GetEmbeddedResource(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + + return l_result; + } + } +} diff --git a/ml_vpc/Settings.cs b/ml_vpc/Settings.cs new file mode 100644 index 0000000..6fbe9f0 --- /dev/null +++ b/ml_vpc/Settings.cs @@ -0,0 +1,144 @@ +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_vpc +{ + static class Settings + { + internal class SettingEvent + { + 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(T p_value) => m_action?.Invoke(p_value); + } + + public enum CookieMode + { + File = 0, + BrowserFirefox, + BrowserBrave, + BrowserChrome, // This one might not work + BrowserChromium, + BrowserEdge, + BrowserOpera, + BrowserSafari, + BrowserVivaldi, + BrowserWhale, + } + + private enum ModSetting + { + Enabled = 0, + Mode, + } + + public static bool Enabled { get; private set; } = true; + public static CookieMode Mode { get; private set; } = CookieMode.File; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + public static readonly SettingEvent OnEnabledChanged = new SettingEvent(); + public static readonly SettingEvent OnModeChanged = new SettingEvent(); + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("VPC", null, true); + + ms_entries = new List() + { + ms_category.CreateEntry(nameof(ModSetting.Enabled), Enabled), + ms_category.CreateEntry(nameof(ModSetting.Mode), (int)CookieMode.File), + }; + + Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue; + Mode = (CookieMode)ms_entries[(int)ModSetting.Mode].BoxedValue; + + MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); + } + + private static System.Collections.IEnumerator WaitMainMenuUi() + { + while(ViewManager.Instance == null) + yield return null; + while(ViewManager.Instance.cohtmlView == null) + yield return null; + while(ViewManager.Instance.cohtmlView.Listener == null) + yield return null; + + ViewManager.Instance.cohtmlView.Listener.ReadyForBindings += () => + { + ViewManager.Instance.cohtmlView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action(OnToggleUpdate)); + ViewManager.Instance.cohtmlView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action(OnDropdownUpdate)); + + }; + ViewManager.Instance.cohtmlView.Listener.FinishLoad += (_) => + { + ViewManager.Instance.cohtmlView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js")); + ViewManager.Instance.cohtmlView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js")); + MelonLoader.MelonCoroutines.Start(UpdateMenuSettings()); + }; + } + + private static System.Collections.IEnumerator UpdateMenuSettings() + { + while(!ViewManager.Instance.IsReady || !ViewManager.Instance.IsViewShown) + yield return null; + + foreach(var l_entry in ms_entries) + ViewManager.Instance.cohtmlView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); + } + + private static void OnToggleUpdate(string p_name, string p_value) + { + try + { + if(Enum.TryParse(p_name, out ModSetting l_setting) && bool.TryParse(p_value, out bool l_value)) + { + switch(l_setting) + { + case ModSetting.Enabled: + { + Enabled = l_value; + OnEnabledChanged.Invoke(Enabled); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + private static void OnDropdownUpdate(string p_name, string p_value) + { + try + { + if(Enum.TryParse(p_name, out ModSetting l_setting) && int.TryParse(p_value, out int l_value)) + { + switch(l_setting) + { + case ModSetting.Mode: + { + Mode = (CookieMode)l_value; + OnModeChanged.Invoke(Mode); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_vpc/Utils.cs b/ml_vpc/Utils.cs new file mode 100644 index 0000000..7a429be --- /dev/null +++ b/ml_vpc/Utils.cs @@ -0,0 +1,12 @@ +using ABI_RC.Core.UI; +using System.Reflection; + +namespace ml_vpc +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic); + + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => (ms_view?.GetValue(p_instance) as cohtml.Net.View)?.ExecuteScript(p_script); + } +} diff --git a/ml_vpc/ml_vpc.csproj b/ml_vpc/ml_vpc.csproj index d00abb8..bef407f 100644 --- a/ml_vpc/ml_vpc.csproj +++ b/ml_vpc/ml_vpc.csproj @@ -6,7 +6,7 @@ VideoPlayerCookies SDraw SDraw - 1.0.2 + 1.0.5 @@ -18,6 +18,11 @@ + + + + + $(CVRPath)/MelonLoader/net35/0Harmony.dll @@ -29,6 +34,16 @@ false false + + $(CVRPath)/ChilloutVR_Data/Managed/cohtml.Net.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/Cohtml.Runtime.dll + false + false + $(CVRPath)/MelonLoader/net35/MelonLoader.dll false diff --git a/ml_vpc/resources/mod_menu.js b/ml_vpc/resources/mod_menu.js new file mode 100644 index 0000000..772bffb --- /dev/null +++ b/ml_vpc/resources/mod_menu.js @@ -0,0 +1,37 @@ +{ + let l_block = document.createElement('div'); + l_block.innerHTML = ` +

+
Video Player Cookies
+
+
+ +
+
Enabled:
+
+
+
+
+ +
+
Cookie fetch mode:
+
+
+
+
+ `; + + document.getElementById('settings-general').appendChild(l_block); + + // Toggles + for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) + modsExtension.addSetting('VPC', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_VPC')); + + // Sliders + for (let l_slider of l_block.querySelectorAll('.inp_slider')) + modsExtension.addSetting('VPC', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_VPC')); + + // Dropdowns + for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown')) + modsExtension.addSetting('VPC', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_VPC')); +}