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;
namespace NAK.AvatarQueueSystemTweaks;

View file

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

View file

@ -1,4 +1,4 @@
using MelonLoader;
using MelonLoader;
using NAK.AvatarQueueSystemTweaks.Properties;
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"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[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"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[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 ABI_RC.Core.Util.AnimatorManager;
using System.Reflection;
using ABI_RC.Systems.Movement;
using ABI.CCK.Scripts;
using HarmonyLib;
using MelonLoader;
using UnityEngine;

View file

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

View file

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

View file

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

View file

@ -3,22 +3,6 @@
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;
using UnityEngine;
using UnityEngine.Rendering;
[RequireComponent(typeof(Camera))]
public class DepthTextureFix : MonoBehaviour
{

View file

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

View file

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

View file

@ -1,121 +1,121 @@
using ABI_RC.Core.Base;
using ABI_RC.Core.Player;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace NAK.SuperAwesomeMod.Components
{
public class CVRCanvasWrapper : MonoBehaviour
{
public bool IsInteractable = true;
public float MaxInteractDistance = 10f;
private Canvas _canvas;
private GraphicRaycaster _graphicsRaycaster;
private static readonly List<RaycastResult> _raycastResults = new();
private static readonly PointerEventData _pointerEventData = new(EventSystem.current);
private static Selectable _workingSelectable;
private Camera _camera;
private RectTransform _rectTransform;
#region Unity Events
private void Awake()
{
if (!TryGetComponent(out _canvas)
|| _canvas.renderMode != RenderMode.WorldSpace)
{
IsInteractable = false;
return;
}
_rectTransform = _canvas.GetComponent<RectTransform>();
}
private void Start()
{
_graphicsRaycaster = _canvas.gameObject.AddComponent<GraphicRaycaster>();
_camera = PlayerSetup.Instance.activeCam;
_canvas.worldCamera = _camera;
}
#endregion Unity Events
#region Public Methods
public bool GetGraphicsHit(Ray worldRay, out RaycastResult result)
{
result = default;
if (!IsInteractable || _camera == null) return false;
// Get the plane of the canvas
Plane canvasPlane = new(transform.forward, transform.position);
// Find where the ray intersects the canvas plane
if (!canvasPlane.Raycast(worldRay, out float distance))
return false;
// Get the world point of intersection
Vector3 worldHitPoint = worldRay.origin + worldRay.direction * distance;
// Check if hit point is within max interaction distance
if (Vector3.Distance(worldRay.origin, worldHitPoint) > MaxInteractDistance)
return false;
// Check if hit point is within canvas bounds
Vector3 localHitPoint = transform.InverseTransformPoint(worldHitPoint);
Rect canvasRect = _rectTransform.rect;
if (!canvasRect.Contains(new Vector2(localHitPoint.x, localHitPoint.y)))
return false;
// Convert world hit point to screen space
Vector2 screenPoint = _camera.WorldToScreenPoint(worldHitPoint);
// Update pointer event data
_pointerEventData.position = screenPoint;
_pointerEventData.delta = Vector2.zero;
// Clear previous results and perform raycast
_raycastResults.Clear();
_graphicsRaycaster.Raycast(_pointerEventData, _raycastResults);
// Early out if no hits
if (_raycastResults.Count == 0)
{
//Debug.Log($"No hits on canvas {_canvas.name}");
return false;
}
// Find first valid interactive UI element
foreach (RaycastResult hit in _raycastResults)
{
if (!hit.isValid)
{
//Debug.Log($"Invalid hit on canvas {_canvas.name}");
continue;
}
// Check if the hit object has a Selectable component and is interactable
GameObject hitObject = hit.gameObject;
if (!hitObject.TryGetComponent(out _workingSelectable)
|| !_workingSelectable.interactable)
{
//Debug.Log($"Non-interactable hit on canvas {_canvas.name} - {hitObject.name}");
continue;
}
//Debug.Log($"Hit on canvas {_canvas.name} with {hitObject.name}");
result = hit;
return true;
}
return false;
}
#endregion Public Methods
}
}
// using ABI_RC.Core.Base;
// using ABI_RC.Core.Player;
// using UnityEngine;
// using UnityEngine.EventSystems;
// using UnityEngine.UI;
//
// namespace NAK.SuperAwesomeMod.Components
// {
// public class CVRCanvasWrapper : MonoBehaviour
// {
// public bool IsInteractable = true;
// public float MaxInteractDistance = 10f;
//
// private Canvas _canvas;
// private GraphicRaycaster _graphicsRaycaster;
// private static readonly List<RaycastResult> _raycastResults = new();
// private static readonly PointerEventData _pointerEventData = new(EventSystem.current);
//
// private static Selectable _workingSelectable;
// private Camera _camera;
// private RectTransform _rectTransform;
//
// #region Unity Events
//
// private void Awake()
// {
// if (!TryGetComponent(out _canvas)
// || _canvas.renderMode != RenderMode.WorldSpace)
// {
// IsInteractable = false;
// return;
// }
//
// _rectTransform = _canvas.GetComponent<RectTransform>();
// }
//
// private void Start()
// {
// _graphicsRaycaster = _canvas.gameObject.AddComponent<GraphicRaycaster>();
// _camera = PlayerSetup.Instance.activeCam;
// _canvas.worldCamera = _camera;
// }
//
// #endregion Unity Events
//
// #region Public Methods
//
// public bool GetGraphicsHit(Ray worldRay, out RaycastResult result)
// {
// result = default;
//
// if (!IsInteractable || _camera == null) return false;
//
// // Get the plane of the canvas
// Plane canvasPlane = new(transform.forward, transform.position);
//
// // Find where the ray intersects the canvas plane
// if (!canvasPlane.Raycast(worldRay, out float distance))
// return false;
//
// // Get the world point of intersection
// Vector3 worldHitPoint = worldRay.origin + worldRay.direction * distance;
//
// // Check if hit point is within max interaction distance
// if (Vector3.Distance(worldRay.origin, worldHitPoint) > MaxInteractDistance)
// return false;
//
// // Check if hit point is within canvas bounds
// Vector3 localHitPoint = transform.InverseTransformPoint(worldHitPoint);
// Rect canvasRect = _rectTransform.rect;
// if (!canvasRect.Contains(new Vector2(localHitPoint.x, localHitPoint.y)))
// return false;
//
// // Convert world hit point to screen space
// Vector2 screenPoint = _camera.WorldToScreenPoint(worldHitPoint);
//
// // Update pointer event data
// _pointerEventData.position = screenPoint;
// _pointerEventData.delta = Vector2.zero;
//
// // Clear previous results and perform raycast
// _raycastResults.Clear();
// _graphicsRaycaster.Raycast(_pointerEventData, _raycastResults);
//
// // Early out if no hits
// if (_raycastResults.Count == 0)
// {
// //Debug.Log($"No hits on canvas {_canvas.name}");
// return false;
// }
//
// // Find first valid interactive UI element
// foreach (RaycastResult hit in _raycastResults)
// {
// if (!hit.isValid)
// {
// //Debug.Log($"Invalid hit on canvas {_canvas.name}");
// continue;
// }
//
// // Check if the hit object has a Selectable component and is interactable
// GameObject hitObject = hit.gameObject;
// if (!hitObject.TryGetComponent(out _workingSelectable)
// || !_workingSelectable.interactable)
// {
// //Debug.Log($"Non-interactable hit on canvas {_canvas.name} - {hitObject.name}");
// continue;
// }
//
// //Debug.Log($"Hit on canvas {_canvas.name} with {hitObject.name}");
//
// result = hit;
// return true;
// }
//
// return false;
// }
//
// #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.UI;
using ABI.CCK.Components;
using NAK.SuperAwesomeMod.Components;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
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
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)
return result;
}
@ -150,7 +149,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
int proximityHits = Physics.SphereCastNonAlloc(
ray.origin,
RAYCAST_SPHERE_RADIUS,
Vector3.up,
ray.direction,
_hits,
0.001f,
_layerMask,
@ -313,6 +312,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
result.hitInteractable = _workingInteractable;
hitValidComponent = true;
}
if (_workingGameObject.TryGetComponent(out _workingPickupable)
&& _workingPickupable.CanPickup
&& IsCVRPickupableWithinRange(_workingPickupable, hit))
@ -343,6 +343,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
#region Protected Methods
protected abstract Ray GetRayFromImpl();
protected abstract Ray GetProximityRayFromImpl();
#endregion Protected Methods
@ -375,10 +376,10 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
return hit.distance <= pickupable.MaxGrabDistance;
}
private static bool IsCVRCanvasWrapperWithinRange(CVRCanvasWrapper canvasWrapper, RaycastHit hit)
{
return hit.distance <= canvasWrapper.MaxInteractDistance;
}
// private static bool IsCVRCanvasWrapperWithinRange(CVRCanvasWrapper canvasWrapper, RaycastHit hit)
// {
// return hit.distance <= canvasWrapper.MaxInteractDistance;
// }
#endregion Utility Because Original Methods Are Broken
}

View file

@ -4,10 +4,24 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
{
public class CVRPlayerRaycasterMouse : CVRPlayerRaycaster
{
private readonly Camera _camera;
#region Constructor
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
? new Ray(_camera.transform.position, _camera.transform.forward)
: _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 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 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.UI;
using NAK.SuperAwesomeMod.Components;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace ABI_RC.Core.Player.Interaction.RaycastImpl
@ -19,6 +17,7 @@ namespace ABI_RC.Core.Player.Interaction.RaycastImpl
// Main raycast hit info
public RaycastHit hit;
public RaycastHit? waterHit; // Only valid if hitWater is true
public Vector2 hitScreenPoint; // Screen coordinates of the hit
// Specific hit components
public Pickupable hitPickupable;

View file

@ -1,5 +1,4 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
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 ABI_RC.Core.Base;
using ABI_RC.Core.Base.Jobs;
using ABI_RC.Core.InteractionSystem;
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 MelonLoader;
using NAK.SuperAwesomeMod.Components;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace NAK.SuperAwesomeMod;
@ -33,13 +32,13 @@ public class SuperAwesomeModMod : MelonMod
BindingFlags.NonPublic | BindingFlags.Static))
);
// patch SharedFilter.ProcessCanvas
HarmonyInstance.Patch(
typeof(SharedFilter).GetMethod(nameof(SharedFilter.ProcessCanvas),
BindingFlags.Public | BindingFlags.Static),
postfix: new HarmonyMethod(typeof(SuperAwesomeModMod).GetMethod(nameof(OnProcessCanvas),
BindingFlags.NonPublic | BindingFlags.Static))
);
// // patch SharedFilter.ProcessCanvas
// HarmonyInstance.Patch(
// typeof(SharedFilter).GetMethod(nameof(SharedFilter.ProcessCanvas),
// BindingFlags.Public | BindingFlags.Static),
// postfix: new HarmonyMethod(typeof(SuperAwesomeModMod).GetMethod(nameof(OnProcessCanvas),
// BindingFlags.NonPublic | BindingFlags.Static))
// );
LoggerInstance.Msg("SuperAwesomeModMod! OnInitializeMelon! :D");
}
@ -53,29 +52,43 @@ public class SuperAwesomeModMod : MelonMod
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)
{
if (c == null)
if (!c)
return;
if (c.gameObject == null)
if (!c.gameObject)
return;
if (c.gameObject.scene.buildIndex > 0)
return;
if ((scene != null)
&& (c.gameObject.scene != scene))
if ((scene != null) && (c.gameObject.scene != scene))
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"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>ASTExtension</RootNamespace>
<RootNamespace>SuperAwesomeMod</RootNamespace>
</PropertyGroup>
<ItemGroup>
<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 ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.UI;
using ABI.CCK.Components;
using ABI.CCK.Components.ScriptableObjects;

View file

@ -4,7 +4,6 @@ using ABI.CCK.Components;
using HarmonyLib;
using MelonLoader;
using NAK.CVRLuaToolsExtension.NamedPipes;
using UnityEngine;
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 UnityEngine;
using MelonLoader;
namespace NAK.LuaNetworkVariables;

View file

@ -17,7 +17,7 @@ using System.Reflection;
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: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
@ -27,6 +27,6 @@ using System.Reflection;
namespace NAK.LuaNetworkVariables.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.2";
public const string Version = "1.0.3";
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 BTKUILib;
using BTKUILib.UIObjects;
using BTKUILib.UIObjects.Components;
using ABI_RC.Systems.UI.UILib;
using ABI_RC.Systems.UI.UILib.UIObjects;
using ABI_RC.Systems.UI.UILib.UIObjects.Components;
using NAK.OriginShift;
namespace NAK.OriginShiftMod.Integrations;

View file

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

View file

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

View file

@ -28,6 +28,7 @@ public static class ThirdPersonAddon
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
GameObject thirdPersonCameraObj = GameObject.Find("_PLAYERLOCAL/[CameraRigDesktop]/Camera/ThirdPersonCameraObj");
thirdPersonCameraObj.AddComponentIfMissing<OriginShiftOcclusionCullingDisabler>();

View file

@ -1,13 +1,9 @@
#if !UNITY_EDITOR
using System.Globalization;
using ABI_RC.Core.UI;
using ABI_RC.Core.Util.AssetFiltering;
using ABI_RC.Systems.Movement;
using MelonLoader;
using NAK.OriginShift.Components;
using NAK.OriginShiftMod.Integrations;
using OriginShift.Integrations;
using UnityEngine;
namespace NAK.OriginShift;
@ -36,7 +32,6 @@ public class OriginShiftMod : MelonMod
// Compatibility Mode
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.PuppetMasterPatches)); // remote player pos
ApplyPatches(typeof(Patches.CVRObjectSyncPatches)); // remote object pos

View file

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

View file

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

View file

@ -18,7 +18,7 @@ public class OriginShiftParticleSystemReceiver : MonoBehaviour
_particleSystems = GetComponentsInChildren<ParticleSystem>(true);
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;
}
}

View file

@ -15,7 +15,7 @@ public class OriginShiftRigidbodyReceiver : MonoBehaviour
_rigidbody = GetComponentInChildren<Rigidbody>();
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;
}
}

View file

@ -18,7 +18,7 @@ public class OriginShiftTrailRendererReceiver : MonoBehaviour
_trailRenderers = GetComponentsInChildren<TrailRenderer>(true);
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;
}
}

View file

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

View file

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

View file

@ -6,11 +6,9 @@ using ABI_RC.Core.Player;
using ABI_RC.Core.Util;
using ABI_RC.Systems.Camera;
using ABI_RC.Systems.Communications.Networking;
using ABI_RC.Systems.GameEventSystem;
using ABI_RC.Systems.Movement;
using ABI.CCK.Components;
using DarkRift;
using ECM2;
using HarmonyLib;
using NAK.OriginShift.Components;
using NAK.OriginShift.Hacks;
@ -129,7 +127,7 @@ internal static class PortableCameraPatches
[HarmonyPatch(typeof(PortableCamera), nameof(PortableCamera.Start))]
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
{
[HarmonyPrefix] // outbound spawnable
@ -180,15 +167,11 @@ internal static class CVRSyncHelperPatches
[HarmonyPrefix] // outbound spawn prop
[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
{
Vector3 position = new(posX, posY, posZ); // imagine not using Vector3
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