mass commit of laziness

This commit is contained in:
NotAKidoS 2025-12-28 20:30:00 -06:00
parent ce992c70ee
commit 6d4fc549d9
167 changed files with 5471 additions and 675 deletions

View file

@ -1,4 +1,4 @@
using MelonLoader; using MelonLoader;
using NAK.AvatarQueueSystemTweaks.Patches; using NAK.AvatarQueueSystemTweaks.Patches;
namespace NAK.AvatarQueueSystemTweaks; namespace NAK.AvatarQueueSystemTweaks;

View file

@ -1,4 +1,3 @@
using System.Collections;
using ABI_RC.Core; using ABI_RC.Core;
using ABI_RC.Core.IO; using ABI_RC.Core.IO;
using ABI_RC.Core.Networking.IO.Social; using ABI_RC.Core.Networking.IO.Social;

View file

@ -1,4 +1,4 @@
using MelonLoader; using MelonLoader;
using NAK.AvatarQueueSystemTweaks.Properties; using NAK.AvatarQueueSystemTweaks.Properties;
using System.Reflection; using System.Reflection;

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk" />

View file

@ -0,0 +1,109 @@
using System.Reflection;
using ABI_RC.Core.Networking.API.Responses;
using ABI_RC.Core.Util.AssetFiltering;
using HarmonyLib;
using MelonLoader;
using UnityEngine;
namespace NAK.BufferParticleFixer;
public class BufferParticleFixerMod : MelonMod
{
private static MelonLogger.Instance Logger;
private static readonly MelonPreferences_Category Category =
MelonPreferences.CreateCategory(nameof(BufferParticleFixer));
private static readonly MelonPreferences_Entry<bool> EntryFixBufferParticles =
Category.CreateEntry(
identifier: "fix_buffer_particles",
true,
display_name: "Fix Buffer Particles",
description: "Should the mod attempt to fix buffer particles by modifying their lifetime and sub-emitter settings?");
public override void OnInitializeMelon()
{
Logger = LoggerInstance;
HarmonyInstance.Patch(
typeof(SharedFilter).GetMethod(nameof(SharedFilter.ProcessParticleComponent),
BindingFlags.Public | BindingFlags.Static),
postfix: new HarmonyMethod(typeof(BufferParticleFixerMod).GetMethod(nameof(OnProcessParticleComponent),
BindingFlags.NonPublic | BindingFlags.Static))
);
}
private static void OnProcessParticleComponent(
string collectionId,
Component particleComponent,
bool physicsCollision,
CompatibilityVersions compatibilityVersion)
{
if (particleComponent is not ParticleSystem particleSystem)
return;
if (!EntryFixBufferParticles.Value)
return;
// Logger.Msg($"Processing particle system on collection '{collectionId}'.");
if (!IsLikelyBufferParticle(particleSystem))
return;
Logger.Msg($"Detected likely buffer particle system '{particleSystem.name}' on collection '{collectionId}'. Applying fix...");
// Set start lifetime to 1000
// All sub-emitters to "Spawn on Birth"
// https://x.com/hfcRedddd/status/1696914565919813679
ParticleSystem.MainModule mainModule = particleSystem.main;
mainModule.startLifetime = 1f;
for (int i = 0; i < particleSystem.subEmitters.subEmittersCount; i++)
{
ParticleSystem subEmitter = particleSystem.subEmitters.GetSubEmitterSystem(i);
if (subEmitter) particleSystem.subEmitters.SetSubEmitterType(i, ParticleSystemSubEmitterType.Birth);
}
}
// https://x.com/hfcRedddd/status/1696913727415537807
private static bool IsLikelyBufferParticle(ParticleSystem ps)
{
// Check if the sub-emitters are children of the particle system
Transform psTransform = ps.transform;
bool hasSubEmitterNotChild = false;
ParticleSystem.SubEmittersModule subEmitters = ps.subEmitters;
int subEmitterCount = subEmitters.subEmittersCount;
for (int i = 0; i < subEmitterCount; i++)
{
ParticleSystem subEmitter = subEmitters.GetSubEmitterSystem(i);
// Skip null sub-emitters
if (!subEmitter)
{
Logger.Warning($"Particle system '{ps.name}' has a null sub-emitter at index {i}.");
continue;
}
// If any sub-emitter is not a child of the particle system, it's likely a buffer particle.
// This setup is also what shits into our logs...
if (!subEmitter.transform.IsChildOf(psTransform))
hasSubEmitterNotChild = true;
}
if (hasSubEmitterNotChild)
{
// Buffer particles have very short lifetimes
if (!(ps.main.startLifetime.constant > 0.05f))
return true;
Logger.Msg($"A potential buffer particle system '{ps.name}' has a start lifetime of {ps.main.startLifetime.constant}, which is longer than expected.");
}
return false;
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.BufferParticleFixer.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.BufferParticleFixer))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.BufferParticleFixer))]
[assembly: MelonInfo(
typeof(NAK.BufferParticleFixer.BufferParticleFixerMod),
nameof(NAK.BufferParticleFixer),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/BufferParticleFixer"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.BufferParticleFixer.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,14 @@
# SearchWithSpacesFix
Fixes search terms that use spaces.
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": -1,
"name": "SearchWithSpacesFix",
"modversion": "1.0.0",
"gameversion": "2024r177",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Fixes search terms that include spaces.",
"searchtags": [
"search",
"spaces",
"fix",
"meow"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r42/SearchWithSpacesFix.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SearchWithSpacesFix/",
"changelog": "- Initial release",
"embedcolor": "#f61963"
}

View file

@ -17,7 +17,7 @@ using System.Reflection;
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CVRGizmos" downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CVRGizmos"
)] )]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] [assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>PlayerCloneAttachment</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\playercloneattachment.assets" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,92 @@
using System.Reflection;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.UI.UIRework.Managers;
using ABI_RC.Systems.ChatBox;
using ABI_RC.Systems.InputManagement;
using HarmonyLib;
using MelonLoader;
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
namespace NAK.ChatBoxTweaks;
public class ChatBoxTweaksMod : MelonMod
{
public override void OnInitializeMelon()
{
HarmonyInstance.Patch(
typeof(KeyboardManager).GetMethod(nameof(KeyboardManager.OnKeyboardSubmit),
BindingFlags.NonPublic | BindingFlags.Instance),
prefix: new HarmonyMethod(typeof(ChatBoxTweaksMod).GetMethod(nameof(OnPreKeyboardManagerKeyboardSubmit),
BindingFlags.NonPublic | BindingFlags.Static)),
postfix: new HarmonyMethod(typeof(ChatBoxTweaksMod).GetMethod(nameof(OnPostKeyboardManagerKeyboardSubmit),
BindingFlags.NonPublic | BindingFlags.Static))
);
}
private static void OnPreKeyboardManagerKeyboardSubmit(ref KeyboardManager __instance, ref KeyboardManager.OpenSource? __state)
{
__state = __instance.KeyboardOpenSource;
}
private static void OnPostKeyboardManagerKeyboardSubmit(ref KeyboardManager.OpenSource? __state)
{
if (__state == KeyboardManager.OpenSource.TextComms) ChatBoxAPI.OpenKeyboard();
}
}
/*
public static class NetworkLoopInjector
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void InjectNetworkFixedUpdate()
{
var playerLoop = PlayerLoop.GetCurrentPlayerLoop();
// Find the FixedUpdate phase
int fixedUpdateIndex = Array.FindIndex(playerLoop.subSystemList, s => s.type == typeof(FixedUpdate));
if (fixedUpdateIndex < 0)
{
Debug.LogError("FixedUpdate not found in player loop!");
return;
}
var fixedUpdate = playerLoop.subSystemList[fixedUpdateIndex];
// Create your custom PlayerLoopSystem
var networkSystem = new PlayerLoopSystem
{
type = typeof(NetworkFixedUpdate),
updateDelegate = NetworkFixedUpdate.Run
};
// Insert at the start so it runs before physics, animation, etc.
var subs = fixedUpdate.subSystemList.ToList();
subs.Insert(0, networkSystem);
fixedUpdate.subSystemList = subs.ToArray();
// Reassign and set back
playerLoop.subSystemList[fixedUpdateIndex] = fixedUpdate;
PlayerLoop.SetPlayerLoop(playerLoop);
Debug.Log("[NetworkLoopInjector] Inserted NetworkFixedUpdate at start of FixedUpdate loop");
}
static class NetworkFixedUpdate
{
static int lastStateFrame = -1;
public static void Run()
{
// Apply your networked object state syncs before physics simulation
Debug.Log("Last State Frame: " + lastStateFrame + " Current Frame: " + Time.frameCount);
lastStateFrame = Time.frameCount;
}
}
}
*/

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.ChatBoxTweaks.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.ChatBoxTweaks))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.ChatBoxTweaks))]
[assembly: MelonInfo(
typeof(NAK.ChatBoxTweaks.ChatBoxTweaksMod),
nameof(NAK.ChatBoxTweaks),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ChatBoxTweaks"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.ChatBoxTweaks.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,22 @@
# YouAreMyPropNowWeAreHavingSoftTacosLater
Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings.
https://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO
There is special logic in place for bringing air vehicles through world loads.
If above the ground you will be placed up to 20m above the spawnpoint of the next world.
## Examples
https://fixupx.com/NotAKidoS/status/1910545346922422675
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": 262,
"name": "YouAreMyPropNowWeAreHavingSoftTacosLater",
"modversion": "1.0.0",
"gameversion": "2025r180",
"loaderversion": "0.7.2",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings.\nhttps://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO\n\nThere is special logic in place for bringing air vehicles through world loads. If above the ground you will be placed up to 20m above the spawnpoint of the next world.",
"searchtags": [
"prop",
"spawn",
"friend",
"load"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/YouAreMyPropNowWeAreHavingSoftTacosLater.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater/",
"changelog": "- Initial release",
"embedcolor": "#f61963"
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>YouAreMineNow</RootNamespace>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,236 @@
using System.Reflection;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Util;
using ABI_RC.Systems.Communications;
using ABI_RC.Systems.GameEventSystem;
using ABI_RC.Systems.RuntimeDebug;
using HarmonyLib;
using MelonLoader;
using UnityEngine;
namespace NAK.YouAreMyPropNowWeAreHavingSoftTacosLater;
public class ChatBubblesMod : MelonMod
{
#region Melon Preferences
private static readonly MelonPreferences_Category Category =
MelonPreferences.CreateCategory(nameof(YouAreMyPropNowWeAreHavingSoftTacosLater));
private static readonly MelonPreferences_Entry<KeyCode> EntryChatBubblesKey =
Category.CreateEntry("keyboard_bind", KeyCode.Y, display_name: "Chat Bubbles Key",
description: "Key to open chat bubble input.");
private static readonly MelonPreferences_Entry<float> EntryBubbleDuration =
Category.CreateEntry("bubble_duration", 5.0f, display_name: "Bubble Duration",
description: "Base duration in seconds for chat bubbles to stay visible.");
private static readonly MelonPreferences_Entry<float> EntryDurationPerChar =
Category.CreateEntry("duration_per_char", 0.05f, display_name: "Duration Per Character",
description: "Additional duration per character in the message.");
private static readonly MelonPreferences_Entry<int> EntryPoolSize =
Category.CreateEntry("pool_size", 20, display_name: "Pool Size",
description: "Maximum number of chat bubbles that can be displayed at once.");
private static readonly MelonPreferences_Entry<Color> EntryBubbleColor =
Category.CreateEntry("bubble_color", new Color(1.0f, 1.0f, 1.0f, 1.0f), display_name: "Bubble Color",
description: "Color of chat bubble text.");
#endregion Melon Preferences
#region Object Pool
private class ChatBubble
{
public CVRPlayerEntity Player { get; set; }
public string Message { get; set; }
public float Duration { get; set; }
public float Timer { get; set; }
public bool IsActive { get; set; }
public void Reset()
{
Player = null;
Message = string.Empty;
Duration = 0f;
Timer = 0f;
IsActive = false;
}
}
private readonly List<ChatBubble> _chatBubblePool = [];
private readonly List<ChatBubble> _activeBubbles = [];
private void InitializePool()
{
MelonLogger.Msg("Initializing chat bubble pool with size: " + EntryPoolSize.Value);
_chatBubblePool.Clear();
for (int i = 0; i < EntryPoolSize.Value; i++)
{
_chatBubblePool.Add(new ChatBubble());
}
}
private ChatBubble GetBubbleFromPool()
{
// First try to find an inactive bubble
foreach (var bubble in _chatBubblePool)
{
if (!bubble.IsActive)
{
bubble.Reset();
bubble.IsActive = true;
_activeBubbles.Add(bubble);
return bubble;
}
}
// If all bubbles are active, reuse the oldest one
if (_activeBubbles.Count > 0)
{
var oldestBubble = _activeBubbles[0];
_activeBubbles.RemoveAt(0);
oldestBubble.Reset();
oldestBubble.IsActive = true;
_activeBubbles.Add(oldestBubble);
return oldestBubble;
}
// This should never happen if the pool is initialized properly
MelonLogger.Warning("No chat bubbles available in pool!");
return null;
}
private void ReturnBubbleToPool(ChatBubble bubble)
{
bubble.IsActive = false;
_activeBubbles.Remove(bubble);
}
#endregion Object Pool
#region Melon Events
public override void OnInitializeMelon()
{
MelonLogger.Msg("Initializing Chat Bubbles Mod");
InitializePool();
CVRGameEventSystem.Communications.TextChat.Local.OnMessageReceived.AddListener(OnLocalMessageReceived);
CVRGameEventSystem.Communications.TextChat.Direct.OnMessageReceived.AddListener(OnGlobalMessageReceived);
CVRGameEventSystem.Communications.TextChat.Global.OnMessageReceived.AddListener(OnDirectMessageReceived);
HarmonyInstance.Patch(
typeof(ViewManager).GetMethod(nameof(ViewManager.SendToWorldUi),
BindingFlags.NonPublic | BindingFlags.Instance),
postfix: new HarmonyMethod(typeof(ChatBubblesMod).GetMethod(nameof(OnViewManagerSendToWorldUi),
BindingFlags.NonPublic | BindingFlags.Static))
);
MelonLogger.Msg("Chat Bubbles Mod initialized successfully");
}
public override void OnUpdate()
{
// Process key input to open chat
if (Input.GetKeyDown(EntryChatBubblesKey.Value))
{
if (!CVRSyncHelper.IsConnectedToGameNetwork())
return;
IsInKeyboardToWriteANiceMessage = true;
ViewManager.Instance.openMenuKeyboard(string.Empty);
}
// Update and render active chat bubbles
UpdateChatBubbles();
}
private void UpdateChatBubbles()
{
// Make a copy of the list to avoid issues when modifying during iteration
var bubblesToUpdate = new List<ChatBubble>(_activeBubbles);
foreach (var bubble in bubblesToUpdate)
{
if (bubble.Player == null || bubble.Player.PuppetMaster == null)
{
ReturnBubbleToPool(bubble);
continue;
}
bubble.Timer += Time.deltaTime;
if (bubble.Timer >= bubble.Duration)
{
ReturnBubbleToPool(bubble);
continue;
}
// Draw the bubble text at the player's nameplate position
var nameplatePosition = bubble.Player.PuppetMaster.GetNamePlateWorldPosition();
// Offset slightly above the nameplate
nameplatePosition.y += 0.2f;
RuntimeGizmos.DrawText(nameplatePosition, bubble.Message, 5, EntryBubbleColor.Value);
}
}
#endregion Melon Events
#region Game Events
public static bool IsInKeyboardToWriteANiceMessage { get; set; }
private static void OnViewManagerSendToWorldUi(string value)
{
if (!IsInKeyboardToWriteANiceMessage) return;
IsInKeyboardToWriteANiceMessage = false;
Comms_Manager.SendLocalTextMessage(value);
Comms_Manager.SendDirectTextMessage(CVRPlayerManager.Instance.NetworkPlayers[0].Uuid, value);
Comms_Manager.SendGlobalTextMessage(value);
MelonLogger.Msg("Sending message: " + value);
}
private void OnLocalMessageReceived(CVRPlayerEntity player, string message)
{
CreateChatBubble(player, message);
MelonLogger.Msg("OnLocalMessageReceived - Player: " + player?.Username + ", Message: " + message);
}
private void OnDirectMessageReceived(CVRPlayerEntity player, string message)
{
CreateChatBubble(player, message);
MelonLogger.Msg("OnDirectMessageReceived - Player: " + player?.Username + ", Message: " + message);
}
private void OnGlobalMessageReceived(CVRPlayerEntity player, string message)
{
CreateChatBubble(player, message);
MelonLogger.Msg("OnGlobalMessageReceived - Player: " + player?.Username + ", Message: " + message);
}
private void CreateChatBubble(CVRPlayerEntity player, string message)
{
if (player == null || player.PuppetMaster == null)
return;
// Calculate duration based on message length
float baseDuration = EntryBubbleDuration.Value;
float charDuration = EntryDurationPerChar.Value * message.Length;
float totalDuration = Mathf.Clamp(baseDuration + charDuration, baseDuration, 15f);
ChatBubble bubble = GetBubbleFromPool();
if (bubble == null) return;
bubble.Player = player;
bubble.Message = message;
bubble.Duration = totalDuration;
bubble.Timer = 0f;
}
#endregion Game Events
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.YouAreMyPropNowWeAreHavingSoftTacosLater.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater))]
[assembly: MelonInfo(
typeof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater.ChatBubblesMod),
nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ChatBubbles"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.YouAreMyPropNowWeAreHavingSoftTacosLater.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,19 @@
# YouAreMyPropNowWeAreHavingSoftTacosLater
Lets you bring held & attached props through world loads.
https://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO
## Examples
https://fixupx.com/NotAKidoS/status/1910545346922422675
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": -1,
"name": "YouAreMyPropNowWeAreHavingSoftTacosLater",
"modversion": "1.0.0",
"gameversion": "2025r179",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Lets you bring held & attached props through world loads.\nhttps://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO",
"searchtags": [
"prop",
"spawn",
"friend",
"load"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/YouAreMyPropNowWeAreHavingSoftTacosLater.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater/",
"changelog": "- Initial Release",
"embedcolor": "#00FFFF"
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>PlayerCloneAttachment</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\playercloneattachment.assets" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,169 @@
using ABI_RC.Core;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.FaceTracking;
using ABI_RC.Systems.InputManagement;
using MelonLoader;
using UnityEngine;
namespace NAK.ControlToUnlockEyes;
public class ControlToUnlockEyesMod : MelonMod
{
public override void OnInitializeMelon()
{
HarmonyInstance.Patch(
typeof(FaceTrackingManager).GetMethod(nameof(FaceTrackingManager.RegisterBuiltinModules),
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance),
postfix: new HarmonyLib.HarmonyMethod(typeof(ControlToUnlockEyesMod).GetMethod(nameof(OnPostFaceTrackingManagerRegisterBuiltinModules),
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static))
);
}
private static void OnPostFaceTrackingManagerRegisterBuiltinModules(FaceTrackingManager __instance)
=> __instance.RegisterEyeModule(new DefaultEyeModule());
public class DefaultEyeModule : IEyeTrackingModule
{
private const float FixedDistance = 10f;
private bool _useFixedDistance = true;
private readonly EyeTrackingData _eyeTrackingData = new();
private bool _dataAvailable;
private bool _running;
private ControllerRay _activeRay;
private Transform _rayDirectionTransform;
private CVRHand lastInteractHand = CVRHand.Right;
public bool Start(bool vr)
{
_running = true;
return true;
}
public void Stop()
{
_running = false;
}
public void Update()
{
if (!PlayerSetup.Instance)
return;
UpdateLastInteractHand();
UpdateFakedEyeTrackingData();
}
private void UpdateLastInteractHand()
{
ControllerRay leftRay = PlayerSetup.Instance.vrRayLeft;
ControllerRay rightRay = PlayerSetup.Instance.vrRayRight;
if (!MetaPort.Instance.isUsingVr)
{
_activeRay = PlayerSetup.Instance.desktopRay;
_rayDirectionTransform = _activeRay.rayDirectionTransform;
return;
}
bool leftAvailable = IsHandAvailable(leftRay, CVRHand.Left);
bool rightAvailable = IsHandAvailable(rightRay, CVRHand.Right);
if (CVRInputManager.Instance.interactLeftDown && leftAvailable)
lastInteractHand = CVRHand.Left;
else if (CVRInputManager.Instance.interactRightDown && rightAvailable)
lastInteractHand = CVRHand.Right;
_activeRay = GetLastInteractRay();
_rayDirectionTransform = _activeRay.rayDirectionTransform;
}
private void UpdateFakedEyeTrackingData()
{
_dataAvailable = _activeRay.CanSelectPlayersAndProps();
if (!_dataAvailable)
return;
_eyeTrackingData.blinking = false;
Transform ourCameraTransform = PlayerSetup.Instance.activeCam.transform;
Vector3 rayForward = _rayDirectionTransform.forward;
float rayDistance = _useFixedDistance ? FixedDistance : _activeRay.Hit.distance;
// TODO: dot product check to flip direction if behind camera
// Convert to camera-local *direction* (normalized) and multiply by selected distance so the gazePoint
// is on a sphere around the camera rather than mapped to a "square".
Vector3 localDir = ourCameraTransform.InverseTransformDirection(rayForward).normalized;
Vector3 localGazePoint = localDir * rayDistance;
_eyeTrackingData.gazePoint = localGazePoint;
}
private ControllerRay GetLastInteractRay()
{
if (!MetaPort.Instance.isUsingVr)
return PlayerSetup.Instance.desktopRay;
ControllerRay leftRay = PlayerSetup.Instance.vrRayLeft;
ControllerRay rightRay = PlayerSetup.Instance.vrRayRight;
if (lastInteractHand == CVRHand.Left && IsHandAvailable(leftRay, CVRHand.Left))
return leftRay;
if (lastInteractHand == CVRHand.Right && IsHandAvailable(rightRay, CVRHand.Right))
return rightRay;
return GetBestAvailableHand();
}
private ControllerRay GetBestAvailableHand()
{
if (!MetaPort.Instance.isUsingVr)
return PlayerSetup.Instance.desktopRay;
ControllerRay leftRay = PlayerSetup.Instance.vrRayLeft;
ControllerRay rightRay = PlayerSetup.Instance.vrRayRight;
bool leftAvailable = IsHandAvailable(leftRay, CVRHand.Left);
bool rightAvailable = IsHandAvailable(rightRay, CVRHand.Right);
if (CVRInputManager.Instance.interactLeftDown && leftAvailable)
return leftRay;
if (CVRInputManager.Instance.interactRightDown && rightAvailable)
return rightRay;
if (lastInteractHand == CVRHand.Left && leftAvailable)
return leftRay;
if (lastInteractHand == CVRHand.Right && rightAvailable)
return rightRay;
if (rightAvailable) return rightRay;
if (leftAvailable) return leftRay;
return rightRay;
}
private static bool IsHandAvailable(ControllerRay ray, CVRHand hand)
{
if (ray.grabbedObject)
return false;
if (CVR_MenuManager.Instance.IsViewShown &&
CVR_MenuManager.Instance.SelectedQuickMenuHand == hand)
return false;
return true;
}
public bool IsRunning() => _running;
public bool IsDataAvailable() => _dataAvailable;
public EyeTrackingData GetTrackingData() => _eyeTrackingData;
public string GetModuleName() => "None";
public string GetModuleShortName() => "None";
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.ControlToUnlockEyes.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.ControlToUnlockEyes))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.ControlToUnlockEyes))]
[assembly: MelonInfo(
typeof(NAK.ControlToUnlockEyes.ControlToUnlockEyesMod),
nameof(NAK.ControlToUnlockEyes),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ControlToUnlockEyes"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.ControlToUnlockEyes.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,22 @@
# YouAreMyPropNowWeAreHavingSoftTacosLater
Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings.
https://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO
There is special logic in place for bringing air vehicles through world loads.
If above the ground you will be placed up to 20m above the spawnpoint of the next world.
## Examples
https://fixupx.com/NotAKidoS/status/1910545346922422675
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": 262,
"name": "YouAreMyPropNowWeAreHavingSoftTacosLater",
"modversion": "1.0.0",
"gameversion": "2025r180",
"loaderversion": "0.7.2",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings.\nhttps://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO\n\nThere is special logic in place for bringing air vehicles through world loads. If above the ground you will be placed up to 20m above the spawnpoint of the next world.",
"searchtags": [
"prop",
"spawn",
"friend",
"load"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/YouAreMyPropNowWeAreHavingSoftTacosLater.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater/",
"changelog": "- Initial release",
"embedcolor": "#f61963"
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>YouAreMineNow</RootNamespace>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,228 @@
using ABI_RC.Core.Util.AssetFiltering;
using System.Reflection;
using HarmonyLib;
using MelonLoader;
using UnityEngine;
namespace NAK.FuckCameras;
public class FuckCamerasMod : MelonMod
{
public override void OnInitializeMelon()
{
HarmonyInstance.Patch(
typeof(SharedFilter).GetMethod(nameof(SharedFilter.ProcessCamera),
BindingFlags.Public | BindingFlags.Static),
postfix: new HarmonyMethod(typeof(FuckCamerasMod).GetMethod(nameof(OnProcessCamera),
BindingFlags.NonPublic | BindingFlags.Static))
);
}
private static void OnProcessCamera(string collectionId, Camera camera)
=> camera.gameObject.AddComponent<FuckCameraComponent>();
public class FuckCameraComponent : MonoBehaviour
{
private Camera _cam;
private int _originalMask;
private Camera _pooledCam;
private bool fuck;
private void Awake()
{
_originalMask = _cam.cullingMask;
_cam.Reset();
// _cam.cullingMask = _originalMask;
}
/*private void OnPreCull()
{
MelonLogger.Msg("PreCull");
if (!TryGetComponent(out _cam)) return;
if (fuck)
{
// unset default layer
_originalMask &= ~(1 << 0);
_cam.cullingMask = _originalMask;
Destroy(this);
return;
}
_originalMask = _cam.cullingMask;
_cam.cullingMask = 0;
fuck = true;
}*/
/*private IEnumerator OnPostRender()
{
MelonLogger.Msg("PostRender");
// Restore the original mask if it has not changed since we set it to 0
if (_cam.cullingMask == 0) _cam.cullingMask = _originalMask;
_cam.enabled = false;
yield return new WaitForEndOfFrame();
_cam.enabled = true;
MelonLogger.Msg("FuckCameraComponent: OnPostRender called for camera: " + _cam.name);
// Destroy now that we have saved the day
enabled = false;
Destroy(this);
}*/
}
public class CameraPoolManager : MonoBehaviour
{
private static CameraPoolManager _instance;
public static CameraPoolManager Instance
{
get
{
if (_instance == null)
{
GameObject obj = new("CameraPoolManager");
obj.SetActive(false);
_instance = obj.AddComponent<CameraPoolManager>();
DontDestroyOnLoad(obj);
}
return _instance;
}
}
private readonly List<GameObject> _cameraObjects = new();
public Camera CreatePooledCamera(Camera source)
{
var go = new GameObject("PooledCamera");
go.SetActive(false);
var camera = go.AddComponent<Camera>();
camera.CopyFrom(source);
_cameraObjects.Add(go);
return camera;
}
public void RestoreCameraProperties(Camera target, Camera pooledCam)
{
// target.CopyFrom(pooledCam);
CopyCameraProperties(pooledCam, target);
}
public void ReleasePooledCamera(Camera pooledCam)
{
if (pooledCam != null)
{
var go = pooledCam.gameObject;
_cameraObjects.Remove(go);
Destroy(go);
}
}
// Skipped known diffs:
// nearClipPlane
// farClipPlane
// fieldOfView
// aspect
// cullingMask
// useOcclusionCulling
// clearFlags
// depthTextureMode
// pixelRect
// targetTexture
public static void CopyCameraProperties(Camera source, Camera target)
{
if (source == null || target == null) return;
target.nearClipPlane = source.nearClipPlane;
target.farClipPlane = source.farClipPlane;
target.fieldOfView = source.fieldOfView;
target.aspect = source.aspect;
int cullingMask = 0;
cullingMask = (cullingMask & ~(1 << 0)) | (source.cullingMask & (1 << 0));
cullingMask = (cullingMask & ~(1 << 1)) | (source.cullingMask & (1 << 1));
cullingMask = (cullingMask & ~(1 << 2)) | (source.cullingMask & (1 << 2));
cullingMask = (cullingMask & ~(1 << 3)) | (source.cullingMask & (1 << 3));
cullingMask = (cullingMask & ~(1 << 4)) | (source.cullingMask & (1 << 4));
cullingMask = (cullingMask & ~(1 << 5)) | (source.cullingMask & (1 << 5));
cullingMask = (cullingMask & ~(1 << 6)) | (source.cullingMask & (1 << 6));
cullingMask = (cullingMask & ~(1 << 7)) | (source.cullingMask & (1 << 7));
cullingMask = (cullingMask & ~(1 << 8)) | (source.cullingMask & (1 << 8));
cullingMask = (cullingMask & ~(1 << 9)) | (source.cullingMask & (1 << 9));
cullingMask = (cullingMask & ~(1 << 10)) | (source.cullingMask & (1 << 10));
cullingMask = (cullingMask & ~(1 << 11)) | (source.cullingMask & (1 << 11));
cullingMask = (cullingMask & ~(1 << 12)) | (source.cullingMask & (1 << 12));
cullingMask = (cullingMask & ~(1 << 13)) | (source.cullingMask & (1 << 13));
cullingMask = (cullingMask & ~(1 << 14)) | (source.cullingMask & (1 << 14));
cullingMask = (cullingMask & ~(1 << 15)) | (source.cullingMask & (1 << 15));
cullingMask = (cullingMask & ~(1 << 16)) | (source.cullingMask & (1 << 16));
cullingMask = (cullingMask & ~(1 << 17)) | (source.cullingMask & (1 << 17));
cullingMask = (cullingMask & ~(1 << 18)) | (source.cullingMask & (1 << 18));
cullingMask = (cullingMask & ~(1 << 19)) | (source.cullingMask & (1 << 19));
cullingMask = (cullingMask & ~(1 << 20)) | (source.cullingMask & (1 << 20));
cullingMask = (cullingMask & ~(1 << 21)) | (source.cullingMask & (1 << 21));
cullingMask = (cullingMask & ~(1 << 22)) | (source.cullingMask & (1 << 22));
cullingMask = (cullingMask & ~(1 << 23)) | (source.cullingMask & (1 << 23));
cullingMask = (cullingMask & ~(1 << 24)) | (source.cullingMask & (1 << 24));
cullingMask = (cullingMask & ~(1 << 25)) | (source.cullingMask & (1 << 25));
cullingMask = (cullingMask & ~(1 << 26)) | (source.cullingMask & (1 << 26));
cullingMask = (cullingMask & ~(1 << 27)) | (source.cullingMask & (1 << 27));
cullingMask = (cullingMask & ~(1 << 28)) | (source.cullingMask & (1 << 28));
cullingMask = (cullingMask & ~(1 << 29)) | (source.cullingMask & (1 << 29));
cullingMask = (cullingMask & ~(1 << 30)) | (source.cullingMask & (1 << 30));
cullingMask = (cullingMask & ~(1 << 31)) | (source.cullingMask & (1 << 31));
target.cullingMask = cullingMask;
target.clearFlags = source.clearFlags;
target.depthTextureMode = source.depthTextureMode;
target.useOcclusionCulling = source.useOcclusionCulling;
target.pixelRect = source.pixelRect;
target.targetTexture = source.targetTexture;
target.renderingPath = source.renderingPath;
target.allowHDR = source.allowHDR;
target.allowMSAA = source.allowMSAA;
target.allowDynamicResolution = source.allowDynamicResolution;
target.forceIntoRenderTexture = source.forceIntoRenderTexture;
target.orthographic = source.orthographic;
target.orthographicSize = source.orthographicSize;
target.depth = source.depth;
target.eventMask = source.eventMask;
target.layerCullSpherical = source.layerCullSpherical;
target.backgroundColor = source.backgroundColor;
target.clearStencilAfterLightingPass = source.clearStencilAfterLightingPass;
target.usePhysicalProperties = source.usePhysicalProperties;
// target.iso = source.iso;
// target.shutterSpeed = source.shutterSpeed;
// target.aperture = source.aperture;
// target.focusDistance = source.focusDistance;
// target.bladeCount = source.bladeCount;
// target.curvature = source.curvature;
// target.barrelClipping = source.barrelClipping;
// target.anamorphism = source.anamorphism;
// target.enabled = source.enabled;
// target.transform.position = source.transform.position;
// target.transform.rotation = source.transform.rotation;
// target.transform.localScale = source.transform.localScale;
target.focalLength = source.focalLength;
target.sensorSize = source.sensorSize;
target.lensShift = source.lensShift;
target.gateFit = source.gateFit;
target.rect = source.rect;
target.targetDisplay = source.targetDisplay;
target.stereoSeparation = source.stereoSeparation;
target.stereoConvergence = source.stereoConvergence;
target.stereoTargetEye = source.stereoTargetEye;
}
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.FuckCameras.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.FuckCameras))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.FuckCameras))]
[assembly: MelonInfo(
typeof(NAK.FuckCameras.FuckCamerasMod),
nameof(NAK.FuckCameras),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckCameras"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.FuckCameras.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,19 @@
# YouAreMyPropNowWeAreHavingSoftTacosLater
Lets you bring held & attached props through world loads.
https://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO
## Examples
https://fixupx.com/NotAKidoS/status/1910545346922422675
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": -1,
"name": "YouAreMyPropNowWeAreHavingSoftTacosLater",
"modversion": "1.0.0",
"gameversion": "2025r179",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Lets you bring held & attached props through world loads.\nhttps://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO",
"searchtags": [
"prop",
"spawn",
"friend",
"load"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/YouAreMyPropNowWeAreHavingSoftTacosLater.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater/",
"changelog": "- Initial Release",
"embedcolor": "#00FFFF"
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>YouAreMineNow</RootNamespace>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,57 @@
using System.Reflection;
using ABI_RC.Core.UI;
using HarmonyLib;
using MelonLoader;
namespace NAK.FuckCohtml2;
public class FuckCohtml2Mod : MelonMod
{
private static readonly MelonPreferences_Category Category =
MelonPreferences.CreateCategory(nameof(FuckCohtml2));
private static readonly MelonPreferences_Entry<bool> EntryFixShouldAdvance =
Category.CreateEntry(
identifier: "fix_should_advance",
true,
display_name: "Fix ShouldAdvance",
description: "Fix CohtmlControlledView.ShouldAdvance to respect the Enabled property.");
private static readonly MelonPreferences_Entry<bool> EntryFixShouldRender =
Category.CreateEntry(
identifier: "fix_should_render",
true,
display_name: "Fix ShouldRender",
description: "Fix CohtmlControlledView.ShouldRender to respect the Enabled property.");
public override void OnInitializeMelon()
{
PatchProperty(nameof(CohtmlControlledView.ShouldAdvance), nameof(OnShouldAdvance));
PatchProperty(nameof(CohtmlControlledView.ShouldRender), nameof(OnShouldRender));
}
private void PatchProperty(string propertyName, string handlerName)
{
PropertyInfo prop = typeof(CohtmlControlledView).GetProperty(propertyName,
BindingFlags.Public | BindingFlags.Instance);
MethodInfo getter = prop!.GetGetMethod(true);
MethodInfo postfixMethod = typeof(FuckCohtml2Mod).GetMethod(handlerName,
BindingFlags.NonPublic | BindingFlags.Static, null,
[typeof(object), typeof(bool).MakeByRefType()], null);
HarmonyInstance.Patch(getter, postfix: new HarmonyMethod(postfixMethod));
}
private static void OnShouldAdvance(object __instance, ref bool __result)
{
if (!EntryFixShouldAdvance.Value) return;
if (__instance is CohtmlControlledView inst) __result &= inst.Enabled;
}
private static void OnShouldRender(object __instance, ref bool __result)
{
if (!EntryFixShouldRender.Value) return;
if (__instance is CohtmlControlledView inst) __result &= inst.Enabled;
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.FuckCohtml2.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.FuckCohtml2))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.FuckCohtml2))]
[assembly: MelonInfo(
typeof(NAK.FuckCohtml2.FuckCohtml2Mod),
nameof(NAK.FuckCohtml2),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckCohtml2"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.FuckCohtml2.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,19 @@
# YouAreMyPropNowWeAreHavingSoftTacosLater
Lets you bring held & attached props through world loads.
https://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO
## Examples
https://fixupx.com/NotAKidoS/status/1910545346922422675
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": -1,
"name": "YouAreMyPropNowWeAreHavingSoftTacosLater",
"modversion": "1.0.0",
"gameversion": "2025r179",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Lets you bring held & attached props through world loads.\nhttps://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO",
"searchtags": [
"prop",
"spawn",
"friend",
"load"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/YouAreMyPropNowWeAreHavingSoftTacosLater.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater/",
"changelog": "- Initial Release",
"embedcolor": "#00FFFF"
}

View file

@ -17,7 +17,7 @@ using System.Reflection;
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckOffUICamera" downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckOffUICamera"
)] )]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] [assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink [assembly: MelonColor(255, 246, 25, 99)] // red-pink

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>YouAreMineNow</RootNamespace>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,39 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Util.AnimatorManager;
using ABI_RC.Systems.GameEventSystem;
using ABI.CCK.Components;
using MelonLoader;
using UnityEngine;
namespace NAK.IFUCKINGHATECAMERAS;
public class IFUCKINGHATECAMERASMod : MelonMod
{
private static readonly MelonPreferences_Category Category =
MelonPreferences.CreateCategory(nameof(IFUCKINGHATECAMERAS));
private static readonly MelonPreferences_Entry<bool> EntryRunHack =
Category.CreateEntry(
identifier: "run_hack",
true,
display_name: "Run Camera Hack (Avatars Only)?",
description: "Should the camera hack run? Btw I fucking hate cameras.");
public override void OnInitializeMelon()
{
CVRGameEventSystem.Avatar.OnRemoteAvatarLoad.AddListener(OnRemoteAvatarLoad);
}
private static void OnRemoteAvatarLoad(CVRPlayerEntity playerEntity, CVRAvatar avatar)
{
if (!EntryRunHack.Value) return;
// HACK: Fixes a native crash (animating camera off on first frame) due to culling in specific worlds.
// I am unsure the root cause, but the local player doesn't crash, and this is similar to what that does.
AvatarAnimatorManager AnimatorManager = playerEntity.PuppetMaster.AnimatorManager;
AnimatorManager.Animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; // Set culling mode to always animate
AnimatorManager.Animator.Update(0f); // Update the animator to force it to do the first frame
AnimatorManager.Animator.cullingMode = AnimatorCullingMode.CullUpdateTransforms; // Set to cull update transforms
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.IFUCKINGHATECAMERAS.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.IFUCKINGHATECAMERAS))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.IFUCKINGHATECAMERAS))]
[assembly: MelonInfo(
typeof(NAK.IFUCKINGHATECAMERAS.IFUCKINGHATECAMERASMod),
nameof(NAK.IFUCKINGHATECAMERAS),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/IFUCKINGHATECAMERAS"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.IFUCKINGHATECAMERAS.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,19 @@
# Tinyboard
Makes the keyboard small and smart.
Few small tweaks to the keyboard:
- Shrinks the keyboard to a size that isn't fit for grandma.
- Adjusts keyboard placement logic to align with the menu that it spawns from.
- Enforces a title on the keyboard input if one is not found.
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": -1,
"name": "Tinyboard",
"modversion": "1.0.0",
"gameversion": "2025r180",
"loaderversion": "0.7.2",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Few small tweaks to the keyboard:\n- Shrinks the keyboard to a size that isn't fit for grandma.\n- Adjusts keyboard placement logic to align with the menu that it spawns from.\n- Enforces a title on the keyboard input if one is not found.",
"searchtags": [
"keyboard",
"menu",
"ui",
"input"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/Tinyboard.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Tinyboard/",
"changelog": "- Initial release",
"embedcolor": "#f61963"
}

View file

@ -1,7 +1,5 @@
using System.Reflection; using System.Reflection;
using ABI_RC.Core.Util.AnimatorManager;
using ABI_RC.Systems.Movement; using ABI_RC.Systems.Movement;
using ABI.CCK.Scripts;
using HarmonyLib; using HarmonyLib;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;

View file

@ -1,4 +1,4 @@
using MelonLoader; using MelonLoader;
using NAK.KeepVelocityOnExitFlight.Properties; using NAK.KeepVelocityOnExitFlight.Properties;
using System.Reflection; using System.Reflection;

View file

@ -1,4 +1,4 @@
using System.Reflection; using System.Reflection;
using ABI_RC.Core.EventSystem; using ABI_RC.Core.EventSystem;
using ABI_RC.Core.IO; using ABI_RC.Core.IO;
using ABI_RC.Core.Networking.API.Responses; using ABI_RC.Core.Networking.API.Responses;

View file

@ -1,4 +1,4 @@
using MelonLoader; using MelonLoader;
using NAK.LazyPrune.Properties; using NAK.LazyPrune.Properties;
using System.Reflection; using System.Reflection;

View file

@ -3,22 +3,6 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
[RequireComponent(typeof(Camera))] [RequireComponent(typeof(Camera))]
public class DepthTextureFix : MonoBehaviour public class DepthTextureFix : MonoBehaviour
{ {

View file

@ -1,8 +1,6 @@
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.InteractionSystem.Base; using ABI_RC.Core.InteractionSystem.Base;
using ABI_RC.Systems.InputManagement;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace ABI_RC.Core.Player.Interaction namespace ABI_RC.Core.Player.Interaction
{ {

View file

@ -1,214 +1,214 @@
using ABI_RC.Core.Player.Interaction.RaycastImpl; // using ABI_RC.Core.Player.Interaction.RaycastImpl;
using ABI_RC.Core.Savior; // using ABI_RC.Core.Savior;
using ABI_RC.Systems.InputManagement; // using ABI_RC.Systems.InputManagement;
using UnityEngine; // using UnityEngine;
//
namespace ABI_RC.Core.Player.Interaction // namespace ABI_RC.Core.Player.Interaction
{ // {
public class CVRPlayerInteractionManager : MonoBehaviour // public class CVRPlayerInteractionManager : MonoBehaviour
{ // {
#region Singleton // #region Singleton
//
public static CVRPlayerInteractionManager Instance { get; private set; } // public static CVRPlayerInteractionManager Instance { get; private set; }
//
#endregion Singleton // #endregion Singleton
//
#region Serialized Fields // #region Serialized Fields
//
[Header("Hand Components")] // [Header("Hand Components")]
[SerializeField] private CVRPlayerHand handVrLeft; // [SerializeField] private CVRPlayerHand handVrLeft;
[SerializeField] private CVRPlayerHand handVrRight; // [SerializeField] private CVRPlayerHand handVrRight;
[SerializeField] private CVRPlayerHand handDesktopRight; // Desktop does not have a left hand // [SerializeField] private CVRPlayerHand handDesktopRight; // Desktop does not have a left hand
//
[Header("Raycast Transforms")] // [Header("Raycast Transforms")]
[SerializeField] private Transform raycastTransformVrRight; // [SerializeField] private Transform raycastTransformVrRight;
[SerializeField] private Transform raycastTransformVrLeft; // [SerializeField] private Transform raycastTransformVrLeft;
[SerializeField] private Transform raycastTransformDesktopRight; // [SerializeField] private Transform raycastTransformDesktopRight;
//
[Header("Settings")] // [Header("Settings")]
[SerializeField] private bool interactionEnabled = true; // [SerializeField] private bool interactionEnabled = true;
[SerializeField] private LayerMask interactionLayerMask = -1; // Default to all layers, will be filtered // [SerializeField] private LayerMask interactionLayerMask = -1; // Default to all layers, will be filtered
//
#endregion Serialized Fields // #endregion Serialized Fields
//
#region Properties // #region Properties
//
private CVRPlayerHand _rightHand; // private CVRPlayerHand _rightHand;
private CVRPlayerHand _leftHand; // private CVRPlayerHand _leftHand;
//
private CVRPlayerRaycaster _rightRaycaster; // private CVRPlayerRaycaster _rightRaycaster;
private CVRPlayerRaycaster _leftRaycaster; // private CVRPlayerRaycaster _leftRaycaster;
//
private CVRRaycastResult _rightRaycastResult; // private CVRRaycastResult _rightRaycastResult;
private CVRRaycastResult _leftRaycastResult; // private CVRRaycastResult _leftRaycastResult;
//
// Input handler // // Input handler
private CVRPlayerInputHandler _inputHandler; // private CVRPlayerInputHandler _inputHandler;
//
// Interaction flags // // Interaction flags
public bool InteractionEnabled // public bool InteractionEnabled
{ // {
get => interactionEnabled; // get => interactionEnabled;
set => interactionEnabled = value; // set => interactionEnabled = value;
} // }
//
#endregion Properties // #endregion Properties
//
#region Unity Events // #region Unity Events
//
private void Awake() // private void Awake()
{ // {
if (Instance != null && Instance != this) // if (Instance != null && Instance != this)
{ // {
Destroy(gameObject); // Destroy(gameObject);
return; // return;
} // }
Instance = this; // Instance = this;
//
// Create the input handler // // Create the input handler
_inputHandler = gameObject.AddComponent<CVRPlayerInputHandler>(); // _inputHandler = gameObject.AddComponent<CVRPlayerInputHandler>();
} // }
//
private void Start() // private void Start()
{ // {
// Setup interaction for current device mode // // Setup interaction for current device mode
SetupInteractionForDeviceMode(); // SetupInteractionForDeviceMode();
//
// Listen for VR mode changes // // Listen for VR mode changes
MetaPort.Instance.onVRModeSwitch.AddListener(SetupInteractionForDeviceMode); // MetaPort.Instance.onVRModeSwitch.AddListener(SetupInteractionForDeviceMode);
} // }
//
private void Update() // private void Update()
{ // {
if (!interactionEnabled) // if (!interactionEnabled)
return; // return;
//
// Process right hand // // Process right hand
if (_rightRaycaster != null) // if (_rightRaycaster != null)
{ // {
// Determine raycast flags based on current mode // // Determine raycast flags based on current mode
CVRPlayerRaycaster.RaycastFlags flags = DetermineRaycastFlags(_rightHand); // CVRPlayerRaycaster.RaycastFlags flags = DetermineRaycastFlags(_rightHand);
//
// Get raycast results // // Get raycast results
_rightRaycastResult = _rightRaycaster.GetRaycastResults(flags); // _rightRaycastResult = _rightRaycaster.GetRaycastResults(flags);
//
// Process input based on raycast results // // Process input based on raycast results
_inputHandler.ProcessInput(CVRHand.Right, _rightRaycastResult); // _inputHandler.ProcessInput(CVRHand.Right, _rightRaycastResult);
} // }
//
// Process left hand (if available) // // Process left hand (if available)
if (_leftRaycaster != null) // if (_leftRaycaster != null)
{ // {
// Determine raycast flags based on current mode // // Determine raycast flags based on current mode
CVRPlayerRaycaster.RaycastFlags flags = DetermineRaycastFlags(_leftHand); // CVRPlayerRaycaster.RaycastFlags flags = DetermineRaycastFlags(_leftHand);
//
// Get raycast results // // Get raycast results
_leftRaycastResult = _leftRaycaster.GetRaycastResults(flags); // _leftRaycastResult = _leftRaycaster.GetRaycastResults(flags);
//
// Process input based on raycast results // // Process input based on raycast results
_inputHandler.ProcessInput(CVRHand.Left, _leftRaycastResult); // _inputHandler.ProcessInput(CVRHand.Left, _leftRaycastResult);
} // }
} // }
//
private void OnDestroy() // private void OnDestroy()
{ // {
// Clean up event listener // // Clean up event listener
if (MetaPort.Instance != null) // if (MetaPort.Instance != null)
MetaPort.Instance.onVRModeSwitch.RemoveListener(SetupInteractionForDeviceMode); // MetaPort.Instance.onVRModeSwitch.RemoveListener(SetupInteractionForDeviceMode);
} // }
//
#endregion Unity Events // #endregion Unity Events
//
#region Public Methods // #region Public Methods
//
/// <summary> // /// <summary>
/// Register a custom tool mode // /// Register a custom tool mode
/// </summary> // /// </summary>
public void RegisterCustomToolMode(System.Action<CVRHand, CVRRaycastResult, InputState> callback) // public void RegisterCustomToolMode(System.Action<CVRHand, CVRRaycastResult, InputState> callback)
{ // {
_inputHandler.RegisterCustomTool(callback); // _inputHandler.RegisterCustomTool(callback);
} // }
//
/// <summary> // /// <summary>
/// Unregister the current custom tool mode // /// Unregister the current custom tool mode
/// </summary> // /// </summary>
public void UnregisterCustomToolMode() // public void UnregisterCustomToolMode()
{ // {
_inputHandler.UnregisterCustomTool(); // _inputHandler.UnregisterCustomTool();
} // }
//
/// <summary> // /// <summary>
/// Set the interaction mode // /// Set the interaction mode
/// </summary> // /// </summary>
public void SetInteractionMode(CVRPlayerInputHandler.InteractionMode mode) // public void SetInteractionMode(CVRPlayerInputHandler.InteractionMode mode)
{ // {
_inputHandler.SetInteractionMode(mode); // _inputHandler.SetInteractionMode(mode);
} // }
//
/// <summary> // /// <summary>
/// Get the raycast result for a specific hand // /// Get the raycast result for a specific hand
/// </summary> // /// </summary>
public CVRRaycastResult GetRaycastResult(CVRHand hand) // public CVRRaycastResult GetRaycastResult(CVRHand hand)
{ // {
return hand == CVRHand.Left ? _leftRaycastResult : _rightRaycastResult; // return hand == CVRHand.Left ? _leftRaycastResult : _rightRaycastResult;
} // }
//
#endregion Public Methods // #endregion Public Methods
//
#region Private Methods // #region Private Methods
//
private void SetupInteractionForDeviceMode() // private void SetupInteractionForDeviceMode()
{ // {
bool isVr = MetaPort.Instance.isUsingVr; // bool isVr = MetaPort.Instance.isUsingVr;
//
if (isVr) // if (isVr)
{ // {
// VR mode // // VR mode
_rightHand = handVrRight; // _rightHand = handVrRight;
_leftHand = handVrLeft; // _leftHand = handVrLeft;
//
// VR uses the controller transform for raycasting // // VR uses the controller transform for raycasting
_rightRaycaster = new CVRPlayerRaycasterTransform(raycastTransformVrRight); // _rightRaycaster = new CVRPlayerRaycasterTransform(raycastTransformVrRight);
_leftRaycaster = new CVRPlayerRaycasterTransform(raycastTransformVrLeft); // _leftRaycaster = new CVRPlayerRaycasterTransform(raycastTransformVrLeft);
} // }
else // else
{ // {
// Desktop mode // // Desktop mode
_rightHand = handDesktopRight; // _rightHand = handDesktopRight;
_leftHand = null; // _leftHand = null;
//
// Desktop uses the mouse position for raycasting when unlocked // // Desktop uses the mouse position for raycasting when unlocked
Camera desktopCamera = PlayerSetup.Instance.desktopCam; // Camera desktopCamera = PlayerSetup.Instance.desktopCam;
_rightRaycaster = new CVRPlayerRaycasterMouse(raycastTransformDesktopRight, desktopCamera); // _rightRaycaster = new CVRPlayerRaycasterMouse(raycastTransformDesktopRight, desktopCamera);
_leftRaycaster = null; // _leftRaycaster = null;
} // }
//
// Set the layer mask for raycasters // // Set the layer mask for raycasters
if (_rightRaycaster != null) // if (_rightRaycaster != null)
_rightRaycaster.SetLayerMask(interactionLayerMask); // _rightRaycaster.SetLayerMask(interactionLayerMask);
//
if (_leftRaycaster != null) // if (_leftRaycaster != null)
_leftRaycaster.SetLayerMask(interactionLayerMask); // _leftRaycaster.SetLayerMask(interactionLayerMask);
} // }
//
private static CVRPlayerRaycaster.RaycastFlags DetermineRaycastFlags(CVRPlayerHand hand) // private static CVRPlayerRaycaster.RaycastFlags DetermineRaycastFlags(CVRPlayerHand hand)
{ // {
// Default to all flags // // Default to all flags
CVRPlayerRaycaster.RaycastFlags flags = CVRPlayerRaycaster.RaycastFlags.All; // CVRPlayerRaycaster.RaycastFlags flags = CVRPlayerRaycaster.RaycastFlags.All;
//
// Check if hand is holding a pickup // // Check if hand is holding a pickup
if (hand != null && hand.IsHoldingObject) // if (hand != null && hand.IsHoldingObject)
{ // {
// When holding an object, only check for COHTML interaction // // When holding an object, only check for COHTML interaction
flags = CVRPlayerRaycaster.RaycastFlags.CohtmlInteract; // flags = CVRPlayerRaycaster.RaycastFlags.CohtmlInteract;
} // }
//
// Could add more conditional flag adjustments here based on the current mode // // Could add more conditional flag adjustments here based on the current mode
// For example, in a teleport tool mode, you might only want world hits // // For example, in a teleport tool mode, you might only want world hits
//
return flags; // return flags;
} // }
//
#endregion Private Methods // #endregion Private Methods
} // }
} // }

View file

@ -1,121 +1,121 @@
using ABI_RC.Core.Base; // using ABI_RC.Core.Base;
using ABI_RC.Core.Player; // using ABI_RC.Core.Player;
using UnityEngine; // using UnityEngine;
using UnityEngine.EventSystems; // using UnityEngine.EventSystems;
using UnityEngine.UI; // using UnityEngine.UI;
//
namespace NAK.SuperAwesomeMod.Components // namespace NAK.SuperAwesomeMod.Components
{ // {
public class CVRCanvasWrapper : MonoBehaviour // public class CVRCanvasWrapper : MonoBehaviour
{ // {
public bool IsInteractable = true; // public bool IsInteractable = true;
public float MaxInteractDistance = 10f; // public float MaxInteractDistance = 10f;
//
private Canvas _canvas; // private Canvas _canvas;
private GraphicRaycaster _graphicsRaycaster; // private GraphicRaycaster _graphicsRaycaster;
private static readonly List<RaycastResult> _raycastResults = new(); // private static readonly List<RaycastResult> _raycastResults = new();
private static readonly PointerEventData _pointerEventData = new(EventSystem.current); // private static readonly PointerEventData _pointerEventData = new(EventSystem.current);
//
private static Selectable _workingSelectable; // private static Selectable _workingSelectable;
private Camera _camera; // private Camera _camera;
private RectTransform _rectTransform; // private RectTransform _rectTransform;
//
#region Unity Events // #region Unity Events
//
private void Awake() // private void Awake()
{ // {
if (!TryGetComponent(out _canvas) // if (!TryGetComponent(out _canvas)
|| _canvas.renderMode != RenderMode.WorldSpace) // || _canvas.renderMode != RenderMode.WorldSpace)
{ // {
IsInteractable = false; // IsInteractable = false;
return; // return;
} // }
//
_rectTransform = _canvas.GetComponent<RectTransform>(); // _rectTransform = _canvas.GetComponent<RectTransform>();
} // }
//
private void Start() // private void Start()
{ // {
_graphicsRaycaster = _canvas.gameObject.AddComponent<GraphicRaycaster>(); // _graphicsRaycaster = _canvas.gameObject.AddComponent<GraphicRaycaster>();
_camera = PlayerSetup.Instance.activeCam; // _camera = PlayerSetup.Instance.activeCam;
_canvas.worldCamera = _camera; // _canvas.worldCamera = _camera;
} // }
//
#endregion Unity Events // #endregion Unity Events
//
#region Public Methods // #region Public Methods
//
public bool GetGraphicsHit(Ray worldRay, out RaycastResult result) // public bool GetGraphicsHit(Ray worldRay, out RaycastResult result)
{ // {
result = default; // result = default;
//
if (!IsInteractable || _camera == null) return false; // if (!IsInteractable || _camera == null) return false;
//
// Get the plane of the canvas // // Get the plane of the canvas
Plane canvasPlane = new(transform.forward, transform.position); // Plane canvasPlane = new(transform.forward, transform.position);
//
// Find where the ray intersects the canvas plane // // Find where the ray intersects the canvas plane
if (!canvasPlane.Raycast(worldRay, out float distance)) // if (!canvasPlane.Raycast(worldRay, out float distance))
return false; // return false;
//
// Get the world point of intersection // // Get the world point of intersection
Vector3 worldHitPoint = worldRay.origin + worldRay.direction * distance; // Vector3 worldHitPoint = worldRay.origin + worldRay.direction * distance;
//
// Check if hit point is within max interaction distance // // Check if hit point is within max interaction distance
if (Vector3.Distance(worldRay.origin, worldHitPoint) > MaxInteractDistance) // if (Vector3.Distance(worldRay.origin, worldHitPoint) > MaxInteractDistance)
return false; // return false;
//
// Check if hit point is within canvas bounds // // Check if hit point is within canvas bounds
Vector3 localHitPoint = transform.InverseTransformPoint(worldHitPoint); // Vector3 localHitPoint = transform.InverseTransformPoint(worldHitPoint);
Rect canvasRect = _rectTransform.rect; // Rect canvasRect = _rectTransform.rect;
if (!canvasRect.Contains(new Vector2(localHitPoint.x, localHitPoint.y))) // if (!canvasRect.Contains(new Vector2(localHitPoint.x, localHitPoint.y)))
return false; // return false;
//
// Convert world hit point to screen space // // Convert world hit point to screen space
Vector2 screenPoint = _camera.WorldToScreenPoint(worldHitPoint); // Vector2 screenPoint = _camera.WorldToScreenPoint(worldHitPoint);
//
// Update pointer event data // // Update pointer event data
_pointerEventData.position = screenPoint; // _pointerEventData.position = screenPoint;
_pointerEventData.delta = Vector2.zero; // _pointerEventData.delta = Vector2.zero;
//
// Clear previous results and perform raycast // // Clear previous results and perform raycast
_raycastResults.Clear(); // _raycastResults.Clear();
_graphicsRaycaster.Raycast(_pointerEventData, _raycastResults); // _graphicsRaycaster.Raycast(_pointerEventData, _raycastResults);
//
// Early out if no hits // // Early out if no hits
if (_raycastResults.Count == 0) // if (_raycastResults.Count == 0)
{ // {
//Debug.Log($"No hits on canvas {_canvas.name}"); // //Debug.Log($"No hits on canvas {_canvas.name}");
return false; // return false;
} // }
//
// Find first valid interactive UI element // // Find first valid interactive UI element
foreach (RaycastResult hit in _raycastResults) // foreach (RaycastResult hit in _raycastResults)
{ // {
if (!hit.isValid) // if (!hit.isValid)
{ // {
//Debug.Log($"Invalid hit on canvas {_canvas.name}"); // //Debug.Log($"Invalid hit on canvas {_canvas.name}");
continue; // continue;
} // }
//
// Check if the hit object has a Selectable component and is interactable // // Check if the hit object has a Selectable component and is interactable
GameObject hitObject = hit.gameObject; // GameObject hitObject = hit.gameObject;
if (!hitObject.TryGetComponent(out _workingSelectable) // if (!hitObject.TryGetComponent(out _workingSelectable)
|| !_workingSelectable.interactable) // || !_workingSelectable.interactable)
{ // {
//Debug.Log($"Non-interactable hit on canvas {_canvas.name} - {hitObject.name}"); // //Debug.Log($"Non-interactable hit on canvas {_canvas.name} - {hitObject.name}");
continue; // continue;
} // }
//
//Debug.Log($"Hit on canvas {_canvas.name} with {hitObject.name}"); // //Debug.Log($"Hit on canvas {_canvas.name} with {hitObject.name}");
//
result = hit; // result = hit;
return true; // return true;
} // }
//
return false; // return false;
} // }
//
#endregion Public Methods // #endregion Public Methods
} // }
} // }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
using ABI_RC.Core;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.InputManagement;
using UnityEngine;
using UnityEngine.EventSystems;
namespace NAK.SuperAwesomeMod.Interaction;
[DefaultExecutionOrder(1000)]
public class CustomBaseInput : BaseInput
{
private Vector2 mousePositionCache;
#region Input Overrides
public override Vector2 mousePosition => Input.mousePosition;
public override bool GetMouseButton(int button)
=> button == (int)CVRHand.Right
? CVRInputManager.Instance.interactLeftValue > 0.75f
: CVRInputManager.Instance.interactRightValue > 0.75f;
public override bool GetMouseButtonDown(int button)
=> button == (int)CVRHand.Right
? CVRInputManager.Instance.interactLeftDown
: CVRInputManager.Instance.interactRightDown;
public override Vector2 mouseScrollDelta => Vector2.zero;
public override float GetAxisRaw(string axisName)
{
return axisName switch
{
"Mouse ScrollWheel" => CVRInputManager.Instance.scrollValue,
"Horizontal" => CVRInputManager.Instance.movementVector.x,
"Vertical" => CVRInputManager.Instance.movementVector.y,
_ => 0f
};
}
public override bool GetButtonDown(string buttonName)
{
return buttonName switch
{
"Mouse ScrollWheel" => CVRInputManager.Instance.scrollValue > 0.1f,
"Horizontal" => CVRInputManager.Instance.movementVector.x > 0.5f,
"Vertical" => CVRInputManager.Instance.movementVector.y > 0.5f,
_ => false
};
}
#endregion Input Overrides
private CVRHand lastInteractHand;
private void Update()
{
if (!MetaPort.Instance.isUsingVr)
{
mousePositionCache = Input.mousePosition;
return;
}
ControllerRay leftRay = PlayerSetup.Instance.vrRayLeft;
ControllerRay rightRay = PlayerSetup.Instance.vrRayRight;
if (leftRay._interactDown) lastInteractHand = leftRay.hand;
if (rightRay._interactDown) lastInteractHand = rightRay.hand;
Camera vrCamera = PlayerSetup.Instance.vrCam;
// transform the raycast position to screen position
Vector3 hitPoint = lastInteractHand == CVRHand.Left
? leftRay.HitPoint
: rightRay.HitPoint;
Vector3 screenPoint = vrCamera.WorldToScreenPoint(hitPoint);
screenPoint.x = Mathf.Clamp(screenPoint.x, 0, Screen.width);
screenPoint.y = Mathf.Clamp(screenPoint.y, 0, Screen.height);
mousePositionCache = new Vector2(screenPoint.x, screenPoint.y);
}
}

View file

@ -0,0 +1,67 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace NAK.SuperAwesomeMod.Interaction;
public class CustomInputModule : StandaloneInputModule
{
bool meow = false;
#region Unity Events
protected override void Start()
{
base.Start();
m_InputOverride = gameObject.AddComponent<CustomBaseInput>();
// Disable other event systems in the scene
DisableOtherEventSystems();
}
#endregion
#region Overrides
public override void Process()
{
CursorLockMode currentLockState = Cursor.lockState;
Cursor.lockState = CursorLockMode.None;
base.Process();
Cursor.lockState = currentLockState;
}
protected override MouseState GetMousePointerEventData(int id)
{
MouseState pointerEventData = base.GetMousePointerEventData(id);
MouseButtonEventData leftEventData = pointerEventData.GetButtonState(PointerEventData.InputButton.Left).eventData;
RaycastResult pointerRaycast = leftEventData.buttonData.pointerCurrentRaycast;
if (meow) leftEventData.buttonData.pointerCurrentRaycast = new RaycastResult();
return pointerEventData;
}
#endregion Overrides
#region Private Methods
private void DisableOtherEventSystems()
{
EventSystem thisEventSystem = GetComponent<EventSystem>();
EventSystem[] systems = FindObjectsOfType<EventSystem>();
foreach (EventSystem system in systems)
{
if (system.gameObject.name == "UniverseLibCanvas") continue;
if (system != thisEventSystem)
{
system.enabled = false;
}
}
}
#endregion Private Methods
}

View file

@ -1,9 +1,7 @@
using ABI_RC.Core.InteractionSystem.Base; using ABI_RC.Core.InteractionSystem.Base;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using ABI.CCK.Components; using ABI.CCK.Components;
using NAK.SuperAwesomeMod.Components;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; using UnityEngine.UI;
namespace ABI_RC.Core.Player.Interaction.RaycastImpl namespace ABI_RC.Core.Player.Interaction.RaycastImpl
@ -108,7 +106,8 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
// Check if there are pickups or interactables in immediate proximity // Check if there are pickups or interactables in immediate proximity
if ((flags & RaycastFlags.ProximityInteract) != 0) if ((flags & RaycastFlags.ProximityInteract) != 0)
{ {
ProcessProximityHits(ray, ref result); // TODO: Offset origin to center of palm based on hand type Ray proximityRay = GetProximityRayFromImpl();
ProcessProximityHits(proximityRay, ref result);
if (result.isProximityHit) if (result.isProximityHit)
return result; return result;
} }
@ -150,7 +149,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
int proximityHits = Physics.SphereCastNonAlloc( int proximityHits = Physics.SphereCastNonAlloc(
ray.origin, ray.origin,
RAYCAST_SPHERE_RADIUS, RAYCAST_SPHERE_RADIUS,
Vector3.up, ray.direction,
_hits, _hits,
0.001f, 0.001f,
_layerMask, _layerMask,
@ -313,6 +312,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
result.hitInteractable = _workingInteractable; result.hitInteractable = _workingInteractable;
hitValidComponent = true; hitValidComponent = true;
} }
if (_workingGameObject.TryGetComponent(out _workingPickupable) if (_workingGameObject.TryGetComponent(out _workingPickupable)
&& _workingPickupable.CanPickup && _workingPickupable.CanPickup
&& IsCVRPickupableWithinRange(_workingPickupable, hit)) && IsCVRPickupableWithinRange(_workingPickupable, hit))
@ -343,6 +343,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
#region Protected Methods #region Protected Methods
protected abstract Ray GetRayFromImpl(); protected abstract Ray GetRayFromImpl();
protected abstract Ray GetProximityRayFromImpl();
#endregion Protected Methods #endregion Protected Methods
@ -375,10 +376,10 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
return hit.distance <= pickupable.MaxGrabDistance; return hit.distance <= pickupable.MaxGrabDistance;
} }
private static bool IsCVRCanvasWrapperWithinRange(CVRCanvasWrapper canvasWrapper, RaycastHit hit) // private static bool IsCVRCanvasWrapperWithinRange(CVRCanvasWrapper canvasWrapper, RaycastHit hit)
{ // {
return hit.distance <= canvasWrapper.MaxInteractDistance; // return hit.distance <= canvasWrapper.MaxInteractDistance;
} // }
#endregion Utility Because Original Methods Are Broken #endregion Utility Because Original Methods Are Broken
} }

View file

@ -4,10 +4,24 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
{ {
public class CVRPlayerRaycasterMouse : CVRPlayerRaycaster public class CVRPlayerRaycasterMouse : CVRPlayerRaycaster
{ {
private readonly Camera _camera; #region Constructor
public CVRPlayerRaycasterMouse(Transform rayOrigin, Camera camera) : base(rayOrigin) { _camera = camera; } public CVRPlayerRaycasterMouse(Transform rayOrigin, Camera camera) : base(rayOrigin) { _camera = camera; }
private readonly Camera _camera;
#endregion Constructor
#region Overrides
protected override Ray GetRayFromImpl() => Cursor.lockState == CursorLockMode.Locked protected override Ray GetRayFromImpl() => Cursor.lockState == CursorLockMode.Locked
? new Ray(_camera.transform.position, _camera.transform.forward) ? new Ray(_camera.transform.position, _camera.transform.forward)
: _camera.ScreenPointToRay(Input.mousePosition); : _camera.ScreenPointToRay(Input.mousePosition);
protected override Ray GetProximityRayFromImpl() => Cursor.lockState == CursorLockMode.Locked
? new Ray(_camera.transform.position, _camera.transform.forward)
: _camera.ScreenPointToRay(Input.mousePosition);
#endregion Overrides
} }
} }

View file

@ -4,7 +4,44 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
{ {
public class CVRPlayerRaycasterTransform : CVRPlayerRaycaster public class CVRPlayerRaycasterTransform : CVRPlayerRaycaster
{ {
public CVRPlayerRaycasterTransform(Transform rayOrigin) : base(rayOrigin) { } #region Proximity Grab
public const float ProximityGrabRadiusScaleDefault = 0.1f;
private float _proximityDetectionRadiusRelativeValue = ProximityGrabRadiusScaleDefault;
private float ProximityDetectionRadius => _proximityDetectionRadiusRelativeValue * PlayerSetup.Instance.GetPlaySpaceScale();
#endregion Proximity Grab
#region Constructor
public CVRPlayerRaycasterTransform(Transform rayOrigin, CVRHand hand) : base(rayOrigin) { _hand = hand; }
private readonly CVRHand _hand;
#endregion Constructor
#region Overrides
protected override Ray GetRayFromImpl() => new(_rayOrigin.position, _rayOrigin.forward); protected override Ray GetRayFromImpl() => new(_rayOrigin.position, _rayOrigin.forward);
protected override Ray GetProximityRayFromImpl()
{
Vector3 handPosition = _rayOrigin.position;
Vector3 handRight = _rayOrigin.right;
// Offset the detection center forward, so we don't grab stuff behind our writs
handPosition += _rayOrigin.forward * (ProximityDetectionRadius * 0.25f);
// Offset the detection center away from the palm, so we don't grab stuff behind our hand palm
Vector3 palmOffset = handRight * (ProximityDetectionRadius * 0.75f);
if (_hand == CVRHand.Left)
handPosition += palmOffset;
else
handPosition -= palmOffset;
return new Ray(handPosition, _hand == CVRHand.Left ? handRight : -handRight);
}
#endregion Overrides
} }
} }

View file

@ -1,8 +1,6 @@
using ABI_RC.Core.InteractionSystem.Base; using ABI_RC.Core.InteractionSystem.Base;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using NAK.SuperAwesomeMod.Components;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; using UnityEngine.UI;
namespace ABI_RC.Core.Player.Interaction.RaycastImpl namespace ABI_RC.Core.Player.Interaction.RaycastImpl
@ -19,6 +17,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
// Main raycast hit info // Main raycast hit info
public RaycastHit hit; public RaycastHit hit;
public RaycastHit? waterHit; // Only valid if hitWater is true public RaycastHit? waterHit; // Only valid if hitWater is true
public Vector2 hitScreenPoint; // Screen coordinates of the hit
// Specific hit components // Specific hit components
public Pickupable hitPickupable; public Pickupable hitPickupable;

View file

@ -1,5 +1,4 @@
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; using UnityEngine.UI;
namespace ABI_RC.Core.Player.Interaction.RaycastImpl namespace ABI_RC.Core.Player.Interaction.RaycastImpl

View file

@ -0,0 +1,362 @@
using ABI_RC.Core.Base;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI.CCK.Components;
using MelonLoader;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace NAK.SuperAwesomeMod.UExplorer;
public class UEMenuHelper : MenuPositionHelperBase
{
#region Singleton
public static void Create()
{
if (Instance != null)
return;
_universeLibCanvas = GameObject.Find("UniverseLibCanvas");
if (_universeLibCanvas == null)
{
MelonLogger.Error(
"Failed to create UniverseLibCanvas"); // TODO: mod logger, casue https://github.com/knah/VRCMods/pull/227
return;
}
_explorerRoot = _universeLibCanvas.transform.Find("com.sinai.unityexplorer_Root").gameObject;
// Fix the canvas so it renders in the UI camera
// _universeLibCanvas.SetLayerRecursive(CVRLayers.UIInternal);
Transform menuParent = new GameObject("UEMenuParent").transform;
menuParent.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
menuParent.localScale = Vector3.one;
DontDestroyOnLoad(menuParent.gameObject);
Transform offsetTransform = new GameObject("UEMenuOffset").transform;
offsetTransform.SetParent(menuParent, false);
offsetTransform.localScale = Vector3.one;
Transform contentTransform = new GameObject("UEMenuContent").transform;
contentTransform.SetParent(offsetTransform, false);
contentTransform.localScale = Vector3.one;
Instance = menuParent.AddComponentIfMissing<UEMenuHelper>();
Instance.menuTransform = contentTransform;
// Instance._offsetTransform = offsetTransform; // Got in MenuPositionHelperBase.Start
// Apply the component filters done in worlds
// foreach (Component c in _universeLibCanvas.GetComponentsInChildren<Component>(true))
// SetupCollidersOnUnityUi(c);
CVRCanvasWrapper.AddForCanvas(_universeLibCanvas.GetComponent<Canvas>(), true);
Instance.ConfigureUECanvasRenderMode(RenderMode.WorldSpace);
}
public static UEMenuHelper Instance { get; private set; }
private static GameObject _universeLibCanvas;
private static GameObject _explorerRoot;
private static RenderMode _currentRenderMode = RenderMode.WorldSpace;
#endregion Singleton
#region Overrides
public override bool IsMenuOpen => _explorerRoot.activeInHierarchy;
public override float MenuScaleModifier => !MetaPort.Instance.isUsingVr ? 1f : 0.3f;
public override float MenuDistanceModifier => !MetaPort.Instance.isUsingVr ? 1.2f : 1f;
#endregion Overrides
#region Unity Events
private void Update()
{
if (Input.GetKeyDown(KeyCode.F9))
ToggleUeCanvasRenderMode();
}
#endregion Unity Events
#region Private Methods
private void ToggleUeCanvasRenderMode()
{
ConfigureUECanvasRenderMode(_currentRenderMode == RenderMode.WorldSpace
? RenderMode.ScreenSpaceOverlay
: RenderMode.WorldSpace);
}
private void ConfigureUECanvasRenderMode(RenderMode targetMode)
{
_currentRenderMode = targetMode;
var canvases = _universeLibCanvas.GetComponentsInChildren<Canvas>(true);
if (targetMode == RenderMode.WorldSpace)
{
foreach (Canvas canvas in canvases)
{
canvas.renderMode = RenderMode.WorldSpace;
canvas.worldCamera = PlayerSetup.Instance.activeCam;
}
_universeLibCanvas.transform.SetParent(menuTransform, false);
_universeLibCanvas.transform.localScale = Vector3.one * 0.0032f;
// Center the canvas on the menuTransform
CenterCanvasOnMenuTransform(_universeLibCanvas, menuTransform);
return;
}
foreach (Canvas canvas in canvases)
{
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.worldCamera = null;
}
_universeLibCanvas.transform.SetParent(null, false);
_universeLibCanvas.transform.localScale = Vector3.one;
}
private void CenterCanvasOnMenuTransform(GameObject canvasRoot, Transform parentTransform)
{
// Find all the rectTransforms under the canvas, determine their bounds, and center the canvas local position
// on the menuTransform
RectTransform canvasTransform = _explorerRoot.transform as RectTransform;
// get the extents of the rectTransform
Vector3[] corners = new Vector3[4];
canvasTransform.GetWorldCorners(corners);
// now center by offsettings its localPosition
Vector3 center = (corners[0] + corners[2]) / 2f;
Vector3 extents = (corners[2] - corners[0]) / 2f;
Vector3 offset = center - extents;
offset.z = 0f; // set z to 0 to avoid depth issues
canvasTransform.localPosition = offset;
MelonLogger.Msg($"Centered canvas on menuTransform: {canvasTransform.localPosition}");
}
private static bool IsFloatValid(float val)
=> (!float.IsNaN(val) && !float.IsInfinity(val));
private static void SetupCollidersOnUnityUi(Component c)
{
GameObject go = c.gameObject;
if (c is Button)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
col.isTrigger = true;
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.05f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
}
if (c is Toggle)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
col.isTrigger = true;
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(Mathf.Max(rectTransform.sizeDelta.x, rectTransform.rect.width),
rectTransform.sizeDelta.y, 0.05f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
//Check Child if Size = 0
if (col.size.x + col.size.y == 0f && go.transform.childCount > 0)
{
var childRectTransform = go.transform.GetChild(0).GetComponent<RectTransform>();
if (childRectTransform != null)
{
newSize = new Vector3(
Mathf.Max(childRectTransform.sizeDelta.x, rectTransform.rect.width),
childRectTransform.sizeDelta.y, 0.1f);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = Vector3.zero;
}
}
}
}
if (c is Slider)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.05f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
col.isTrigger = true;
}
if (c is EventTrigger)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.025f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
col.isTrigger = true;
}
if (c is InputField || c is TMP_InputField)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
col.isTrigger = true;
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.05f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
}
if (c is ScrollRect)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
col.isTrigger = true;
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.025f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.025f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.025f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.025f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
}
if (c is Dropdown)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
col.isTrigger = true;
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.05f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.05f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.05f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.05f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
}
//Canvas
if (c is Canvas)
{
BoxCollider col = go.AddComponentIfMissing<BoxCollider>();
var rectTransform = go.GetComponent<RectTransform>();
if (rectTransform)
{
Vector3 newSize = new Vector3(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y,
0.0125f / rectTransform.lossyScale.z);
if (!IsFloatValid(newSize.x))
newSize.x = 0.0125f;
if (!IsFloatValid(newSize.y))
newSize.y = 0.0125f;
if (!IsFloatValid(newSize.z))
newSize.z = 0.0125f;
col.size = newSize;
col.center = new Vector3(col.size.x * (0.5f - rectTransform.pivot.x),
col.size.y * (0.5f - rectTransform.pivot.y), 0f);
}
col.isTrigger = true;
}
}
#endregion Private Methods
}

View file

@ -1,15 +1,14 @@
using System.Reflection; using System.Reflection;
using ABI_RC.Core.Base;
using ABI_RC.Core.Base.Jobs; using ABI_RC.Core.Base.Jobs;
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Player.Interaction.RaycastImpl;
using ABI_RC.Core.Util.AssetFiltering;
using ABI.CCK.Components;
using HarmonyLib; using HarmonyLib;
using MelonLoader; using MelonLoader;
using NAK.SuperAwesomeMod.Components;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace NAK.SuperAwesomeMod; namespace NAK.SuperAwesomeMod;
@ -33,13 +32,13 @@ public class SuperAwesomeModMod : MelonMod
BindingFlags.NonPublic | BindingFlags.Static)) BindingFlags.NonPublic | BindingFlags.Static))
); );
// patch SharedFilter.ProcessCanvas // // patch SharedFilter.ProcessCanvas
HarmonyInstance.Patch( // HarmonyInstance.Patch(
typeof(SharedFilter).GetMethod(nameof(SharedFilter.ProcessCanvas), // typeof(SharedFilter).GetMethod(nameof(SharedFilter.ProcessCanvas),
BindingFlags.Public | BindingFlags.Static), // BindingFlags.Public | BindingFlags.Static),
postfix: new HarmonyMethod(typeof(SuperAwesomeModMod).GetMethod(nameof(OnProcessCanvas), // postfix: new HarmonyMethod(typeof(SuperAwesomeModMod).GetMethod(nameof(OnProcessCanvas),
BindingFlags.NonPublic | BindingFlags.Static)) // BindingFlags.NonPublic | BindingFlags.Static))
); // );
LoggerInstance.Msg("SuperAwesomeModMod! OnInitializeMelon! :D"); LoggerInstance.Msg("SuperAwesomeModMod! OnInitializeMelon! :D");
} }
@ -53,29 +52,43 @@ public class SuperAwesomeModMod : MelonMod
private static void OnPlayerSetupStart() private static void OnPlayerSetupStart()
{ {
CVRRaycastDebugManager.Initialize(PlayerSetup.Instance.desktopCam); // CVRRaycastDebugManager.Initialize(PlayerSetup.Instance.desktopCam);
// UEMenuHelper.Create();
} }
private static void OnShitLoaded(Component c, List<Task> asyncTasks = null, Scene? scene = null) private static void OnShitLoaded(Component c, List<Task> asyncTasks = null, Scene? scene = null)
{ {
if (c == null) if (!c)
return; return;
if (c.gameObject == null) if (!c.gameObject)
return; return;
if (c.gameObject.scene.buildIndex > 0) if (c.gameObject.scene.buildIndex > 0)
return; return;
if ((scene != null) if ((scene != null) && (c.gameObject.scene != scene))
&& (c.gameObject.scene != scene))
return; return;
if (c is Canvas canvas) canvas.gameObject.AddComponent<CVRCanvasWrapper>(); if (c is InputField input)
{
input.AddComponentIfMissing<InputFocusIntentDetector>();
} }
private static void OnProcessCanvas(string collectionId, Canvas canvas) if (c is TMPro.TMP_InputField tmpInput)
{ {
canvas.gameObject.AddComponent<CVRCanvasWrapper>(); tmpInput.AddComponentIfMissing<InputFocusIntentDetector>();
}
}
public class InputFocusIntentDetector : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
if (TryGetComponent(out TMPro.TMP_InputField input) && input.isActiveAndEnabled)
ViewManager.Instance.openMenuKeyboard(input);
else if (TryGetComponent(out InputField inputField) && inputField.isActiveAndEnabled)
ViewManager.Instance.openMenuKeyboard(inputField);
}
} }
} }

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<RootNamespace>ASTExtension</RootNamespace> <RootNamespace>SuperAwesomeMod</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="BTKUILib"> <Reference Include="BTKUILib">

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk" />

View file

@ -0,0 +1,89 @@
using System.Collections;
using ABI_RC.Core;
using ABI_RC.Core.IO;
using ABI_RC.Core.Util.Encryption;
using ABI_RC.Systems.GameEventSystem;
using HarmonyLib;
using MelonLoader;
using UnityEngine;
namespace NAK.ByeByePerformanceThankYouAMD;
public class ByeByePerformanceThankYouAMDMod : MelonMod
{
private static MelonLogger.Instance Logger;
private static readonly MelonPreferences_Category Category =
MelonPreferences.CreateCategory(nameof(ByeByePerformanceThankYouAMD));
private static readonly MelonPreferences_Entry<bool> EntryDisableMaterialInstancing =
Category.CreateEntry(
identifier: "disable_material_instancing",
true,
display_name: "Disable Material Instancing",
description: "Disables material instancing to mitigate a shit visual issue on AMD");
public override void OnInitializeMelon()
{
Logger = LoggerInstance;
ApplyPatches(typeof(CVREncryptionRouter_Patches));
}
private void ApplyPatches(Type type)
{
try
{
HarmonyInstance.PatchAll(type);
}
catch (Exception e)
{
LoggerInstance.Msg($"Failed while patching {type.Name}!");
LoggerInstance.Error(e);
}
}
internal static void ScanForInstancedMaterials()
{
if (!EntryDisableMaterialInstancing.Value)
return;
Logger.Msg("An Asset Bundle has loaded, scanning for instanced materials to disable...");
if (Resources.FindObjectsOfTypeAll(typeof(Material)) is not Material[] allMaterials)
{
Logger.Msg("No materials found.");
return;
}
int count = 0;
foreach (Material material in allMaterials)
{
if (!material || !material.enableInstancing) continue;
material.enableInstancing = false;
count++;
}
Logger.Msg($"Finished scanning for instanced materials. Disabled instancing on {count} loaded materials.");
}
}
internal static class CVREncryptionRouter_Patches
{
[HarmonyPostfix]
[HarmonyPatch(typeof(CVREncryptionRouter), nameof(CVREncryptionRouter.LoadEncryptedBundle), typeof(bool))]
private static void CVREncryptionRouter_LoadEncryptedBundle_Postfix(ref IEnumerator __result)
{
__result = Wrapper(__result);
}
private static IEnumerator Wrapper(IEnumerator inner)
{
yield return null; // before start
while (inner.MoveNext())
yield return inner.Current;
// after finish
ByeByePerformanceThankYouAMDMod.ScanForInstancedMaterials();
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.ByeByePerformanceThankYouAMD.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.ByeByePerformanceThankYouAMD))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.ByeByePerformanceThankYouAMD))]
[assembly: MelonInfo(
typeof(NAK.ByeByePerformanceThankYouAMD.ByeByePerformanceThankYouAMDMod),
nameof(NAK.ByeByePerformanceThankYouAMD),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ByeByePerformanceThankYouAMD"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.ByeByePerformanceThankYouAMD.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,14 @@
# SearchWithSpacesFix
Fixes search terms that use spaces.
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,23 @@
{
"_id": -1,
"name": "SearchWithSpacesFix",
"modversion": "1.0.0",
"gameversion": "2024r177",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Fixes search terms that include spaces.",
"searchtags": [
"search",
"spaces",
"fix",
"meow"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r42/SearchWithSpacesFix.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SearchWithSpacesFix/",
"changelog": "- Initial release",
"embedcolor": "#f61963"
}

View file

@ -1,5 +1,4 @@
using System.Text; using System.Text;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI.CCK.Components.ScriptableObjects; using ABI.CCK.Components.ScriptableObjects;

View file

@ -4,7 +4,6 @@ using ABI.CCK.Components;
using HarmonyLib; using HarmonyLib;
using MelonLoader; using MelonLoader;
using NAK.CVRLuaToolsExtension.NamedPipes; using NAK.CVRLuaToolsExtension.NamedPipes;
using UnityEngine;
namespace NAK.CVRLuaToolsExtension; namespace NAK.CVRLuaToolsExtension;

View file

@ -0,0 +1,103 @@
using ABI_RC.Core.UI.UIRework;
using ABI_RC.Systems.InputManagement;
using UnityEngine;
namespace NAK.DummyMenu;
public class DummyMenuManager : CVRUIManagerBaseInput
{
#region Boilerplate
public static DummyMenuManager Instance { get; private set; }
public override void Start()
{
if (Instance && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
base.Start();
ListenForModSettingChanges();
}
public override void ReloadView()
{
if (IsViewShown && !ModSettings.EntryReloadMenuEvenWhenOpen.Value) return;
TrySetMenuPage();
base.ReloadView();
}
public override void OnFinishedLoad(string _)
{
base.OnFinishedLoad(_);
// Usually done in OnReadyForBindings, but that isn't called for my menu :)
_cohtmlRenderMaterial = _uiRenderer.sharedMaterial;
// ReSharper disable thrice Unity.PreferAddressByIdToGraphicsParams
_cohtmlRenderMaterial.SetTexture("_DesolvePattern", pattern);
_cohtmlRenderMaterial.SetTexture("_DesolveTiming", timing);
_cohtmlRenderMaterial.SetTextureScale("_DesolvePattern", new Vector2(16, 9));
}
public void ToggleView()
{
bool invertShown = !IsViewShown;
ToggleView(invertShown);
CursorLockManager.Instance.SetUnlockWithId(invertShown, nameof(DummyMenuManager));
}
#endregion Boilerplate
private void ListenForModSettingChanges()
{
ModSettings.EntryPageCouiPath.OnEntryValueChanged.Subscribe((oldValue, newValue) =>
{
DummyMenuMod.Logger.Msg($"Changing COUI page from {oldValue} to {newValue}");
cohtmlView.Page = newValue;
});
ModSettings.EntryPageWidth.OnEntryValueChanged.Subscribe((oldValue, newValue) =>
{
DummyMenuMod.Logger.Msg($"Changing COUI width from {oldValue} to {newValue}");
cohtmlView.Width = newValue;
DummyMenuPositionHelper.Instance.UpdateAspectRatio(cohtmlView.Width, cohtmlView.Height);
});
ModSettings.EntryPageHeight.OnEntryValueChanged.Subscribe((oldValue, newValue) =>
{
DummyMenuMod.Logger.Msg($"Changing COUI height from {oldValue} to {newValue}");
cohtmlView.Height = newValue;
DummyMenuPositionHelper.Instance.UpdateAspectRatio(cohtmlView.Width, cohtmlView.Height);
});
ModSettings.EntryToggleMeToResetModifiers.OnEntryValueChanged.Subscribe((_, newValue) =>
{
if (!newValue) return;
DummyMenuMod.Logger.Msg("Resetting modifiers to default because the toggle was enabled");
ModSettings.EntryToggleMeToResetModifiers.Value = false;
ModSettings.EntryDesktopMenuScaleModifier.ResetToDefault();
ModSettings.EntryDesktopMenuDistanceModifier.ResetToDefault();
ModSettings.EntryVrMenuScaleModifier.ResetToDefault();
ModSettings.EntryVrMenuDistanceModifier.ResetToDefault();
});
}
public void TrySetMenuPage()
{
string couiPath = ModSettings.EntryPageCouiPath.Value;
string fullCouiPath = CreateDummyMenu.GetFullCouiPath(couiPath);
if (File.Exists(fullCouiPath))
{
DummyMenuMod.Logger.Msg($"Found COUI page at {fullCouiPath}. Setting it as the controlledview page.");
cohtmlView.Page = "coui://" + couiPath;
}
else
{
DummyMenuMod.Logger.Error($"No COUI page found at {fullCouiPath}. Please create one at that path, or change the mod setting to point to an existing file. Using default example page instead.");
cohtmlView.Page = "coui://" + CreateDummyMenu.ExampleDummyMenuPath;
}
}
}

View file

@ -0,0 +1,37 @@
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Savior;
using UnityEngine;
namespace NAK.DummyMenu;
[DefaultExecutionOrder(16000)] // just before ControllerRay
public class DummyMenuPositionHelper : MenuPositionHelperBase
{
public static DummyMenuPositionHelper Instance { get; private set; }
private void Awake()
{
if (Instance && Instance != this)
{
Destroy(this);
return;
}
Instance = this;
}
public override bool IsMenuOpen => DummyMenuManager.Instance.IsViewShown;
public override float MenuScaleModifier => MetaPort.Instance.isUsingVr ? ModSettings.EntryVrMenuScaleModifier.Value : ModSettings.EntryDesktopMenuScaleModifier.Value;
public override float MenuDistanceModifier => MetaPort.Instance.isUsingVr ? ModSettings.EntryVrMenuDistanceModifier.Value : ModSettings.EntryDesktopMenuDistanceModifier.Value;
public void UpdateAspectRatio(float width, float height)
{
if (width <= 0f || height <= 0f)
return;
_menuAspectRatio = width / height;
float normalizedWidth = width / Mathf.Max(width, height);
float normalizedHeight = height / Mathf.Max(width, height);
menuTransform.localScale = new Vector3(normalizedWidth, normalizedHeight, 1f);
}
}

View file

@ -0,0 +1,243 @@
using ABI_RC.Core;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.UI;
using UnityEngine;
namespace NAK.DummyMenu;
public static class CreateDummyMenu
{
public static void Create()
{
CreateDefaultExamplePageIfNeeded();
GameObject cohtmlRootObject = GameObject.Find("Cohtml");
// Create menu rig
// Cohtml -> Root -> Offset -> Menu
GameObject dummyMenuRoot = new("DummyMenuRoot");
GameObject dummyMenuOffset = new("DummyMenuOffset");
GameObject dummyMenuItself = new("DummyMenu");
dummyMenuItself.transform.SetParent(dummyMenuOffset.transform);
dummyMenuOffset.transform.SetParent(dummyMenuRoot.transform);
dummyMenuRoot.transform.SetParent(cohtmlRootObject.transform);
// Add dummy menu position helper
DummyMenuPositionHelper positionHelper = dummyMenuRoot.AddComponent<DummyMenuPositionHelper>();
positionHelper._offsetTransform = dummyMenuOffset.transform;
positionHelper.menuTransform = dummyMenuItself.transform;
// Add components to menu (MeshFilter, MeshRenderer, Animator, CohtmlControlledView)
MeshFilter meshFilter = dummyMenuItself.AddComponent<MeshFilter>();
MeshRenderer meshRenderer = dummyMenuItself.AddComponent<MeshRenderer>();
Animator animator = dummyMenuItself.AddComponent<Animator>();
CohtmlControlledView controlledView = dummyMenuItself.AddComponent<CohtmlControlledView>();
// Add dummy menu manager
DummyMenuManager menuManager = dummyMenuItself.AddComponent<DummyMenuManager>();
menuManager.cohtmlView = controlledView;
menuManager._viewAnimator = animator;
menuManager._uiRenderer = meshRenderer;
// Steal from main menu
menuManager.pattern = ViewManager.Instance.pattern;
menuManager.timing = ViewManager.Instance.timing;
meshFilter.mesh = ViewManager.Instance.GetComponent<MeshFilter>().sharedMesh;
meshRenderer.sharedMaterial = null; // assign empty material
animator.runtimeAnimatorController = ViewManager.Instance.GetComponent<Animator>().runtimeAnimatorController;
// Put everything on UI Internal layer
dummyMenuRoot.SetLayerRecursive(CVRLayers.UIInternal);
// Apply initial settings
menuManager.TrySetMenuPage();
float pageWidth = ModSettings.EntryPageWidth.Value;
float pageHeight = ModSettings.EntryPageHeight.Value;
positionHelper.UpdateAspectRatio(pageWidth, pageHeight);
}
internal const string ExampleDummyMenuPath = "UIResources/DummyMenu/_example.html";
internal static string GetFullCouiPath(string couiPath) => Path.Combine(Application.streamingAssetsPath, "Cohtml", couiPath);
private static void CreateDefaultExamplePageIfNeeded()
{
// Check if there is a file at our default path
string fullPath = GetFullCouiPath(ExampleDummyMenuPath);
if (File.Exists(fullPath))
{
DummyMenuMod.Logger.Msg($"Dummy menu HTML file already exists at {fullPath}. No need to create a new one.");
return;
}
DummyMenuMod.Logger.Msg($"No dummy menu HTML file found at {fullPath}. Creating a default one.");
// Create the directory if it doesn't exist
string directory = Path.GetDirectoryName(fullPath);
if (!Directory.Exists(directory)) Directory.CreateDirectory(directory!);
// Create a default HTML file
using StreamWriter writer = new(fullPath, false);
writer.Write(DefaultHtmlContent);
writer.Flush();
writer.Close();
DummyMenuMod.Logger.Msg($"Created default dummy menu HTML file at {fullPath}. You can now open the dummy menu in-game.");
}
#region Default HTML Content
private const string DefaultHtmlContent = """
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Cohtml Bubble Pop 1280×720</title>
<style>
html,body {
height:100%; margin:0;
background:#061018;
display:flex; align-items:center; justify-content:center;
font-family:Arial,Helvetica,sans-serif;
}
#stage {
width:1280px; height:720px;
position:relative; overflow:hidden;
border-radius:20px;
border:6px solid rgba(0,255,255,0.12);
box-shadow:0 20px 60px rgba(0,0,0,0.7), inset 0 0 80px rgba(0,255,255,0.03);
background:linear-gradient(135deg,#03121a 0%,#071826 60%);
-webkit-user-select:none; user-select:none;
}
.panel {
position:absolute; left:50%; top:50%;
transform:translate(-50%,-50%);
text-align:center; color:#dff9ff;
pointer-events:none;
z-index:10;
}
.title {
font-size:56px; font-weight:700; letter-spacing:1px;
margin:0; text-shadow:0 6px 30px rgba(0,255,255,0.06);
}
.subtitle { font-size:20px; opacity:0.85; margin-top:6px; }
/* bubbles */
.bubble {
position:absolute; border-radius:50%;
pointer-events:auto;
will-change:transform,opacity;
box-shadow:0 6px 40px rgba(0,255,255,0.12),
inset 0 -8px 30px rgba(255,255,255,0.02);
display:block; transform-origin:center center;
}
.pop-anim { animation:pop .28s ease forwards }
@keyframes pop {
0%{transform:scale(1)}
50%{transform:scale(1.25)}
100%{transform:scale(0);opacity:0}
}
</style>
</head>
<body>
<div id="stage" role="application" aria-label="Bubble pop scene">
<div class="panel">
<h1 class="title">Hello World</h1>
<div class="subtitle">Bubble pop Gameface fixed</div>
</div>
</div>
<script>
(function(){
const stage = document.getElementById('stage');
const bubbles = new Set();
let lastTime = performance.now();
function rand(min,max){ return Math.random()*(max-min)+min; }
function createBubbleAt(x){
const rect = stage.getBoundingClientRect();
const size = Math.round(rand(28,110));
const b = document.createElement('div');
b.className='bubble';
b.style.width = size + 'px';
b.style.height = size + 'px';
const left = Math.max(0, Math.min(rect.width - size, x - size/2));
b.dataset.x = left;
b.dataset.y = rect.height + size;
b.style.left = left + 'px';
b.style.top = rect.height + 'px';
b.style.background =
'radial-gradient(circle at 30% 25%, rgba(255,255,255,0.9), rgba(255,255,255,0.25) 10%, rgba(0,200,230,0.18) 40%, rgba(0,40,60,0.06) 100%)';
b.style.border = '1px solid rgba(255,255,255,0.08)';
b.style.opacity = '0';
b._vy = -rand(20,120);
b._vx = rand(-40,40);
b._rot = rand(-120,120);
b._life = 0;
b._size = size;
b._ttl = rand(4200,12000);
b._popped = false;
b.addEventListener('pointerdown', (e)=>{
e.stopPropagation();
popBubble(b);
});
stage.appendChild(b);
bubbles.add(b);
return b;
}
function popBubble(b){
if(!b || b._popped) return;
b._popped = true;
b.classList.add('pop-anim');
setTimeout(()=>{ try{ b.remove(); }catch{} bubbles.delete(b); },300);
}
let autoInterval = setInterval(()=>{
const rect = stage.getBoundingClientRect();
createBubbleAt(rand(40, rect.width - 40));
}, 420);
stage.addEventListener('pointerdown', (e)=>{
if(e.target.classList.contains('bubble')) return;
const rect = stage.getBoundingClientRect();
const x = e.clientX - rect.left;
createBubbleAt(x);
});
function animate(now){
const dt = Math.min(80, now - lastTime);
lastTime = now;
const rect = stage.getBoundingClientRect();
for(const b of Array.from(bubbles)){
if(b._popped) continue;
b._life += dt;
const nx = parseFloat(b.dataset.x) + b._vx * (dt/1000);
const ny = parseFloat(b.dataset.y) + b._vy * (dt/1000);
b.dataset.x = nx; b.dataset.y = ny;
const rot = (b._rot * (b._life/1000));
b.style.transform = `rotate(${rot}deg)`;
b.style.left = nx + 'px'; b.style.top = ny + 'px';
const ttl = b._ttl;
if(b._life > ttl){
const over = (b._life - ttl)/1000;
b.style.opacity = Math.max(0, 1 - over*2);
}else{
b.style.opacity = Math.min(1, b._life / 200);
}
if(ny + b._size < -150 || parseFloat(b.style.opacity) <= 0.01){
try{ b.remove(); }catch{} bubbles.delete(b);
}
}
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
})();
</script>
</body>
</html>
""";
#endregion Default HTML Content
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>YouAreMineNow</RootNamespace>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,21 @@
using ABI_RC.Systems.GameEventSystem;
using MelonLoader;
using UnityEngine;
namespace NAK.DummyMenu;
public class DummyMenuMod : MelonMod
{
internal static MelonLogger.Instance Logger;
public override void OnInitializeMelon()
{
Logger = LoggerInstance;
CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(CreateDummyMenu.Create);
}
public override void OnUpdate()
{
if (Input.GetKeyDown(ModSettings.EntryToggleDummyMenu.Value)) DummyMenuManager.Instance.ToggleView();
}
}

View file

@ -0,0 +1,64 @@
using cohtml;
using MelonLoader;
using UnityEngine;
namespace NAK.DummyMenu;
public static class ModSettings
{
private static readonly MelonPreferences_Category Category = MelonPreferences.CreateCategory(nameof(DummyMenu));
internal static readonly MelonPreferences_Entry<KeyCode> EntryToggleDummyMenu =
Category.CreateEntry(
identifier: "toggle_dummy_menu",
default_value: KeyCode.F3,
display_name: "Toggle Menu Key",
description: "Key used to toggle the dummy menu.");
internal static readonly MelonPreferences_Entry<string> EntryPageCouiPath =
Category.CreateEntry(
identifier: "page_coui_path",
default_value: "UIResources/DummyMenu/menu.html",
display_name: "Page Coui Path",
description: "Path to the folder containing the root menu html. This is relative to the StreamingAssets folder.");
internal static readonly MelonPreferences_Entry<int> EntryPageWidth =
Category.CreateEntry("page_width", CohtmlView.DefaultWidth,
display_name: "Page Width",
description: "Width of the menu page in pixels. Default is 1280 pixels.");
internal static readonly MelonPreferences_Entry<int> EntryPageHeight =
Category.CreateEntry("page_height", CohtmlView.DefaultHeight,
display_name: "Page Height",
description: "Height of the menu page in pixels. Default is 720 pixels.");
internal static readonly MelonPreferences_Entry<bool> EntryReloadMenuEvenWhenOpen =
Category.CreateEntry("reload_menu_even_when_open", false,
display_name: "Reload Menu Even When Open",
description: "If enabled, the menu will be reloaded even if it is already open.");
internal static readonly MelonPreferences_Entry<float> EntryVrMenuScaleModifier =
Category.CreateEntry("vr_menu_scale_modifier", 0.75f,
display_name: "VR Menu Scale Modifier",
description: "Adjusts the scale of the menu while in VR. Default is 0.75.");
internal static readonly MelonPreferences_Entry<float> EntryDesktopMenuScaleModifier =
Category.CreateEntry("desktop_menu_scale_modifier", 1f,
display_name: "Desktop Menu Scale Modifier",
description: "Adjusts the scale of the menu while in Desktop mode. Default is 1.");
internal static readonly MelonPreferences_Entry<float> EntryVrMenuDistanceModifier =
Category.CreateEntry("vr_menu_distance_modifier", 1f,
display_name: "VR Menu Distance Modifier",
description: "Adjusts the distance of the menu from the camera while in VR. Default is 1.");
internal static readonly MelonPreferences_Entry<float> EntryDesktopMenuDistanceModifier =
Category.CreateEntry("desktop_menu_distance_modifier", 1.2f,
display_name: "Desktop Menu Distance Modifier",
description: "Adjusts the distance of the menu from the camera while in Desktop mode. Default is 1.2.");
internal static readonly MelonPreferences_Entry<bool> EntryToggleMeToResetModifiers =
Category.CreateEntry("toggle_me_to_reset_modifiers", false,
display_name: "Toggle Me To Reset Modifiers",
description: "If enabled, toggling the menu will reset any scale/distance modifiers applied by other mods.");
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.DummyMenu.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.DummyMenu))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.DummyMenu))]
[assembly: MelonInfo(
typeof(NAK.DummyMenu.DummyMenuMod),
nameof(NAK.DummyMenu),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DummyMenu"
)]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
namespace NAK.DummyMenu.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -1,6 +1,4 @@
using ABI_RC.Core.Player; using MelonLoader;
using MelonLoader;
using UnityEngine;
namespace NAK.LuaNetworkVariables; namespace NAK.LuaNetworkVariables;

View file

@ -17,7 +17,7 @@ using System.Reflection;
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LuaNetworkVariables" downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LuaNetworkVariables"
)] )]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] [assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink [assembly: MelonColor(255, 246, 25, 99)] // red-pink
@ -27,6 +27,6 @@ using System.Reflection;
namespace NAK.LuaNetworkVariables.Properties; namespace NAK.LuaNetworkVariables.Properties;
internal static class AssemblyInfoParams internal static class AssemblyInfoParams
{ {
public const string Version = "1.0.2"; public const string Version = "1.0.3";
public const string Author = "NotAKidoS"; public const string Author = "NotAKidoS";
} }

View file

@ -1,23 +0,0 @@
{
"_id": 234,
"name": "WhereAmIPointing",
"modversion": "1.0.1",
"gameversion": "2024r175",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction.",
"searchtags": [
"controller",
"ray",
"line",
"tomato"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r40/WhereAmIPointing.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing/",
"changelog": "- Fixed line renderer alpha not being reset when the menu is closed.",
"embedcolor": "#f61963"
}

View file

@ -1,7 +1,7 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using BTKUILib; using ABI_RC.Systems.UI.UILib;
using BTKUILib.UIObjects; using ABI_RC.Systems.UI.UILib.UIObjects;
using BTKUILib.UIObjects.Components; using ABI_RC.Systems.UI.UILib.UIObjects.Components;
using NAK.OriginShift; using NAK.OriginShift;
namespace NAK.OriginShiftMod.Integrations; namespace NAK.OriginShiftMod.Integrations;

View file

@ -1,6 +1,6 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using BTKUILib; using ABI_RC.Systems.UI.UILib;
using BTKUILib.UIObjects; using ABI_RC.Systems.UI.UILib.UIObjects;
using NAK.OriginShift; using NAK.OriginShift;
namespace NAK.OriginShiftMod.Integrations; namespace NAK.OriginShiftMod.Integrations;

View file

@ -1,7 +1,7 @@
using System.Reflection; using System.Reflection;
using BTKUILib; using ABI_RC.Systems.UI.UILib;
using BTKUILib.UIObjects; using ABI_RC.Systems.UI.UILib.UIObjects;
using BTKUILib.UIObjects.Components; using ABI_RC.Systems.UI.UILib.UIObjects.Components;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;

View file

@ -28,6 +28,7 @@ public static class ThirdPersonAddon
private static IEnumerator FixThirdPersonCompatibility() private static IEnumerator FixThirdPersonCompatibility()
{ {
yield return null; // wait a frame for the camera to be setup
yield return null; // wait a frame for the camera to be setup yield return null; // wait a frame for the camera to be setup
GameObject thirdPersonCameraObj = GameObject.Find("_PLAYERLOCAL/[CameraRigDesktop]/Camera/ThirdPersonCameraObj"); GameObject thirdPersonCameraObj = GameObject.Find("_PLAYERLOCAL/[CameraRigDesktop]/Camera/ThirdPersonCameraObj");
thirdPersonCameraObj.AddComponentIfMissing<OriginShiftOcclusionCullingDisabler>(); thirdPersonCameraObj.AddComponentIfMissing<OriginShiftOcclusionCullingDisabler>();

View file

@ -1,13 +1,9 @@
#if !UNITY_EDITOR #if !UNITY_EDITOR
using System.Globalization;
using ABI_RC.Core.UI;
using ABI_RC.Core.Util.AssetFiltering; using ABI_RC.Core.Util.AssetFiltering;
using ABI_RC.Systems.Movement;
using MelonLoader; using MelonLoader;
using NAK.OriginShift.Components; using NAK.OriginShift.Components;
using NAK.OriginShiftMod.Integrations; using NAK.OriginShiftMod.Integrations;
using OriginShift.Integrations; using OriginShift.Integrations;
using UnityEngine;
namespace NAK.OriginShift; namespace NAK.OriginShift;
@ -36,7 +32,6 @@ public class OriginShiftMod : MelonMod
// Compatibility Mode // Compatibility Mode
ApplyPatches(typeof(Patches.PlayerSetupPatches)); // net ik, camera occlusion culling ApplyPatches(typeof(Patches.PlayerSetupPatches)); // net ik, camera occlusion culling
ApplyPatches(typeof(Patches.Comms_ClientPatches)); // voice pos
ApplyPatches(typeof(Patches.CVRSyncHelperPatches)); // spawnable pos ApplyPatches(typeof(Patches.CVRSyncHelperPatches)); // spawnable pos
ApplyPatches(typeof(Patches.PuppetMasterPatches)); // remote player pos ApplyPatches(typeof(Patches.PuppetMasterPatches)); // remote player pos
ApplyPatches(typeof(Patches.CVRObjectSyncPatches)); // remote object pos ApplyPatches(typeof(Patches.CVRObjectSyncPatches)); // remote object pos

View file

@ -26,7 +26,7 @@
// //
// #region Constants // #region Constants
// //
// private const string ModId = "MelonMod.NAK.RelativeSync"; // private const string ModId = "MelonMod.NAK.RelativeSyncJitterFix";
// //
// #endregion // #endregion
// //

View file

@ -1,11 +1,9 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using JetBrains.Annotations;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
#if !UNITY_EDITOR #if !UNITY_EDITOR
using ABI_RC.Core.Util; using ABI_RC.Core.Util;
using ABI_RC.Core.Util.AssetFiltering;
#endif #endif
namespace NAK.OriginShift.Components; namespace NAK.OriginShift.Components;

View file

@ -18,7 +18,7 @@ public class OriginShiftParticleSystemReceiver : MonoBehaviour
_particleSystems = GetComponentsInChildren<ParticleSystem>(true); _particleSystems = GetComponentsInChildren<ParticleSystem>(true);
if (_particleSystems.Length == 0) if (_particleSystems.Length == 0)
{ {
OriginShiftMod.Logger.Error("OriginShiftParticleSystemReceiver: No ParticleSystems found on GameObject: " + gameObject.name, this); // OriginShiftMod.Logger.Error("OriginShiftParticleSystemReceiver: No ParticleSystems found on GameObject: " + gameObject.name, this);
enabled = false; enabled = false;
} }
} }

View file

@ -15,7 +15,7 @@ public class OriginShiftRigidbodyReceiver : MonoBehaviour
_rigidbody = GetComponentInChildren<Rigidbody>(); _rigidbody = GetComponentInChildren<Rigidbody>();
if (_rigidbody == null) if (_rigidbody == null)
{ {
OriginShiftMod.Logger.Error("OriginShiftRigidbodyReceiver: No Rigidbody found on GameObject: " + gameObject.name, this); // OriginShiftMod.Logger.Error("OriginShiftRigidbodyReceiver: No Rigidbody found on GameObject: " + gameObject.name, this);
enabled = false; enabled = false;
} }
} }

View file

@ -18,7 +18,7 @@ public class OriginShiftTrailRendererReceiver : MonoBehaviour
_trailRenderers = GetComponentsInChildren<TrailRenderer>(true); _trailRenderers = GetComponentsInChildren<TrailRenderer>(true);
if (_trailRenderers.Length == 0) if (_trailRenderers.Length == 0)
{ {
OriginShiftMod.Logger.Error("OriginShiftTrailRendererReceiver: No TrailRenderers found on GameObject: " + gameObject.name, this); // OriginShiftMod.Logger.Error("OriginShiftTrailRendererReceiver: No TrailRenderers found on GameObject: " + gameObject.name, this);
enabled = false; enabled = false;
} }
} }

View file

@ -1,4 +1,3 @@
using System;
using ABI_RC.Core.Base; using ABI_RC.Core.Base;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI.CCK.Components; using ABI.CCK.Components;

View file

@ -1,5 +1,3 @@
using System.Collections;
using ABI_RC.Core;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using NAK.OriginShift.Components; using NAK.OriginShift.Components;
using NAK.OriginShift.Extensions; using NAK.OriginShift.Extensions;

View file

@ -6,11 +6,9 @@ using ABI_RC.Core.Player;
using ABI_RC.Core.Util; using ABI_RC.Core.Util;
using ABI_RC.Systems.Camera; using ABI_RC.Systems.Camera;
using ABI_RC.Systems.Communications.Networking; using ABI_RC.Systems.Communications.Networking;
using ABI_RC.Systems.GameEventSystem;
using ABI_RC.Systems.Movement; using ABI_RC.Systems.Movement;
using ABI.CCK.Components; using ABI.CCK.Components;
using DarkRift; using DarkRift;
using ECM2;
using HarmonyLib; using HarmonyLib;
using NAK.OriginShift.Components; using NAK.OriginShift.Components;
using NAK.OriginShift.Hacks; using NAK.OriginShift.Hacks;
@ -129,7 +127,7 @@ internal static class PortableCameraPatches
[HarmonyPatch(typeof(PortableCamera), nameof(PortableCamera.Start))] [HarmonyPatch(typeof(PortableCamera), nameof(PortableCamera.Start))]
private static void Postfix_PortableCamera_Start(ref PortableCamera __instance) private static void Postfix_PortableCamera_Start(ref PortableCamera __instance)
{ {
__instance.cameraComponent.AddComponentIfMissing<OriginShiftOcclusionCullingDisabler>(); __instance.CameraComponent.AddComponentIfMissing<OriginShiftOcclusionCullingDisabler>();
} }
} }
@ -143,17 +141,6 @@ internal static class PathingCameraPatches
} }
} }
internal static class Comms_ClientPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(Comms_Client), nameof(Comms_Client.SetPosition))]
private static void Prefix_Comms_Client_GetPlayerMovementData(ref Vector3 listenerPosition)
{
if (OriginShiftManager.CompatibilityMode) // adjust root position back to absolute world position
listenerPosition = OriginShiftManager.GetAbsolutePosition(listenerPosition);
}
}
internal static class CVRSyncHelperPatches internal static class CVRSyncHelperPatches
{ {
[HarmonyPrefix] // outbound spawnable [HarmonyPrefix] // outbound spawnable
@ -180,15 +167,11 @@ internal static class CVRSyncHelperPatches
[HarmonyPrefix] // outbound spawn prop [HarmonyPrefix] // outbound spawn prop
[HarmonyPatch(typeof(CVRSyncHelper), nameof(CVRSyncHelper.SpawnProp))] [HarmonyPatch(typeof(CVRSyncHelper), nameof(CVRSyncHelper.SpawnProp))]
private static void Prefix_CVRSyncHelper_SpawnProp(ref float posX, ref float posY, ref float posZ) private static void Prefix_CVRSyncHelper_SpawnProp(ref Vector3 position)
{ {
if (OriginShiftManager.CompatibilityMode) // adjust root position back to absolute world position if (OriginShiftManager.CompatibilityMode) // adjust root position back to absolute world position
{ {
Vector3 position = new(posX, posY, posZ); // imagine not using Vector3
position = OriginShiftManager.GetAbsolutePosition(position); position = OriginShiftManager.GetAbsolutePosition(position);
posX = position.x;
posY = position.y;
posZ = position.z;
} }
} }

Some files were not shown because too many files have changed in this diff Show more