diff --git a/README.md b/README.md index 1cea0ac..20937c3 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,12 @@ Merged set of MelonLoader mods for ChilloutVR. | Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) | |:---------:|:----------:|:--------------:| :----------------------------------------------------------------| | [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.5 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes | +| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| :hourglass: On review | | [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.5 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes | | [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.9 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes | | [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes | | [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.1 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes | +| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| :hourglass: On review | | [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes | **Archived mods:** diff --git a/ml_amt/Scripts.cs b/ml_amt/ResourcesHandler.cs similarity index 86% rename from ml_amt/Scripts.cs rename to ml_amt/ResourcesHandler.cs index 243c0e6..7c1479c 100644 --- a/ml_amt/Scripts.cs +++ b/ml_amt/ResourcesHandler.cs @@ -4,9 +4,9 @@ using System.Reflection; namespace ml_amt { - static class Scripts + static class ResourcesHandler { - public static string GetEmbeddedScript(string p_name) + public static string GetEmbeddedResource(string p_name) { string l_result = ""; Assembly l_assembly = Assembly.GetExecutingAssembly(); diff --git a/ml_amt/Settings.cs b/ml_amt/Settings.cs index 0e2de51..0253865 100644 --- a/ml_amt/Settings.cs +++ b/ml_amt/Settings.cs @@ -73,8 +73,8 @@ namespace ml_amt }; ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => { - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); + 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()); }; diff --git a/ml_asl/Main.cs b/ml_asl/Main.cs new file mode 100644 index 0000000..66f77d0 --- /dev/null +++ b/ml_asl/Main.cs @@ -0,0 +1,25 @@ +using ABI_RC.Core.Player; +using System.Reflection; + +namespace ml_asl +{ + public class AvatarSyncedLook : MelonLoader.MelonMod + { + public override void OnInitializeMelon() + { + Settings.Init(); + + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(AvatarSyncedLook).GetMethod(nameof(OnPlayerAvatarMovementDataUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + } + + static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData) + { + if(Settings.Enabled && (__instance.eyeMovement != null)) + ____playerAvatarMovementData.EyeTrackingOverride = true; + } + } +} diff --git a/ml_asl/Properties/AssemblyInfo.cs b/ml_asl/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..641578e --- /dev/null +++ b/ml_asl/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.0.0", "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/ResourcesHandler.cs b/ml_asl/ResourcesHandler.cs new file mode 100644 index 0000000..77a0c35 --- /dev/null +++ b/ml_asl/ResourcesHandler.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Reflection; + +namespace ml_asl +{ + static class ResourcesHandler + { + public static string GetEmbeddedResource(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception) { } + + return l_result; + } + } +} diff --git a/ml_asl/Settings.cs b/ml_asl/Settings.cs new file mode 100644 index 0000000..251d8ae --- /dev/null +++ b/ml_asl/Settings.cs @@ -0,0 +1,75 @@ +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_asl +{ + static class Settings + { + public enum ModSetting + { + Enabled = 0 + } + + public static bool Enabled { get; private set; } = true; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + static public event Action EnabledChange; + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("ASL", null, true); + + ms_entries = new List() + { + ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled) + }; + + Enabled = (bool)ms_entries[(int)ModSetting.Enabled].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.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) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.Enabled: + { + Enabled = bool.Parse(p_value); + EnabledChange?.Invoke(Enabled); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); + } + } + } +} diff --git a/ml_asl/Utils.cs b/ml_asl/Utils.cs new file mode 100644 index 0000000..ad7ddb3 --- /dev/null +++ b/ml_asl/Utils.cs @@ -0,0 +1,13 @@ +using ABI_RC.Core.UI; +using System.Reflection; +using UnityEngine; + +namespace ml_asl +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); + + static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + } +} diff --git a/ml_asl/ml_asl.csproj b/ml_asl/ml_asl.csproj new file mode 100644 index 0000000..a80c681 --- /dev/null +++ b/ml_asl/ml_asl.csproj @@ -0,0 +1,56 @@ + + + + netstandard2.1 + x64 + AvatarSyncedLook + SDraw + None + AvatarSyncedLook + + + + + + + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + + + + + + + + + + + + + diff --git a/ml_asl/resources/mod_menu.js b/ml_asl/resources/mod_menu.js new file mode 100644 index 0000000..edcb0c3 --- /dev/null +++ b/ml_asl/resources/mod_menu.js @@ -0,0 +1,22 @@ +// Add own menu +{ + let l_block = document.createElement('div'); + l_block.innerHTML = ` +
+
Avatar Synced Look
+
+
+ +
+
Enabled:
+
+
+
+
+ `; + document.getElementById('settings-interaction').appendChild(l_block); + + // Toggles + for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) + modsExtension.addSetting('ASL', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_ASL')); +} diff --git a/ml_lme/Scripts.cs b/ml_lme/ResourcesHandler.cs similarity index 86% rename from ml_lme/Scripts.cs rename to ml_lme/ResourcesHandler.cs index 9f38c99..302657d 100644 --- a/ml_lme/Scripts.cs +++ b/ml_lme/ResourcesHandler.cs @@ -4,9 +4,9 @@ using System.Reflection; namespace ml_lme { - static class Scripts + static class ResourcesHandler { - public static string GetEmbeddedScript(string p_name) + public static string GetEmbeddedResource(string p_name) { string l_result = ""; Assembly l_assembly = Assembly.GetExecutingAssembly(); diff --git a/ml_lme/Settings.cs b/ml_lme/Settings.cs index 5a06b90..5c6b94c 100644 --- a/ml_lme/Settings.cs +++ b/ml_lme/Settings.cs @@ -121,8 +121,8 @@ namespace ml_lme }; ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => { - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); + 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()); }; diff --git a/ml_mods_cvr.sln b/ml_mods_cvr.sln index 8a8dfbb..35b3cf5 100644 --- a/ml_mods_cvr.sln +++ b/ml_mods_cvr.sln @@ -18,6 +18,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_vei", "ml_vei\ml_vei.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_prm", "ml_prm\ml_prm.csproj", "{C4C3F080-379F-49DB-ADC6-6328BE884AE3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_asl", "ml_asl\ml_asl.csproj", "{5B4EC6EF-541A-47D2-B602-915205590553}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_pin", "ml_pin\ml_pin.csproj", "{7E493C28-7202-46F8-9789-D6C6FF7E5241}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -44,6 +48,12 @@ Global {C4C3F080-379F-49DB-ADC6-6328BE884AE3}.Debug|x64.Build.0 = Debug|x64 {C4C3F080-379F-49DB-ADC6-6328BE884AE3}.Release|x64.ActiveCfg = Release|x64 {C4C3F080-379F-49DB-ADC6-6328BE884AE3}.Release|x64.Build.0 = Release|x64 + {5B4EC6EF-541A-47D2-B602-915205590553}.Debug|x64.ActiveCfg = Debug|x64 + {5B4EC6EF-541A-47D2-B602-915205590553}.Release|x64.ActiveCfg = Release|x64 + {5B4EC6EF-541A-47D2-B602-915205590553}.Release|x64.Build.0 = Release|x64 + {7E493C28-7202-46F8-9789-D6C6FF7E5241}.Debug|x64.ActiveCfg = Debug|x64 + {7E493C28-7202-46F8-9789-D6C6FF7E5241}.Release|x64.ActiveCfg = Release|x64 + {7E493C28-7202-46F8-9789-D6C6FF7E5241}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ml_pam/Scripts.cs b/ml_pam/ResourcesHandler.cs similarity index 86% rename from ml_pam/Scripts.cs rename to ml_pam/ResourcesHandler.cs index 5509d9e..748dac3 100644 --- a/ml_pam/Scripts.cs +++ b/ml_pam/ResourcesHandler.cs @@ -4,9 +4,9 @@ using System.Reflection; namespace ml_pam { - static class Scripts + static class ResourcesHandler { - public static string GetEmbeddedScript(string p_name) + public static string GetEmbeddedResources(string p_name) { string l_result = ""; Assembly l_assembly = Assembly.GetExecutingAssembly(); diff --git a/ml_pam/Settings.cs b/ml_pam/Settings.cs index c0ee537..ff841ac 100644 --- a/ml_pam/Settings.cs +++ b/ml_pam/Settings.cs @@ -70,8 +70,8 @@ namespace ml_pam }; ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => { - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResources("mods_extension.js")); + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResources("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()); }; diff --git a/ml_pin/Main.cs b/ml_pin/Main.cs new file mode 100644 index 0000000..17a7c51 --- /dev/null +++ b/ml_pin/Main.cs @@ -0,0 +1,117 @@ +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.Systems.GameEventSystem; +using System; +using System.Collections; + +namespace ml_pin +{ + public class PlayersInstanceNotifier : MelonLoader.MelonMod + { + SoundManager m_soundManager = null; + + public override void OnInitializeMelon() + { + Settings.Init(); + ResourcesHandler.ExtractAudioResources(); + + MelonLoader.MelonCoroutines.Start(WaitForInstances()); + } + + public override void OnDeinitializeMelon() + { + m_soundManager = null; + } + + IEnumerator WaitForInstances() + { + if(InterfaceAudio.Instance == null) + yield return null; + + m_soundManager = new SoundManager(); + m_soundManager.LoadSounds(); + + CVRGameEventSystem.Player.OnJoin.AddListener(OnPlayerJoin); + CVRGameEventSystem.Player.OnLeave.AddListener(OnPlayerLeave); + } + + void OnPlayerJoin(PlayerDescriptor p_player) + { + try + { + bool l_isFriend = Friends.FriendsWith(p_player.ownerId); + bool l_notify = true; + + switch(Settings.NotifyType) + { + case Settings.NotificationType.None: + l_notify = false; + break; + case Settings.NotificationType.Friends: + l_notify = (ShouldNotifyInCurrentInstance() && l_isFriend); + break; + case Settings.NotificationType.All: + l_notify = ShouldNotifyInCurrentInstance(); + break; + } + l_notify |= (Settings.FriendsAlways && l_isFriend); + + if(l_notify) + { + if(l_isFriend) + m_soundManager?.PlaySound(SoundManager.SoundType.FriendJoin); + else + m_soundManager?.PlaySound(SoundManager.SoundType.PlayerJoin); + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Warning(e); + } + } + void OnPlayerLeave(PlayerDescriptor p_player) + { + try + { + bool l_isFriend = Friends.FriendsWith(p_player.ownerId); + bool l_notify = true; + + switch(Settings.NotifyType) + { + case Settings.NotificationType.None: + l_notify = false; + break; + case Settings.NotificationType.Friends: + l_notify = (ShouldNotifyInCurrentInstance() && l_isFriend); + break; + case Settings.NotificationType.All: + l_notify = ShouldNotifyInCurrentInstance(); + break; + } + l_notify |= (Settings.FriendsAlways && l_isFriend); + + if(l_notify) + { + if(l_isFriend) + m_soundManager?.PlaySound(SoundManager.SoundType.FriendLeave); + else + m_soundManager?.PlaySound(SoundManager.SoundType.PlayerLeave); + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Warning(e); + } + } + + bool ShouldNotifyInCurrentInstance() + { + bool l_isInPublic = Settings.NotifyInPublic && MetaPort.Instance.CurrentInstancePrivacy.Contains("Public"); + bool l_isInFriends = Settings.NotifyInFriends && MetaPort.Instance.CurrentInstancePrivacy.Contains("Friends"); + bool l_isInPrivate = Settings.NotifyInPrivate && MetaPort.Instance.CurrentInstancePrivacy.Contains("invite"); + return (l_isInPublic || l_isInFriends || l_isInPrivate); + } + } +} diff --git a/ml_pin/Properties/AssemblyInfo.cs b/ml_pin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2f15188 --- /dev/null +++ b/ml_pin/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.0.0", "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/ResourcesHandler.cs b/ml_pin/ResourcesHandler.cs new file mode 100644 index 0000000..8a60dda --- /dev/null +++ b/ml_pin/ResourcesHandler.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace ml_pin +{ + static class ResourcesHandler + { + const string c_modName = "PlayersInstanceNotifier"; + + static readonly List ms_audioResources = new List() + { + "Chime.wav", + "DoorClose.wav" + }; + + public static void ExtractAudioResources() + { + 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); + + string l_filePath = Path.Combine(l_dirPath, "player_join.wav"); + if(!File.Exists(l_filePath)) + ExtractAudioFile(ms_audioResources[0], l_filePath); + + l_filePath = Path.Combine(l_dirPath, "player_leave.wav"); + if(!File.Exists(l_filePath)) + ExtractAudioFile(ms_audioResources[1], l_filePath); + + l_filePath = Path.Combine(l_dirPath, "friend_join.wav"); + if(!File.Exists(l_filePath)) + ExtractAudioFile(ms_audioResources[0], l_filePath); + + l_filePath = Path.Combine(l_dirPath, "friend_leave.wav"); + if(!File.Exists(l_filePath)) + ExtractAudioFile(ms_audioResources[1], l_filePath); + } + + static void ExtractAudioFile(string p_name, string p_path) + { + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + try + { + Stream l_resourceStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + 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."); + } + } + + public static string GetEmbeddedResource(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception) { } + + return l_result; + } + } +} diff --git a/ml_pin/Settings.cs b/ml_pin/Settings.cs new file mode 100644 index 0000000..d8a45f9 --- /dev/null +++ b/ml_pin/Settings.cs @@ -0,0 +1,166 @@ +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_pin +{ + static class Settings + { + 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; + + static public event Action NotifyTypeChange; + static public event Action VolumeChange; + static public event Action NotifyInPublicChange; + static public event Action NotifyInFriendsChange; + static public event Action NotifyInPrivateChange; + static public event Action FriendsAlwaysChange; + + 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) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.NotifyInPublic: + { + NotifyInPublic = bool.Parse(p_value); + NotifyInPublicChange?.Invoke(NotifyInPublic); + } + break; + + case ModSetting.NotifyInFriends: + { + NotifyInFriends = bool.Parse(p_value); + NotifyInFriendsChange?.Invoke(NotifyInFriends); + } + break; + + case ModSetting.NotifyInPrivate: + { + NotifyInPrivate = bool.Parse(p_value); + NotifyInPrivateChange?.Invoke(NotifyInPrivate); + } + break; + + case ModSetting.FriendsAlways: + { + FriendsAlways = bool.Parse(p_value); + FriendsAlwaysChange?.Invoke(FriendsAlways); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); + } + } + + static void OnSliderUpdate(string p_name, string p_value) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.Volume: + { + Volume = int.Parse(p_value) * 0.01f; + VolumeChange?.Invoke(Volume); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value); + } + } + + static void OnDropdownUpdate(string p_name, string p_value) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.NotifyType: + { + NotifyType = (NotificationType)int.Parse(p_value); + NotifyTypeChange?.Invoke(NotifyType); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value); + } + } + } +} diff --git a/ml_pin/SoundManager.cs b/ml_pin/SoundManager.cs new file mode 100644 index 0000000..417e04f --- /dev/null +++ b/ml_pin/SoundManager.cs @@ -0,0 +1,70 @@ +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); + } + } +} diff --git a/ml_pin/Utils.cs b/ml_pin/Utils.cs new file mode 100644 index 0000000..105f01a --- /dev/null +++ b/ml_pin/Utils.cs @@ -0,0 +1,12 @@ +using ABI_RC.Core.UI; +using System.Reflection; + +namespace ml_pin +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); + + static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + } +} diff --git a/ml_pin/ml_pin.csproj b/ml_pin/ml_pin.csproj new file mode 100644 index 0000000..3e5f704 --- /dev/null +++ b/ml_pin/ml_pin.csproj @@ -0,0 +1,75 @@ + + + + netstandard2.1 + x64 + PlayersInstanceNotifier + SDraw + None + PlayersInstanceNotifier + + + + + + + + + + + + + + + + + + + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AudioModule.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestModule.dll + false + + + + + + + + diff --git a/ml_pin/resources/Chime.wav b/ml_pin/resources/Chime.wav new file mode 100644 index 0000000..1692bab Binary files /dev/null and b/ml_pin/resources/Chime.wav differ diff --git a/ml_pin/resources/DoorClose.wav b/ml_pin/resources/DoorClose.wav new file mode 100644 index 0000000..2af0329 Binary files /dev/null and b/ml_pin/resources/DoorClose.wav differ diff --git a/ml_pin/resources/mod_menu.js b/ml_pin/resources/mod_menu.js new file mode 100644 index 0000000..17de871 --- /dev/null +++ b/ml_pin/resources/mod_menu.js @@ -0,0 +1,64 @@ +{ + 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')); +} diff --git a/ml_vei/Scripts.cs b/ml_vei/ResourcesHandler.cs similarity index 86% rename from ml_vei/Scripts.cs rename to ml_vei/ResourcesHandler.cs index 82ef9de..dc85942 100644 --- a/ml_vei/Scripts.cs +++ b/ml_vei/ResourcesHandler.cs @@ -4,9 +4,9 @@ using System.Reflection; namespace ml_vei { - static class Scripts + static class ResourcesHandler { - public static string GetEmbeddedScript(string p_name) + public static string GetEmbeddedResource(string p_name) { string l_result = ""; Assembly l_assembly = Assembly.GetExecutingAssembly(); diff --git a/ml_vei/Settings.cs b/ml_vei/Settings.cs index d8d2eea..72f9d6c2 100644 --- a/ml_vei/Settings.cs +++ b/ml_vei/Settings.cs @@ -64,8 +64,8 @@ namespace ml_vei }; ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => { - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); - ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); + 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()); }; diff --git a/ml_vei/ml_vei.csproj b/ml_vei/ml_vei.csproj index fe15f35..10674f2 100644 --- a/ml_vei/ml_vei.csproj +++ b/ml_vei/ml_vei.csproj @@ -26,9 +26,11 @@ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll