New mods: AvatarSyncedLook and PlayersInstanceNotifier

Minor changes
This commit is contained in:
SDraw 2023-12-24 11:53:57 +03:00
parent 72aeffb656
commit e13bb8af23
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
28 changed files with 841 additions and 16 deletions

View file

@ -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) | | 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 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 | | [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 | | [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 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 | | [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 | | [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes |
**Archived mods:** **Archived mods:**

View file

@ -4,9 +4,9 @@ using System.Reflection;
namespace ml_amt 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 = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();

View file

@ -73,8 +73,8 @@ namespace ml_amt
}; };
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{ {
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js"));
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js"));
foreach(var l_entry in ms_entries) foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
}; };

25
ml_asl/Main.cs Normal file
View file

@ -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;
}
}
}

View file

@ -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)]

View file

@ -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;
}
}
}

75
ml_asl/Settings.cs Normal file
View file

@ -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<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static public event Action<bool> EnabledChange;
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("ASL", null, true);
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
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<string, string>(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);
}
}
}
}

13
ml_asl/Utils.cs Normal file
View file

@ -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);
}
}

56
ml_asl/ml_asl.csproj Normal file
View file

@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms>
<PackageId>AvatarSyncedLook</PackageId>
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>AvatarSyncedLook</Product>
</PropertyGroup>
<ItemGroup>
<None Remove="resources\mod_menu.js" />
</ItemGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="cohtml.Net">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Cohtml.Runtime">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="MelonLoader">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
<EmbeddedResource Include="resources\mod_menu.js" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
</Target>
</Project>

View file

@ -0,0 +1,22 @@
// Add own menu
{
let l_block = document.createElement('div');
l_block.innerHTML = `
<div class ="settings-subcategory">
<div class ="subcategory-name">Avatar Synced Look</div>
<div class ="subcategory-description"></div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Enabled: </div>
<div class ="option-input">
<div id="Enabled" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
`;
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'));
}

View file

@ -4,9 +4,9 @@ using System.Reflection;
namespace ml_lme 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 = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();

View file

@ -121,8 +121,8 @@ namespace ml_lme
}; };
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{ {
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js"));
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js"));
foreach(var l_entry in ms_entries) foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
}; };

View file

@ -18,6 +18,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_vei", "ml_vei\ml_vei.csp
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_prm", "ml_prm\ml_prm.csproj", "{C4C3F080-379F-49DB-ADC6-6328BE884AE3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_prm", "ml_prm\ml_prm.csproj", "{C4C3F080-379F-49DB-ADC6-6328BE884AE3}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 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}.Debug|x64.Build.0 = Debug|x64
{C4C3F080-379F-49DB-ADC6-6328BE884AE3}.Release|x64.ActiveCfg = Release|x64 {C4C3F080-379F-49DB-ADC6-6328BE884AE3}.Release|x64.ActiveCfg = Release|x64
{C4C3F080-379F-49DB-ADC6-6328BE884AE3}.Release|x64.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -4,9 +4,9 @@ using System.Reflection;
namespace ml_pam 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 = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();

View file

@ -70,8 +70,8 @@ namespace ml_pam
}; };
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{ {
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResources("mods_extension.js"));
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResources("mod_menu.js"));
foreach(var l_entry in ms_entries) foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
}; };

117
ml_pin/Main.cs Normal file
View file

@ -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);
}
}
}

View file

@ -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)]

View file

@ -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<string> ms_audioResources = new List<string>()
{
"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;
}
}
}

166
ml_pin/Settings.cs Normal file
View file

@ -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<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static public event Action<NotificationType> NotifyTypeChange;
static public event Action<float> VolumeChange;
static public event Action<bool> NotifyInPublicChange;
static public event Action<bool> NotifyInFriendsChange;
static public event Action<bool> NotifyInPrivateChange;
static public event Action<bool> FriendsAlwaysChange;
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("PIN", null, true);
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
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<string, string>(OnToggleUpdate));
ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate));
ViewManager.Instance.gameMenuView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action<string, string>(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);
}
}
}
}

70
ml_pin/SoundManager.cs Normal file
View file

@ -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);
}
}
}

12
ml_pin/Utils.cs Normal file
View file

@ -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);
}
}

75
ml_pin/ml_pin.csproj Normal file
View file

@ -0,0 +1,75 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms>
<PackageId>PlayersInstanceNotifier</PackageId>
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>PlayersInstanceNotifier</Product>
</PropertyGroup>
<ItemGroup>
<None Remove="resources\Chime.wav" />
<None Remove="resources\DoorClose.wav" />
<None Remove="resources\mod_menu.js" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="resources\Chime.wav" />
<EmbeddedResource Include="resources\DoorClose.wav" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
<EmbeddedResource Include="resources\mod_menu.js" />
</ItemGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="cohtml.Net">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Cohtml.Runtime">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="MelonLoader">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
</Target>
</Project>

BIN
ml_pin/resources/Chime.wav Normal file

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,64 @@
{
let l_block = document.createElement('div');
l_block.innerHTML = `
<div class ="settings-subcategory">
<div class ="subcategory-name">Players Instance Notifier</div>
<div class ="subcategory-description"></div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Notify of: </div>
<div class ="option-input">
<div id="NotifyType" class ="inp_dropdown no-scroll" data-options="0:None,1:Friends,2:All" data-current="2"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Mixed volume: </div>
<div class ="option-input">
<div id="Volume" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="100"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Notify in public instances: </div>
<div class ="option-input">
<div id="NotifyInPublic" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Notify in friends instances: </div>
<div class ="option-input">
<div id="NotifyInFriends" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Notify in private instances: </div>
<div class ="option-input">
<div id="NotifyInPrivate" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Always notify of friends: </div>
<div class ="option-input">
<div id="FriendsAlways" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
`;
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'));
}

View file

@ -4,9 +4,9 @@ using System.Reflection;
namespace ml_vei 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 = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();

View file

@ -64,8 +64,8 @@ namespace ml_vei
}; };
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{ {
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js"));
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js"));
foreach(var l_entry in ms_entries) foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
}; };

View file

@ -26,9 +26,11 @@
</Reference> </Reference>
<Reference Include="cohtml.Net"> <Reference Include="cohtml.Net">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
<Private>false</Private>
</Reference> </Reference>
<Reference Include="Cohtml.Runtime"> <Reference Include="Cohtml.Runtime">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
<Private>false</Private>
</Reference> </Reference>
<Reference Include="MelonLoader"> <Reference Include="MelonLoader">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>