diff --git a/README.md b/README.md index 0fae0c6..5697fa6 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,25 @@ -Merged set of MelonLoader mods for ChilloutVR. - -**Table for game build 2023r175:** -| Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) | -|:---------:|:----------:|:--------------:| :----------------------------------------------------------------| -| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.4.0 [:arrow_down:](../../releases/latest/download/ml_amt.dll)|| -| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_asl.dll)|| -| [Better Fingers Tracking](/ml_bft/README.md) | ml_bft | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_bft.dll)|| -| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.4 [:arrow_down:](../../releases/latest/download/ml_dht.dll) || -| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.5.1 [:arrow_down:](../../releases/latest/download/ml_lme.dll)|| -| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.3 [:arrow_down:](../../releases/latest/download/ml_pam.dll)|| -| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)|| -| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.6 [:arrow_down:](../../releases/latest/download/ml_prm.dll)|| -| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.5 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)|| -| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_vei.dll)|| - -**Archived mods:** -| Full name | Short name | Notes | -|:---------:|:----------:|-------| -| Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` | -| Desktop Reticle Switch | ml_drs | Boring functionality | -| Extended Game Notifications | ml_egn | In-game feature since 2023r172 update | -| Four Point Tracking | ml_fpt | In-game feature since 2022r170 update | -| Game Main Fixes | ml_gmf | In-game feature since 2023r172 update | -| Server Connection Info | ml_sci | Superseded by `Extended Game Notifications` +Merged set of MelonLoader mods for ChilloutVR. + +**Table for game build 2023r175:** +| Full name | Short name | Latest version | +|:---------:|:----------:|:--------------:| :----------------------------------------------------------------| +| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.4.0 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| +| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| +| [Better Fingers Tracking](/ml_bft/README.md) | ml_bft | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_bft.dll)| +| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.4 [:arrow_down:](../../releases/latest/download/ml_dht.dll)| +| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.5.1 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| +| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.3 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| +| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| +| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.7 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| +| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.7 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| +| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| + +**Archived mods:** +| Full name | Short name | Notes | +|:---------:|:----------:|-------| +| Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` | +| Desktop Reticle Switch | ml_drs | Boring functionality | +| Extended Game Notifications | ml_egn | In-game feature since 2023r172 update | +| Four Point Tracking | ml_fpt | In-game feature since 2022r170 update | +| Game Main Fixes | ml_gmf | In-game feature since 2023r172 update | +| Server Connection Info | ml_sci | Superseded by `Extended Game Notifications` diff --git a/ml_pin/Properties/AssemblyInfo.cs b/ml_pin/Properties/AssemblyInfo.cs index cadf860..1662188 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.0.6", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.0.7", "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/README.md b/ml_pin/README.md index 87e0caf..05abdf0 100644 --- a/ml_pin/README.md +++ b/ml_pin/README.md @@ -1,28 +1,29 @@ -# Players Instance Notifier -This mod implements sound notifications for players joining and leaving. -This can be considered as attempt of [JoinNotifier](https://github.com/knah/VRCMods/tree/master/JoinNotifier) revival. - -# Installation -* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) -* Get [latest release DLL](../../../releases/latest): - * Put `ml_pin.dll` in `Mods` folder of game - -# Usage -Available mod's settings in `Settings - Audio - Players Instance Notifier`: -* **Notify of:** players notification filter type, available filters: `None`, `Friends`, `All`; `All` by default. -* **Mixed volume:** volume of notifications; `100` by default. - * Note: Respects game's interface volume setting and mixes with it accordingly. -* **Notify in public instances:** notifies in `Public` instances; `true` by default. -* **Notify in friends instances:** notifies in `Friends of friends` and `Friends` instances; `true` by default. -* **Notify in private instances:** notifies in `Everyone can invite` and `Owner must invite` instances; `true` by default. -* **Always notify of friends:** notifies friends join/leave no matter what; `false` by default. - -# Custom notification sounds -You can setup your own notification sounds. -Go to `/UserData/PlayersInstanceNotifier` and replace to your preferable sounds. - -Available sounds for replacement: -* **player_join.wav** -* **player_leave.wav** -* **friend_join.wav** -* **friend_leave.wav** +# Players Instance Notifier +This mod implements sound notifications for players joining and leaving. +This can be considered as attempt of [JoinNotifier](https://github.com/knah/VRCMods/tree/master/JoinNotifier) revival. + +# Installation +* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) +* Get [latest release DLL](../../../releases/latest): + * Put `ml_pin.dll` in `Mods` folder of game + +# Usage +Available mod's settings in `Settings - Audio - Players Instance Notifier`: +* **Notify of:** players notification filter type, available filters: `None`, `Friends`, `All`; `All` by default. +* **Mixed volume:** volume of notifications; `100` by default. + * Note: Respects game's interface volume setting and mixes with it accordingly. +* **Delay between notifications:** prevents notification until previous one is finished; `true` by default. +* **Notify in public instances:** notifies in `Public` instances; `true` by default. +* **Notify in friends instances:** notifies in `Friends of friends` and `Friends` instances; `true` by default. +* **Notify in private instances:** notifies in `Everyone can invite` and `Owner must invite` instances; `true` by default. +* **Always notify of friends:** notifies friends join/leave no matter what; `false` by default. + +# Custom notification sounds +You can setup your own notification sounds. +Go to `/UserData/PlayersInstanceNotifier` and replace to your preferable sounds. + +Available sounds for replacement: +* **player_join.wav** +* **player_leave.wav** +* **friend_join.wav** +* **friend_leave.wav** diff --git a/ml_pin/Settings.cs b/ml_pin/Settings.cs index 9826202..76b8682 100644 --- a/ml_pin/Settings.cs +++ b/ml_pin/Settings.cs @@ -1,195 +1,207 @@ -using ABI_RC.Core.InteractionSystem; -using System; -using System.Collections.Generic; - -namespace ml_pin -{ - static class Settings - { - internal class SettingEvent - { - event Action m_action; - public void AddHandler(Action p_listener) => m_action += p_listener; - public void RemoveHandler(Action p_listener) => m_action -= p_listener; - public void Invoke(T p_value) => m_action?.Invoke(p_value); - } - - public enum NotificationType - { - None = 0, - Friends, - All - }; - - enum ModSetting - { - NotifyType, - Volume, - NotifyInPublic, - NotifyInFriends, - NotifyInPrivate, - FriendsAlways - }; - - public static NotificationType NotifyType { get; private set; } = NotificationType.All; - public static float Volume { get; private set; } = 1.0f; - public static bool NotifyInPublic { get; private set; } = true; - public static bool NotifyInFriends { get; private set; } = true; - public static bool NotifyInPrivate { get; private set; } = true; - public static bool FriendsAlways { get; private set; } = false; - - static MelonLoader.MelonPreferences_Category ms_category = null; - static List ms_entries = null; - - public static readonly SettingEvent OnNotifyTypeChanged = new SettingEvent(); - public static readonly SettingEvent OnVolumeChanged = new SettingEvent(); - public static readonly SettingEvent OnNotifyInPublicChanged = new SettingEvent(); - public static readonly SettingEvent OnNotifyInFriendsChanged = new SettingEvent(); - public static readonly SettingEvent OnNotifyInPrivateChanged = new SettingEvent(); - public static readonly SettingEvent OnFriendsAlwaysChanged = new SettingEvent(); - - internal static void Init() - { - ms_category = MelonLoader.MelonPreferences.CreateCategory("PIN", null, true); - - ms_entries = new List() - { - ms_category.CreateEntry(ModSetting.NotifyType.ToString(), (int)NotifyType), - ms_category.CreateEntry(ModSetting.Volume.ToString(), (int)(Volume * 100f)), - ms_category.CreateEntry(ModSetting.NotifyInPublic.ToString(), NotifyInPublic), - ms_category.CreateEntry(ModSetting.NotifyInFriends.ToString(), NotifyInFriends), - ms_category.CreateEntry(ModSetting.NotifyInPrivate.ToString(), NotifyInPrivate), - ms_category.CreateEntry(ModSetting.FriendsAlways.ToString(), FriendsAlways), - }; - - NotifyType = (NotificationType)(int)ms_entries[(int)ModSetting.NotifyType].BoxedValue; - Volume = (int)ms_entries[(int)ModSetting.Volume].BoxedValue * 0.01f; - NotifyInPublic = (bool)ms_entries[(int)ModSetting.NotifyInPublic].BoxedValue; - NotifyInFriends = (bool)ms_entries[(int)ModSetting.NotifyInFriends].BoxedValue; - NotifyInPrivate = (bool)ms_entries[(int)ModSetting.NotifyInPrivate].BoxedValue; - FriendsAlways = (bool)ms_entries[(int)ModSetting.FriendsAlways].BoxedValue; - - MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); - } - - static System.Collections.IEnumerator WaitMainMenuUi() - { - while(ViewManager.Instance == null) - yield return null; - while(ViewManager.Instance.gameMenuView == null) - yield return null; - while(ViewManager.Instance.gameMenuView.Listener == null) - yield return null; - - ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => - { - ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action(OnToggleUpdate)); - ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action(OnSliderUpdate)); - ViewManager.Instance.gameMenuView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action(OnDropdownUpdate)); - }; - ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => - { - ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js")); - ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js")); - foreach(var l_entry in ms_entries) - ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); - }; - } - - 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.NotifyInPublic: - { - NotifyInPublic = l_value; - OnNotifyInPublicChanged.Invoke(NotifyInPublic); - } - break; - - case ModSetting.NotifyInFriends: - { - NotifyInFriends = l_value; - OnNotifyInFriendsChanged.Invoke(NotifyInFriends); - } - break; - - case ModSetting.NotifyInPrivate: - { - NotifyInPrivate = l_value; - OnNotifyInPrivateChanged.Invoke(NotifyInPrivate); - } - break; - - case ModSetting.FriendsAlways: - { - FriendsAlways = l_value; - OnFriendsAlwaysChanged.Invoke(FriendsAlways); - } - break; - } - - ms_entries[(int)l_setting].BoxedValue = l_value; - } - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnSliderUpdate(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.Volume: - { - Volume = l_value * 0.01f; - OnVolumeChanged.Invoke(Volume); - } - break; - } - - ms_entries[(int)l_setting].BoxedValue = l_value; - } - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - 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.NotifyType: - { - NotifyType = (NotificationType)l_value; - OnNotifyTypeChanged.Invoke(NotifyType); - } - break; - } - - ms_entries[(int)l_setting].BoxedValue = l_value; - } - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - } -} +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_pin +{ + static class Settings + { + internal class SettingEvent + { + event Action m_action; + public void AddHandler(Action p_listener) => m_action += p_listener; + public void RemoveHandler(Action p_listener) => m_action -= p_listener; + public void Invoke(T p_value) => m_action?.Invoke(p_value); + } + + public enum NotificationType + { + None = 0, + Friends, + All + }; + + enum ModSetting + { + NotifyType, + Volume, + Delay, + NotifyInPublic, + NotifyInFriends, + NotifyInPrivate, + FriendsAlways + }; + + public static NotificationType NotifyType { get; private set; } = NotificationType.All; + public static float Volume { get; private set; } = 1.0f; + public static bool Delay { get; private set; } = true; + public static bool NotifyInPublic { get; private set; } = true; + public static bool NotifyInFriends { get; private set; } = true; + public static bool NotifyInPrivate { get; private set; } = true; + public static bool FriendsAlways { get; private set; } = false; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + public static readonly SettingEvent OnNotifyTypeChanged = new SettingEvent(); + public static readonly SettingEvent OnVolumeChanged = new SettingEvent(); + public static readonly SettingEvent OnDelayChange = new SettingEvent(); + public static readonly SettingEvent OnNotifyInPublicChanged = new SettingEvent(); + public static readonly SettingEvent OnNotifyInFriendsChanged = new SettingEvent(); + public static readonly SettingEvent OnNotifyInPrivateChanged = new SettingEvent(); + public static readonly SettingEvent OnFriendsAlwaysChanged = new SettingEvent(); + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("PIN", null, true); + + ms_entries = new List() + { + ms_category.CreateEntry(ModSetting.NotifyType.ToString(), (int)NotifyType), + ms_category.CreateEntry(ModSetting.Volume.ToString(), (int)(Volume * 100f)), + ms_category.CreateEntry(ModSetting.Delay.ToString(), Delay), + ms_category.CreateEntry(ModSetting.NotifyInPublic.ToString(), NotifyInPublic), + ms_category.CreateEntry(ModSetting.NotifyInFriends.ToString(), NotifyInFriends), + ms_category.CreateEntry(ModSetting.NotifyInPrivate.ToString(), NotifyInPrivate), + ms_category.CreateEntry(ModSetting.FriendsAlways.ToString(), FriendsAlways), + }; + + NotifyType = (NotificationType)(int)ms_entries[(int)ModSetting.NotifyType].BoxedValue; + Volume = (int)ms_entries[(int)ModSetting.Volume].BoxedValue * 0.01f; + Delay = (bool)ms_entries[(int)ModSetting.Delay].BoxedValue; + NotifyInPublic = (bool)ms_entries[(int)ModSetting.NotifyInPublic].BoxedValue; + NotifyInFriends = (bool)ms_entries[(int)ModSetting.NotifyInFriends].BoxedValue; + NotifyInPrivate = (bool)ms_entries[(int)ModSetting.NotifyInPrivate].BoxedValue; + FriendsAlways = (bool)ms_entries[(int)ModSetting.FriendsAlways].BoxedValue; + + MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); + } + + static System.Collections.IEnumerator WaitMainMenuUi() + { + while(ViewManager.Instance == null) + yield return null; + while(ViewManager.Instance.gameMenuView == null) + yield return null; + while(ViewManager.Instance.gameMenuView.Listener == null) + yield return null; + + ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => + { + ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action(OnToggleUpdate)); + ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action(OnSliderUpdate)); + ViewManager.Instance.gameMenuView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action(OnDropdownUpdate)); + }; + ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => + { + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js")); + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js")); + foreach(var l_entry in ms_entries) + ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); + }; + } + + 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.Delay: + { + Delay = l_value; + OnDelayChange.Invoke(Delay); + } + break; + + case ModSetting.NotifyInPublic: + { + NotifyInPublic = l_value; + OnNotifyInPublicChanged.Invoke(NotifyInPublic); + } + break; + + case ModSetting.NotifyInFriends: + { + NotifyInFriends = l_value; + OnNotifyInFriendsChanged.Invoke(NotifyInFriends); + } + break; + + case ModSetting.NotifyInPrivate: + { + NotifyInPrivate = l_value; + OnNotifyInPrivateChanged.Invoke(NotifyInPrivate); + } + break; + + case ModSetting.FriendsAlways: + { + FriendsAlways = l_value; + OnFriendsAlwaysChanged.Invoke(FriendsAlways); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnSliderUpdate(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.Volume: + { + Volume = l_value * 0.01f; + OnVolumeChanged.Invoke(Volume); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + 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.NotifyType: + { + NotifyType = (NotificationType)l_value; + OnNotifyTypeChanged.Invoke(NotifyType); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_pin/SoundManager.cs b/ml_pin/SoundManager.cs index 417e04f..46faf6e 100644 --- a/ml_pin/SoundManager.cs +++ b/ml_pin/SoundManager.cs @@ -1,70 +1,99 @@ -using ABI_RC.Core.AudioEffects; -using System.Collections; -using System.IO; -using UnityEngine; -using UnityEngine.Networking; - -namespace ml_pin -{ - class SoundManager - { - public enum SoundType - { - PlayerJoin = 0, - PlayerLeave, - FriendJoin, - FriendLeave - } - - const string c_modName = "PlayersInstanceNotifier"; - - bool m_loaded = false; - readonly AudioClip[] m_clips = null; - - internal SoundManager() - { - m_clips = new AudioClip[4]; - for(int i = 0; i < 4; i++) - m_clips[i] = null; - } - public void LoadSounds() - { - if(!m_loaded) - { - MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.PlayerJoin, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "player_join.wav"))); - MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.PlayerLeave, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "player_leave.wav"))); - MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.FriendJoin, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "friend_join.wav"))); - MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.FriendLeave, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "friend_leave.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.isNetworkError || l_uwr.isHttpError) - { - 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(SoundType p_type) - { - if(m_loaded && (m_clips[(int)p_type] != null)) - InterfaceAudio.Instance.UserInterfaceAudio.PlayOneShot(m_clips[(int)p_type], Settings.Volume); - } - } -} +using ABI_RC.Core.AudioEffects; +using System; +using System.Collections; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace ml_pin +{ + class SoundManager + { + public enum SoundType + { + PlayerJoin = 0, + PlayerLeave, + FriendJoin, + FriendLeave + } + + const string c_modName = "PlayersInstanceNotifier"; + + bool m_loaded = false; + readonly AudioClip[] m_clips = null; + int[] m_clipDelays = null; + int[] m_playTicks = null; + + internal SoundManager() + { + m_clips = new AudioClip[4]; + for(int i = 0; i < 4; i++) + m_clips[i] = null; + + m_clipDelays = new int[4]; + m_clipDelays[(int)SoundType.PlayerJoin] = 708; + m_clipDelays[(int)SoundType.PlayerLeave] = 380; + m_clipDelays[(int)SoundType.FriendJoin] = 708; + m_clipDelays[(int)SoundType.FriendLeave] = 380; + + m_playTicks = new int[4]; + for(int i = 0; i < 4; i++) + m_playTicks[i] = 0; + } + public void LoadSounds() + { + if(!m_loaded) + { + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.PlayerJoin, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "player_join.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.PlayerLeave, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "player_leave.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.FriendJoin, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "friend_join.wav"))); + MelonLoader.MelonCoroutines.Start(LoadAudioClip(SoundType.FriendLeave, Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, c_modName, "friend_leave.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.isNetworkError || l_uwr.isHttpError) + { + 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; + m_clipDelays[(int)p_type] = (int)(l_clip.length * 1000f); + } + + public void PlaySound(SoundType p_type) + { + if(m_loaded && (m_clips[(int)p_type] != null)) + { + if(Settings.Delay) + { + int l_tick = Environment.TickCount; + if(l_tick - m_playTicks[(int)p_type] > m_clipDelays[(int)p_type]) + { + m_playTicks[(int)p_type] = l_tick; + InterfaceAudio.Instance.UserInterfaceAudio.PlayOneShot(m_clips[(int)p_type], Settings.Volume); + } + } + else + { + m_playTicks[(int)p_type] = Environment.TickCount; + InterfaceAudio.Instance.UserInterfaceAudio.PlayOneShot(m_clips[(int)p_type], Settings.Volume); + } + } + } + } +} diff --git a/ml_pin/ml_pin.csproj b/ml_pin/ml_pin.csproj index 02cd787..6cf02d1 100644 --- a/ml_pin/ml_pin.csproj +++ b/ml_pin/ml_pin.csproj @@ -7,7 +7,7 @@ SDraw None PlayersInstanceNotifier - 1.0.6 + 1.0.7 diff --git a/ml_pin/resources/mod_menu.js b/ml_pin/resources/mod_menu.js index 17de871..61aa058 100644 --- a/ml_pin/resources/mod_menu.js +++ b/ml_pin/resources/mod_menu.js @@ -1,64 +1,71 @@ -{ - let l_block = document.createElement('div'); - l_block.innerHTML = ` -
-
Players Instance Notifier
-
-
- -
-
Notify of:
-
-
-
-
- -
-
Mixed volume:
-
-
-
-
- -
-
Notify in public instances:
-
-
-
-
- -
-
Notify in friends instances:
-
-
-
-
- -
-
Notify in private instances:
-
-
-
-
- -
-
Always notify of friends:
-
-
-
-
- `; - document.getElementById('settings-audio').appendChild(l_block); - - // Toggles - for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) - modsExtension.addSetting('PIN', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_PIN')); - - // Sliders - for (let l_slider of l_block.querySelectorAll('.inp_slider')) - modsExtension.addSetting('PIN', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_PIN')); - - // Dropdowns - for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown')) - modsExtension.addSetting('PIN', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_PIN')); -} +{ + let l_block = document.createElement('div'); + l_block.innerHTML = ` +
+
Players Instance Notifier
+
+
+ +
+
Notify of:
+
+
+
+
+ +
+
Mixed volume:
+
+
+
+
+ +
+
Delay between notifications:
+
+
+
+
+ +
+
Notify in public instances:
+
+
+
+
+ +
+
Notify in friends instances:
+
+
+
+
+ +
+
Notify in private instances:
+
+
+
+
+ +
+
Always notify of friends:
+
+
+
+
+ `; + document.getElementById('settings-audio').appendChild(l_block); + + // Toggles + for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) + modsExtension.addSetting('PIN', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_PIN')); + + // Sliders + for (let l_slider of l_block.querySelectorAll('.inp_slider')) + modsExtension.addSetting('PIN', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_PIN')); + + // Dropdowns + for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown')) + modsExtension.addSetting('PIN', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_PIN')); +} diff --git a/ml_prm/Main.cs b/ml_prm/Main.cs index 6568e12..5ba3357 100644 --- a/ml_prm/Main.cs +++ b/ml_prm/Main.cs @@ -15,6 +15,7 @@ namespace ml_prm Settings.Init(); ModUi.Init(); GameEvents.Init(HarmonyInstance); + WorldHandler.Init(); // Whitelist the toggle script (typeof(SharedFilter).GetField("_localComponentWhitelist", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null) as HashSet)?.Add(typeof(RagdollToggle)); @@ -24,6 +25,8 @@ namespace ml_prm public override void OnDeinitializeMelon() { + WorldHandler.DeInit(); + if(m_localController != null) UnityEngine.Object.Destroy(m_localController); m_localController = null; diff --git a/ml_prm/Properties/AssemblyInfo.cs b/ml_prm/Properties/AssemblyInfo.cs index 36562ba..39c2454 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.1.6", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.1.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(2)] [assembly: MelonLoader.MelonOptionalDependencies("BTKUILib")] diff --git a/ml_prm/README.md b/ml_prm/README.md index 8fc4126..af7d4d0 100644 --- a/ml_prm/README.md +++ b/ml_prm/README.md @@ -45,6 +45,9 @@ Optional mod's settings in [UIExpansionKit](https://github.com/ddakebono/Chillou Available additional parameters for AAS animator: * **`Ragdolled`:** defines current ragdoll state; boolean. * Note: Can be set as local-only (not synced) if starts with `#` character. + +# World restriction +World creators can restrict ragdolling by creating empty game object with name `[RagdollRestriction]` anywhere in scene. # Unity Editor Script You can also trigger the ragdoll via animations on your avatar. To do this you need: diff --git a/ml_prm/RagdollController.cs b/ml_prm/RagdollController.cs index 66343ac..12e4b2c 100644 --- a/ml_prm/RagdollController.cs +++ b/ml_prm/RagdollController.cs @@ -213,7 +213,7 @@ namespace ml_prm if(m_downTime >= Settings.RecoverDelay) { SwitchRagdoll(); - m_downTime = float.MinValue; // One attepmt to recover + m_downTime = float.MinValue; // One attempt to recover } } @@ -363,13 +363,13 @@ namespace ml_prm m_rigidBodies.Add(l_body); l_body.isKinematic = true; l_body.angularDrag = Settings.AngularDrag; - l_body.drag = (Utils.IsWorldSafe() ? Settings.MovementDrag : 1f); + l_body.drag = (WorldHandler.IsSafeWorld() ? Settings.MovementDrag : 1f); l_body.useGravity = false; l_body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; l_body.gameObject.layer = LayerMask.NameToLayer("PlayerLocal"); GravityInfluencer l_gravInfluencer = l_body.gameObject.AddComponent(); - l_gravInfluencer.SetActiveGravity((!Utils.IsWorldSafe() || Settings.Gravity)); + l_gravInfluencer.SetActiveGravity((!WorldHandler.IsSafeWorld() || Settings.Gravity)); m_gravityInfluencers.Add(l_gravInfluencer); } @@ -398,7 +398,7 @@ namespace ml_prm if((l_body != null) && (l_collider != null) && (l_puppetTransforms[i] == m_puppetReferences.hips || l_puppetTransforms[i] == m_puppetReferences.spine || l_puppetTransforms[i] == m_puppetReferences.chest)) { PhysicsInfluencer l_physicsInfluencer = l_puppetTransforms[i].gameObject.AddComponent(); - l_physicsInfluencer.airDrag = (Utils.IsWorldSafe() ? Settings.MovementDrag : 1f); + l_physicsInfluencer.airDrag = (WorldHandler.IsSafeWorld() ? Settings.MovementDrag : 1f); l_physicsInfluencer.airAngularDrag = Settings.AngularDrag; l_physicsInfluencer.fluidDrag = 3f; l_physicsInfluencer.fluidAngularDrag = 1f; @@ -572,7 +572,7 @@ namespace ml_prm { if(m_avatarReady) { - float l_drag = (Utils.IsWorldSafe() ? p_value : 1f); + float l_drag = (WorldHandler.IsSafeWorld() ? p_value : 1f); foreach(Rigidbody l_body in m_rigidBodies) { l_body.drag = l_drag; @@ -601,7 +601,7 @@ namespace ml_prm { if(m_avatarReady) { - bool l_gravity = (!Utils.IsWorldSafe() || p_state); + bool l_gravity = (!WorldHandler.IsSafeWorld() || p_state); foreach(PhysicsInfluencer l_influencer in m_physicsInfluencers) l_influencer.enabled = l_gravity; foreach(GravityInfluencer l_influencer in m_gravityInfluencers) @@ -618,8 +618,8 @@ namespace ml_prm { if(m_physicsMaterial != null) { - bool l_slipperiness = (Settings.Slipperiness && Utils.IsWorldSafe()); - bool l_bounciness = (Settings.Bounciness && Utils.IsWorldSafe()); + bool l_slipperiness = (Settings.Slipperiness && WorldHandler.IsSafeWorld()); + bool l_bounciness = (Settings.Bounciness && WorldHandler.IsSafeWorld()); m_physicsMaterial.dynamicFriction = (l_slipperiness ? 0f : c_defaultFriction); m_physicsMaterial.staticFriction = (l_slipperiness ? 0f : c_defaultFriction); m_physicsMaterial.frictionCombine = (l_slipperiness ? PhysicMaterialCombine.Minimum : PhysicMaterialCombine.Average); @@ -631,7 +631,7 @@ namespace ml_prm { if(m_avatarReady) { - bool l_buoyancy = (!Utils.IsWorldSafe() || p_state); + bool l_buoyancy = (!WorldHandler.IsSafeWorld() || p_state); foreach(PhysicsInfluencer l_influencer in m_physicsInfluencers) l_influencer.enableInfluence = l_buoyancy; @@ -672,7 +672,7 @@ namespace ml_prm PlayerSetup.Instance.animatorManager.CancelEmote = true; m_ragdolledParameter.SetValue(true); - if(!Utils.IsWorldSafe()) + if(!WorldHandler.IsSafeWorld()) { m_reachedGround = false; // Force player to unragdoll and reach ground first m_groundedTime = 0f; @@ -683,8 +683,8 @@ namespace ml_prm foreach(Rigidbody l_body in m_rigidBodies) l_body.isKinematic = false; - Vector3 l_velocity = Vector3.ClampMagnitude(m_velocity * (Utils.IsWorldSafe() ? Settings.VelocityMultiplier : 1f), Utils.GetWorldMovementLimit()); - if(Settings.ViewVelocity && Utils.IsWorldSafe()) + Vector3 l_velocity = Vector3.ClampMagnitude(m_velocity * (WorldHandler.IsSafeWorld() ? Settings.VelocityMultiplier : 1f), WorldHandler.GetMovementLimit()); + if(Settings.ViewVelocity && WorldHandler.IsSafeWorld()) { float l_mag = l_velocity.magnitude; l_velocity = PlayerSetup.Instance.GetActiveCamera().transform.forward * l_mag; @@ -708,7 +708,7 @@ namespace ml_prm { BetterBetterCharacterController.Instance.TeleportPlayerTo(m_puppetReferences.hips.position, PlayerSetup.Instance.GetPlayerRotation().eulerAngles, false, false); TryRestoreMovement(); - if(!Utils.IsWorldSafe()) + if(!WorldHandler.IsSafeWorld()) { Vector3 l_vec = BetterBetterCharacterController.Instance.GetVelocity(); l_vec.y = Mathf.Clamp(l_vec.y, float.MinValue, 0f); @@ -775,6 +775,9 @@ namespace ml_prm bool CanRagdoll() { + if(WorldHandler.IsRestrictedWorld()) + return false; + bool l_result = m_reachedGround; l_result &= !BodySystem.isCalibrating; l_result &= !BetterBetterCharacterController.Instance.IsSitting(); diff --git a/ml_prm/Utils.cs b/ml_prm/Utils.cs index 52aacf9..0c8414d 100644 --- a/ml_prm/Utils.cs +++ b/ml_prm/Utils.cs @@ -16,21 +16,6 @@ namespace ml_prm static readonly FieldInfo ms_influencerTouchingVolumes = typeof(PhysicsInfluencer).GetField("_touchingVolumes", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_influencerSubmergedColliders = typeof(PhysicsInfluencer).GetField("_submergedColliders", BindingFlags.NonPublic | BindingFlags.Instance); - public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr); - public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying); - 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 void ClearFluidVolumes(this BetterBetterCharacterController p_instance) => (ms_touchingVolumes.GetValue(p_instance) as List)?.Clear(); public static void CopyGlobal(this Transform p_source, Transform p_target) diff --git a/ml_prm/WorldHandler.cs b/ml_prm/WorldHandler.cs new file mode 100644 index 0000000..bdf3b50 --- /dev/null +++ b/ml_prm/WorldHandler.cs @@ -0,0 +1,44 @@ +using ABI.CCK.Components; +using ABI_RC.Systems.GameEventSystem; +using UnityEngine; + +namespace ml_prm +{ + static class WorldHandler + { + static bool ms_safeWorld = true; + static bool ms_restrictedWorld = false; + static float ms_movementLimit = 1f; + + internal static void Init() + { + CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoad); + } + + internal static void DeInit() + { + CVRGameEventSystem.World.OnLoad.RemoveListener(OnWorldLoad); + } + + static void OnWorldLoad(string p_id) + { + ms_safeWorld = ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying); + ms_movementLimit = 1f; + + GameObject l_restrictObj = GameObject.Find("[RagdollRestriction]"); + ms_restrictedWorld = ((l_restrictObj == null) ? false : (l_restrictObj.scene.name != "DontDestroyOnLoad")); + + if(CVRWorld.Instance != null) + { + ms_movementLimit = CVRWorld.Instance.baseMovementSpeed; + ms_movementLimit *= CVRWorld.Instance.sprintMultiplier; + ms_movementLimit *= CVRWorld.Instance.inAirMovementMultiplier; + ms_movementLimit *= CVRWorld.Instance.flyMultiplier; + } + } + + public static bool IsSafeWorld() => ms_safeWorld; + public static bool IsRestrictedWorld() => ms_restrictedWorld; + public static float GetMovementLimit() => ms_movementLimit; + } +} diff --git a/ml_prm/ml_prm.csproj b/ml_prm/ml_prm.csproj index 8e67917..0dd2e62 100644 --- a/ml_prm/ml_prm.csproj +++ b/ml_prm/ml_prm.csproj @@ -4,7 +4,7 @@ netstandard2.1 x64 PlayerRagdollMod - 1.1.6 + 1.1.7 SDraw None PlayerRagdollMod