mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2026-06-22 22:38:30 +00:00
[NAK_CVR_Mods] Unfucked for 2026r182
This commit is contained in:
parent
c13dc8375a
commit
281403d68b
209 changed files with 3936 additions and 1122 deletions
|
|
@ -0,0 +1,37 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace NAK.PropLoadingHexagon.Components;
|
||||
|
||||
public class LoadingHexagonController : MonoBehaviour
|
||||
{
|
||||
public bool IsLoadingCanceled { get; set; }
|
||||
|
||||
[SerializeField] private SkinnedMeshRenderer _hexRenderer;
|
||||
[SerializeField] private TMPro.TextMeshPro _loadingText;
|
||||
[SerializeField] private Transform[] _hexTransforms;
|
||||
private float _scale;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_scale < 1f)
|
||||
{
|
||||
_scale += Time.deltaTime * 4f;
|
||||
transform.GetChild(0).localScale = Vector3.one * _scale;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// give slightly different rotation to each hexagon
|
||||
_hexTransforms[i].Rotate(Vector3.up, 30f * Time.deltaTime * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLoadingText(string text)
|
||||
{
|
||||
if (_loadingText.text == text) return;
|
||||
_loadingText.text = text;
|
||||
}
|
||||
|
||||
public void SetLoadingShape(float value)
|
||||
=> _hexRenderer.SetBlendShapeWeight(0, value);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// using NAK.PropLoadingHexagon.Components;
|
||||
// using UnityEngine;
|
||||
//
|
||||
// namespace NAK.PropLoadingHexagon.Integrations;
|
||||
//
|
||||
// // ReSharper disable once ClassNeverInstantiated.Global
|
||||
// public class TheClapperIntegration
|
||||
// {
|
||||
// public static void Init()
|
||||
// {
|
||||
// PropLoadingHexagonMod.OnPropPlaceholderCreated += (placeholder) =>
|
||||
// {
|
||||
// if (placeholder.TryGetComponent(out LoadingHexagonController loadingHexagon))
|
||||
// ClappableLoadingHex.Create(loadingHexagon);
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public class ClappableLoadingHex : Kafe.TheClapper.Clappable
|
||||
// {
|
||||
// private LoadingHexagonController _loadingHexagon;
|
||||
//
|
||||
// public override void OnClapped(Vector3 clappablePosition)
|
||||
// {
|
||||
// if (_loadingHexagon == null) return;
|
||||
// _loadingHexagon.IsLoadingCanceled = true;
|
||||
// Kafe.TheClapper.TheClapper.EmitParticles(clappablePosition, new Color(1f, 1f, 0f), 2f); // why this internal
|
||||
// }
|
||||
//
|
||||
// public static void Create(LoadingHexagonController loadingHexagon)
|
||||
// {
|
||||
// GameObject target = loadingHexagon.transform.GetChild(0).gameObject;
|
||||
// if (!target.gameObject.TryGetComponent(out ClappableLoadingHex clappableHexagon))
|
||||
// clappableHexagon = target.AddComponent<ClappableLoadingHex>();
|
||||
// clappableHexagon._loadingHexagon = loadingHexagon;
|
||||
// }
|
||||
// }
|
||||
313
.Deprecated/PropLoadingHexagon/Main.cs
Normal file
313
.Deprecated/PropLoadingHexagon/Main.cs
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
using System.Reflection;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using ABI_RC.Core.IO;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Util;
|
||||
using DarkRift;
|
||||
using HarmonyLib;
|
||||
using MelonLoader;
|
||||
using NAK.PropLoadingHexagon.Components;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace NAK.PropLoadingHexagon;
|
||||
|
||||
public class PropLoadingHexagonMod : MelonMod
|
||||
{
|
||||
internal static Action<GameObject> OnPropPlaceholderCreated;
|
||||
|
||||
private static readonly List<LoadingPropMarker> Loading_Hex_List = new();
|
||||
private static GameObject loadingHexContainer;
|
||||
private static GameObject loadingHexPrefab;
|
||||
|
||||
#region Melon Preferences
|
||||
|
||||
private static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(nameof(PropLoadingHexagon));
|
||||
|
||||
private static readonly MelonPreferences_Entry<bool> EntryKeepIndicatorWhenFiltered =
|
||||
Category.CreateEntry("keep_indicator_when_filtered", false,
|
||||
"Keep Loading Hex When Filtered", description: "Keeps the loading hexagon when the prop is filtered.");
|
||||
|
||||
#endregion Melon Preferences
|
||||
|
||||
#region Melon Events
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
HarmonyInstance.Patch( // create prop placeholder container
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.Start),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
postfix: new HarmonyMethod(typeof(PropLoadingHexagonMod).GetMethod(nameof(OnPlayerSetupStart),
|
||||
BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
|
||||
HarmonyInstance.Patch( // spawn prop placeholder
|
||||
typeof(CVRSyncHelper).GetMethod(nameof(CVRSyncHelper.SpawnPropFromNetwork),
|
||||
BindingFlags.Public | BindingFlags.Static),
|
||||
postfix: new HarmonyMethod(typeof(PropLoadingHexagonMod).GetMethod(nameof(OnPropSpawnedFromNetwork),
|
||||
BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
|
||||
HarmonyInstance.Patch( // delete mode on prop placeholder
|
||||
typeof(ControllerRay).GetMethod(nameof(ControllerRay.HandleSpawnableClicked),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
prefix: new HarmonyMethod(typeof(PropLoadingHexagonMod).GetMethod(nameof(OnHandleSpawnableClicked),
|
||||
BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
|
||||
// this luckily is called for all props, not just our own, otherwise we'd be fucked
|
||||
HarmonyInstance.Patch( // check for if prop failed to load
|
||||
typeof(CVRSyncHelper).GetMethod(nameof(CVRSyncHelper.DeleteMyPropByInstanceIdOverNetwork),
|
||||
BindingFlags.Public | BindingFlags.Static),
|
||||
postfix: new HarmonyMethod(typeof(PropLoadingHexagonMod).GetMethod(nameof(OnDeleteMyPropByInstanceIdOverNetwork),
|
||||
BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
|
||||
LoadAssetBundle();
|
||||
//HandleIntegrations();
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
if (Loading_Hex_List.Count <= 0)
|
||||
return;
|
||||
|
||||
for (int i = Loading_Hex_List.Count - 1; i >= 0; i--)
|
||||
{
|
||||
LoadingPropMarker marker = Loading_Hex_List[i];
|
||||
if (marker.propData == null // i dont think can happen
|
||||
|| string.IsNullOrEmpty(marker.propData.InstanceId) // prop data likely recycled
|
||||
|| (marker.propData.Wrapper != null && marker.propData.Spawnable != null)) // prop has spawned
|
||||
{
|
||||
marker.Reset();
|
||||
Loading_Hex_List.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
|
||||
if (marker.IsLoadingCanceled)
|
||||
{
|
||||
marker.Cancel();
|
||||
marker.Reset();
|
||||
Loading_Hex_List.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
|
||||
marker.Update();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Melon Events
|
||||
|
||||
// #region Integrations
|
||||
//
|
||||
// [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
|
||||
// private void HandleIntegrations()
|
||||
// {
|
||||
// if (RegisteredMelons.Any(it => it.Info.Name == "TheClapper"))
|
||||
// {
|
||||
// LoggerInstance.Msg("Initializing TheClapper integration.");
|
||||
// Integrations.TheClapperIntegration.Init();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #endregion Integrations
|
||||
|
||||
#region Asset Bundle Loading
|
||||
|
||||
private const string LoadingHexagonAssets = "loading_hexagon.assets";
|
||||
private const string LoadingHexagonPrefab = "Assets/Mods/PropLoadingHexagon/Loading_Hexagon_Root.prefab";
|
||||
|
||||
private void LoadAssetBundle()
|
||||
{
|
||||
LoggerInstance.Msg($"Loading required asset bundle...");
|
||||
using Stream resourceStream = MelonAssembly.Assembly.GetManifestResourceStream(LoadingHexagonAssets);
|
||||
using MemoryStream memoryStream = new();
|
||||
if (resourceStream == null) {
|
||||
LoggerInstance.Error($"Failed to load {LoadingHexagonAssets}!");
|
||||
return;
|
||||
}
|
||||
|
||||
resourceStream.CopyTo(memoryStream);
|
||||
AssetBundle assetBundle = AssetBundle.LoadFromMemory(memoryStream.ToArray());
|
||||
if (assetBundle == null) {
|
||||
LoggerInstance.Error($"Failed to load {LoadingHexagonAssets}! Asset bundle is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
loadingHexPrefab = assetBundle.LoadAsset<GameObject>(LoadingHexagonPrefab);
|
||||
if (loadingHexPrefab == null) {
|
||||
LoggerInstance.Error($"Failed to load {LoadingHexagonPrefab}! Prefab is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
// modify prefab so nameplate billboard tmp shader is used
|
||||
MeshRenderer tmp = loadingHexPrefab.GetComponentInChildren<MeshRenderer>();
|
||||
tmp.sharedMaterial.shader = Shader.Find("Alpha Blend Interactive/TextMeshPro/Mobile/Distance Field-BillboardFacing");
|
||||
tmp.sharedMaterial.SetFloat(Shader.PropertyToID("_FadeStartDistance"), 0f);
|
||||
tmp.sharedMaterial.SetFloat(Shader.PropertyToID("_FadeEndDistance"), 0f);
|
||||
|
||||
LoggerInstance.Msg("Asset bundle successfully loaded!");
|
||||
}
|
||||
|
||||
#endregion Asset Bundle Loading
|
||||
|
||||
#region Harmony Patches
|
||||
|
||||
private static void OnPlayerSetupStart()
|
||||
{
|
||||
if (loadingHexContainer != null) return;
|
||||
loadingHexContainer = new GameObject("NAK.LoadingHexContainer");
|
||||
Object.DontDestroyOnLoad(loadingHexContainer);
|
||||
}
|
||||
|
||||
private static void OnPropSpawnedFromNetwork(Message message)
|
||||
{
|
||||
// thank
|
||||
// https://feedback.abinteractive.net/p/gameeventsystem-spawnable-onload-is-kinda-useless
|
||||
|
||||
using DarkRiftReader reader = message.GetReader();
|
||||
var assetId = reader.ReadString();
|
||||
var instanceId = reader.ReadString();
|
||||
CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(match => match.InstanceId == instanceId);
|
||||
if (propData == null)
|
||||
return; // props blocked by filter or player blocks, or just broken
|
||||
|
||||
if (!CVRDownloadManager.Instance._downloadTasks.TryGetValue(assetId, out DownloadTask downloadTask))
|
||||
return; // no download task, no prop placeholder
|
||||
|
||||
// create loadingPropLoadingHexagonPropHex
|
||||
LoadingPropMarker loadingHex = new() { downloadTask = downloadTask };
|
||||
loadingHex.Initialize(propData);
|
||||
OnPropPlaceholderCreated?.Invoke(loadingHex.loadingObject);
|
||||
|
||||
// add to list
|
||||
Loading_Hex_List.Add(loadingHex);
|
||||
}
|
||||
|
||||
private static void OnHandleSpawnableClicked(ref ControllerRay __instance)
|
||||
{
|
||||
if (!__instance._interactDown)
|
||||
return; // not interacted, no need to check
|
||||
|
||||
if (PlayerSetup.Instance.GetCurrentPropSelectionMode()
|
||||
!= PlayerSetup.PropSelectionMode.Delete)
|
||||
return; // not in delete mode, no need to check
|
||||
|
||||
LoadingHexagonController propLoadingHex = __instance.hitTransform.GetComponentInParent<LoadingHexagonController>();
|
||||
if (propLoadingHex != null) propLoadingHex.IsLoadingCanceled = true; // cancel loading
|
||||
}
|
||||
|
||||
private static void OnDeleteMyPropByInstanceIdOverNetwork(string instanceId)
|
||||
{
|
||||
// find prop data
|
||||
CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(match => match.InstanceId == instanceId);
|
||||
if (propData == null) return; // shouldn't happen
|
||||
|
||||
// check if shits null to nuke loading hex
|
||||
if (propData.Wrapper != null && propData.Spawnable != null)
|
||||
return;
|
||||
|
||||
LoadingPropMarker marker = Loading_Hex_List.Find(match => match.propData == propData);
|
||||
if (marker == null) return;
|
||||
|
||||
if (EntryKeepIndicatorWhenFiltered.Value)
|
||||
{
|
||||
marker.IsLikelyBlocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
marker.Cancel();
|
||||
marker.Reset();
|
||||
Loading_Hex_List.Remove(marker);
|
||||
}
|
||||
|
||||
#endregion Harmony Patches
|
||||
|
||||
#region LoadingPropMarker Class
|
||||
|
||||
private class LoadingPropMarker
|
||||
{
|
||||
public bool IsLikelyBlocked { get; set; }
|
||||
|
||||
internal DownloadTask downloadTask;
|
||||
internal CVRSyncHelper.PropData propData;
|
||||
internal GameObject loadingObject;
|
||||
private LoadingHexagonController loadingHexComponent;
|
||||
|
||||
// ReSharper disable once ParameterHidesMember
|
||||
public void Initialize(CVRSyncHelper.PropData propData)
|
||||
{
|
||||
this.propData = propData;
|
||||
if (loadingObject == null)
|
||||
{
|
||||
loadingObject = Object.Instantiate(loadingHexPrefab, Vector3.zero, Quaternion.identity, loadingHexContainer.transform);
|
||||
loadingHexComponent = loadingObject.GetComponent<LoadingHexagonController>();
|
||||
|
||||
float avatarHeight = PlayerSetup.Instance.AvatarHeight;
|
||||
Transform hexTransform = loadingObject.transform;
|
||||
hexTransform.localScale = Vector3.one * avatarHeight / 4f; // scale modifier
|
||||
hexTransform.GetChild(0).position = Vector3.up * avatarHeight / 2f; // position modifier
|
||||
|
||||
Update(); // set initial position and rotation
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLoadingCanceled
|
||||
=> loadingHexComponent.IsLoadingCanceled;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
string text;
|
||||
if (!IsLikelyBlocked)
|
||||
{
|
||||
float progress = downloadTask.Progress;
|
||||
if (downloadTask == null
|
||||
|| downloadTask.Status == DownloadTask.ExecutionStatus.Complete
|
||||
|| downloadTask.Progress >= 100f)
|
||||
text = "Loading";
|
||||
else if (downloadTask.Status == DownloadTask.ExecutionStatus.Failed)
|
||||
text = "Error";
|
||||
else
|
||||
text = $"{progress} %";
|
||||
|
||||
loadingHexComponent.SetLoadingShape(progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = "Filtered";
|
||||
}
|
||||
|
||||
loadingHexComponent.SetLoadingText(text);
|
||||
loadingObject.transform.SetPositionAndRotation(
|
||||
new Vector3(propData.PositionX, propData.PositionY, propData.PositionZ),
|
||||
Quaternion.Euler(propData.RotationX, propData.RotationY, propData.RotationZ));
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
CVRDownloadManager.Instance.CancelAttachment(propData.InstanceId);
|
||||
if (propData.Spawnable == null)
|
||||
propData.Recycle();
|
||||
else
|
||||
propData.Spawnable.Delete();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (loadingObject != null)
|
||||
{
|
||||
Object.Destroy(loadingObject);
|
||||
loadingObject = null;
|
||||
}
|
||||
|
||||
propData = null;
|
||||
downloadTask = null;
|
||||
loadingObject = null;
|
||||
loadingHexComponent = null;
|
||||
IsLikelyBlocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion LoadingPropMarker Class
|
||||
}
|
||||
11
.Deprecated/PropLoadingHexagon/PropLoadingHexagon.csproj
Normal file
11
.Deprecated/PropLoadingHexagon/PropLoadingHexagon.csproj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<RootNamespace>PropLoadingHexagon</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\loading_hexagon.assets">
|
||||
<LogicalName>loading_hexagon.assets</LogicalName>
|
||||
</EmbeddedResource> </ItemGroup>
|
||||
</Project>
|
||||
32
.Deprecated/PropLoadingHexagon/Properties/AssemblyInfo.cs
Normal file
32
.Deprecated/PropLoadingHexagon/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using NAK.PropLoadingHexagon.Properties;
|
||||
using MelonLoader;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyTitle(nameof(NAK.PropLoadingHexagon))]
|
||||
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||
[assembly: AssemblyProduct(nameof(NAK.PropLoadingHexagon))]
|
||||
|
||||
[assembly: MelonInfo(
|
||||
typeof(NAK.PropLoadingHexagon.PropLoadingHexagonMod),
|
||||
nameof(NAK.PropLoadingHexagon),
|
||||
AssemblyInfoParams.Version,
|
||||
AssemblyInfoParams.Author,
|
||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PropLoadingHexagon"
|
||||
)]
|
||||
|
||||
[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.PropLoadingHexagon.Properties;
|
||||
internal static class AssemblyInfoParams
|
||||
{
|
||||
public const string Version = "1.0.2";
|
||||
public const string Author = "Exterrata & NotAKidoS";
|
||||
}
|
||||
18
.Deprecated/PropLoadingHexagon/README.md
Normal file
18
.Deprecated/PropLoadingHexagon/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# PropLoadingHexagon
|
||||
|
||||
https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117
|
||||
|
||||
Adds a hexagon indicator to downloading props. Indicator is styled to look similar to Portals.
|
||||
|
||||
Can use Delete Mode ~~& The Clapper~~ on loading hexagons to cancel stuck downloads. Setting is provided to display the hexagon for Blocked/Filtered props.
|
||||
|
||||
---
|
||||
|
||||
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ChilloutVR.
|
||||
https://docs.chilloutvr.net/official/legal/tos/#6-modding-our-game
|
||||
|
||||
> This mod is an independent creation not affiliated with, supported by, or approved by ChilloutVR.
|
||||
|
||||
> 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 ChilloutVR.
|
||||
BIN
.Deprecated/PropLoadingHexagon/Resources/loading_hexagon.assets
Normal file
BIN
.Deprecated/PropLoadingHexagon/Resources/loading_hexagon.assets
Normal file
Binary file not shown.
23
.Deprecated/PropLoadingHexagon/format.json
Normal file
23
.Deprecated/PropLoadingHexagon/format.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"_id": 220,
|
||||
"name": "PropLoadingHexagon",
|
||||
"modversion": "1.0.2",
|
||||
"gameversion": "2025r181",
|
||||
"loaderversion": "0.7.2",
|
||||
"modtype": "Mod",
|
||||
"author": "Exterrata & NotAKidoS",
|
||||
"description": "Adds a hexagon indicator to downloading props. Indicator is styled to look similar to Portals.\n\nCan use Delete Mode on loading hexagons to cancel stuck downloads. Setting is provided to display the hexagon for Blocked/Filtered props.",
|
||||
"searchtags": [
|
||||
"prop",
|
||||
"spawn",
|
||||
"indicator",
|
||||
"loading"
|
||||
],
|
||||
"requirements": [
|
||||
"None"
|
||||
],
|
||||
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/PropLoadingHexagon.dll",
|
||||
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PropLoadingHexagon/",
|
||||
"changelog": "- Rebuilt for CVR 2025r181",
|
||||
"embedcolor": "#f61963"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue