diff --git a/.Deprecated/AutoSyncTransforms/Main.cs b/.Deprecated/AutoSyncTransforms/Main.cs deleted file mode 100644 index 24529b4..0000000 --- a/.Deprecated/AutoSyncTransforms/Main.cs +++ /dev/null @@ -1,106 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using HarmonyLib; -using MelonLoader; -using UnityEngine; - -namespace NAK.WhereAmIPointing; - -public class WhereAmIPointingMod : MelonMod -{ - #region Melon Preferences - - // cannot disable because then id need extra logic to reset the alpha :) - // private const string SettingsCategory = nameof(WhereAmIPointingMod); - // - // private static readonly MelonPreferences_Category Category = - // MelonPreferences.CreateCategory(SettingsCategory); - // - // private static readonly MelonPreferences_Entry Entry_Enabled = - // Category.CreateEntry("enabled", true, display_name: "Enabled",description: "Toggle WhereAmIPointingMod entirely."); - - #endregion Melon Preferences - - public override void OnInitializeMelon() - { - ApplyPatches(typeof(ControllerRay_Patches)); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #region Patches - - private static class ControllerRay_Patches - { - private const float ORIGINAL_ALPHA = 0.502f; - private const float INTERACTION_ALPHA = 0.1f; - private const float RAY_LENGTH = 1000f; // game normally raycasts to PositiveInfinity... -_- - - [HarmonyPostfix] - [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.LateUpdate))] - private static void Postfix_ControllerRay_LateUpdate(ref ControllerRay __instance) - { - if (__instance.isDesktopRay - || !__instance.enabled - || !__instance.IsTracking() - || !__instance.lineRenderer) - return; - - UpdateLineRendererAlpha(__instance); - - if (__instance.lineRenderer.enabled - || !ShouldOverrideLineRenderer(__instance)) - return; - - UpdateLineRendererPosition(__instance); - } - - private static void UpdateLineRendererAlpha(ControllerRay instance) - { - Material material = instance.lineRenderer.material; - Color color = material.color; - - bool anyMenuOpen = ViewManager.Instance.IsAnyMenuOpen; - float targetAlpha = (!anyMenuOpen || instance.uiActive) ? ORIGINAL_ALPHA : INTERACTION_ALPHA; - if (!(Math.Abs(color.a - targetAlpha) > float.Epsilon)) - return; - - color.a = targetAlpha; - material.color = color; - } - - private static bool ShouldOverrideLineRenderer(ControllerRay instance) - { - if (!ViewManager.Instance.IsAnyMenuOpen) - return false; - - if (CVR_MenuManager.Instance.IsQuickMenuOpen - && instance.hand == CVR_MenuManager.Instance.SelectedQuickMenuHand) - return false; - - return true; - } - - private static void UpdateLineRendererPosition(ControllerRay instance) - { - Vector3 rayOrigin = instance.rayDirectionTransform.position; - Vector3 rayEnd = rayOrigin + instance.rayDirectionTransform.forward * RAY_LENGTH; - - instance.lineRenderer.SetPosition(0, instance.lineRenderer.transform.InverseTransformPoint(rayOrigin)); - instance.lineRenderer.SetPosition(1, instance.lineRenderer.transform.InverseTransformPoint(rayEnd)); - instance.lineRenderer.enabled = true; - } - } - - #endregion Patches -} \ No newline at end of file diff --git a/.Deprecated/AutoSyncTransforms/Properties/AssemblyInfo.cs b/.Deprecated/AutoSyncTransforms/Properties/AssemblyInfo.cs deleted file mode 100644 index 48c359f..0000000 --- a/.Deprecated/AutoSyncTransforms/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.WhereAmIPointing.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.WhereAmIPointing))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.WhereAmIPointing))] - -[assembly: MelonInfo( - typeof(NAK.WhereAmIPointing.WhereAmIPointingMod), - nameof(NAK.WhereAmIPointing), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing" -)] - -[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.WhereAmIPointing.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.1"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/AutoSyncTransforms/README.md b/.Deprecated/AutoSyncTransforms/README.md deleted file mode 100644 index c78a56a..0000000 --- a/.Deprecated/AutoSyncTransforms/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# WhereAmIPointing - -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. - ---- - -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. diff --git a/.Deprecated/AutoSyncTransforms/WhereAmIPointing.csproj b/.Deprecated/AutoSyncTransforms/WhereAmIPointing.csproj deleted file mode 100644 index 728edb7..0000000 --- a/.Deprecated/AutoSyncTransforms/WhereAmIPointing.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - net48 - - diff --git a/.Deprecated/AutoSyncTransforms/format.json b/.Deprecated/AutoSyncTransforms/format.json deleted file mode 100644 index 654911a..0000000 --- a/.Deprecated/AutoSyncTransforms/format.json +++ /dev/null @@ -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" -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Exclusions.cs b/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Exclusions.cs deleted file mode 100644 index cedbcdd..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Exclusions.cs +++ /dev/null @@ -1,229 +0,0 @@ -using ABI.CCK.Components; -using UnityEngine; - -namespace NAK.AvatarCloneTest; - -public partial class AvatarClone -{ - #region Exclusions - - private FPRExclusion[] _exclusions; - - private void AddExclusionToHeadIfNeeded() - { - if (!TryGetComponent(out Animator animator) - || !animator.isHuman - || !animator.avatar - || !animator.avatar.isValid) - return; - - Transform head = animator.GetBoneTransform(HumanBodyBones.Head); - if (!head) - return; - - GameObject headGo = head.gameObject; - if (headGo.TryGetComponent(out FPRExclusion exclusion)) - return; - - exclusion = headGo.AddComponent(); - exclusion.target = head; - exclusion.isShown = false; - } - - private void InitializeExclusions() - { - _exclusions = GetComponentsInChildren(true); - var exclusionRoots = new Dictionary(_exclusions.Length); - - // **1. Precompute Exclusions** - foreach (FPRExclusion exclusion in _exclusions) - { - Transform target = exclusion.target ??= exclusion.transform; - if (exclusionRoots.ContainsKey(target) || !target.gameObject.scene.IsValid()) - continue; - - AvatarCloneExclusion behaviour = new AvatarCloneExclusion(this, target); - exclusion.behaviour = behaviour; - exclusionRoots.Add(target, behaviour); - } - - // Process Exclusion Transforms - Renderer ourRenderer; - - void ProcessTransformHierarchy(Transform current, Transform root, AvatarCloneExclusion behaviour) - { - if (exclusionRoots.ContainsKey(current) && current != root) return; - - behaviour.affectedTransforms.Add(current); - if (current.TryGetComponent(out ourRenderer)) - behaviour.affectedRenderers.Add(ourRenderer); - - for (int i = 0; i < current.childCount; i++) - { - Transform child = current.GetChild(i); - if (!exclusionRoots.ContainsKey(child)) - ProcessTransformHierarchy(child, root, behaviour); - } - } - - foreach (var entry in exclusionRoots) - { - Transform rootTransform = entry.Key; - AvatarCloneExclusion behaviour = entry.Value; - ProcessTransformHierarchy(rootTransform, rootTransform, behaviour); - behaviour.affectedTransformSet = new HashSet(behaviour.affectedTransforms); - } - - // ------------------------------ - // **OPTIMIZED EXCLUSION BONE MAPPING** - // ------------------------------ - - Dictionary.ValueCollection exclusionBehaviours = exclusionRoots.Values; - int skinnedCount = _skinnedClones.Count; - - // **2. Precompute Bone-to-Exclusion Mapping** - int estimatedBoneCount = skinnedCount * 20; // Estimated bones per skinned mesh - var boneToExclusion = new Dictionary>(estimatedBoneCount); - - foreach (AvatarCloneExclusion behaviour in exclusionBehaviours) - { - foreach (Transform bone in behaviour.affectedTransformSet) - { - if (!boneToExclusion.TryGetValue(bone, out var list)) - { - list = new List(2); - boneToExclusion[bone] = list; - } - list.Add(behaviour); - } - } - - // **3. Process Skinned Mesh Renderers** - for (int s = 0; s < skinnedCount; s++) - { - SkinnedMeshRenderer source = _skinnedRenderers[s]; - var bones = source.bones; // Cache bones array - - SkinnedMeshRenderer smr = _skinnedClones[s]; - int boneCount = bones.Length; - - for (int i = 0; i < boneCount; i++) - { - Transform bone = bones[i]; - - // **Skip if the bone isn't mapped to exclusions** - if (!bone // Skip null bones - || !boneToExclusion.TryGetValue(bone, out var behaviours)) - continue; - - // **Avoid redundant dictionary lookups** - for (int j = 0; j < behaviours.Count; j++) - { - AvatarCloneExclusion behaviour = behaviours[j]; - - if (!behaviour.skinnedToBoneIndex.TryGetValue(smr, out var indices)) - { - indices = new List(4); - behaviour.skinnedToBoneIndex[smr] = indices; - } - - indices.Add(i); - } - } - } - - ApplyInitialExclusionState(); - } - - public void ApplyInitialExclusionState() - { - foreach (FPRExclusion exclusion in _exclusions) - { - exclusion._wasShown = exclusion.isShown; - if (!exclusion.isShown) exclusion.UpdateExclusions(); - } - } - - public void HandleExclusionUpdate(AvatarCloneExclusion exclusion, bool isShown) - { -#if ENABLE_PROFILER - s_UpdateExclusions.Begin(); -#endif - - // **1. Update Renderer Visibility** - foreach (Renderer renderer in exclusion.affectedRenderers) - { - if (renderer is SkinnedMeshRenderer skinned) - { - int index = _skinnedRenderers.IndexOf(skinned); - if (index >= 0) _skinnedClones[index].gameObject.SetActive(isShown); - } - else if (renderer is MeshRenderer mesh) - { - int index = _meshRenderers.IndexOf(mesh); - if (index >= 0) - { - if (Setting_CloneMeshRenderers) - { - _meshClones[index].gameObject.SetActive(isShown); - } - else - { - // Other renderer (never cloned) - update shadow casting state - _sourceShouldBeHiddenFromFPR[index] = !isShown; // When hidden, use for shadows - } - } - } - else if (renderer) - { - int index = _otherRenderers.IndexOf(renderer); - if (index >= 0) - { - int shadowIndex = index + (Setting_CloneMeshRenderers ? _meshRenderers.Count : 0); - _sourceShouldBeHiddenFromFPR[shadowIndex] = !isShown; // When hidden, use for shadows - } - } - } - - // **2. Update Bone References in Skinned Mesh Renderers** - UpdateSkinnedMeshBones(exclusion, exclusion._shrinkBone, isShown); - -#if ENABLE_PROFILER - s_UpdateExclusions.End(); -#endif - } - - private void UpdateSkinnedMeshBones(AvatarCloneExclusion exclusion, Transform shrinkBone, bool isShown) - { -#if ENABLE_PROFILER - s_HandleBoneUpdates.Begin(); -#endif - - foreach (var smrEntry in exclusion.skinnedToBoneIndex) - { - SkinnedMeshRenderer smr = smrEntry.Key; - var indices = smrEntry.Value; - bool needsUpdate = false; - - var parentBones = smr.transform.parent.GetComponent().bones; - var cloneBones = smr.bones; - Array.Resize(ref cloneBones, parentBones.Length); - - // Only modify our bones, other exclusions may have modified others - for (int i = 0; i < indices.Count; i++) - { - int index = indices[i]; - if (!isShown) cloneBones[index] = shrinkBone; - else cloneBones[index] = parentBones[index]; - needsUpdate = true; - } - if (needsUpdate) smr.bones = cloneBones; - } - -#if ENABLE_PROFILER - s_HandleBoneUpdates.End(); -#endif - } - - #endregion Exclusions -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Init.cs b/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Init.cs deleted file mode 100644 index f02fe9b..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Init.cs +++ /dev/null @@ -1,304 +0,0 @@ -using ABI_RC.Core.Player.ShadowClone; -using UnityEngine; -using UnityEngine.Rendering; - -namespace NAK.AvatarCloneTest; - -public partial class AvatarClone -{ - #region Initialization - - private void InitializeCollections() - { -#if ENABLE_PROFILER - s_InitializeData.Begin(); -#endif - - // Initialize source collections - _skinnedRenderers = new List(); - _blendShapeWeights = new List>(); - - _meshRenderers = new List(); - _meshFilters = new List(); - - _otherRenderers = new List(); - - // Initialize clone collections - _skinnedClones = new List(); - _skinnedCloneMaterials = new List(); - _skinnedCloneCullingMaterials = new List(); - - if (Setting_CloneMeshRenderers) - { - _meshClones = new List(); - _meshCloneFilters = new List(); - _meshCloneMaterials = new List(); - _meshCloneCullingMaterials = new List(); - } - - // Initialize shared resources - _materialWorkingList = new List(); - _propertyBlock = new MaterialPropertyBlock(); - -#if ENABLE_PROFILER - s_InitializeData.End(); -#endif - } - - private void CollectRenderers() - { - #if ENABLE_PROFILER - s_InitializeData.Begin(); - #endif - - var renderers = GetComponentsInChildren(true); - var currentIndex = 0; - var nonCloned = 0; - - // Single pass: directly categorize renderers - foreach (Renderer renderer in renderers) - { - switch (renderer) - { - case SkinnedMeshRenderer skinned when skinned.sharedMesh != null: - AddSkinnedRenderer(skinned); - currentIndex++; - break; - - case MeshRenderer mesh: - MeshFilter filter = mesh.GetComponent(); - if (filter != null && filter.sharedMesh != null) - { - if (Setting_CloneMeshRenderers) - { - AddMeshRenderer(mesh, filter); - } - else - { - AddMeshRenderer(mesh, filter); - nonCloned++; - } - currentIndex++; - } - break; - - default: - AddOtherRenderer(renderer); - currentIndex++; - nonCloned++; - break; - } - } - - _rendererActiveStates = new bool[currentIndex]; - _originalShadowCastingMode = new ShadowCastingMode[currentIndex]; - _sourceShouldBeHiddenFromFPR = new bool[nonCloned]; - - #if ENABLE_PROFILER - s_InitializeData.End(); - #endif - } - - private void AddSkinnedRenderer(SkinnedMeshRenderer renderer) - { -#if ENABLE_PROFILER - s_AddRenderer.Begin(); -#endif - - _skinnedRenderers.Add(renderer); - - // Clone materials array for clone renderer - var materials = renderer.sharedMaterials; - var cloneMaterials = new Material[materials.Length]; - for (int i = 0; i < materials.Length; i++) cloneMaterials[i] = materials[i]; - _skinnedCloneMaterials.Add(cloneMaterials); - - // Cache culling materials - var cullingMaterialArray = new Material[materials.Length]; -#if !UNITY_EDITOR - for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = ShadowCloneUtils.cullingMaterial; -#else - for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = cullingMaterial; -#endif - _skinnedCloneCullingMaterials.Add(cullingMaterialArray); - - // Cache blend shape weights - var weights = new List(renderer.sharedMesh.blendShapeCount); - for (int i = 0; i < renderer.sharedMesh.blendShapeCount; i++) weights.Add(0f); - _blendShapeWeights.Add(weights); - -#if ENABLE_PROFILER - s_AddRenderer.End(); -#endif - } - - private void AddMeshRenderer(MeshRenderer renderer, MeshFilter filter) - { -#if ENABLE_PROFILER - s_AddRenderer.Begin(); -#endif - - _meshRenderers.Add(renderer); - _meshFilters.Add(filter); - - if (!Setting_CloneMeshRenderers) return; - - // Clone materials array for clone renderer - var materials = renderer.sharedMaterials; - var cloneMaterials = new Material[materials.Length]; - for (int i = 0; i < materials.Length; i++) cloneMaterials[i] = materials[i]; - _meshCloneMaterials.Add(cloneMaterials); - - // Cache culling materials - var cullingMaterialArray = new Material[materials.Length]; -#if !UNITY_EDITOR - for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = ShadowCloneUtils.cullingMaterial; -#else - for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = cullingMaterial; -#endif - _meshCloneCullingMaterials.Add(cullingMaterialArray); - -#if ENABLE_PROFILER - s_AddRenderer.End(); -#endif - } - - private void AddOtherRenderer(Renderer renderer) - { -#if ENABLE_PROFILER - s_AddRenderer.Begin(); -#endif - _otherRenderers.Add(renderer); -#if ENABLE_PROFILER - s_AddRenderer.End(); -#endif - } - - private void CreateClones() - { -#if ENABLE_PROFILER - s_InitializeData.Begin(); -#endif - - // Always create skinned mesh clones - int skinnedCount = _skinnedRenderers.Count; - for (int i = 0; i < skinnedCount; i++) - { - CreateSkinnedClone(i); - } - - // Optionally create mesh clones - if (Setting_CloneMeshRenderers) - { - int meshCount = _meshRenderers.Count; - for (int i = 0; i < meshCount; i++) - { - CreateMeshClone(i); - } - } - -#if ENABLE_PROFILER - s_InitializeData.End(); -#endif - } - - private void CreateSkinnedClone(int index) - { -#if ENABLE_PROFILER - s_CreateClone.Begin(); -#endif - - SkinnedMeshRenderer source = _skinnedRenderers[index]; - - GameObject clone = new(source.name + "_Clone") - { - layer = CLONE_LAYER - }; - - clone.transform.SetParent(source.transform, false); - - SkinnedMeshRenderer cloneRenderer = clone.AddComponent(); - - // Basic setup - cloneRenderer.sharedMaterials = _skinnedCloneMaterials[index]; - cloneRenderer.shadowCastingMode = ShadowCastingMode.Off; - cloneRenderer.probeAnchor = source.probeAnchor; - cloneRenderer.sharedMesh = source.sharedMesh; - cloneRenderer.rootBone = source.rootBone; - cloneRenderer.bones = source.bones; - -#if !UNITY_EDITOR - cloneRenderer.localBounds = new Bounds(source.localBounds.center, source.localBounds.size * 2f); -#endif - - // Quality settings - cloneRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; - cloneRenderer.allowOcclusionWhenDynamic = false; - cloneRenderer.updateWhenOffscreen = false; - cloneRenderer.skinnedMotionVectors = false; - cloneRenderer.forceMatrixRecalculationPerRender = false; - cloneRenderer.quality = SkinQuality.Bone4; - - source.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; - source.allowOcclusionWhenDynamic = false; - source.updateWhenOffscreen = false; - source.skinnedMotionVectors = false; - source.forceMatrixRecalculationPerRender = false; - source.quality = SkinQuality.Bone4; - - // Add to clone list - _skinnedClones.Add(cloneRenderer); - -#if ENABLE_PROFILER - s_CreateClone.End(); -#endif - } - - private void CreateMeshClone(int index) - { -#if ENABLE_PROFILER - s_CreateClone.Begin(); -#endif - - MeshRenderer source = _meshRenderers[index]; - MeshFilter sourceFilter = _meshFilters[index]; - - GameObject clone = new(source.name + "_Clone") - { - layer = CLONE_LAYER - }; - - clone.transform.SetParent(source.transform, false); - - MeshRenderer cloneRenderer = clone.AddComponent(); - MeshFilter cloneFilter = clone.AddComponent(); - - // Basic setup - cloneRenderer.sharedMaterials = _meshCloneMaterials[index]; - cloneRenderer.shadowCastingMode = ShadowCastingMode.Off; - cloneRenderer.probeAnchor = source.probeAnchor; - -#if !UNITY_EDITOR - cloneRenderer.localBounds = new Bounds(source.localBounds.center, source.localBounds.size * 2f); -#endif - - cloneFilter.sharedMesh = sourceFilter.sharedMesh; - - // Quality settings - cloneRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; - cloneRenderer.allowOcclusionWhenDynamic = false; - - source.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; - source.allowOcclusionWhenDynamic = false; - - // Add to clone lists - _meshClones.Add(cloneRenderer); - _meshCloneFilters.Add(cloneFilter); - -#if ENABLE_PROFILER - s_CreateClone.End(); -#endif - } - - #endregion Initialization -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.RenderState.cs b/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.RenderState.cs deleted file mode 100644 index 40c1df7..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.RenderState.cs +++ /dev/null @@ -1,212 +0,0 @@ -using UnityEngine; -using UnityEngine.Rendering; - -namespace NAK.AvatarCloneTest; - -public partial class AvatarClone -{ - #region Render State Management - - private void MyOnPreCull(Camera cam) - { -#if UNITY_EDITOR - // Scene & Preview cameras are not needed - if (cam.cameraType != CameraType.Game) - return; -#endif - -#if ENABLE_PROFILER - s_PreCullUpdate.Begin(); -#endif - - bool isOurUiCamera = IsUIInternalCamera(cam); - bool rendersOurPlayerLayer = CameraRendersPlayerLocalLayer(cam); - bool rendersOurCloneLayer = CameraRendersPlayerCloneLayer(cam); - - bool rendersBothPlayerLayers = rendersOurPlayerLayer && rendersOurCloneLayer; - - // Handle shadow casting when camera renders both layers - if (!_sourcesSetForShadowCasting - && rendersBothPlayerLayers) - { - ConfigureSourceShadowCasting(true); - _sourcesSetForShadowCasting = true; - } - else if (_sourcesSetForShadowCasting && !rendersBothPlayerLayers) - { - ConfigureSourceShadowCasting(false); - _sourcesSetForShadowCasting = false; - } - - // Handle UI culling for clone layer - if (!_clonesSetForUiCulling - && isOurUiCamera && rendersOurCloneLayer) - { - ConfigureCloneUICulling(true); - _clonesSetForUiCulling = true; - } - else if (_clonesSetForUiCulling) - { - ConfigureCloneUICulling(false); - _clonesSetForUiCulling = false; - } - -#if ENABLE_PROFILER - s_PreCullUpdate.End(); -#endif - } - - private void ConfigureSourceShadowCasting(bool setSourcesToShadowCast) - { -#if ENABLE_PROFILER - s_ConfigureShadowCasting.Begin(); -#endif - - int currentIndex = 0; - int shadowArrayIndex = 0; - - // Handle skinned mesh renderers (always have clones) - int skinnedCount = _skinnedRenderers.Count; - for (int i = 0; i < skinnedCount; i++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) continue; - - SkinnedMeshRenderer source = _skinnedRenderers[i]; - - if (setSourcesToShadowCast) - { - ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode; - if (originalMode == ShadowCastingMode.Off) - source.forceRenderingOff = true; - else - source.shadowCastingMode = ShadowCastingMode.ShadowsOnly; - } - else - { - source.shadowCastingMode = _originalShadowCastingMode[currentIndex]; - source.forceRenderingOff = false; - } - } - - // Handle mesh renderers based on clone setting - if (Setting_CloneMeshRenderers) - { - int meshCount = _meshRenderers.Count; - for (int i = 0; i < meshCount; i++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) continue; - - MeshRenderer source = _meshRenderers[i]; - - if (setSourcesToShadowCast) - { - ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode; - if (originalMode == ShadowCastingMode.Off) - source.forceRenderingOff = true; - else - source.shadowCastingMode = ShadowCastingMode.ShadowsOnly; - } - else - { - source.shadowCastingMode = _originalShadowCastingMode[currentIndex]; - source.forceRenderingOff = false; - } - } - } - else - { - // When not cloned, mesh renderers use the shadow casting array - int meshCount = _meshRenderers.Count; - for (int i = 0; i < meshCount; i++, shadowArrayIndex++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) continue; - if (!_sourceShouldBeHiddenFromFPR[shadowArrayIndex]) continue; - - MeshRenderer source = _meshRenderers[i]; - - if (setSourcesToShadowCast) - { - ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode; - if (originalMode == ShadowCastingMode.Off) - source.forceRenderingOff = true; - else - source.shadowCastingMode = ShadowCastingMode.ShadowsOnly; - } - else - { - source.shadowCastingMode = _originalShadowCastingMode[currentIndex]; - source.forceRenderingOff = false; - } - } - } - - // Handle other renderers (never cloned) - int otherCount = _otherRenderers.Count; - for (int i = 0; i < otherCount; i++, shadowArrayIndex++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) continue; - if (!_sourceShouldBeHiddenFromFPR[shadowArrayIndex]) continue; - - Renderer source = _otherRenderers[i]; - - if (setSourcesToShadowCast) - { - ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode; - if (originalMode == ShadowCastingMode.Off) - source.forceRenderingOff = true; - else - source.shadowCastingMode = ShadowCastingMode.ShadowsOnly; - } - else - { - source.shadowCastingMode = _originalShadowCastingMode[currentIndex]; - source.forceRenderingOff = false; - } - } - -#if ENABLE_PROFILER - s_ConfigureShadowCasting.End(); -#endif - } - - private void ConfigureCloneUICulling(bool enableCulling) - { -#if ENABLE_PROFILER - s_ConfigureUICulling.Begin(); -#endif - - // Set the materials to our culling materials - int currentIndex = 0; - - int skinnedCount = _skinnedRenderers.Count; - for (int i = 0; i < skinnedCount; i++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) - continue; - - _skinnedClones[i].sharedMaterials = enableCulling ? - _skinnedCloneCullingMaterials[i] : - _skinnedCloneMaterials[i]; - } - - if (Setting_CloneMeshRenderers) - { - int meshCount = _meshRenderers.Count; - for (int i = 0; i < meshCount; i++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) - continue; - - _meshClones[i].sharedMaterials = enableCulling ? - _meshCloneCullingMaterials[i] : - _meshCloneMaterials[i]; - } - } - -#if ENABLE_PROFILER - s_ConfigureUICulling.End(); -#endif - } - - #endregion Render State Management -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.StateSync.cs b/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.StateSync.cs deleted file mode 100644 index eb70282..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.StateSync.cs +++ /dev/null @@ -1,156 +0,0 @@ -using UnityEngine; - -namespace NAK.AvatarCloneTest; - -public partial class AvatarClone -{ - #region State Syncing - - private void SyncEnabledState() - { -#if ENABLE_PROFILER - s_CopyEnabledState.Begin(); -#endif - - int currentIndex = 0; - - // Update skinned mesh renderers - int skinnedCount = _skinnedRenderers.Count; - for (int i = 0; i < skinnedCount; i++, currentIndex++) - { - SkinnedMeshRenderer source = _skinnedRenderers[i]; - _skinnedClones[i].enabled = _rendererActiveStates[currentIndex] = IsRendererActive(source); - } - - // Update mesh renderers - int meshCount = _meshRenderers.Count; - for (int i = 0; i < meshCount; i++, currentIndex++) - { - MeshRenderer source = _meshRenderers[i]; - if (Setting_CloneMeshRenderers) _meshClones[i].enabled = _rendererActiveStates[currentIndex] = IsRendererActive(source); - else _rendererActiveStates[currentIndex] = IsRendererActive(source); - } - - // Update other renderers - int otherCount = _otherRenderers.Count; - for (int i = 0; i < otherCount; i++, currentIndex++) - { - Renderer source = _otherRenderers[i]; - _rendererActiveStates[currentIndex] = IsRendererActive(source); - } - -#if ENABLE_PROFILER - s_CopyEnabledState.End(); -#endif - } - - private void SyncMaterials() - { -#if ENABLE_PROFILER - s_CopyMaterials.Begin(); -#endif - int currentIndex = 0; - - // Sync skinned mesh materials - int skinnedCount = _skinnedRenderers.Count; - for (int i = 0; i < skinnedCount; i++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) - continue; - - CopyMaterialsAndProperties( - _skinnedRenderers[i], - _skinnedClones[i], - _propertyBlock, - _materialWorkingList, - _skinnedCloneMaterials[i]); - } - - // Sync mesh materials if enabled - if (Setting_CloneMeshRenderers) - { - int meshCount = _meshRenderers.Count; - for (int i = 0; i < meshCount; i++, currentIndex++) - { - if (!_rendererActiveStates[currentIndex]) - continue; - - CopyMaterialsAndProperties( - _meshRenderers[i], - _meshClones[i], - _propertyBlock, - _materialWorkingList, - _meshCloneMaterials[i]); - } - } - -#if ENABLE_PROFILER - s_CopyMaterials.End(); -#endif - } - - private void SyncBlendShapes() - { -#if ENABLE_PROFILER - s_CopyBlendShapes.Begin(); -#endif - - int skinnedCount = _skinnedRenderers.Count; - for (int i = 0; i < skinnedCount; i++) - { - SkinnedMeshRenderer source = _skinnedRenderers[i]; - if (!_rendererActiveStates[i]) - continue; - - CopyBlendShapes( - source, - _skinnedClones[i], - _blendShapeWeights[i]); - } - -#if ENABLE_PROFILER - s_CopyBlendShapes.End(); -#endif - } - - private static void CopyMaterialsAndProperties( - Renderer source, - Renderer clone, - MaterialPropertyBlock propertyBlock, - List workingList, - Material[] cloneMaterials) - { - source.GetSharedMaterials(workingList); - - int matCount = workingList.Count; - bool hasChanged = false; - - for (int i = 0; i < matCount; i++) - { - if (ReferenceEquals(workingList[i], cloneMaterials[i])) continue; - cloneMaterials[i] = workingList[i]; - hasChanged = true; - } - if (hasChanged) clone.sharedMaterials = cloneMaterials; - - source.GetPropertyBlock(propertyBlock); - clone.SetPropertyBlock(propertyBlock); - } - - private static void CopyBlendShapes( - SkinnedMeshRenderer source, - SkinnedMeshRenderer clone, - List weights) - { - int weightCount = weights.Count; - for (int i = 0; i < weightCount; i++) - { - float weight = source.GetBlendShapeWeight(i); - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (weight == weights[i]) continue; // Halves the work - clone.SetBlendShapeWeight(i, weights[i] = weight); - } - } - - #endregion State Syncing -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Util.cs b/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Util.cs deleted file mode 100644 index 63f0d45..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Util.cs +++ /dev/null @@ -1,81 +0,0 @@ -using ABI_RC.Core.Player; -using MagicaCloth; -using MagicaCloth2; -using UnityEngine; - -namespace NAK.AvatarCloneTest; - -public partial class AvatarClone -{ - #region Utilities - - private static bool IsRendererActive(Renderer renderer) - => renderer && renderer.enabled && renderer.gameObject.activeInHierarchy; - - private static bool CameraRendersPlayerLocalLayer(Camera cam) - => (cam.cullingMask & (1 << LOCAL_LAYER)) != 0; - - private static bool CameraRendersPlayerCloneLayer(Camera cam) - => (cam.cullingMask & (1 << CLONE_LAYER)) != 0; - - private static bool IsUIInternalCamera(Camera cam) -#if !UNITY_EDITOR - => cam == PlayerSetup.Instance.activeUiCam; -#else - => cam.gameObject.layer == 15; -#endif - - #endregion Utilities - - #region Magica Cloth Support - - private void SetupMagicaClothSupport() - { - var magicaCloths1 = GetComponentsInChildren(true); - foreach (BaseCloth magicaCloth1 in magicaCloths1) - magicaCloth1.SetCullingMode(PhysicsTeam.TeamCullingMode.Off); - - var magicaCloths2 = base.GetComponentsInChildren(true); - foreach (MagicaCloth2.MagicaCloth magicaCloth2 in magicaCloths2) - magicaCloth2.serializeData.cullingSettings.cameraCullingMode = CullingSettings.CameraCullingMode.AnimatorLinkage; - } - - public void OnMagicaClothMeshSwapped(Renderer render, Mesh newMesh) - { - switch (render) - { - case MeshRenderer mesh: - { - int index = _meshRenderers.IndexOf(mesh); - if (index != -1) _meshCloneFilters[index].sharedMesh = newMesh; - break; - } - case SkinnedMeshRenderer skinned: - { - int index = _skinnedRenderers.IndexOf(skinned); - if (index != -1) - { - // Copy the mesh - _skinnedClones[index].sharedMesh = newMesh; - - // Copy appended bones if count is different - var cloneBones = _skinnedClones[index].bones; // alloc - var sourceBones = skinned.bones; // alloc - - int cloneBoneCount = cloneBones.Length; - int sourceBoneCount = sourceBones.Length; - if (cloneBoneCount != sourceBoneCount) - { - // Copy the new bones only - Array.Resize(ref cloneBones, sourceBoneCount); - for (int i = cloneBoneCount; i < sourceBoneCount; i++) cloneBones[i] = sourceBones[i]; - _skinnedClones[index].bones = cloneBones; - } - } - break; - } - } - } - - #endregion Magica Cloth Support -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.cs b/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.cs deleted file mode 100644 index ed15ec1..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.cs +++ /dev/null @@ -1,147 +0,0 @@ -using NAK.AvatarCloneTest; -using UnityEngine; -using UnityEngine.Rendering; - -namespace NAK.AvatarCloneTest; - -public partial class AvatarClone : MonoBehaviour -{ - #region Constants - - private const int LOCAL_LAYER = 8; - private const int CLONE_LAYER = 9; - - #endregion Constants - - #region Profiler Markers - -#if ENABLE_PROFILER - private static readonly ProfilerMarker s_CopyEnabledState = new($"{nameof(AvatarClone)}.{nameof(SyncEnabledState)}"); - private static readonly ProfilerMarker s_CopyMaterials = new($"{nameof(AvatarClone)}.{nameof(CopyMaterialsAndProperties)}"); - private static readonly ProfilerMarker s_CopyBlendShapes = new($"{nameof(AvatarClone)}.{nameof(CopyBlendShapes)}"); - private static readonly ProfilerMarker s_InitializeData = new($"{nameof(AvatarClone)}.Initialize"); - private static readonly ProfilerMarker s_UpdateExclusions = new($"{nameof(AvatarClone)}.{nameof(HandleExclusionUpdate)}"); - private static readonly ProfilerMarker s_CollectExclusionData = new($"{nameof(AvatarClone)}.{nameof(CollectExclusionData)}"); - private static readonly ProfilerMarker s_HandleBoneUpdates = new($"{nameof(AvatarClone)}.{nameof(UpdateSkinnedMeshBones)}"); - private static readonly ProfilerMarker s_PreCullUpdate = new($"{nameof(AvatarClone)}.{nameof(MyOnPreCull)}"); - private static readonly ProfilerMarker s_ConfigureShadowCasting = new($"{nameof(AvatarClone)}.{nameof(ConfigureSourceShadowCasting)}"); - private static readonly ProfilerMarker s_ConfigureUICulling = new($"{nameof(AvatarClone)}.{nameof(ConfigureCloneUICulling)}"); - private static readonly ProfilerMarker s_AddRenderer = new($"{nameof(AvatarClone)}.AddRenderer"); - private static readonly ProfilerMarker s_CreateClone = new($"{nameof(AvatarClone)}.CreateClone"); -#endif - - #endregion Profiler Markers - - #region Settings - - public bool Setting_CloneMeshRenderers; - public bool Setting_CopyMaterials = true; - public bool Setting_CopyBlendShapes = true; - - #endregion Settings - - #region Source Collections - Cloned Renderers - - // Skinned mesh renderers (always cloned) - private List _skinnedRenderers; - private List> _blendShapeWeights; - - // Mesh renderers (optionally cloned) - private List _meshRenderers; - private List _meshFilters; - - #endregion Source Collections - Cloned Renderers - - #region Source Collections - Non-Cloned Renderers - - // All other renderers (never cloned) - private List _otherRenderers; - - // True if source renderer should hide. False if source renderer should show. - // Only used for non-cloned renderers (MeshRenderers and other Renderers). - private bool[] _sourceShouldBeHiddenFromFPR; - // Three states: On, ShadowsOnly, Off - private ShadowCastingMode[] _originalShadowCastingMode; - - #endregion Source Collections - Non-Cloned Renderers - - #region Clone Collections - - // Skinned mesh clones - private List _skinnedClones; - private List _skinnedCloneMaterials; - private List _skinnedCloneCullingMaterials; - - // Mesh clones (optional) - private List _meshClones; - private List _meshCloneFilters; - private List _meshCloneMaterials; - private List _meshCloneCullingMaterials; - - #endregion Clone Collections - - #region Shared Resources - - private List _materialWorkingList; // Used for GetSharedMaterials - private MaterialPropertyBlock _propertyBlock; - - #endregion Shared Resources - - #region State - - private bool _sourcesSetForShadowCasting; - private bool _clonesSetForUiCulling; - private bool[] _rendererActiveStates; - - #endregion State - - #region Unity Events - - private void Start() - { - Setting_CloneMeshRenderers = AvatarCloneTestMod.EntryCloneMeshRenderers.Value; - - InitializeCollections(); - CollectRenderers(); - CreateClones(); - AddExclusionToHeadIfNeeded(); - InitializeExclusions(); - SetupMagicaClothSupport(); - - // bool animatesClone = transform.Find("[ExplicitlyAnimatesVisualClones]") != null; - // Setting_CopyMaterials = !animatesClone; - // Setting_CopyBlendShapes = !animatesClone; - // Animator animator = GetComponent(); - // if (animator && animatesClone) animator.Rebind(); - - // Likely a Unity bug with where we can touch shadowCastingMode & forceRenderingOff -#if !UNITY_EDITOR - Camera.onPreCull += MyOnPreCull; -#else - Camera.onPreRender += MyOnPreCull; -#endif - } - - private void LateUpdate() - { - SyncEnabledState(); - - if (Setting_CopyMaterials && AvatarCloneTestMod.EntryCopyMaterials.Value) - SyncMaterials(); - - if (Setting_CopyBlendShapes && AvatarCloneTestMod.EntryCopyBlendShapes.Value) - SyncBlendShapes(); - } - - private void OnDestroy() - { - // Likely a Unity bug with where we can touch shadowCastingMode & forceRenderingOff -#if !UNITY_EDITOR - Camera.onPreCull -= MyOnPreCull; -#else - Camera.onPreRender -= MyOnPreCull; -#endif - } - - #endregion Unity Events -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarClone/FPRExclusion/AvatarCloneExclusion.cs b/.Deprecated/AvatarCloneTest/AvatarClone/FPRExclusion/AvatarCloneExclusion.cs deleted file mode 100644 index 4f38900..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarClone/FPRExclusion/AvatarCloneExclusion.cs +++ /dev/null @@ -1,39 +0,0 @@ -using ABI.CCK.Components; -using UnityEngine; - -namespace NAK.AvatarCloneTest; - -public class AvatarCloneExclusion : IExclusionBehaviour -{ - public readonly Dictionary> skinnedToBoneIndex = new(); - public readonly List affectedTransforms = new(); - public readonly List affectedRenderers = new(); - public HashSet affectedTransformSet = new(); - - private readonly AvatarClone _cloneSystem; - private readonly Transform _target; - internal Transform _shrinkBone; - - public bool isImmuneToGlobalState { get; set; } - - public AvatarCloneExclusion(AvatarClone cloneSystem, Transform target) - { - _cloneSystem = cloneSystem; - _target = target; - } - - public void UpdateExclusions(bool isShown, bool shrinkToZero) - { - if (_shrinkBone == null) - { - // Create shrink bone parented directly to target - _shrinkBone = new GameObject($"{_target.name}_Shrink").transform; - _shrinkBone.SetParent(_target, false); - } - // Set scale based on shrink mode - _shrinkBone.localScale = shrinkToZero ? Vector3.zero : Vector3.positiveInfinity; - - // Replace the bone references with the shrink bone for the indicies we modify - _cloneSystem.HandleExclusionUpdate(this, isShown); - } -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/AvatarCloneTest.csproj b/.Deprecated/AvatarCloneTest/AvatarCloneTest.csproj deleted file mode 100644 index 2e58669..0000000 --- a/.Deprecated/AvatarCloneTest/AvatarCloneTest.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - LocalCloneFix - - - TRACE;TRACE; - - diff --git a/.Deprecated/AvatarCloneTest/Main.cs b/.Deprecated/AvatarCloneTest/Main.cs deleted file mode 100644 index 3bb4f09..0000000 --- a/.Deprecated/AvatarCloneTest/Main.cs +++ /dev/null @@ -1,81 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.EventSystem; -using ABI_RC.Core.Savior; -using MelonLoader; -using UnityEngine; - -namespace NAK.AvatarCloneTest; - -public class AvatarCloneTestMod : MelonMod -{ - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(AvatarCloneTest)); - - internal static readonly MelonPreferences_Entry EntryUseAvatarCloneTest = - Category.CreateEntry("use_avatar_clone_test", true, - "Use Avatar Clone", description: "Uses the Avatar Clone setup for the local avatar."); - - internal static readonly MelonPreferences_Entry EntryCloneMeshRenderers = - Category.CreateEntry("clone_mesh_renderers", false, - "Clone Mesh Renderers", description: "Clones the mesh renderers from the original avatar to the clone."); - - internal static readonly MelonPreferences_Entry EntryCopyBlendShapes = - Category.CreateEntry("copy_blend_shapes", true, - "Copy Blend Shapes", description: "Copies the blend shapes from the original avatar to the clone."); - - internal static readonly MelonPreferences_Entry EntryCopyMaterials = - Category.CreateEntry("copy_materials", true, - "Copy Materials", description: "Copies the materials from the original avatar to the clone."); - - #endregion Melon Preferences - - #region Melon Events - - public override void OnInitializeMelon() - { - ApplyPatches(typeof(Patches)); // slapped together a fix cause HarmonyInstance.Patch was null ref for no reason? - } - - public override void OnUpdate() - { - // press f1 to find all cameras that arent tagged main and set them tno not render CVRLayers.PlayerClone - if (Input.GetKeyDown(KeyCode.F1)) - { - foreach (var camera in UnityEngine.Object.FindObjectsOfType()) - { - if (camera.tag != "MainCamera") - { - camera.cullingMask &= ~(1 << CVRLayers.PlayerClone); - } - } - } - - // if pressing ctrl + r, reload avatar - if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.R)) - { - var player = MetaPort.Instance.currentAvatarGuid; - AssetManagement.Instance.LoadLocalAvatar(player); - } - } - - #endregion Melon Events - - #region Melon Mod Utilities - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #endregion Melon Mod Utilities -} \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/Patches.cs b/.Deprecated/AvatarCloneTest/Patches.cs deleted file mode 100644 index ecd64c5..0000000 --- a/.Deprecated/AvatarCloneTest/Patches.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.Player; -using ABI_RC.Core.Player.TransformHider; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.Camera; -using HarmonyLib; -using UnityEngine; - -namespace NAK.AvatarCloneTest; - -public static class Patches -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(TransformHiderUtils), nameof(TransformHiderUtils.SetupAvatar))] - private static bool OnSetupAvatar(GameObject avatar) - { - if (!AvatarCloneTestMod.EntryUseAvatarCloneTest.Value) return true; - avatar.AddComponent(); - return false; - } -} - \ No newline at end of file diff --git a/.Deprecated/AvatarCloneTest/Properties/AssemblyInfo.cs b/.Deprecated/AvatarCloneTest/Properties/AssemblyInfo.cs deleted file mode 100644 index 84f8de2..0000000 --- a/.Deprecated/AvatarCloneTest/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.AvatarCloneTest.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.AvatarCloneTest))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.AvatarCloneTest))] - -[assembly: MelonInfo( - typeof(NAK.AvatarCloneTest.AvatarCloneTestMod), - nameof(NAK.AvatarCloneTest), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/AvatarCloneTest" -)] - -[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.AvatarCloneTest.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.3"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_Utils.cs b/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_Utils.cs deleted file mode 100644 index 9c9eb20..0000000 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_Utils.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Reflection; -using BTKUILib; -using BTKUILib.UIObjects; -using BTKUILib.UIObjects.Components; -using MelonLoader; -using UnityEngine; - -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon -{ - #region Melon Preference Helpers - - private static ToggleButton AddMelonToggle(ref Category category, MelonPreferences_Entry entry) - { - ToggleButton toggle = category.AddToggle(entry.DisplayName, entry.Description, entry.Value); - toggle.OnValueUpdated += b => entry.Value = b; - return toggle; - } - - private static SliderFloat AddMelonSlider(ref Category category, MelonPreferences_Entry entry, float min, - float max, int decimalPlaces = 2, bool allowReset = true) - { - SliderFloat slider = category.AddSlider(entry.DisplayName, entry.Description, - Mathf.Clamp(entry.Value, min, max), min, max, decimalPlaces, entry.DefaultValue, allowReset); - slider.OnValueUpdated += f => entry.Value = f; - return slider; - } - - private static Button AddMelonStringInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) - { - Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); - button.OnPress += () => QuickMenuAPI.OpenKeyboard(entry.Value, s => entry.Value = s); - return button; - } - - private static Button AddMelonNumberInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) - { - Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); - button.OnPress += () => QuickMenuAPI.OpenNumberInput(entry.DisplayName, entry.Value, f => entry.Value = f); - return button; - } - - // private static SliderFloat AddMelonSlider(ref Page page, MelonPreferences_Entry entry, float min, float max, int decimalPlaces = 2, bool allowReset = true) - // { - // SliderFloat slider = page.AddSlider(entry.DisplayName, entry.Description, Mathf.Clamp(entry.Value, min, max), min, max, decimalPlaces, entry.DefaultValue, allowReset); - // slider.OnValueUpdated += f => entry.Value = f; - // return slider; - // } - - /// - /// Helper method to create a category that saves its collapsed state to a MelonPreferences entry. - /// - /// - /// - /// - /// - private static Category AddMelonCategory(ref Page page, MelonPreferences_Entry entry, bool showHeader = true) - { - Category category = page.AddCategory(entry.DisplayName, showHeader, true, entry.Value); - category.OnCollapse += b => entry.Value = b; - return category; - } - - #endregion - - #region Icon Utils - - private static Stream GetIconStream(string iconName) - { - Assembly assembly = Assembly.GetExecutingAssembly(); - string assemblyName = assembly.GetName().Name; - return assembly.GetManifestResourceStream($"{assemblyName}.resources.{iconName}"); - } - - #endregion -} \ No newline at end of file diff --git a/.Deprecated/BullshitWatcher/BullshitWatcher.csproj b/.Deprecated/BullshitWatcher/BullshitWatcher.csproj deleted file mode 100644 index 4e44ed3..0000000 --- a/.Deprecated/BullshitWatcher/BullshitWatcher.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net48 - - - - ..\.ManagedLibs\TheClapper.dll - - - diff --git a/.Deprecated/BullshitWatcher/Main.cs b/.Deprecated/BullshitWatcher/Main.cs deleted file mode 100644 index aa46305..0000000 --- a/.Deprecated/BullshitWatcher/Main.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Reflection; -using HarmonyLib; -using MelonLoader; -using UnityEngine; - -namespace NAK.BullshitWatcher; - -// Slapped together to log where bullshit values are coming from, -// instead of creating the same Unity Explorer patch for the 100th time - -public class BullshitWatcherMod : MelonMod -{ - #region Initialize - - public override void OnInitializeMelon() - { - #region Transform Patches - - Type transformType = typeof(Transform); - - // Properties - PatchProperty(transformType, nameof(Transform.position)); - PatchProperty(transformType, nameof(Transform.localPosition)); - PatchProperty(transformType, nameof(Transform.rotation)); - PatchProperty(transformType, nameof(Transform.localRotation)); - PatchProperty(transformType, nameof(Transform.localScale)); - PatchProperty(transformType, nameof(Transform.right)); - PatchProperty(transformType, nameof(Transform.up)); - PatchProperty(transformType, nameof(Transform.forward)); - - // Methods - PatchMethod(transformType, nameof(Transform.SetPositionAndRotation)); - PatchMethod(transformType, nameof(Transform.SetLocalPositionAndRotation)); - PatchMethod(transformType, nameof(Transform.Translate), new[] { typeof(Vector3), typeof(Space) }); - PatchMethod(transformType, nameof(Transform.Translate), new[] { typeof(Vector3) }); - PatchMethod(transformType, nameof(Transform.Translate), new[] { typeof(Vector3), typeof(Transform) }); - PatchMethod(transformType, nameof(Transform.Rotate), new[] { typeof(Vector3), typeof(Space) }); - PatchMethod(transformType, nameof(Transform.Rotate), new[] { typeof(Vector3) }); - PatchMethod(transformType, nameof(Transform.Rotate), new[] { typeof(Vector3), typeof(float), typeof(Space) }); - PatchMethod(transformType, nameof(Transform.RotateAround)); - PatchMethod(transformType, nameof(Transform.LookAt), new[] { typeof(Vector3), typeof(Vector3) }); - - #endregion - - #region Rigidbody Patches - - Type rigidbodyType = typeof(Rigidbody); - - // Properties - PatchProperty(rigidbodyType, nameof(Rigidbody.position)); - PatchProperty(rigidbodyType, nameof(Rigidbody.rotation)); - PatchProperty(rigidbodyType, nameof(Rigidbody.velocity)); - PatchProperty(rigidbodyType, nameof(Rigidbody.angularVelocity)); - PatchProperty(rigidbodyType, nameof(Rigidbody.centerOfMass)); - - // Methods - PatchMethod(rigidbodyType, nameof(Rigidbody.MovePosition)); - PatchMethod(rigidbodyType, nameof(Rigidbody.MoveRotation)); - PatchMethod(rigidbodyType, nameof(Rigidbody.AddForce), new[] { typeof(Vector3), typeof(ForceMode) }); - PatchMethod(rigidbodyType, nameof(Rigidbody.AddRelativeForce), new[] { typeof(Vector3), typeof(ForceMode) }); - PatchMethod(rigidbodyType, nameof(Rigidbody.AddTorque), new[] { typeof(Vector3), typeof(ForceMode) }); - PatchMethod(rigidbodyType, nameof(Rigidbody.AddRelativeTorque), new[] { typeof(Vector3), typeof(ForceMode) }); - PatchMethod(rigidbodyType, nameof(Rigidbody.AddForceAtPosition), new[] { typeof(Vector3), typeof(Vector3), typeof(ForceMode) }); - - #endregion - } - - private void PatchProperty(Type type, string propertyName) - { - PropertyInfo property = type.GetProperty(propertyName); - if (property!.SetMethod != null) - { - HarmonyInstance.Patch( - property.SetMethod, - prefix: new HarmonyMethod(typeof(BullshitWatcherMod).GetMethod(nameof(OnSetValue), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - } - - private void PatchMethod(Type type, string methodName, Type[] parameters = null) - { - MethodInfo method; - if (parameters != null) - { - method = type.GetMethod(methodName, - BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy, - null, - parameters, - null); - } - else - { - var methods = type.GetMethods() - .Where(m => m.Name == methodName && !m.IsGenericMethod) - .ToArray(); - - // If there's only one method with this name, use it - if (methods.Length == 1) - { - method = methods[0]; - } - else - { - // This is fine :) - LoggerInstance.Error($"Multiple methods found for {type.Name}.{methodName}, skipping ambiguous patch"); - return; - } - } - - if (method != null) - { - HarmonyInstance.Patch( - method, - prefix: new HarmonyMethod(typeof(BullshitWatcherMod).GetMethod(nameof(OnMethodCall), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - else - { - MelonLogger.Warning($"Could not find method {type.Name}.{methodName}"); - } - } - - #endregion - - #region Validation Methods - - private static bool ContainsBullshitValue(Vector3 vector) - { - return float.IsNaN(vector.x) || float.IsNaN(vector.y) || float.IsNaN(vector.z) || - float.IsInfinity(vector.x) || float.IsInfinity(vector.y) || float.IsInfinity(vector.z) || - float.IsNegativeInfinity(vector.x) || float.IsNegativeInfinity(vector.y) || float.IsNegativeInfinity(vector.z); - } - - private static bool ContainsBullshitValue(Quaternion quaternion) - { - return float.IsNaN(quaternion.x) || float.IsNaN(quaternion.y) || float.IsNaN(quaternion.z) || float.IsNaN(quaternion.w) || - float.IsInfinity(quaternion.x) || float.IsInfinity(quaternion.y) || float.IsInfinity(quaternion.z) || float.IsInfinity(quaternion.w) || - float.IsNegativeInfinity(quaternion.x) || float.IsNegativeInfinity(quaternion.y) || - float.IsNegativeInfinity(quaternion.z) || float.IsNegativeInfinity(quaternion.w); - } - - private static bool ContainsBullshitValue(float value) - { - return float.IsNaN(value) || float.IsInfinity(value) || float.IsNegativeInfinity(value); - } - - private static bool ContainsBullshitValues(object[] values) - { - foreach (var value in values) - { - if (value == null) continue; - - switch (value) - { - case Vector3 v3: - if (ContainsBullshitValue(v3)) return true; - break; - case Quaternion q: - if (ContainsBullshitValue(q)) return true; - break; - case float f: - if (ContainsBullshitValue(f)) return true; - break; - } - } - return false; - } - - #endregion - - #region Logging Methods - - private static void LogBullshitValue(string componentType, string propertyName, Component component, object value) - { - MelonLogger.Error($"Bullshit {componentType}.{propertyName} value detected on GameObject '{GetGameObjectPath(component.gameObject)}': {value}"); - MelonLogger.Error(Environment.StackTrace); - } - - private static void LogBullshitMethod(string componentType, string methodName, Component component, object[] parameters) - { - MelonLogger.Error($"Bullshit parameters in {componentType}.{methodName} call on GameObject '{GetGameObjectPath(component.gameObject)}'"); - for (int i = 0; i < parameters.Length; i++) - { - if (parameters[i] != null) - MelonLogger.Error($" Parameter {i}: {parameters[i]}"); - } - MelonLogger.Error(Environment.StackTrace); - } - - private static string GetGameObjectPath(GameObject obj) - { - string path = obj.name; - Transform parent = obj.transform.parent; - - while (parent != null) - { - path = $"{parent.name}/{path}"; - parent = parent.parent; - } - - return path; - } - - #endregion - - #region Harmony Patches - - private static void OnSetValue(object value, Component __instance) - { - if (value == null) return; - - var componentType = __instance.GetType().Name; - var propertyName = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name.Replace("set_", ""); - - switch (value) - { - case Vector3 v3 when ContainsBullshitValue(v3): - LogBullshitValue(componentType, propertyName, __instance, v3); - break; - case Quaternion q when ContainsBullshitValue(q): - LogBullshitValue(componentType, propertyName, __instance, q); - break; - case float f when ContainsBullshitValue(f): - LogBullshitValue(componentType, propertyName, __instance, f); - break; - } - } - - private static void OnMethodCall(object[] __args, Component __instance) - { - if (__args == null || __args.Length == 0) return; - - if (ContainsBullshitValues(__args)) - { - var componentType = __instance.GetType().Name; - var methodName = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name; - LogBullshitMethod(componentType, methodName, __instance, __args); - } - } - - #endregion -} \ No newline at end of file diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Constants.cs b/.Deprecated/CVRGizmos/Popcron.Gizmos/Constants.cs deleted file mode 100644 index 56f8ef4..0000000 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Constants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Popcron; - -public class Constants -{ - public const string UniqueIdentifier = "Popcron.Gizmos"; - public const string EnabledKey = UniqueIdentifier + ".Enabled"; -} \ No newline at end of file diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/LineDrawer.cs b/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/LineDrawer.cs deleted file mode 100644 index 0f17498..0000000 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/LineDrawer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UnityEngine; - -namespace Popcron; - -public class LineDrawer : Drawer -{ - public LineDrawer() - { - - } - - public override int Draw(ref Vector3[] buffer, params object[] args) - { - buffer[0] = (Vector3)args[0]; - buffer[1] = (Vector3)args[1]; - return 2; - } -} \ No newline at end of file diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Element.cs b/.Deprecated/CVRGizmos/Popcron.Gizmos/Element.cs deleted file mode 100644 index 1ea97d5..0000000 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Element.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UnityEngine; - -namespace Popcron; - -internal class Element -{ - public Vector3[] points = { }; - public Color color = Color.white; - public bool dashed = false; - public Matrix4x4 matrix = Matrix4x4.identity; -} \ No newline at end of file diff --git a/.Deprecated/ControlToUnlockMouse/ControlToUnlockMouse.csproj b/.Deprecated/ControlToUnlockMouse/ControlToUnlockMouse.csproj deleted file mode 100644 index 0f08b56..0000000 --- a/.Deprecated/ControlToUnlockMouse/ControlToUnlockMouse.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - SpawnableReceiveOwnChanges - - diff --git a/.Deprecated/ControlToUnlockMouse/Main.cs b/.Deprecated/ControlToUnlockMouse/Main.cs deleted file mode 100644 index 4ca9845..0000000 --- a/.Deprecated/ControlToUnlockMouse/Main.cs +++ /dev/null @@ -1,308 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.InteractionSystem.Base; -using ABI_RC.Core.Player; -using System.Reflection; -using cohtml.Net; -using HarmonyLib; -using UnityEngine; -using MelonLoader; -using Object = UnityEngine.Object; - -namespace NAK.ControlToUnlockMouse; - -public class ControlToUnlockMouseMod : MelonMod -{ - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(ControlToUnlockMouseMod)); - - internal static readonly MelonPreferences_Entry EntryOriginPivotPoint = - Category.CreateEntry("no_rotate_pivot_point", NoRotatePivotPoint.Pickupable, - "NoRotation Pickupable Pivot Point", "The pivot point to use when no rotation object is grabbed."); - - public enum NoRotatePivotPoint - { - Pickupable, - AvatarHead, - AvatarChest, - AvatarClosestShoulder, - } - - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.Awake), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(ControlToUnlockMouseMod).GetMethod(nameof(OnPlayerSetupAwake), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(CVR_MenuManager).GetMethod(nameof(CVR_MenuManager.Start), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(ControlToUnlockMouseMod).GetMethod(nameof(OnMenuManagerStart), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(ControllerRay).GetMethod(nameof(ControllerRay.HandleUnityUI), - BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(ControlToUnlockMouseMod).GetMethod(nameof(OnControllerRayHandleUnityUIDirectAndIndirect), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(ControllerRay).GetMethod(nameof(ControllerRay.HandleIndirectUnityUI), - BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(ControlToUnlockMouseMod).GetMethod(nameof(OnControllerRayHandleUnityUIDirectAndIndirect), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(ControllerRay).GetMethod(nameof(ControllerRay.LateUpdate), - BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(ControlToUnlockMouseMod).GetMethod(nameof(OnPreControllerRayLateUpdate), - BindingFlags.NonPublic | BindingFlags.Static)), - postfix: new HarmonyMethod(typeof(ControlToUnlockMouseMod).GetMethod(nameof(OnPostControllerRayLateUpdate), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - private static void OnPlayerSetupAwake(PlayerSetup __instance) - { - // Get original fields - LayerMask layerMask = __instance.desktopRay.generalMask; - - // Destroy the existing desktop ray - Object.Destroy(__instance.desktopRay); - - // Get the desktop camera - Camera desktopCam = __instance.desktopCam; - - // Create a new child object under the desktop camera for the ray - GameObject rayObject = new("DesktopRay") - { - transform = - { - parent = desktopCam.transform, - localPosition = Vector3.zero, - localRotation = Quaternion.identity - } - }; - - // Add ControllerRay component - ControllerRay newRay = rayObject.AddComponent(); - newRay.isDesktopRay = true; - newRay.isInteractionRay = true; - newRay.RayDirection = Vector3.forward; - newRay.generalMask = layerMask; - newRay.hand = CVRHand.Right; // Important to even work - newRay.attachmentDistance = 0f; - newRay.currentAttachmentDistance = 0f; - - // Assign new ray to desktopRay field - __instance.desktopRay = newRay; - - // Add our custom controller script - DesktopRayController rayController = rayObject.AddComponent(); - rayController.controllerRay = newRay; - rayController.desktopCamera = desktopCam; - } - - private static void OnMenuManagerStart(CVR_MenuManager __instance) - { - __instance.desktopControllerRay = PlayerSetup.Instance.desktopRay; - } - - private static bool OnControllerRayHandleUnityUIDirectAndIndirect(ControllerRay __instance) - { - return !__instance.isDesktopRay || Cursor.lockState == CursorLockMode.Locked; - } - - private static void OnPreControllerRayLateUpdate(ControllerRay __instance, ref bool __state) - { - if (!__instance.isDesktopRay) - return; - - ViewManager menu = ViewManager.Instance; - __state = menu._gameMenuOpen; - - if (!__state) menu._gameMenuOpen = Cursor.lockState != CursorLockMode.Locked; - } - - private static void OnPostControllerRayLateUpdate(ControllerRay __instance, ref bool __state) - { - if (!__instance.isDesktopRay) return; - ViewManager.Instance._gameMenuOpen = __state; - } -} - -public class DesktopRayController : MonoBehaviour -{ - internal ControllerRay controllerRay; - internal Camera desktopCamera; - - private void Update() - { - // Toggle desktop mouse mode based on Control key state - if (Input.GetKeyDown(KeyCode.LeftControl)) - { - if (!ViewManager.Instance.IsAnyMenuOpen) RootLogic.CursorLock(false); - } - - if (Input.GetKeyUp(KeyCode.LeftControl)) - { - if (!ViewManager.Instance.IsAnyMenuOpen) RootLogic.CursorLock(true); - } - - Transform rayRoot = controllerRay.transform; - Transform rayDirection = controllerRay.rayDirectionTransform; - Transform attachment = controllerRay.attachmentPoint; - Camera cam = desktopCamera; - - if (Cursor.lockState == CursorLockMode.Locked) - { - // Reset local position when unlocked - rayRoot.localPosition = Vector3.zero; - rayRoot.localRotation = Quaternion.identity; - - // Reset local position and rotation when locked - rayDirection.localPosition = new Vector3(0f, 0f, 0.001f); - rayDirection.localRotation = Quaternion.identity; - } - else - { - bool isAnyMenuOpen = ViewManager.Instance.IsAnyMenuOpen; - Pickupable grabbedObject = controllerRay.grabbedObject; - - // Only do when not holding an origin object - Vector3 screenPos = new(Input.mousePosition.x, Input.mousePosition.y); - - if (isAnyMenuOpen) - { - // Center the ray - rayRoot.localPosition = Vector3.zero; - } - else if (grabbedObject && !grabbedObject.IsObjectRotationAllowed) - { - // Specialized movement of ray around pickupable pivot - Vector3 pivotPoint = grabbedObject.transform.position; - Vector3 pivotPointCenter = grabbedObject.RootTransform.position; - - PlayerSetup playerSetup = PlayerSetup.Instance; - if (playerSetup != null && playerSetup._animator != null && playerSetup._animator.isHuman) - { - Animator animator = playerSetup._animator; - switch (ControlToUnlockMouseMod.EntryOriginPivotPoint.Value) - { - case ControlToUnlockMouseMod.NoRotatePivotPoint.AvatarHead: - { - Transform headBone = animator.GetBoneTransform(HumanBodyBones.Head); - if (headBone != null) pivotPoint = headBone.position; - break; - } - case ControlToUnlockMouseMod.NoRotatePivotPoint.AvatarChest: - { - if (playerSetup._avatar != null) - { - Transform chestBone = animator.GetBoneTransform(HumanBodyBones.Chest); - if (chestBone != null) pivotPoint = chestBone.position; - } - break; - } - case ControlToUnlockMouseMod.NoRotatePivotPoint.AvatarClosestShoulder: - { - if (playerSetup._avatar != null) - { - Transform leftShoulder = animator.GetBoneTransform(HumanBodyBones.LeftShoulder); - Transform rightShoulder = animator.GetBoneTransform(HumanBodyBones.RightShoulder); - if (leftShoulder != null || rightShoulder != null) - { - if (leftShoulder != null && rightShoulder != null) - { - pivotPoint = Vector3.Distance(leftShoulder.position, pivotPoint) < Vector3.Distance(rightShoulder.position, pivotPoint) - ? leftShoulder.position - : rightShoulder.position; - } - else if (leftShoulder != null) - { - pivotPoint = leftShoulder.position; - } - else - { - pivotPoint = rightShoulder.position; - } - } - } - break; - } - case ControlToUnlockMouseMod.NoRotatePivotPoint.Pickupable: - default: - break; - } - } - - // Get local position of pivotPoint relative to rayRoot - // This is shit but i cant wrap my head around the proper way to compute this lol - Vector3 localPivotPoint = rayRoot.InverseTransformPoint(pivotPoint); - Vector3 localPivotPointCenter = rayRoot.InverseTransformPoint(pivotPointCenter); - localPivotPoint.x = localPivotPointCenter.x; // Maintain local X - localPivotPoint.y = localPivotPointCenter.y; // Maintain local Y - - // Compute target world position based on the mouse and attachment distance. - screenPos.z = 10f; - Vector3 targetWorldPos = cam.ScreenToWorldPoint(screenPos); - - // Desired direction from the pivot point (grabbed object) to the target world position. - Vector3 directionToTarget = targetWorldPos - rayRoot.TransformPoint(localPivotPoint);; - - if (directionToTarget.sqrMagnitude < 1e-6f) - directionToTarget = rayRoot.forward; // Fallback if mouse is centered - - // Calculate the target rotation for rayRoot. - Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, cam.transform.up); - - // Get the current local offset of the grabbed object relative to rayRoot. - Vector3 localPickupOffset = rayRoot.InverseTransformPoint(pivotPoint); - - // Compute the new rayRoot position to keep the grabbed object (child) at pivotPoint. - Vector3 newRayRootPos = pivotPoint - (targetRotation * localPickupOffset); - - // Apply the new rotation and position. - rayRoot.rotation = targetRotation; - rayRoot.position = newRayRootPos; - } - else - { - float distance; - if (grabbedObject) - { - // This position is calculated basically same way as below in BasePickupHandler, - // but not determined by ray hit - distance = attachment.localPosition.z; - } - else - { - // Compute distance forward from ray - Vector3 localOffset = rayRoot.InverseTransformPoint(controllerRay._hit.point); - distance = localOffset.z; - } - - screenPos.z = distance; - - // Compute world position from where mouse is on screen - Vector3 worldPos = cam.ScreenToWorldPoint(screenPos); - - // Normal movement of ray - Vector3 newLocalPos = rayRoot.parent.InverseTransformPoint(worldPos); - newLocalPos.z = rayRoot.localPosition.z; // Maintain local Z - rayRoot.localPosition = newLocalPos; - } - - // Compute mouse ray in world space - Ray mouseRay = cam.ScreenPointToRay(Input.mousePosition); - rayDirection.position = mouseRay.origin; - rayDirection.rotation = Quaternion.LookRotation(mouseRay.direction, cam.transform.up); - } - } -} \ No newline at end of file diff --git a/.Deprecated/ControlToUnlockMouse/Properties/AssemblyInfo.cs b/.Deprecated/ControlToUnlockMouse/Properties/AssemblyInfo.cs deleted file mode 100644 index 9e54143..0000000 --- a/.Deprecated/ControlToUnlockMouse/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.ControlToUnlockMouse.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.ControlToUnlockMouse))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.ControlToUnlockMouse))] - -[assembly: MelonInfo( - typeof(NAK.ControlToUnlockMouse.ControlToUnlockMouseMod), - nameof(NAK.ControlToUnlockMouse), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ControlToUnlockMouse" -)] - -[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.ControlToUnlockMouse.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/ControlToUnlockMouse/README.md b/.Deprecated/ControlToUnlockMouse/README.md deleted file mode 100644 index 1c4c5bc..0000000 --- a/.Deprecated/ControlToUnlockMouse/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# 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. diff --git a/.Deprecated/ControlToUnlockMouse/format.json b/.Deprecated/ControlToUnlockMouse/format.json deleted file mode 100644 index f8950ca..0000000 --- a/.Deprecated/ControlToUnlockMouse/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_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" -} \ No newline at end of file diff --git a/.Deprecated/FuckMagicaCloth2/FuckMagicaCloth2.csproj b/.Deprecated/FuckMagicaCloth2/FuckMagicaCloth2.csproj deleted file mode 100644 index bec5b03..0000000 --- a/.Deprecated/FuckMagicaCloth2/FuckMagicaCloth2.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - LoadedObjectHack - - diff --git a/.Deprecated/FuckMagicaCloth2/Main.cs b/.Deprecated/FuckMagicaCloth2/Main.cs deleted file mode 100644 index 2b1421d..0000000 --- a/.Deprecated/FuckMagicaCloth2/Main.cs +++ /dev/null @@ -1,49 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using HarmonyLib; -using MagicaCloth2; -using MelonLoader; -using Unity.Burst; -using Unity.Collections; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine; -using UnityEngine.Jobs; - -namespace NAK.FuckOffMagicaCloth2; - -public class FuckOffMagicaCloth2Mod : MelonMod -{ - private static MelonLogger.Instance Logger; - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - ApplyPatches(typeof(MagicaCloth_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 class MagicaCloth_Patches - { - [HarmonyPrefix] - [HarmonyPatch(typeof(MagicaCloth2.MagicaCloth), nameof(MagicaCloth2.MagicaCloth.Awake))] - private static void MagicaCloth_Awake_Prefix(MagicaCloth2.MagicaCloth __instance) - { - __instance.SerializeData.selfCollisionConstraint.selfMode = SelfCollisionConstraint.SelfCollisionMode.None; - __instance.SerializeData.selfCollisionConstraint.syncMode = SelfCollisionConstraint.SelfCollisionMode.None; - } - } -} \ No newline at end of file diff --git a/.Deprecated/FuckMagicaCloth2/format.json b/.Deprecated/FuckMagicaCloth2/format.json deleted file mode 100644 index ddf506b..0000000 --- a/.Deprecated/FuckMagicaCloth2/format.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "_id": -1, - "name": "AASDefaultProfileFix", - "modversion": "1.0.0", - "gameversion": "2024r175", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Fixes the Default AAS profile not being applied when loading into an avatar without a profile selected.\n\nBy default, the game will not apply anything and the avatar will default to the state found within the Controller parameters.", - "searchtags": [ - "aas", - "profile", - "default", - "fix", - "meow" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r33/AASDefaultProfileFix.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/AASDefaultProfileFix/", - "changelog": "- Initial release", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/FuckOffUICamera/CohtmlRenderForwarder.cs b/.Deprecated/FuckOffUICamera/CohtmlRenderForwarder.cs deleted file mode 100644 index 5f5dd2a..0000000 --- a/.Deprecated/FuckOffUICamera/CohtmlRenderForwarder.cs +++ /dev/null @@ -1,35 +0,0 @@ -using ABI_RC.Core.UI; -using UnityEngine; - -namespace NAK.FuckOffUICamera; - -[RequireComponent(typeof(Camera))] -public class CohtmlRenderForwarder : MonoBehaviour -{ - #region Private Variables - - private CohtmlControlledView[] controlledViews; - - #endregion Private Variables - - #region Unity Events - - private void OnPreRender() - { - if (controlledViews == null) return; - foreach (CohtmlControlledView view in controlledViews) - if (view) view.OnPreRender(); - } - - #endregion Unity Events - - #region Public Methods - - public static void Setup(Camera camera, params CohtmlControlledView[] views) - { - CohtmlRenderForwarder forwarder = camera.gameObject.AddComponent(); - forwarder.controlledViews = views; - } - - #endregion Public Methods -} \ No newline at end of file diff --git a/.Deprecated/FuckOffUICamera/CommandBufferManager.cs b/.Deprecated/FuckOffUICamera/CommandBufferManager.cs deleted file mode 100644 index aa76f1f..0000000 --- a/.Deprecated/FuckOffUICamera/CommandBufferManager.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.Rendering; - -namespace NAK.FuckOffUICamera; - -public class CommandBufferManager : MonoBehaviour -{ - #region Private Variables - - private CommandBuffer commandBuffer; - private Camera targetCamera; - private Renderer[] targetRenderers; - private bool[] rendererEnabledStates; - private const string CommandBufferName = "CustomRenderPass"; - private bool _didSetup; - - #endregion Private Variables - - #region Unity Events - - private IEnumerator Start() - { - yield return new WaitForSeconds(2f); // I have no idea why this needs to be delayed - _didSetup = true; - OnEnable(); - } - - private void OnEnable() - { - if (!_didSetup) return; - if (targetCamera == null || targetRenderers == null) - return; - - SetupEnabledStateCollection(); - SetupCommandBuffer(); - } - - private void OnDisable() - { - CleanupCommandBuffer(); - } - - private void LateUpdate() - { - if (targetRenderers == null - || rendererEnabledStates == null) - return; - - bool needsRebuild = false; - - // Check if any renderer enabled states have changed - int targetRenderersLength = targetRenderers.Length; - for (int i = 0; i < targetRenderersLength; i++) - { - if (targetRenderers[i] == null) continue; - - bool currentState = targetRenderers[i].enabled && targetRenderers[i].gameObject.activeInHierarchy; - if (currentState == rendererEnabledStates[i]) - continue; - - rendererEnabledStates[i] = currentState; - needsRebuild = true; - } - - if (needsRebuild) RebuildCommandBuffer(); - } - - #endregion Unity Events - - #region Public Methods - - public static void Setup(Camera camera, params Renderer[] renderers) - { - CommandBufferManager manager = camera.gameObject.AddComponent(); - manager.targetCamera = camera; - manager.targetRenderers = renderers; - } - - #endregion Public Methods - - #region Private Methods - - private void SetupEnabledStateCollection() - { - if (rendererEnabledStates != null) - Array.Resize(ref rendererEnabledStates, targetRenderers.Length); - else - rendererEnabledStates = new bool[targetRenderers.Length]; - } - - private void SetupCommandBuffer() - { - commandBuffer = new CommandBuffer(); - commandBuffer.name = CommandBufferName; - - // Set render target and clear depth - commandBuffer.SetRenderTarget(new RenderTargetIdentifier(BuiltinRenderTextureType.CameraTarget, - 0, CubemapFace.Unknown, RenderTargetIdentifier.AllDepthSlices)); - - commandBuffer.ClearRenderTarget(true, false, Color.clear); - - for (int i = 0; i < targetRenderers.Length; i++) - { - Renderer renderer = targetRenderers[i]; - if (renderer == null || !rendererEnabledStates[i]) - continue; - - commandBuffer.DrawRenderer(renderer, renderer.sharedMaterial); - renderer.forceRenderingOff = true; - } - - targetCamera.AddCommandBuffer(CameraEvent.AfterImageEffects, commandBuffer); - - Debug.Log($"Command buffer setup for {targetCamera.name} with {targetRenderers.Length} renderers."); - } - - private void RebuildCommandBuffer() - { - CleanupCommandBuffer(); - SetupCommandBuffer(); - } - - private void CleanupCommandBuffer() - { - if (targetCamera == null || commandBuffer == null) - return; - - // Re-enable normal rendering for all renderers - if (targetRenderers != null) - { - foreach (Renderer renderer in targetRenderers) - { - if (renderer != null) - renderer.forceRenderingOff = false; - } - } - - targetCamera.RemoveCommandBuffer(CameraEvent.AfterImageEffects, commandBuffer); - commandBuffer = null; - } - - #endregion Private Methods -} \ No newline at end of file diff --git a/.Deprecated/FuckOffUICamera/Main.cs b/.Deprecated/FuckOffUICamera/Main.cs deleted file mode 100644 index e4c9092..0000000 --- a/.Deprecated/FuckOffUICamera/Main.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Reflection; -using ABI_RC.Core; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using ABI_RC.Core.UI; -using HarmonyLib; -using MelonLoader; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace NAK.FuckOffUICamera; - -public class FuckOffUICameraMod : MelonMod -{ - private static MelonLogger.Instance Logger; - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - } - - public override void OnSceneWasLoaded(int buildIndex, string sceneName) - { - if (buildIndex != 2) return; - if (_isInitialized) return; - SetupShittyMod(); - _isInitialized = true; - } - - private bool _isInitialized; - - private static void SetupShittyMod() - { - // Find all renderers under Cohtml object - GameObject cohtml = GameObject.Find("Cohtml"); - if (cohtml == null) - { - Logger.Error("Cohtml object not found!"); - return; - } - - // Find all CohtmlControlledView objects - var allMenuCohtml = Object.FindObjectsOfType(includeInactive: true); - var allUiInternalRenderers = Object.FindObjectsOfType(includeInactive: true) - .Where(x => x.gameObject.layer == CVRLayers.UIInternal) - .ToArray(); - - //var allMenuRenderers = cohtml.GetComponentsInChildren(true); - - // Add hud renderer to the list of renderers - Renderer hudRenderer = CohtmlHud.Instance.GetComponent(); - // Array.Resize(ref allMenuRenderers, allMenuRenderers.Length + 1); - // allMenuRenderers[^1] = hudRenderer; - - // Fix shader on the hud renderer - Material material = hudRenderer.sharedMaterial; - material.shader = Shader.Find("Alpha Blend Interactive/MenuFX"); - - // Setup command buffer manager for desktop camera - CommandBufferManager.Setup(PlayerSetup.Instance.desktopCam, allUiInternalRenderers); - CohtmlRenderForwarder.Setup(PlayerSetup.Instance.desktopCam, allMenuCohtml); - - // Setup command buffer manager for vr camera - CommandBufferManager.Setup(PlayerSetup.Instance.vrCam, allUiInternalRenderers); - CohtmlRenderForwarder.Setup(PlayerSetup.Instance.vrCam, allMenuCohtml); - - // Disable the ui cameras - PlayerSetup.Instance.desktopUiCam.gameObject.SetActive(false); - PlayerSetup.Instance.vrUiCam.gameObject.SetActive(false); - - Logger.Msg("Disabled UI cameras and setup command buffer manager for Cohtml renderers."); - } -} \ No newline at end of file diff --git a/.Deprecated/FuckOffUICamera/format.json b/.Deprecated/FuckOffUICamera/format.json deleted file mode 100644 index f8950ca..0000000 --- a/.Deprecated/FuckOffUICamera/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_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" -} \ No newline at end of file diff --git a/.Deprecated/IKSimulatedRootAngleFix/README.md b/.Deprecated/IKSimulatedRootAngleFix/README.md deleted file mode 100644 index 8a46af2..0000000 --- a/.Deprecated/IKSimulatedRootAngleFix/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# IKSimulatedRootAngleFix - -Fixes a small issue with Desktop & HalfBody root angle being incorrectly calculated while on rotating Movement Parents. If you've ever noticed your body/feet insisting on facing opposite of the direction you are rotating, this fixes that. - ---- - -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. diff --git a/.Deprecated/LegacyContentMitigation/Components/CameraCallbackLogger.cs b/.Deprecated/LegacyContentMitigation/Components/CameraCallbackLogger.cs deleted file mode 100644 index ecd37be..0000000 --- a/.Deprecated/LegacyContentMitigation/Components/CameraCallbackLogger.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Collections; -using UnityEngine; -using System.Text; -using MelonLoader; - -namespace NAK.LegacyContentMitigation.Debug; - -public class CameraCallbackLogger -{ - private static CameraCallbackLogger instance; - private readonly List frameCallbacks = new(); - private bool isListening; - private readonly StringBuilder logBuilder = new(); - - public static CameraCallbackLogger Instance => instance ??= new CameraCallbackLogger(); - - private void RegisterCallbacks() - { - Camera.onPreCull += (cam) => LogCallback(cam, "OnPreCull"); - Camera.onPreRender += (cam) => LogCallback(cam, "OnPreRender"); - Camera.onPostRender += (cam) => LogCallback(cam, "OnPostRender"); - } - - private void UnregisterCallbacks() - { - Camera.onPreCull -= (cam) => LogCallback(cam, "OnPreCull"); - Camera.onPreRender -= (cam) => LogCallback(cam, "OnPreRender"); - Camera.onPostRender -= (cam) => LogCallback(cam, "OnPostRender"); - } - - public void LogCameraEvents() - { - MelonCoroutines.Start(LoggingCoroutine()); - } - - private IEnumerator LoggingCoroutine() - { - yield return null; // idk at what point in frame start occurs - - // First frame: Register and listen - RegisterCallbacks(); - isListening = true; - yield return null; - - // Second frame: Log and cleanup - isListening = false; - PrintFrameLog(); - UnregisterCallbacks(); - } - - private void LogCallback(Camera camera, string callbackName) - { - if (!isListening) return; - frameCallbacks.Add($"{camera.name} - {callbackName} (Depth: {camera.depth})"); - } - - private void PrintFrameLog() - { - logBuilder.Clear(); - logBuilder.AppendLine("\nCamera Callbacks for Frame:"); - - foreach (var callback in frameCallbacks) - logBuilder.AppendLine(callback); - - LegacyContentMitigationMod.Logger.Msg(logBuilder.ToString()); - - frameCallbacks.Clear(); - } -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/Components/FaceMirror.cs b/.Deprecated/LegacyContentMitigation/Components/FaceMirror.cs deleted file mode 100644 index 65f3e3b..0000000 --- a/.Deprecated/LegacyContentMitigation/Components/FaceMirror.cs +++ /dev/null @@ -1,100 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using NAK.LegacyContentMitigation; -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.XR; - -namespace LegacyContentMitigation.Components; - -public class FaceMirror : MonoBehaviour -{ - private Camera _parentCamera; - private Camera _camera; - public Rect shiftRect; - private CommandBuffer _viewportBuffer; - - private void Start() { - _parentCamera = GetComponent(); - _camera = new GameObject("Face Mirror").AddComponent(); - _camera.transform.parent = transform; - _camera.CopyFrom(_parentCamera); - _camera.ResetReplacementShader(); - _camera.depth = 99; - _camera.clearFlags = CameraClearFlags.Depth; - _camera.transform.position += transform.forward * 0.5f; - _camera.transform.rotation *= Quaternion.Euler(0, 180, 0); - - // View only CVRLayers.PlayerLocal - _camera.cullingMask = 1 << CVRLayers.PlayerLocal; - - // Create and cache the command buffer - _viewportBuffer = new CommandBuffer(); - _viewportBuffer.SetViewport(shiftRect); - - _camera.AddCommandBuffer(CameraEvent.BeforeDepthTexture, _viewportBuffer); - _camera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, _viewportBuffer); - _camera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _viewportBuffer); - _camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, _viewportBuffer); - } - - private void Update() - { - if (ModSettings.EntryUseFaceMirror.Value == false) - { - _camera.enabled = false; - return; - } - _camera.enabled = true; - - // Update camera distance - _camera.transform.localPosition = Vector3.forward * ModSettings.EntryFaceMirrorDistance.Value; - - // Get the display resolution based on VR status - int displayWidth, displayHeight; - if (MetaPort.Instance.isUsingVr) - { - displayWidth = XRSettings.eyeTextureWidth; - displayHeight = XRSettings.eyeTextureHeight; - } - else - { - displayWidth = Screen.width; - displayHeight = Screen.height; - } - - // Calculate pixel sizes first - float pixelSizeX = ModSettings.EntryFaceMirrorSizeX.Value * displayWidth; - float pixelSizeY = ModSettings.EntryFaceMirrorSizeY.Value * displayHeight; - - // Calculate offsets from center - float pixelOffsetX = (ModSettings.EntryFaceMirrorOffsetX.Value * displayWidth) - (pixelSizeX * 0.5f) + (displayWidth * 0.5f); - float pixelOffsetY = (ModSettings.EntryFaceMirrorOffsetY.Value * displayHeight) - (pixelSizeY * 0.5f) + (displayHeight * 0.5f); - - _camera.transform.localScale = Vector3.one * ModSettings.EntryFaceMirrorCameraScale.Value; - - Vector3 playerup = PlayerSetup.Instance.transform.up; - Vector3 cameraForward = _parentCamera.transform.forward; - - // Check if playerup and cameraForward are nearly aligned - if (Mathf.Abs(Vector3.Dot(playerup, cameraForward)) <= Mathf.Epsilon) { - playerup = -_parentCamera.transform.forward; - cameraForward = _parentCamera.transform.up; - } - - _camera.transform.rotation = Quaternion.LookRotation(-cameraForward, playerup); - - // Create viewport rect with pixel values - shiftRect = new Rect( - pixelOffsetX, - pixelOffsetY, - pixelSizeX, - pixelSizeY - ); - - // Update the cached buffer's viewport - _viewportBuffer.Clear(); - _viewportBuffer.SetViewport(shiftRect); - } -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/Components/FakeMultiPassHack.cs b/.Deprecated/LegacyContentMitigation/Components/FakeMultiPassHack.cs deleted file mode 100644 index fcc7169..0000000 --- a/.Deprecated/LegacyContentMitigation/Components/FakeMultiPassHack.cs +++ /dev/null @@ -1,230 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.Player; -using ABI_RC.Systems.UI; -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.XR; - -namespace NAK.LegacyContentMitigation; - -public class FakeMultiPassHack : MonoBehaviour -{ - private static readonly int s_WorldSpaceCameraPos = Shader.PropertyToID("_WorldSpaceCameraPos"); - - public static Action OnMultiPassActiveChanged; - - #region Properties - - public static FakeMultiPassHack Instance { get; set; } - public bool IsActive => IsEnabled && isActiveAndEnabled; - public bool IsEnabled { get; private set; } - public Camera.MonoOrStereoscopicEye RenderingEye { get; private set; } - - #endregion - - #region Private Fields - - private Camera _mainCamera; - private Camera _leftEye; - private Camera _rightEye; - - private GameObject _leftEyeObject; - private GameObject _rightEyeObject; - - private RenderTexture _leftTexture; - private RenderTexture _rightTexture; - - private CommandBuffer _shaderGlobalBuffer; - private CommandBuffer _leftEyeBuffer; - - private int CachedCullingMask; - private bool _isInitialized; - - #endregion - - #region Unity Lifecycle - - private void OnEnable() - { - Camera.onPreRender += OnPreRenderCallback; - if (IsEnabled) _mainCamera.cullingMask = 0; - } - - private void OnDisable() - { - Camera.onPreRender -= OnPreRenderCallback; - if (IsEnabled) _mainCamera.cullingMask = CachedCullingMask; - } - - private void OnDestroy() - { - if (_leftEye != null) RemoveCameraFromWorldTransitionSystem(_leftEye); - if (_rightEye != null) RemoveCameraFromWorldTransitionSystem(_rightEye); - - if (_leftTexture != null) _leftTexture.Release(); - if (_rightTexture != null) _rightTexture.Release(); - _shaderGlobalBuffer?.Release(); - _leftEyeBuffer?.Release(); - - if (_leftEyeObject != null) Destroy(_leftEyeObject); - if (_rightEyeObject != null) Destroy(_rightEyeObject); - - return; - void RemoveCameraFromWorldTransitionSystem(Camera cam) - { - if (cam.TryGetComponent(out WorldTransitionCamera effectCam)) Destroy(effectCam); - WorldTransitionSystem.Cameras.Remove(cam); - } - } - - #endregion - - #region Public Methods - - public void SetMultiPassActive(bool active) - { - if (active == IsEnabled) return; - IsEnabled = active; - - if (active && !_isInitialized) DoInitialSetup(); - - _mainCamera.cullingMask = IsActive ? 0 : CachedCullingMask; - - OnMultiPassActiveChanged?.Invoke(active); - } - - public void OnMainCameraChanged() - { - if (!_isInitialized) return; - - CachedCullingMask = _mainCamera.cullingMask; - if (IsActive) _mainCamera.cullingMask = 0; - - CVRTools.CopyToDestCam(_mainCamera, _leftEye); - CVRTools.CopyToDestCam(_mainCamera, _rightEye); - } - - #endregion - - #region Initialization - - private void DoInitialSetup() - { - _mainCamera = GetComponent(); - CachedCullingMask = _mainCamera.cullingMask; - - _shaderGlobalBuffer = new CommandBuffer(); - _leftEyeBuffer = new CommandBuffer(); - - SetupEye("Left Eye", out _leftEyeObject, out _leftEye, _leftEyeBuffer); - SetupEye("Right Eye", out _rightEyeObject, out _rightEye, null); - - _isInitialized = true; - - return; - void SetupEye(string camName, out GameObject eyeObj, out Camera eye, CommandBuffer eyeBuffer) - { - eyeObj = new GameObject(camName); - eyeObj.transform.parent = transform; - eyeObj.transform.localScale = Vector3.one; - eye = eyeObj.AddComponent(); - eye.enabled = false; - - // Correct camera world space pos (nameplate shader) - eye.AddCommandBuffer(CameraEvent.BeforeDepthTexture, _shaderGlobalBuffer); - eye.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, _shaderGlobalBuffer); - eye.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _shaderGlobalBuffer); - - // normalizedViewport parameter is ignored, so we cannot draw mesh on right eye :) - if (eyeBuffer != null) - { - eye.AddCommandBuffer(CameraEvent.BeforeDepthTexture, eyeBuffer); - eye.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, eyeBuffer); - eye.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, eyeBuffer); - // notice how we pass fucked param vs UnityEngine.Rendering.XRUtils - eyeBuffer.DrawOcclusionMesh(new RectInt(0, 0, 0, 0)); - } - - WorldTransitionSystem.AddCamera(eye); - CVRTools.CopyToDestCam(_mainCamera, eye); - } - } - - #endregion - - #region Rendering - - private void OnPreRenderCallback(Camera cam) - { - if (!IsEnabled || !_isInitialized) return; - - if (cam.CompareTag("MainCamera")) - { - EnsureRenderTexturesCreated(); - RenderEyePair(); - } - } - - private void EnsureRenderTexturesCreated() - { - int eyeWidth = XRSettings.eyeTextureWidth; - int eyeHeight = XRSettings.eyeTextureHeight; - - bool needsUpdate = _leftTexture == null || _rightTexture == null || - _leftTexture.width != eyeWidth || _leftTexture.height != eyeHeight; - - if (!needsUpdate) return; - - if (_leftTexture != null) _leftTexture.Release(); - if (_rightTexture != null) _rightTexture.Release(); - - _leftTexture = new RenderTexture(eyeWidth, eyeHeight, 24, RenderTextureFormat.ARGBHalf); - _rightTexture = new RenderTexture(eyeWidth, eyeHeight, 24, RenderTextureFormat.ARGBHalf); - } - - private void RenderEyePair() - { - _shaderGlobalBuffer.Clear(); - _shaderGlobalBuffer.SetGlobalVector(s_WorldSpaceCameraPos, _mainCamera.transform.position); - - Camera realVRCamera = PlayerSetup.Instance.vrCam; - - RenderingEye = Camera.MonoOrStereoscopicEye.Left; - PlayerSetup.Instance.vrCam = _leftEye; // so we trigger head hiding - RenderEye(_leftEye, _leftTexture, Camera.StereoscopicEye.Left); - - RenderingEye = Camera.MonoOrStereoscopicEye.Right; - PlayerSetup.Instance.vrCam = _rightEye; // so we trigger head hiding - RenderEye(_rightEye, _rightTexture, Camera.StereoscopicEye.Right); - - RenderingEye = Camera.MonoOrStereoscopicEye.Mono; // bleh - PlayerSetup.Instance.vrCam = realVRCamera; // reset back to real cam - - return; - void RenderEye(Camera eyeCamera, RenderTexture targetTexture, Camera.StereoscopicEye eye) - { - eyeCamera.CopyFrom(_mainCamera); - eyeCamera.targetTexture = targetTexture; - eyeCamera.cullingMask = CachedCullingMask; - eyeCamera.stereoTargetEye = StereoTargetEyeMask.None; - eyeCamera.cullingMatrix = _mainCamera.cullingMatrix; - eyeCamera.projectionMatrix = _mainCamera.GetStereoProjectionMatrix(eye); - eyeCamera.worldToCameraMatrix = _mainCamera.GetStereoViewMatrix(eye); - eyeCamera.Render(); - eyeCamera.ResetCullingMatrix(); - } - } - - private void OnRenderImage(RenderTexture source, RenderTexture destination) - { - if (!IsEnabled || !_isInitialized || _leftTexture == null || _rightTexture == null) - { - Graphics.Blit(source, destination); - return; - } - - Graphics.CopyTexture(_leftTexture, 0, destination, 0); - Graphics.CopyTexture(_rightTexture, 0, destination, 1); - } - #endregion -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/Integrations/BtkUiAddon.cs b/.Deprecated/LegacyContentMitigation/Integrations/BtkUiAddon.cs deleted file mode 100644 index db20f11..0000000 --- a/.Deprecated/LegacyContentMitigation/Integrations/BtkUiAddon.cs +++ /dev/null @@ -1,86 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Networking.API.Responses; -using ABI.CCK.Components; -using BTKUILib; -using BTKUILib.UIObjects; -using BTKUILib.UIObjects.Components; -using NAK.LegacyContentMitigation.Debug; - -namespace NAK.LegacyContentMitigation.Integrations; - -public static class BtkUiAddon -{ - private static ToggleButton _currentModState; - - public static void Initialize() - { - // Create menu late to ensure we at bottom. - // Doing this cause these settings are "Advanced" & mostly for debugging. - QuickMenuAPI.OnMenuGenerated += SetupCategory; - } - - private static void SetupCategory(CVR_MenuManager _) - { - QuickMenuAPI.OnMenuGenerated -= SetupCategory; - - Category category = QuickMenuAPI.MiscTabPage.AddCategory(ModSettings.LCM_SettingsCategory, ModSettings.ModName, true, true, true); - - ToggleButton autoButton = category.AddMelonToggle(ModSettings.EntryAutoForLegacyWorlds); - autoButton.OnValueUpdated += (_) => - { - if (CVRWorld.CompatibilityVersion == CompatibilityVersions.NotSpi) - QuickMenuAPI.ShowNotice("Legacy World Notice", - "You must reload this World Bundle for Shader Replacement to be undone / applied. " + - "Load into a different world and then rejoin."); - }; - - _currentModState = category.AddToggle(string.Empty, string.Empty, false); - _currentModState.OnValueUpdated += OnCurrentStateToggled; - - Button printCameraCallbacksButton = category.AddButton("DEBUG LOG CAMERAS", - string.Empty, "Records Camera events & logs them next frame. Useful for determining camera render order shenanigans."); - printCameraCallbacksButton.OnPress += () => CameraCallbackLogger.Instance.LogCameraEvents(); - - OnMultiPassActiveChanged(FakeMultiPassHack.Instance.IsEnabled); - FakeMultiPassHack.OnMultiPassActiveChanged += OnMultiPassActiveChanged; - } - - private static void OnCurrentStateToggled(bool state) - { - if (state) - { - _currentModState.ToggleValue = false; // dont visually update - QuickMenuAPI.ShowConfirm("Legacy Mitigation Warning", - "This will change how the main VR view is rendered and cause a noticeable performance hit. " + - "It is recommended to only enable this within Worlds that require it (Legacy Content/Broken Shaders). " + - "Shader Replacement will not occur for ALL content that is loaded while enabled. " + - "If this World is Legacy and already Shader Replaced, you must enable Auto For Legacy Worlds instead, " + - "load a different World, and then join back.", - () => - { - FakeMultiPassHack.Instance.SetMultiPassActive(true); - OnMultiPassActiveChanged(true); - }); - } - else - { - FakeMultiPassHack.Instance.SetMultiPassActive(false); - OnMultiPassActiveChanged(false); - } - } - - private static void OnMultiPassActiveChanged(bool state) - { - _currentModState.ToggleValue = state; - if (state) - { - _currentModState.ToggleName = "Currently Active"; - _currentModState.ToggleTooltip = "Fake Multi Pass is currently active."; - } - else - { - _currentModState.ToggleName = "Currently Inactive"; - _currentModState.ToggleTooltip = "Fake Multi Pass is inactive."; - } - } -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/LegacyContentMitigation.csproj b/.Deprecated/LegacyContentMitigation/LegacyContentMitigation.csproj deleted file mode 100644 index d8860da..0000000 --- a/.Deprecated/LegacyContentMitigation/LegacyContentMitigation.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net48 - - - - ..\.ManagedLibs\BTKUILib.dll - - - ..\.ManagedLibs\TheClapper.dll - - - diff --git a/.Deprecated/LegacyContentMitigation/Main.cs b/.Deprecated/LegacyContentMitigation/Main.cs deleted file mode 100644 index 5d1c1b9..0000000 --- a/.Deprecated/LegacyContentMitigation/Main.cs +++ /dev/null @@ -1,68 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using MelonLoader; -using UnityEngine; - -namespace NAK.LegacyContentMitigation; - -public class LegacyContentMitigationMod : MelonMod -{ - internal static MelonLogger.Instance Logger; - - #region Melon Preferences - - // private static readonly MelonPreferences_Category Category = - // MelonPreferences.CreateCategory(nameof(LegacyContentMitigationMod)); - // - // private static readonly MelonPreferences_Entry EntryEnabled = - // Category.CreateEntry( - // "use_legacy_mitigation", - // true, - // "Enabled", - // description: "Enable legacy content camera hack when in Legacy worlds."); - - #endregion Melon Preferences - - #region Melon Events - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - - ApplyPatches(typeof(Patches.PlayerSetup_Patches)); // add MultiPassCamera to VR camera - ApplyPatches(typeof(Patches.SceneLoaded_Patches)); // enable / disable in legacy worlds - ApplyPatches(typeof(Patches.CVRWorld_Patches)); // post processing shit - ApplyPatches(typeof(Patches.CVRTools_Patches)); // prevent shader replacement when fix is active - ApplyPatches(typeof(Patches.HeadHiderManager_Patches)); // prevent main cam triggering early head hide - ApplyPatches(typeof(Patches.CVRMirror_Patches)); - - InitializeIntegration("BTKUILib", Integrations.BtkUiAddon.Initialize); // quick menu options - } - - #endregion Melon Events - - #region Melon Mod Utilities - - private static void InitializeIntegration(string modName, Action integrationAction) - { - if (RegisteredMelons.All(it => it.Info.Name != modName)) - return; - - Logger.Msg($"Initializing {modName} integration."); - integrationAction.Invoke(); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #endregion Melon Mod Utilities -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/ModSettings.cs b/.Deprecated/LegacyContentMitigation/ModSettings.cs deleted file mode 100644 index a20c93d..0000000 --- a/.Deprecated/LegacyContentMitigation/ModSettings.cs +++ /dev/null @@ -1,52 +0,0 @@ -using MelonLoader; - -namespace NAK.LegacyContentMitigation; - -internal static class ModSettings -{ - #region Constants - - internal const string ModName = nameof(LegacyContentMitigation); - internal const string LCM_SettingsCategory = "Legacy Content Mitigation"; - - #endregion Constants - - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(ModName); - - internal static readonly MelonPreferences_Entry EntryAutoForLegacyWorlds = - Category.CreateEntry("auto_for_legacy_worlds", true, - "Auto For Legacy Worlds", description: "Should Legacy View be auto enabled for detected Legacy worlds?"); - - internal static readonly MelonPreferences_Entry EntryFaceMirrorDistance = - Category.CreateEntry("face_mirror_distance", 0.5f, - "Face Mirror Distance", description: "Distance from the camera to place the face mirror."); - - internal static readonly MelonPreferences_Entry EntryFaceMirrorOffsetX = - Category.CreateEntry("face_mirror_offset_x", 0f, - "Face Mirror Offset X", description: "Offset the face mirror on the X axis."); - - internal static readonly MelonPreferences_Entry EntryFaceMirrorOffsetY = - Category.CreateEntry("face_mirror_offset_y", 0f, - "Face Mirror Offset Y", description: "Offset the face mirror on the Y axis."); - - internal static readonly MelonPreferences_Entry EntryFaceMirrorSizeX = - Category.CreateEntry("face_mirror_size_x", 0.5f, - "Face Mirror Size X", description: "Size of the face mirror on the X axis."); - - internal static readonly MelonPreferences_Entry EntryFaceMirrorSizeY = - Category.CreateEntry("face_mirror_size_y", 0.5f, - "Face Mirror Size Y", description: "Size of the face mirror on the Y axis."); - - internal static readonly MelonPreferences_Entry EntryFaceMirrorCameraScale = - Category.CreateEntry("face_mirror_camera_scale", 1f, - "Face Mirror Camera Scale", description: "Scale of the face mirror camera."); - - internal static readonly MelonPreferences_Entry EntryUseFaceMirror = - Category.CreateEntry("use_face_mirror", true, - "Use Face Mirror", description: "Should the face mirror be used?"); - - #endregion Melon Preferences -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/Patches.cs b/.Deprecated/LegacyContentMitigation/Patches.cs deleted file mode 100644 index a389282..0000000 --- a/.Deprecated/LegacyContentMitigation/Patches.cs +++ /dev/null @@ -1,145 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.Base; -using ABI_RC.Core.Base.Jobs; -using ABI_RC.Core.Networking.API.Responses; -using ABI_RC.Core.Player; -using ABI_RC.Core.Player.LocalClone; -using ABI_RC.Core.Player.TransformHider; -using ABI.CCK.Components; -using cohtml; -using cohtml.Net; -using HarmonyLib; -using LegacyContentMitigation.Components; -using UnityEngine; -using UnityEngine.Rendering.PostProcessing; -using UnityEngine.SceneManagement; - -namespace NAK.LegacyContentMitigation.Patches; - -internal static class PlayerSetup_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))] - private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance) - { - __instance.vrCam.AddComponentIfMissing(); - __instance.desktopCam.AddComponentIfMissing(); - - FakeMultiPassHack.Instance = __instance.vrCam.AddComponentIfMissing(); - FakeMultiPassHack.Instance.enabled = ModSettings.EntryAutoForLegacyWorlds.Value; - } -} - -internal static class SceneLoaded_Patches -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(SceneLoaded), nameof(SceneLoaded.OnSceneLoadedHandleJob))] - private static void Prefix_SceneLoaded_OnSceneLoadedHandleJob(Scene scene, LoadSceneMode mode) - { - if (mode == LoadSceneMode.Additive) return; - - if (!ModSettings.EntryAutoForLegacyWorlds.Value) - { - LegacyContentMitigationMod.Logger.Msg("LegacyContentMitigationMod is disabled."); - FakeMultiPassHack.Instance.SetMultiPassActive(false); - return; - } - - bool sceneIsNotSpi = CVRWorld.CompatibilityVersion == CompatibilityVersions.NotSpi; - string logText = sceneIsNotSpi - ? "Legacy world detected, enabling legacy content mitigation." - : "Loaded scene is not considered Legacy content. Disabling if active."; - - LegacyContentMitigationMod.Logger.Msg(logText); - FakeMultiPassHack.Instance.SetMultiPassActive(sceneIsNotSpi); - } -} - -internal static class CVRWorld_Patches -{ - // Third Person patches same methods: - // https://github.com/NotAKidoS/NAK_CVR_Mods/blob/3d6b1bbd59d23be19fe3594e104ad26e4ac0adcd/ThirdPerson/Patches.cs#L15-L22 - [HarmonyPriority(Priority.Last)] - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRWorld), nameof(CVRWorld.CopyRefCamValues))] - [HarmonyPatch(typeof(CVRWorld), nameof(CVRWorld.SetDefaultCamValues))] - private static void Postfix_CVRWorld_SetDefaultCamValues(ref CVRWorld __instance) - { - LegacyContentMitigationMod.Logger.Msg("Legacy world camera values updated."); - FakeMultiPassHack.Instance.OnMainCameraChanged(); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRWorld), nameof(CVRWorld.UpdatePostProcessing))] - private static void Postfix_CVRWorld_UpdatePostProcessing(ref CVRWorld __instance) - { - if (!FakeMultiPassHack.Instance.IsActive) return; - foreach (PostProcessEffectSettings motionBlur in __instance._postProcessingMotionBlurList) - motionBlur.active = false; // force off cause its fucked and no one cares - } -} - -internal static class CVRTools_Patches -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(CVRTools), nameof(CVRTools.ReplaceShaders), typeof(Material), typeof(string))] - private static bool Prefix_CVRTools_ReplaceShaders(Material material, string fallbackShaderName = "") - { - // When in a legacy world with the hack enabled, do not replace shaders - return !FakeMultiPassHack.Instance.IsActive; - } -} - -internal static class HeadHiderManager_Patches -{ - // despite the visual clone not being normally accessible, i fix it cause mod: - // https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/VisualCloneFix - - [HarmonyPrefix] - [HarmonyPatch(typeof(TransformHiderManager), nameof(TransformHiderManager.OnPreRenderCallback))] - [HarmonyPatch(typeof(TransformHiderManager), nameof(TransformHiderManager.OnPreRenderCallback))] - [HarmonyPatch(typeof(LocalCloneManager), nameof(LocalCloneManager.OnPreRenderCallback))] - [HarmonyPatch(typeof(LocalCloneManager), nameof(LocalCloneManager.OnPostRenderCallback))] - private static bool Prefix_HeadHiderManagers_OnRenderCallbacks(Camera cam) - { - if (!FakeMultiPassHack.Instance.IsActive) - return true; // not active, no need - - // dont let real camera trigger head hiding to occur or reset- leave it to the left/right eyes - return !cam.CompareTag("MainCamera"); // we spoof playersetup.activeCam, need check tag - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(TransformHiderManager), nameof(TransformHiderManager.OnPostRenderCallback))] - [HarmonyPatch(typeof(LocalCloneManager), nameof(LocalCloneManager.OnPostRenderCallback))] - private static void Prefix_HeadHiderManagers_OnPostRenderCallback(Camera cam, ref MonoBehaviour __instance) - { - if (!FakeMultiPassHack.Instance.IsActive) return; - - if (FakeMultiPassHack.Instance.RenderingEye == Camera.MonoOrStereoscopicEye.Left) - SetResetAfterRenderFlag(__instance, true); // so right eye mirror sees head - - if (FakeMultiPassHack.Instance.RenderingEye == Camera.MonoOrStereoscopicEye.Right) - SetResetAfterRenderFlag(__instance, !TransformHiderManager.s_UseCloneToCullUi); // dont undo if ui culling - - return; - void SetResetAfterRenderFlag(MonoBehaviour headHiderManager, bool flag) - { - if (headHiderManager is LocalCloneManager localCloneManager) - localCloneManager._resetAfterThisRender = flag; - else if (headHiderManager is TransformHiderManager transformHiderManager) - transformHiderManager._resetAfterThisRender = flag; - } - } -} - -internal static class CVRMirror_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRMirror), nameof(CVRMirror.CopyCameraProperties))] - private static void Postfix_CVRMirror_CopyCameraProperties(ref CVRMirror __instance) - { - __instance.m_ReflectionCamera.ResetCullingMatrix(); - } - -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/Properties/AssemblyInfo.cs b/.Deprecated/LegacyContentMitigation/Properties/AssemblyInfo.cs deleted file mode 100644 index 5969d71..0000000 --- a/.Deprecated/LegacyContentMitigation/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.LegacyContentMitigation.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.LegacyContentMitigation))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.LegacyContentMitigation))] - -[assembly: MelonInfo( - typeof(NAK.LegacyContentMitigation.LegacyContentMitigationMod), - nameof(NAK.LegacyContentMitigation), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LegacyContentMitigation" -)] - -[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.LegacyContentMitigation.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.2"; - public const string Author = "Exterrata & NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/LegacyContentMitigation/README.md b/.Deprecated/LegacyContentMitigation/README.md deleted file mode 100644 index 0236abe..0000000 --- a/.Deprecated/LegacyContentMitigation/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# LegacyContentMitigation - -Experimental mod that manually renders both VR eyes separately while in Legacy Worlds. - -The mod will automatically kick-in when loading NonSpi Worlds & prevent Shader Replacement for all content while active. You can also manually toggle it within the BTKUI Misc tab for experimenting. - --# There is an obvious performance penalty with rendering the world 2x & toggling the head hiding per eye, so enabling the mod outside of Legacy Worlds is not recommended. - ---- - -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. diff --git a/.Deprecated/LegacyContentMitigation/format.json b/.Deprecated/LegacyContentMitigation/format.json deleted file mode 100644 index e801124..0000000 --- a/.Deprecated/LegacyContentMitigation/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 247, - "name": "LegacyContentMitigation", - "modversion": "1.0.2", - "gameversion": "2024r177", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "Exterrata, NotAKidoS", - "description": "Experimental mod that manually renders both VR eyes separately while in Legacy Worlds.\n\nThe mod will automatically kick-in when loading NonSpi Worlds & prevent Shader Replacement for all content while active. You can also manually toggle it within the BTKUI Misc tab for experimenting.\n\n-# There is an obvious performance penalty with rendering the world 2x & toggling the head hiding per eye, so enabling the mod outside of Legacy Worlds is not recommended.", - "searchtags": [ - "legacy", - "content", - "spi", - "shaders" - ], - "requirements": [ - "BTKUILib" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r44/LegacyContentMitigation.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LegacyContentMitigation/", - "changelog": "- Initial release", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/MutualMute/Main.cs b/.Deprecated/MutualMute/Main.cs deleted file mode 100644 index d02924b..0000000 --- a/.Deprecated/MutualMute/Main.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Reflection; -using ABI_RC.Systems.Communications.Audio.Components; -using HarmonyLib; -using MelonLoader; - -namespace NAK.MutualMute; - -public class MutualMuteMod : MelonMod -{ - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(Comms_ParticipantPipeline).GetMethod(nameof(Comms_ParticipantPipeline.SetFlowControlState), - BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(MutualMuteMod).GetMethod(nameof(OnSetFlowControlState), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - private static void OnSetFlowControlState( - ref bool state, - Comms_ParticipantPipeline __instance) - => state &= !__instance._selfModerationMute; -} \ No newline at end of file diff --git a/.Deprecated/MutualMute/MutualMute.csproj b/.Deprecated/MutualMute/MutualMute.csproj deleted file mode 100644 index 267718d..0000000 --- a/.Deprecated/MutualMute/MutualMute.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - net48 - TwoWayMute - - - - ..\.ManagedLibs\TheClapper.dll - - - diff --git a/.Deprecated/MutualMute/README.md b/.Deprecated/MutualMute/README.md deleted file mode 100644 index 599d292..0000000 --- a/.Deprecated/MutualMute/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# MutualMute - -Adjusts the self moderation muting behaviour to also prevent the muted user from hearing you. - -Basically- if you mute someone, they will also not be able to hear you. - -#### This will work even of the other user does not have the mod installed, as it modifies the BBC Flow Control system. - ---- - -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. diff --git a/.Deprecated/MutualMute/format.json b/.Deprecated/MutualMute/format.json deleted file mode 100644 index e815ba1..0000000 --- a/.Deprecated/MutualMute/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 246, - "name": "MutualMute", - "modversion": "1.0.1", - "gameversion": "2025r178", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Adjusts the self moderation muting behaviour to also prevent the muted user from hearing you.\n\nBasically- if you mute someone, they will also not be able to hear you.\n-# This will work even of the other user does not have the mod installed, as it modifies the BBC Flow Control system.", - "searchtags": [ - "mute", - "communication", - "meow", - "car" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r45/MutualMute.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/MutualMute/", - "changelog": "- Stole name idea, now it's MutualMute", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/Nevermind/README.md b/.Deprecated/Nevermind/README.md deleted file mode 100644 index 0626800..0000000 --- a/.Deprecated/Nevermind/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Nevermind - -Provides a cancel keybind for downloading/loading worlds on Desktop. - ---- - -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. diff --git a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/Main.cs b/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/Main.cs deleted file mode 100644 index 7687ea6..0000000 --- a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/Main.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Reflection; -using ABI_RC.Core.Player; -using HarmonyLib; -using MelonLoader; -using UnityEngine; - -namespace NAK.RemoteAvatarDisablingCameraOnFirstFrameFix; - -public class RemoteAvatarDisablingCameraOnFirstFrameFixMod : MelonMod -{ - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(PuppetMaster).GetMethod(nameof(PuppetMaster.AvatarInstantiated), - BindingFlags.Public | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(RemoteAvatarDisablingCameraOnFirstFrameFixMod).GetMethod(nameof(OnPuppetMasterAvatarInstantiated), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - private static void OnPuppetMasterAvatarInstantiated(PuppetMaster __instance) - { - if (__instance._animator == null) return; - - __instance._animator.WriteDefaultValues(); - __instance._animator.keepAnimatorStateOnDisable = false; - __instance._animator.writeDefaultValuesOnDisable = false; - } - - // private static void OnPuppetMasterAvatarInstantiated(PuppetMaster __instance) - // { - // if (__instance._animator == null) return; - // - // // Set culling mode to always animate - // __instance._animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; - // - // // Update the animator to force it to do the first frame - // __instance._animator.Update(0f); - // - // // Set culling mode back to cull update transforms - // __instance._animator.cullingMode = AnimatorCullingMode.CullUpdateTransforms; - // } -} \ No newline at end of file diff --git a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/Properties/AssemblyInfo.cs b/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/Properties/AssemblyInfo.cs deleted file mode 100644 index 9f70d66..0000000 --- a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MelonLoader; -using NAK.RemoteAvatarDisablingCameraOnFirstFrameFix.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.RemoteAvatarDisablingCameraOnFirstFrameFix))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.RemoteAvatarDisablingCameraOnFirstFrameFix))] - -[assembly: MelonInfo( - typeof(NAK.RemoteAvatarDisablingCameraOnFirstFrameFix.RemoteAvatarDisablingCameraOnFirstFrameFixMod), - nameof(NAK.RemoteAvatarDisablingCameraOnFirstFrameFix), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RemoteAvatarDisablingCameraOnFirstFrameFix" -)] - -[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] -[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] -[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] -[assembly: HarmonyDontPatchAll] - -namespace NAK.RemoteAvatarDisablingCameraOnFirstFrameFix.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/README.md b/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/README.md deleted file mode 100644 index 8a46af2..0000000 --- a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# IKSimulatedRootAngleFix - -Fixes a small issue with Desktop & HalfBody root angle being incorrectly calculated while on rotating Movement Parents. If you've ever noticed your body/feet insisting on facing opposite of the direction you are rotating, this fixes that. - ---- - -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. diff --git a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/RemoteAvatarDisablingCameraOnFirstFrameFix.csproj b/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/RemoteAvatarDisablingCameraOnFirstFrameFix.csproj deleted file mode 100644 index bec5b03..0000000 --- a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/RemoteAvatarDisablingCameraOnFirstFrameFix.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - LoadedObjectHack - - diff --git a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/format.json b/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/format.json deleted file mode 100644 index ddf506b..0000000 --- a/.Deprecated/RemoteAvatarDisablingCameraOnFirstFrameFix/format.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "_id": -1, - "name": "AASDefaultProfileFix", - "modversion": "1.0.0", - "gameversion": "2024r175", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Fixes the Default AAS profile not being applied when loading into an avatar without a profile selected.\n\nBy default, the game will not apply anything and the avatar will default to the state found within the Controller parameters.", - "searchtags": [ - "aas", - "profile", - "default", - "fix", - "meow" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r33/AASDefaultProfileFix.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/AASDefaultProfileFix/", - "changelog": "- Initial release", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/SearchWithSpacesFix/Main.cs b/.Deprecated/SearchWithSpacesFix/Main.cs deleted file mode 100644 index 8de7baa..0000000 --- a/.Deprecated/SearchWithSpacesFix/Main.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using ABI_RC.Core.InteractionSystem; -using HarmonyLib; -using MelonLoader; - -namespace NAK.SearchWithSpacesFix; - -public class SearchWithSpacesFixMod : MelonMod -{ - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(ViewManager).GetMethod(nameof(ViewManager.GetSearchResults), - BindingFlags.Public | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(SearchWithSpacesFixMod).GetMethod(nameof(OnPreGetSearchResults), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - // this is so crazy - - private static void OnPreGetSearchResults(ref string searchTerm) - => searchTerm = searchTerm.Replace(" ", "_"); - - // this is so crazy -} \ No newline at end of file diff --git a/.Deprecated/SearchWithSpacesFix/Properties/AssemblyInfo.cs b/.Deprecated/SearchWithSpacesFix/Properties/AssemblyInfo.cs deleted file mode 100644 index 8ba0f9a..0000000 --- a/.Deprecated/SearchWithSpacesFix/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.SearchWithSpacesFix.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.SearchWithSpacesFix))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.SearchWithSpacesFix))] - -[assembly: MelonInfo( - typeof(NAK.SearchWithSpacesFix.SearchWithSpacesFixMod), - nameof(NAK.SearchWithSpacesFix), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SearchWithSpacesFix" -)] - -[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.SearchWithSpacesFix.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/SearchWithSpacesFix/README.md b/.Deprecated/SearchWithSpacesFix/README.md deleted file mode 100644 index 1c4c5bc..0000000 --- a/.Deprecated/SearchWithSpacesFix/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# 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. diff --git a/.Deprecated/SearchWithSpacesFix/format.json b/.Deprecated/SearchWithSpacesFix/format.json deleted file mode 100644 index f8950ca..0000000 --- a/.Deprecated/SearchWithSpacesFix/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_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" -} \ No newline at end of file diff --git a/.Deprecated/SmartReticle/Main.cs b/.Deprecated/SmartReticle/Main.cs deleted file mode 100644 index 4533b0c..0000000 --- a/.Deprecated/SmartReticle/Main.cs +++ /dev/null @@ -1,104 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using ABI_RC.Core.UI; -using HarmonyLib; -using MelonLoader; -using UnityEngine; - -namespace NAK.SmartReticle; - -public class SmartReticleMod : MelonMod -{ - #region Melon Preferences - - private const string SettingsCategory = nameof(SmartReticleMod); - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(SettingsCategory); - - private static readonly MelonPreferences_Entry Entry_Enabled = - Category.CreateEntry("enabled", true, display_name: "Enabled",description: "Toggle SmartReticleMod entirely."); - - private static readonly MelonPreferences_Entry Entry_HideTimeout = - Category.CreateEntry("hide_timeout", 1f, display_name: "Hide Timeout (s)", description: "Timeout before the reticle hides again. Set to 0 to instantly hide."); - - #endregion Melon Preferences - - public override void OnInitializeMelon() - { - ApplyPatches(typeof(ControllerRay_Patches)); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #region Patches - - private static class ControllerRay_Patches - { - private static Transform _mainMenuTransform; - private static Transform _quickMenuTransform; - private static float _lastDisplayedTime; - - [HarmonyPostfix] - [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.Start))] - private static void Postfix_ControllerRay_Start() - { - _mainMenuTransform = ViewManager.Instance.transform; - _quickMenuTransform = CVR_MenuManager.Instance.transform; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.DisplayAuraHighlight))] - private static void Postfix_ControllerRay_DisplayAuraHighlight(ref ControllerRay __instance) - { - if (!Entry_Enabled.Value) - return; - - GameObject pointer; - if (__instance.isDesktopRay) // in desktop mode - pointer = CohtmlHud.Instance.desktopPointer; - else if (__instance.isHeadRay) // in VR mode with no controllers - pointer = __instance.backupCrossHair; - else - return; - - if (!pointer.activeSelf) - { - _lastDisplayedTime = 0; // reset time - return; // pointing at menu or cursor / controllers active - } - - bool shouldDisplayPointer = (__instance._interact // pressing mouse1 or mouse2 - || __instance._isTryingToPickup - // using some tool/utility - || (PlayerSetup.Instance.GetCurrentPropSelectionMode() - != PlayerSetup.PropSelectionMode.None) - // hit something- other than the two menus - || (__instance._objectWasHit - && (__instance.hitTransform != _mainMenuTransform - && __instance.hitTransform != _quickMenuTransform))); - - if (shouldDisplayPointer) - { - _lastDisplayedTime = Time.time; - return; - } - - if (Time.time - _lastDisplayedTime > Entry_HideTimeout.Value) - pointer.SetActive(false); - } - } - - #endregion Patches -} \ No newline at end of file diff --git a/.Deprecated/SmartReticle/Properties/AssemblyInfo.cs b/.Deprecated/SmartReticle/Properties/AssemblyInfo.cs deleted file mode 100644 index e6b4778..0000000 --- a/.Deprecated/SmartReticle/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.SmartReticle.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.SmartReticle))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.SmartReticle))] - -[assembly: MelonInfo( - typeof(NAK.SmartReticle.SmartReticleMod), - nameof(NAK.SmartReticle), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SmartReticle" -)] - -[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.SmartReticle.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.1"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/SmartReticle/README.md b/.Deprecated/SmartReticle/README.md deleted file mode 100644 index 98a3060..0000000 --- a/.Deprecated/SmartReticle/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# SmartReticle - -Simple mod that makes the Desktop/VR Head reticle only appear when hovering over an interactable, holding an interaction button, or when using a tool. - ---- - -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. diff --git a/.Deprecated/SmartReticle/SmartReticle.csproj b/.Deprecated/SmartReticle/SmartReticle.csproj deleted file mode 100644 index 728edb7..0000000 --- a/.Deprecated/SmartReticle/SmartReticle.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - net48 - - diff --git a/.Deprecated/SmartReticle/format.json b/.Deprecated/SmartReticle/format.json deleted file mode 100644 index b01de27..0000000 --- a/.Deprecated/SmartReticle/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 233, - "name": "SmartReticle", - "modversion": "1.0.1", - "gameversion": "2024r175", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Simple mod that makes the Desktop/VR Head reticle only appear when hovering over an interactable, holding an interaction button, or when using a tool.", - "searchtags": [ - "reticle", - "hud", - "cursor", - "dot" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r40/SmartReticle.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SmartReticle/", - "changelog": "- Adjusted to also work for the VR head reticle.", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/DepthCameraFix.cs b/.Deprecated/SuperAwesomeMod/DepthCameraFix.cs deleted file mode 100644 index 933cfa6..0000000 --- a/.Deprecated/SuperAwesomeMod/DepthCameraFix.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace NAK.SuperAwesomeMod; - -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 -{ - private Camera cam; - private CommandBuffer beforeCommandBuffer; - private CommandBuffer afterCommandBuffer; - - void Start() - { - cam = GetComponent(); - - // Ensure camera generates depth texture - cam.depthTextureMode |= DepthTextureMode.Depth; - - // Create command buffers - beforeCommandBuffer = new CommandBuffer(); - beforeCommandBuffer.name = "DepthTextureFix_Before"; - - afterCommandBuffer = new CommandBuffer(); - afterCommandBuffer.name = "DepthTextureFix_After"; - - // Add command buffers at the right events - cam.AddCommandBuffer(CameraEvent.BeforeDepthTexture, beforeCommandBuffer); - cam.AddCommandBuffer(CameraEvent.AfterDepthTexture, afterCommandBuffer); - } - - void OnPreRender() - { - // Set up command buffers each frame to handle dynamic changes - SetupCommandBuffers(); - } - - void SetupCommandBuffers() - { - // Get current camera viewport in pixels - Rect pixelRect = cam.pixelRect; - - // Before depth texture: override viewport to full screen - beforeCommandBuffer.Clear(); - beforeCommandBuffer.SetViewport(new Rect(0, 0, Screen.width, Screen.height)); - - // After depth texture: restore original viewport - afterCommandBuffer.Clear(); - afterCommandBuffer.SetViewport(pixelRect); - } - - void OnDestroy() - { - // Clean up - if (cam != null) - { - if (beforeCommandBuffer != null) - { - cam.RemoveCommandBuffer(CameraEvent.BeforeDepthTexture, beforeCommandBuffer); - beforeCommandBuffer.Dispose(); - } - - if (afterCommandBuffer != null) - { - cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, afterCommandBuffer); - afterCommandBuffer.Dispose(); - } - } - } - - void OnDisable() - { - if (cam != null) - { - if (beforeCommandBuffer != null) - cam.RemoveCommandBuffer(CameraEvent.BeforeDepthTexture, beforeCommandBuffer); - if (afterCommandBuffer != null) - cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, afterCommandBuffer); - } - } - - void OnEnable() - { - if (cam != null && beforeCommandBuffer != null && afterCommandBuffer != null) - { - cam.AddCommandBuffer(CameraEvent.BeforeDepthTexture, beforeCommandBuffer); - cam.AddCommandBuffer(CameraEvent.AfterDepthTexture, afterCommandBuffer); - } - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/CVRPlayerHand.cs b/.Deprecated/SuperAwesomeMod/Interaction/CVRPlayerHand.cs deleted file mode 100644 index 949b638..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/CVRPlayerHand.cs +++ /dev/null @@ -1,42 +0,0 @@ -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 -{ - public class CVRPlayerHand : MonoBehaviour - { - #region Fields - - [SerializeField] - private CVRHand _hand; - - // Pickup rig - [SerializeField] private Transform rayDirection; - [SerializeField] private Transform _attachmentPoint; - [SerializeField] private Transform _pivotPoint; - [SerializeField] private VelocityTracker _velocityTracker; - - // Pickup state - private bool _isHoldingObject; - private Pickupable _heldPickupable; - private Pickupable _proximityPickupable; - - #endregion Fields - - #region Unity Events - - - #endregion Unity Events - - #region Private Methods - - #endregion Private Methods - - #region Public Methods - - #endregion Public Methods - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/CVRPlayerInteractionManager.cs b/.Deprecated/SuperAwesomeMod/Interaction/CVRPlayerInteractionManager.cs deleted file mode 100644 index 2c0d56e..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/CVRPlayerInteractionManager.cs +++ /dev/null @@ -1,214 +0,0 @@ -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(); - } - - 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 - - /// - /// Register a custom tool mode - /// - public void RegisterCustomToolMode(System.Action callback) - { - _inputHandler.RegisterCustomTool(callback); - } - - /// - /// Unregister the current custom tool mode - /// - public void UnregisterCustomToolMode() - { - _inputHandler.UnregisterCustomTool(); - } - - /// - /// Set the interaction mode - /// - public void SetInteractionMode(CVRPlayerInputHandler.InteractionMode mode) - { - _inputHandler.SetInteractionMode(mode); - } - - /// - /// Get the raycast result for a specific hand - /// - 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 - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/Components/CVRCanvasWrapper.cs b/.Deprecated/SuperAwesomeMod/Interaction/Components/CVRCanvasWrapper.cs deleted file mode 100644 index 2ec46bd..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/Components/CVRCanvasWrapper.cs +++ /dev/null @@ -1,121 +0,0 @@ -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 _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(); - } - - private void Start() - { - _graphicsRaycaster = _canvas.gameObject.AddComponent(); - _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 - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycaster.cs b/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycaster.cs deleted file mode 100644 index aa4205a..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycaster.cs +++ /dev/null @@ -1,385 +0,0 @@ -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 -{ - public abstract class CVRPlayerRaycaster - { - #region Enums - - [Flags] - public enum RaycastFlags - { - None = 0, - TelepathicCandidate = 1 << 0, - ProximityInteract = 1 << 1, - RayInteract = 1 << 2, - CohtmlInteract = 1 << 3, - All = ~0 - } - - #endregion Enums - - #region Constants - - private const float MAX_RAYCAST_DISTANCE = 100f; // Max distance you can raycast - private const float RAYCAST_SPHERE_RADIUS = 0.1f; // Radius of the proximity sphere - private const float TELEPATHIC_SPHERE_RADIUS = 0.3f; // Radius of the telepathic sphere - private const float MAX_TELEPATHIC_DISTANCE = 20f; // Max distance for telepathic grab - private const int MAX_RAYCAST_HITS = 100; // Hit buffer size, high due to triggers, which we use lots in CCK - - // Global setting is Collide, but better to be explicit about what we need - private const QueryTriggerInteraction _triggerInteraction = QueryTriggerInteraction.Collide; - - // Layers that are reserved for other purposes or illegal to interact with - private const int RESERVED_OR_ILLEGAL_LAYERS = (1 << CVRLayers.IgnoreRaycast) - | (1 << CVRLayers.MirrorReflection) - | (1 << CVRLayers.PlayerLocal); - - #endregion Constants - - #region Static Fields - - private static readonly RaycastHit[] _hits = new RaycastHit[MAX_RAYCAST_HITS]; - private static readonly Comparer _hitsComparer = Comparer.Create((hit1, hit2) => - { - bool isUI1 = hit1.collider.gameObject.layer == CVRLayers.UIInternal; - bool isUI2 = hit2.collider.gameObject.layer == CVRLayers.UIInternal; - - // Prioritize UIInternal hits - if (isUI1 && !isUI2) return -1; // UIInternal comes first - if (!isUI1 && isUI2) return 1; // Non-UIInternal comes after - - // If both are UIInternal or both are not, sort by distance - return hit1.distance.CompareTo(hit2.distance); - }); - - private static readonly LayerMask _telepathicLayerMask = 1 << CVRLayers.MirrorReflection; - - // Working variables to avoid repeated allocations - private static Collider _workingCollider; - private static GameObject _workingGameObject; - private static Pickupable _workingPickupable; - private static Interactable _workingInteractable; - private static Selectable _workingSelectable; - private static ICanvasElement _workingCanvasElement; - - #endregion Static Fields - - #region Private Fields - - private LayerMask _layerMask; // Default to no layers so we know if we fucked up - - #endregion Private Fields - - #region Constructor - - protected CVRPlayerRaycaster(Transform rayOrigin) => _rayOrigin = rayOrigin; - protected readonly Transform _rayOrigin; - - #endregion Constructor - - #region Public Methods - - public void SetLayerMask(LayerMask layerMask) - { - layerMask &= ~RESERVED_OR_ILLEGAL_LAYERS; - _layerMask = layerMask; - } - - public CVRRaycastResult GetRaycastResults(RaycastFlags flags = RaycastFlags.All) - { - // Early out if we don't want to do anything - if (flags == RaycastFlags.None) return default; - - Ray ray = GetRayFromImpl(); - CVRRaycastResult result = new(); - - // Always check COHTML first - if ((flags & RaycastFlags.CohtmlInteract) != 0 - && TryProcessCohtmlHit(ray, ref result)) - return result; - - // 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 - if (result.isProximityHit) - return result; - } - - // Check for regular raycast hits - if ((flags & RaycastFlags.RayInteract) != 0) - ProcessRaycastHits(ray, ref result); - - // If we hit something, check for telepathic grab candidates at the hit point - if ((flags & RaycastFlags.TelepathicCandidate) != 0 && result.hit.collider) - ProcessTelepathicGrabCandidate(result.hit.point, ref result); - - return result; - } - - #endregion Public Methods - - #region Private Methods - - private static bool TryProcessCohtmlHit(Ray ray, ref CVRRaycastResult result) - { - CohtmlControlledView hitView = CohtmlViewInputHandler.Instance.RayToView(ray, - out float _, out Vector2 hitCoords); - if (hitView == null) return false; - - result.hitCohtml = true; - result.hitCohtmlView = hitView; - result.hitCohtmlCoords = hitCoords; - - // Manually check for pickups & interactables on the hit view (future-proofing for menu grabbing) - if (hitView.TryGetComponent(out _workingInteractable)) result.hitInteractable = _workingInteractable; - if (hitView.TryGetComponent(out _workingPickupable)) result.hitPickupable = _workingPickupable; - - return true; - } - - private void ProcessProximityHits(Ray ray, ref CVRRaycastResult result) - { - int proximityHits = Physics.SphereCastNonAlloc( - ray.origin, - RAYCAST_SPHERE_RADIUS, - Vector3.up, - _hits, - 0.001f, - _layerMask, - _triggerInteraction - ); - - if (proximityHits <= 0) return; - - Array.Sort(_hits, 0, proximityHits, _hitsComparer); - - for (int i = 0; i < proximityHits; i++) - { - RaycastHit hit = _hits[i]; - _workingCollider = hit.collider; - _workingGameObject = _workingCollider.gameObject; - - // Skip things behind the ray origin - if (Vector3.Dot(ray.direction, hit.point - ray.origin) < 0) - continue; - - // Check for interactables & pickupables in proximity - if (!TryProcessInteractables(hit, ref result)) - continue; - - result.isProximityHit = true; - break; - } - } - - private void ProcessRaycastHits(Ray ray, ref CVRRaycastResult result) - { - // Get all hits including triggers, sorted by UI Internal layer & distance - int hitCount = Physics.RaycastNonAlloc(ray, - _hits, - MAX_RAYCAST_DISTANCE, - _layerMask, - _triggerInteraction); - - if (hitCount <= 0) return; - - Array.Sort(_hits, 0, hitCount, _hitsComparer); - - for (int i = 0; i < hitCount; i++) - { - RaycastHit hit = _hits[i]; - _workingCollider = hit.collider; - _workingGameObject = _workingCollider.gameObject; - - // Special case where we only get the closest water hit position. - // As the array is sorted by distance, we only need to check if we didn't hit water yet. - if (!result.hitWater) TryProcessFluidVolume(hit, ref result); - - // Check for hits in order of priority - - if (TryProcessSelectable(hit, ref result)) - break; // Hit a Unity UI Selectable (Button, Slider, etc.) - - if (TryProcessCanvasElement(hit, ref result)) - break; // Hit a Unity UI Canvas Element (ScrollRect, idk what else yet) - - if (TryProcessInteractables(hit, ref result)) - break; // Hit an in-range Interactable or Pickup - - if (TryProcessWorldHit(hit, ref result)) - break; // Hit a non-trigger collider (world, end of ray) - } - } - - private void ProcessTelepathicGrabCandidate(Vector3 hitPoint, ref CVRRaycastResult result) - { - // If we already hit a pickupable, we don't need to check for telepathic grab candidates - if (result.hitPickupable) - { - result.hasTelepathicGrabCandidate = true; - result.telepathicPickupable = result.hitPickupable; - result.telepathicGrabPoint = hitPoint; - return; - } - - // If the hit distance is too far, don't bother checking for telepathic grab candidates - if (Vector3.Distance(hitPoint, _rayOrigin.position) > MAX_TELEPATHIC_DISTANCE) - return; - - // Check for mirror reflection triggers in a sphere around the hit point - int telepathicHits = Physics.SphereCastNonAlloc( - hitPoint, - TELEPATHIC_SPHERE_RADIUS, - Vector3.up, - _hits, - 0.001f, - _telepathicLayerMask, - QueryTriggerInteraction.Collide - ); - - if (telepathicHits <= 0) return; - - // Look for pickupable objects near our hit point - var nearestDistance = float.MaxValue; - for (int i = 0; i < telepathicHits; i++) - { - RaycastHit hit = _hits[i]; - _workingCollider = hit.collider; - // _workingGameObject = _workingCollider.gameObject; - - Transform parentTransform = _workingCollider.transform.parent; - if (!parentTransform - || !parentTransform.TryGetComponent(out _workingPickupable) - || !_workingPickupable.CanPickup) - continue; - - var distance = Vector3.Distance(hitPoint, hit.point); - if (!(distance < nearestDistance)) - continue; - - result.hasTelepathicGrabCandidate = true; - result.telepathicPickupable = _workingPickupable; - result.telepathicGrabPoint = hitPoint; - nearestDistance = distance; - } - } - - private static bool TryProcessSelectable(RaycastHit hit, ref CVRRaycastResult result) - { - if (!_workingGameObject.TryGetComponent(out _workingSelectable)) - return false; - - result.hitUnityUi = true; - result.hitSelectable = _workingSelectable; - result.hit = hit; - return true; - } - - private static bool TryProcessCanvasElement(RaycastHit hit, ref CVRRaycastResult result) - { - if (!_workingGameObject.TryGetComponent(out _workingCanvasElement)) - return false; - - result.hitUnityUi = true; - result.hitCanvasElement = _workingCanvasElement; - result.hit = hit; - return true; - } - - private static void TryProcessFluidVolume(RaycastHit hit, ref CVRRaycastResult result) - { - if (_workingGameObject.layer != CVRLayers.Water) return; - - result.hitWater = true; - result.waterHit = hit; - } - - private static bool TryProcessInteractables(RaycastHit hit, ref CVRRaycastResult result) - { - bool hitValidComponent = false; - - if (_workingGameObject.TryGetComponent(out _workingInteractable) - && _workingInteractable.CanInteract - && IsCVRInteractableWithinRange(_workingInteractable, hit)) - { - result.hitInteractable = _workingInteractable; - hitValidComponent = true; - } - if (_workingGameObject.TryGetComponent(out _workingPickupable) - && _workingPickupable.CanPickup - && IsCVRPickupableWithinRange(_workingPickupable, hit)) - { - result.hitPickupable = _workingPickupable; - hitValidComponent = true; - } - - if (!hitValidComponent) - return false; - - result.hit = hit; - return true; - } - - private static bool TryProcessWorldHit(RaycastHit hit, ref CVRRaycastResult result) - { - if (_workingCollider.isTrigger) - return false; - - result.hitWorld = true; - result.hit = hit; - return true; - } - - #endregion Private Methods - - #region Protected Methods - - protected abstract Ray GetRayFromImpl(); - - #endregion Protected Methods - - #region Utility Because Original Methods Are Broken - - private static bool IsCVRInteractableWithinRange(Interactable interactable, RaycastHit hit) - { - if (interactable is not CVRInteractable cvrInteractable) - return true; - - foreach (CVRInteractableAction action in cvrInteractable.actions) - { - if (action.actionType - is not (CVRInteractableAction.ActionRegister.OnInteractDown - or CVRInteractableAction.ActionRegister.OnInteractUp - or CVRInteractableAction.ActionRegister.OnInputDown - or CVRInteractableAction.ActionRegister.OnInputUp)) - continue; - - float maxDistance = action.floatVal; - if (Mathf.Approximately(maxDistance, 0f) - || hit.distance <= maxDistance) - return true; // Interactable is within range - } - return false; - } - - private static bool IsCVRPickupableWithinRange(Pickupable pickupable, RaycastHit hit) - { - return hit.distance <= pickupable.MaxGrabDistance; - } - - private static bool IsCVRCanvasWrapperWithinRange(CVRCanvasWrapper canvasWrapper, RaycastHit hit) - { - return hit.distance <= canvasWrapper.MaxInteractDistance; - } - - #endregion Utility Because Original Methods Are Broken - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycasterMouse.cs b/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycasterMouse.cs deleted file mode 100644 index 5f5e638..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycasterMouse.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UnityEngine; - -namespace ABI_RC.Core.Player.Interaction.RaycastImpl -{ - public class CVRPlayerRaycasterMouse : CVRPlayerRaycaster - { - private readonly Camera _camera; - public CVRPlayerRaycasterMouse(Transform rayOrigin, Camera camera) : base(rayOrigin) { _camera = camera; } - protected override Ray GetRayFromImpl() => Cursor.lockState == CursorLockMode.Locked - ? new Ray(_camera.transform.position, _camera.transform.forward) - : _camera.ScreenPointToRay(Input.mousePosition); - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycasterTransform.cs b/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycasterTransform.cs deleted file mode 100644 index cc62dab..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRPlayerRaycasterTransform.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UnityEngine; - -namespace ABI_RC.Core.Player.Interaction.RaycastImpl -{ - public class CVRPlayerRaycasterTransform : CVRPlayerRaycaster - { - public CVRPlayerRaycasterTransform(Transform rayOrigin) : base(rayOrigin) { } - protected override Ray GetRayFromImpl() => new(_rayOrigin.position, _rayOrigin.forward); - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRRaycastResult.cs b/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRRaycastResult.cs deleted file mode 100644 index 6165354..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/CVRRaycastResult.cs +++ /dev/null @@ -1,38 +0,0 @@ -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 -{ - public struct CVRRaycastResult - { - // Hit flags - public bool hitWorld; // Any non-specific collision - public bool hitWater; // Hit a fluid volume - public bool hitCohtml; // Specifically hit a COHTML view (Main/Quick Menu) - public bool isProximityHit; // Hit was from proximity sphere check - public bool hitUnityUi; // Hit a canvas - - // Main raycast hit info - public RaycastHit hit; - public RaycastHit? waterHit; // Only valid if hitWater is true - - // Specific hit components - public Pickupable hitPickupable; - public Interactable hitInteractable; - public Selectable hitSelectable; - public ICanvasElement hitCanvasElement; - - // COHTML specific results - public CohtmlControlledView hitCohtmlView; - public Vector2 hitCohtmlCoords; - - // Telepathic pickup - public bool hasTelepathicGrabCandidate; - public Pickupable telepathicPickupable; - public Vector3 telepathicGrabPoint; - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/RaycastDebug.cs b/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/RaycastDebug.cs deleted file mode 100644 index 5000f89..0000000 --- a/.Deprecated/SuperAwesomeMod/Interaction/RaycastImpl/RaycastDebug.cs +++ /dev/null @@ -1,312 +0,0 @@ -using UnityEngine; -using UnityEngine.EventSystems; -using UnityEngine.UI; - -namespace ABI_RC.Core.Player.Interaction.RaycastImpl -{ -public class CVRRaycastDebugManager : MonoBehaviour -{ - #region Singleton - - private static CVRRaycastDebugManager _instance; - public static CVRRaycastDebugManager Instance => _instance; - - public static void Initialize(Camera camera) - { - if (_instance != null) return; - - var go = new GameObject("RaycastDebugManager"); - _instance = go.AddComponent(); - DontDestroyOnLoad(go); - - _instance.Setup(camera); - } - - #endregion - - #region Private Fields - - private CVRPlayerRaycasterMouse _raycaster; - private CVRRaycastResult _lastResult; - private System.Diagnostics.Stopwatch _stopwatch; - - // Performance tracking - private const int ROLLING_AVERAGE_SAMPLES = 60; // 1 second at 60fps - private readonly float[] _timeHistory = new float[ROLLING_AVERAGE_SAMPLES]; - private int _currentSampleIndex; - private float _lastRaycastTime; - private float _minRaycastTime = float.MaxValue; - private float _maxRaycastTime; - private float _rollingAverageTime; - private bool _historyFilled; - - private const int DEBUG_PANEL_WIDTH = 300; - private const int DEBUG_PANEL_MARGIN = 10; - private const float MOUSE_CURSOR_SIZE = 24f; - private const float CURSOR_OFFSET = MOUSE_CURSOR_SIZE / 2f; - - private GUIStyle _labelStyle; - private GUIStyle _headerStyle; - private GUIStyle _boxStyle; - - private static readonly Color32 TIMING_COLOR = new(255, 255, 150, 255); // Yellow - private static readonly Color32 COHTML_COLOR = new(150, 255, 150, 255); // Green - private static readonly Color32 UI_COLOR = new(150, 150, 255, 255); // Blue - private static readonly Color32 UNITY_UI_COLOR = new(255, 200, 150, 255); // Orange - private static readonly Color32 INTERACT_COLOR = new(255, 150, 150, 255); // Red - private static readonly Color32 WATER_COLOR = new(150, 255, 255, 255); // Cyan - private static readonly Color32 TELEPATHIC_COLOR = new(255, 150, 255, 255);// Purple - private static readonly Color32 SELECTABLE_COLOR = new(200, 150, 255, 255);// Light Purple - - #endregion - - #region Setup - - private void Setup(Camera camera) - { - _raycaster = new CVRPlayerRaycasterMouse(transform, camera); - _raycaster.SetLayerMask(Physics.DefaultRaycastLayers); - _stopwatch = new System.Diagnostics.Stopwatch(); - } - - #endregion - - #region MonoBehaviour - - private void Update() - { - _stopwatch.Restart(); - _lastResult = _raycaster.GetRaycastResults(); - _stopwatch.Stop(); - - UpdatePerformanceMetrics(); - } - - private void UpdatePerformanceMetrics() - { - // Calculate current frame time - _lastRaycastTime = _stopwatch.ElapsedTicks / (float)System.TimeSpan.TicksPerMillisecond; - - // Update min/max - _minRaycastTime = Mathf.Min(_minRaycastTime, _lastRaycastTime); - _maxRaycastTime = Mathf.Max(_maxRaycastTime, _lastRaycastTime); - - // Update rolling average - _timeHistory[_currentSampleIndex] = _lastRaycastTime; - - // Calculate rolling average based on filled samples - float sum = 0f; - int sampleCount = _historyFilled ? ROLLING_AVERAGE_SAMPLES : _currentSampleIndex + 1; - - for (int i = 0; i < sampleCount; i++) - sum += _timeHistory[i]; - - _rollingAverageTime = sum / sampleCount; - - // Update index for next frame - _currentSampleIndex = (_currentSampleIndex + 1) % ROLLING_AVERAGE_SAMPLES; - if (_currentSampleIndex == 0) - _historyFilled = true; - } - - private void OnGUI() - { - InitializeStyles(); - DrawDebugPanel(); - } - - #endregion - - #region Drawing Methods - - private void InitializeStyles() - { - if (_labelStyle != null) return; - - _labelStyle = new GUIStyle - { - normal = { textColor = Color.white }, - fontSize = 12, - padding = new RectOffset(5, 5, 2, 2), - margin = new RectOffset(5, 5, 0, 0) - }; - - _headerStyle = new GUIStyle - { - normal = { textColor = Color.white }, - fontSize = 14, - fontStyle = FontStyle.Bold, - padding = new RectOffset(5, 5, 5, 5), - margin = new RectOffset(5, 5, 5, 5) - }; - - _boxStyle = new GUIStyle(GUI.skin.box) - { - padding = new RectOffset(10, 10, 5, 5), - margin = new RectOffset(5, 5, 5, 5) - }; - } - - private void DrawDebugPanel() - { - var rect = new Rect( - Screen.width - DEBUG_PANEL_WIDTH - DEBUG_PANEL_MARGIN, - DEBUG_PANEL_MARGIN, - DEBUG_PANEL_WIDTH, - Screen.height - (DEBUG_PANEL_MARGIN * 2) - ); - - GUI.Box(rect, ""); - GUILayout.BeginArea(rect); - - GUI.backgroundColor = Color.black; - GUILayout.Label("Raycast Debug Info", _headerStyle); - - DrawPerformanceSection(); - DrawCohtmlSection(); - DrawUnityUISection(); - DrawSelectableSection(); - DrawInteractionSection(); - DrawWaterSection(); - DrawTelepathicSection(); - DrawWorldHitSection(); - - GUILayout.EndArea(); - } - - private void DrawPerformanceSection() - { - GUI.backgroundColor = TIMING_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("Performance", _headerStyle); - DrawLabel("Last Raycast", $"{_lastRaycastTime:F3} ms"); - DrawLabel("Average (1s)", $"{_rollingAverageTime:F3} ms"); - DrawLabel("Min", $"{_minRaycastTime:F3} ms"); - DrawLabel("Max", $"{_maxRaycastTime:F3} ms"); - GUILayout.EndVertical(); - } - - private void DrawCohtmlSection() - { - if (!_lastResult.hitCohtml) return; - - GUI.backgroundColor = COHTML_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("COHTML Hit", _headerStyle); - DrawLabel("View", _lastResult.hitCohtmlView.name); - DrawLabel("Coords", _lastResult.hitCohtmlCoords.ToString()); - GUILayout.EndVertical(); - } - - private void DrawUnityUISection() - { - if (!_lastResult.hitUnityUi || _lastResult.hitCanvasElement == null) return; - - GUI.backgroundColor = UNITY_UI_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("Unity UI Hit", _headerStyle); - - var canvasElement = _lastResult.hitCanvasElement; - var gameObject = canvasElement as MonoBehaviour; - - DrawLabel("Canvas Element", gameObject != null ? gameObject.name : "Unknown"); - DrawLabel("Element Type", canvasElement.GetType().Name); - - if (gameObject != null) - { - DrawLabel("GameObject", gameObject.gameObject.name); - - if (gameObject.transform.parent != null) - DrawLabel("Parent", gameObject.transform.parent.name); - } - - GUILayout.EndVertical(); - } - - private void DrawSelectableSection() - { - if (_lastResult.hitSelectable == null) return; - - GUI.backgroundColor = SELECTABLE_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("UI Selectable", _headerStyle); - DrawLabel("Selectable", _lastResult.hitSelectable.name); - DrawLabel("Selectable Type", _lastResult.hitSelectable.GetType().Name); - DrawLabel("Is Interactable", _lastResult.hitSelectable.interactable.ToString()); - DrawLabel("Navigation Mode", _lastResult.hitSelectable.navigation.mode.ToString()); - - if (_lastResult.hitSelectable is Toggle toggle) - DrawLabel("Toggle State", toggle.isOn.ToString()); - else if (_lastResult.hitSelectable is Slider slider) - DrawLabel("Slider Value", slider.value.ToString("F2")); - else if (_lastResult.hitSelectable is Scrollbar scrollbar) - DrawLabel("Scrollbar Value", scrollbar.value.ToString("F2")); - - GUILayout.EndVertical(); - } - - private void DrawInteractionSection() - { - if (!_lastResult.hitPickupable && !_lastResult.hitInteractable) return; - - GUI.backgroundColor = INTERACT_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("Interaction", _headerStyle); - if (_lastResult.hitPickupable) - DrawLabel("Pickupable", _lastResult.hitPickupable.name); - if (_lastResult.hitInteractable) - DrawLabel("Interactable", _lastResult.hitInteractable.name); - DrawLabel("Is Proximity", _lastResult.isProximityHit.ToString()); - GUILayout.EndVertical(); - } - - private void DrawWaterSection() - { - if (!_lastResult.hitWater || !_lastResult.waterHit.HasValue) return; - - GUI.backgroundColor = WATER_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("Water Surface", _headerStyle); - DrawLabel("Hit Point", _lastResult.waterHit.Value.point.ToString("F2")); - DrawLabel("Surface Normal", _lastResult.waterHit.Value.normal.ToString("F2")); - GUILayout.EndVertical(); - } - - private void DrawTelepathicSection() - { - if (!_lastResult.hasTelepathicGrabCandidate) return; - - GUI.backgroundColor = TELEPATHIC_COLOR; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("Telepathic Grab", _headerStyle); - DrawLabel("Target", _lastResult.telepathicPickupable.name); - DrawLabel("Grab Point", _lastResult.telepathicGrabPoint.ToString("F2")); - GUILayout.EndVertical(); - } - - private void DrawWorldHitSection() - { - if (_lastResult.hitCohtml || - _lastResult.hitPickupable || - _lastResult.hitInteractable || - _lastResult.hitUnityUi || - !_lastResult.hitWorld || - _lastResult.hit.collider == null) return; - - GUI.backgroundColor = Color.grey; - GUILayout.BeginVertical(_boxStyle); - GUILayout.Label("World Hit", _headerStyle); - DrawLabel("Object", _lastResult.hit.collider.name); - DrawLabel("Distance", _lastResult.hit.distance.ToString("F2")); - DrawLabel("Point", _lastResult.hit.point.ToString("F2")); - GUILayout.EndVertical(); - } - - private void DrawLabel(string label, string value) - { - GUILayout.Label($"{label}: {value}", _labelStyle); - } - - #endregion -} -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Main.cs b/.Deprecated/SuperAwesomeMod/Main.cs deleted file mode 100644 index 5c410f2..0000000 --- a/.Deprecated/SuperAwesomeMod/Main.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Reflection; -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.SceneManagement; - -namespace NAK.SuperAwesomeMod; - -public class SuperAwesomeModMod : MelonMod -{ - #region Melon Events - - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.Start), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(SuperAwesomeModMod).GetMethod(nameof(OnPlayerSetupStart), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(SceneLoaded).GetMethod(nameof(SceneLoaded.FilterWorldComponent), - BindingFlags.NonPublic | BindingFlags.Static), - postfix: new HarmonyMethod(typeof(SuperAwesomeModMod).GetMethod(nameof(OnShitLoaded), - 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"); - } - - public override void OnApplicationQuit() - { - LoggerInstance.Msg("SuperAwesomeModMod! OnApplicationQuit! D:"); - } - - #endregion Melon Events - - private static void OnPlayerSetupStart() - { - CVRRaycastDebugManager.Initialize(PlayerSetup.Instance.desktopCam); - } - - private static void OnShitLoaded(Component c, List asyncTasks = null, Scene? scene = null) - { - if (c == null) - return; - - if (c.gameObject == null) - return; - - if (c.gameObject.scene.buildIndex > 0) - return; - - if ((scene != null) - && (c.gameObject.scene != scene)) - return; - - if (c is Canvas canvas) canvas.gameObject.AddComponent(); - } - - private static void OnProcessCanvas(string collectionId, Canvas canvas) - { - canvas.gameObject.AddComponent(); - } -} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/README.md b/.Deprecated/SuperAwesomeMod/README.md deleted file mode 100644 index 399e70a..0000000 --- a/.Deprecated/SuperAwesomeMod/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# ASTExtension - -Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): -- VR Gesture to scale -- Persistent height -- Copy height from others - -Best used with Avatar Scale Tool, but will attempt to work with found scaling setups. -Requires already having Avatar Scaling on the avatar. This is **not** Universal Scaling. - -## Supported Setups - -ASTExtension will attempt to work with the following setups: - -**Parameter Names:** -- AvatarScale -- Scale -- Scaler -- Scale/Scale -- Height -- LoliModifier -- AvatarSize -- Size -- SizeScale -- Scaling - -These parameter names are not case sensitive and have been gathered from polling the community for common parameter names. - -Assuming the parameter is a float, ASTExtension will attempt to use it as the height parameter. Will automatically calibrate to the height range of the found parameter, assuming the scaling animation is in a blend tree / state using motion time & is linear. The scaling animation state **must be active** at time of avatar load. - -The max value ASTExtension will drive the parameter to is 100. As the mod is having to guess the max height, it may not be accurate if the max height is not capped at a multiple of 10. - -Examples: -- `AvatarScale` - 0 to 1 (slider) - - This is the default setup for Avatar Scale Tool and will work perfectly. -- `Scale` - 0 to 100 (input single) - - This will also work perfectly as the max height is a multiple of 10. -- `Height` - 0 to 2 (input single) - - This will not work properly. The max value to drive the parameter to is not a multiple of 10, and as such ASTExtension will believe the parameter range is 0 to 1. -- `BurntToast` - 0 to 10 (input single) - - This will not work properly. The parameter name is not recognized by ASTExtension. - -If your setup is theoretically supported but not working, it is likely the scaling animation is not linear or has loop enabled if using Motion Time, making the first and last frame identical height. In this case, you will need to fix your animation clip curves / blend tree to be linear &|| not loop, or use Avatar Scale Tool to generate a new scaling animation. - ---- - -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. diff --git a/.Deprecated/SuperAwesomeMod/format.json b/.Deprecated/SuperAwesomeMod/format.json deleted file mode 100644 index 5ad14ea..0000000 --- a/.Deprecated/SuperAwesomeMod/format.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "_id": 223, - "name": "ASTExtension", - "modversion": "1.0.2", - "gameversion": "2025r178", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool):\n- VR Gesture to scale\n- Persistent height\n- Copy height from others\n\nBest used with Avatar Scale Tool, but will attempt to work with found scaling setups.\nRequires already having Avatar Scaling on the avatar. This is **not** Universal Scaling.", - "searchtags": [ - "tool", - "scaling", - "height", - "extension", - "avatar" - ], - "requirements": [ - "BTKUILib" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r45/ASTExtension.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ASTExtension/", - "changelog": "- Fixes for 2025r178", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/VisualCloneFix/README.md b/.Deprecated/VisualCloneFix/README.md deleted file mode 100644 index cc12a9c..0000000 --- a/.Deprecated/VisualCloneFix/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# VisualCloneFix - -Fixes the Visual Clone system and allows you to use it again. - -Using the Visual Clone should be faster than the default Head Hiding & Shadow Clones, but will add a longer hitch on initial avatar load. - -**NOTE:** The Visual Clone is still an experimental feature that was temporarily removed in [ChilloutVR 2024r175 Hotfix 1](https://abinteractive.net/blog/chilloutvr_2024r175_hotfix_1), so there may be bugs or issues with it. - ---- - -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. diff --git a/.Deprecated/VisualCloneFix/format.json b/.Deprecated/VisualCloneFix/format.json deleted file mode 100644 index 6634e9f..0000000 --- a/.Deprecated/VisualCloneFix/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 221, - "name": "VisualCloneFix", - "modversion": "1.0.1", - "gameversion": "2024r175", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Fixes the Visual Clone system and allows you to use it again.\n\nUsing the Visual Clone should be faster than the default Head Hiding & Shadow Clones, but will add a longer hitch on initial avatar load.\n\n**NOTE:** The Visual Clone is still an experimental feature that was temporarily removed in [ChilloutVR 2024r175 Hotfix 1](https://abinteractive.net/blog/chilloutvr_2024r175_hotfix_1), so there may be bugs or issues with it.", - "searchtags": [ - "visual", - "clone", - "head", - "hiding" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r36/VisualCloneFix.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/VisualCloneFix/", - "changelog": "- Fixed FPRExclusions IsShown state being inverted when toggled.\n- Fixed head FPRExclusion generation not checking for existing exclusion.\n- Sped up FindExclusionVertList by 100x by not being an idiot. This heavily reduces avatar hitch with Visual Clone active.", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/WhereAmIPointing/Main.cs b/.Deprecated/WhereAmIPointing/Main.cs deleted file mode 100644 index 24529b4..0000000 --- a/.Deprecated/WhereAmIPointing/Main.cs +++ /dev/null @@ -1,106 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using HarmonyLib; -using MelonLoader; -using UnityEngine; - -namespace NAK.WhereAmIPointing; - -public class WhereAmIPointingMod : MelonMod -{ - #region Melon Preferences - - // cannot disable because then id need extra logic to reset the alpha :) - // private const string SettingsCategory = nameof(WhereAmIPointingMod); - // - // private static readonly MelonPreferences_Category Category = - // MelonPreferences.CreateCategory(SettingsCategory); - // - // private static readonly MelonPreferences_Entry Entry_Enabled = - // Category.CreateEntry("enabled", true, display_name: "Enabled",description: "Toggle WhereAmIPointingMod entirely."); - - #endregion Melon Preferences - - public override void OnInitializeMelon() - { - ApplyPatches(typeof(ControllerRay_Patches)); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #region Patches - - private static class ControllerRay_Patches - { - private const float ORIGINAL_ALPHA = 0.502f; - private const float INTERACTION_ALPHA = 0.1f; - private const float RAY_LENGTH = 1000f; // game normally raycasts to PositiveInfinity... -_- - - [HarmonyPostfix] - [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.LateUpdate))] - private static void Postfix_ControllerRay_LateUpdate(ref ControllerRay __instance) - { - if (__instance.isDesktopRay - || !__instance.enabled - || !__instance.IsTracking() - || !__instance.lineRenderer) - return; - - UpdateLineRendererAlpha(__instance); - - if (__instance.lineRenderer.enabled - || !ShouldOverrideLineRenderer(__instance)) - return; - - UpdateLineRendererPosition(__instance); - } - - private static void UpdateLineRendererAlpha(ControllerRay instance) - { - Material material = instance.lineRenderer.material; - Color color = material.color; - - bool anyMenuOpen = ViewManager.Instance.IsAnyMenuOpen; - float targetAlpha = (!anyMenuOpen || instance.uiActive) ? ORIGINAL_ALPHA : INTERACTION_ALPHA; - if (!(Math.Abs(color.a - targetAlpha) > float.Epsilon)) - return; - - color.a = targetAlpha; - material.color = color; - } - - private static bool ShouldOverrideLineRenderer(ControllerRay instance) - { - if (!ViewManager.Instance.IsAnyMenuOpen) - return false; - - if (CVR_MenuManager.Instance.IsQuickMenuOpen - && instance.hand == CVR_MenuManager.Instance.SelectedQuickMenuHand) - return false; - - return true; - } - - private static void UpdateLineRendererPosition(ControllerRay instance) - { - Vector3 rayOrigin = instance.rayDirectionTransform.position; - Vector3 rayEnd = rayOrigin + instance.rayDirectionTransform.forward * RAY_LENGTH; - - instance.lineRenderer.SetPosition(0, instance.lineRenderer.transform.InverseTransformPoint(rayOrigin)); - instance.lineRenderer.SetPosition(1, instance.lineRenderer.transform.InverseTransformPoint(rayEnd)); - instance.lineRenderer.enabled = true; - } - } - - #endregion Patches -} \ No newline at end of file diff --git a/.Deprecated/WhereAmIPointing/Properties/AssemblyInfo.cs b/.Deprecated/WhereAmIPointing/Properties/AssemblyInfo.cs deleted file mode 100644 index 48c359f..0000000 --- a/.Deprecated/WhereAmIPointing/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.WhereAmIPointing.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.WhereAmIPointing))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.WhereAmIPointing))] - -[assembly: MelonInfo( - typeof(NAK.WhereAmIPointing.WhereAmIPointingMod), - nameof(NAK.WhereAmIPointing), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing" -)] - -[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.WhereAmIPointing.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.1"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Deprecated/WhereAmIPointing/README.md b/.Deprecated/WhereAmIPointing/README.md deleted file mode 100644 index c78a56a..0000000 --- a/.Deprecated/WhereAmIPointing/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# WhereAmIPointing - -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. - ---- - -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. diff --git a/.Deprecated/WhereAmIPointing/WhereAmIPointing.csproj b/.Deprecated/WhereAmIPointing/WhereAmIPointing.csproj deleted file mode 100644 index 728edb7..0000000 --- a/.Deprecated/WhereAmIPointing/WhereAmIPointing.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - net48 - - diff --git a/.Deprecated/WhereAmIPointing/format.json b/.Deprecated/WhereAmIPointing/format.json deleted file mode 100644 index 654911a..0000000 --- a/.Deprecated/WhereAmIPointing/format.json +++ /dev/null @@ -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" -} \ No newline at end of file diff --git a/.Deprecated/AASBufferFix/AASBufferFix.csproj b/.DepricatedMods/AASBufferFix/AASBufferFix.csproj similarity index 100% rename from .Deprecated/AASBufferFix/AASBufferFix.csproj rename to .DepricatedMods/AASBufferFix/AASBufferFix.csproj diff --git a/.Deprecated/AASBufferFix/AASBufferHelper.cs b/.DepricatedMods/AASBufferFix/AASBufferHelper.cs similarity index 100% rename from .Deprecated/AASBufferFix/AASBufferHelper.cs rename to .DepricatedMods/AASBufferFix/AASBufferHelper.cs diff --git a/.Deprecated/AASBufferFix/HarmonyPatches.cs b/.DepricatedMods/AASBufferFix/HarmonyPatches.cs similarity index 100% rename from .Deprecated/AASBufferFix/HarmonyPatches.cs rename to .DepricatedMods/AASBufferFix/HarmonyPatches.cs diff --git a/.Deprecated/AASBufferFix/Main.cs b/.DepricatedMods/AASBufferFix/Main.cs similarity index 100% rename from .Deprecated/AASBufferFix/Main.cs rename to .DepricatedMods/AASBufferFix/Main.cs diff --git a/.Deprecated/AASBufferFix/Properties/AssemblyInfo.cs b/.DepricatedMods/AASBufferFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/AASBufferFix/Properties/AssemblyInfo.cs rename to .DepricatedMods/AASBufferFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/AASBufferFix/README.md b/.DepricatedMods/AASBufferFix/README.md similarity index 100% rename from .Deprecated/AASBufferFix/README.md rename to .DepricatedMods/AASBufferFix/README.md diff --git a/.Deprecated/AASBufferFix/Utils.cs b/.DepricatedMods/AASBufferFix/Utils.cs similarity index 100% rename from .Deprecated/AASBufferFix/Utils.cs rename to .DepricatedMods/AASBufferFix/Utils.cs diff --git a/.Deprecated/AASBufferFix/format.json b/.DepricatedMods/AASBufferFix/format.json similarity index 100% rename from .Deprecated/AASBufferFix/format.json rename to .DepricatedMods/AASBufferFix/format.json diff --git a/.Deprecated/AlternateIKSystem/AlternateIKSystem.csproj b/.DepricatedMods/AlternateIKSystem/AlternateIKSystem.csproj similarity index 100% rename from .Deprecated/AlternateIKSystem/AlternateIKSystem.csproj rename to .DepricatedMods/AlternateIKSystem/AlternateIKSystem.csproj diff --git a/.Deprecated/AlternateIKSystem/HarmonyPatches.cs b/.DepricatedMods/AlternateIKSystem/HarmonyPatches.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/HarmonyPatches.cs rename to .DepricatedMods/AlternateIKSystem/HarmonyPatches.cs diff --git a/.Deprecated/AlternateIKSystem/IK/BodyControl.cs b/.DepricatedMods/AlternateIKSystem/IK/BodyControl.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/BodyControl.cs rename to .DepricatedMods/AlternateIKSystem/IK/BodyControl.cs diff --git a/.Deprecated/AlternateIKSystem/IK/IKCalibrator.cs b/.DepricatedMods/AlternateIKSystem/IK/IKCalibrator.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/IKCalibrator.cs rename to .DepricatedMods/AlternateIKSystem/IK/IKCalibrator.cs diff --git a/.Deprecated/AlternateIKSystem/IK/IKHandlers/IKHandler.cs b/.DepricatedMods/AlternateIKSystem/IK/IKHandlers/IKHandler.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/IKHandlers/IKHandler.cs rename to .DepricatedMods/AlternateIKSystem/IK/IKHandlers/IKHandler.cs diff --git a/.Deprecated/AlternateIKSystem/IK/IKHandlers/IKHandlerDesktop.cs b/.DepricatedMods/AlternateIKSystem/IK/IKHandlers/IKHandlerDesktop.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/IKHandlers/IKHandlerDesktop.cs rename to .DepricatedMods/AlternateIKSystem/IK/IKHandlers/IKHandlerDesktop.cs diff --git a/.Deprecated/AlternateIKSystem/IK/IKHandlers/IKHandlerHalfBody.cs b/.DepricatedMods/AlternateIKSystem/IK/IKHandlers/IKHandlerHalfBody.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/IKHandlers/IKHandlerHalfBody.cs rename to .DepricatedMods/AlternateIKSystem/IK/IKHandlers/IKHandlerHalfBody.cs diff --git a/.Deprecated/AlternateIKSystem/IK/IKManager.cs b/.DepricatedMods/AlternateIKSystem/IK/IKManager.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/IKManager.cs rename to .DepricatedMods/AlternateIKSystem/IK/IKManager.cs diff --git a/.Deprecated/AlternateIKSystem/IK/MusclePoses.cs b/.DepricatedMods/AlternateIKSystem/IK/MusclePoses.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/MusclePoses.cs rename to .DepricatedMods/AlternateIKSystem/IK/MusclePoses.cs diff --git a/.Deprecated/AlternateIKSystem/IK/Tracking/SteamVRTrackerManager.cs b/.DepricatedMods/AlternateIKSystem/IK/Tracking/SteamVRTrackerManager.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/Tracking/SteamVRTrackerManager.cs rename to .DepricatedMods/AlternateIKSystem/IK/Tracking/SteamVRTrackerManager.cs diff --git a/.Deprecated/AlternateIKSystem/IK/VRIKHelpers/VRIKLocomotionData.cs b/.DepricatedMods/AlternateIKSystem/IK/VRIKHelpers/VRIKLocomotionData.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/VRIKHelpers/VRIKLocomotionData.cs rename to .DepricatedMods/AlternateIKSystem/IK/VRIKHelpers/VRIKLocomotionData.cs diff --git a/.Deprecated/AlternateIKSystem/IK/VRIKHelpers/VRIKUtils.cs b/.DepricatedMods/AlternateIKSystem/IK/VRIKHelpers/VRIKUtils.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/VRIKHelpers/VRIKUtils.cs rename to .DepricatedMods/AlternateIKSystem/IK/VRIKHelpers/VRIKUtils.cs diff --git a/.Deprecated/AlternateIKSystem/IK/WeightManipulators/BodyParts/BodyPart.cs b/.DepricatedMods/AlternateIKSystem/IK/WeightManipulators/BodyParts/BodyPart.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/WeightManipulators/BodyParts/BodyPart.cs rename to .DepricatedMods/AlternateIKSystem/IK/WeightManipulators/BodyParts/BodyPart.cs diff --git a/.Deprecated/AlternateIKSystem/IK/WeightManipulators/DeviceControlManipulator.cs b/.DepricatedMods/AlternateIKSystem/IK/WeightManipulators/DeviceControlManipulator.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/WeightManipulators/DeviceControlManipulator.cs rename to .DepricatedMods/AlternateIKSystem/IK/WeightManipulators/DeviceControlManipulator.cs diff --git a/.Deprecated/AlternateIKSystem/IK/WeightManipulators/Interface/IWeightManipulator.cs b/.DepricatedMods/AlternateIKSystem/IK/WeightManipulators/Interface/IWeightManipulator.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/WeightManipulators/Interface/IWeightManipulator.cs rename to .DepricatedMods/AlternateIKSystem/IK/WeightManipulators/Interface/IWeightManipulator.cs diff --git a/.Deprecated/AlternateIKSystem/IK/WeightManipulators/TrackingControlManipulator.cs b/.DepricatedMods/AlternateIKSystem/IK/WeightManipulators/TrackingControlManipulator.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/WeightManipulators/TrackingControlManipulator.cs rename to .DepricatedMods/AlternateIKSystem/IK/WeightManipulators/TrackingControlManipulator.cs diff --git a/.Deprecated/AlternateIKSystem/IK/WeightManipulators/WeightManipulatorManager.cs b/.DepricatedMods/AlternateIKSystem/IK/WeightManipulators/WeightManipulatorManager.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/IK/WeightManipulators/WeightManipulatorManager.cs rename to .DepricatedMods/AlternateIKSystem/IK/WeightManipulators/WeightManipulatorManager.cs diff --git a/.Deprecated/AlternateIKSystem/Integrations/BTKUIAddon.cs b/.DepricatedMods/AlternateIKSystem/Integrations/BTKUIAddon.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/Integrations/BTKUIAddon.cs rename to .DepricatedMods/AlternateIKSystem/Integrations/BTKUIAddon.cs diff --git a/.Deprecated/AlternateIKSystem/LICENSE.txt b/.DepricatedMods/AlternateIKSystem/LICENSE.txt similarity index 100% rename from .Deprecated/AlternateIKSystem/LICENSE.txt rename to .DepricatedMods/AlternateIKSystem/LICENSE.txt diff --git a/.Deprecated/AlternateIKSystem/Main.cs b/.DepricatedMods/AlternateIKSystem/Main.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/Main.cs rename to .DepricatedMods/AlternateIKSystem/Main.cs diff --git a/.Deprecated/AlternateIKSystem/ModSettings.cs b/.DepricatedMods/AlternateIKSystem/ModSettings.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/ModSettings.cs rename to .DepricatedMods/AlternateIKSystem/ModSettings.cs diff --git a/.Deprecated/AlternateIKSystem/Properties/AssemblyInfo.cs b/.DepricatedMods/AlternateIKSystem/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/AlternateIKSystem/Properties/AssemblyInfo.cs rename to .DepricatedMods/AlternateIKSystem/Properties/AssemblyInfo.cs diff --git a/.Deprecated/AlternateIKSystem/README.md b/.DepricatedMods/AlternateIKSystem/README.md similarity index 100% rename from .Deprecated/AlternateIKSystem/README.md rename to .DepricatedMods/AlternateIKSystem/README.md diff --git a/.Deprecated/AlternateIKSystem/format.json b/.DepricatedMods/AlternateIKSystem/format.json similarity index 100% rename from .Deprecated/AlternateIKSystem/format.json rename to .DepricatedMods/AlternateIKSystem/format.json diff --git a/.Deprecated/BadAnimatorFix/BadAnimatorFix.csproj b/.DepricatedMods/BadAnimatorFix/BadAnimatorFix.csproj similarity index 100% rename from .Deprecated/BadAnimatorFix/BadAnimatorFix.csproj rename to .DepricatedMods/BadAnimatorFix/BadAnimatorFix.csproj diff --git a/.Deprecated/BadAnimatorFix/BadAnimatorFixManager.cs b/.DepricatedMods/BadAnimatorFix/BadAnimatorFixManager.cs similarity index 100% rename from .Deprecated/BadAnimatorFix/BadAnimatorFixManager.cs rename to .DepricatedMods/BadAnimatorFix/BadAnimatorFixManager.cs diff --git a/.Deprecated/BadAnimatorFix/BadAnimatorFixer.cs b/.DepricatedMods/BadAnimatorFix/BadAnimatorFixer.cs similarity index 100% rename from .Deprecated/BadAnimatorFix/BadAnimatorFixer.cs rename to .DepricatedMods/BadAnimatorFix/BadAnimatorFixer.cs diff --git a/.Deprecated/BadAnimatorFix/HarmonyPatches.cs b/.DepricatedMods/BadAnimatorFix/HarmonyPatches.cs similarity index 100% rename from .Deprecated/BadAnimatorFix/HarmonyPatches.cs rename to .DepricatedMods/BadAnimatorFix/HarmonyPatches.cs diff --git a/.Deprecated/BadAnimatorFix/Main.cs b/.DepricatedMods/BadAnimatorFix/Main.cs similarity index 100% rename from .Deprecated/BadAnimatorFix/Main.cs rename to .DepricatedMods/BadAnimatorFix/Main.cs diff --git a/.Deprecated/BadAnimatorFix/Properties/AssemblyInfo.cs b/.DepricatedMods/BadAnimatorFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/BadAnimatorFix/Properties/AssemblyInfo.cs rename to .DepricatedMods/BadAnimatorFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/BadAnimatorFix/format.json b/.DepricatedMods/BadAnimatorFix/format.json similarity index 100% rename from .Deprecated/BadAnimatorFix/format.json rename to .DepricatedMods/BadAnimatorFix/format.json diff --git a/.Deprecated/Blackout/AssetHandler.cs b/.DepricatedMods/Blackout/AssetHandler.cs similarity index 100% rename from .Deprecated/Blackout/AssetHandler.cs rename to .DepricatedMods/Blackout/AssetHandler.cs diff --git a/.Deprecated/Blackout/Blackout.csproj b/.DepricatedMods/Blackout/Blackout.csproj similarity index 100% rename from .Deprecated/Blackout/Blackout.csproj rename to .DepricatedMods/Blackout/Blackout.csproj diff --git a/.Deprecated/Blackout/BlackoutController.cs b/.DepricatedMods/Blackout/BlackoutController.cs similarity index 100% rename from .Deprecated/Blackout/BlackoutController.cs rename to .DepricatedMods/Blackout/BlackoutController.cs diff --git a/.Deprecated/Blackout/HarmonyPatches.cs b/.DepricatedMods/Blackout/HarmonyPatches.cs similarity index 100% rename from .Deprecated/Blackout/HarmonyPatches.cs rename to .DepricatedMods/Blackout/HarmonyPatches.cs diff --git a/.Deprecated/Blackout/Integrations/BTKUIAddon.cs b/.DepricatedMods/Blackout/Integrations/BTKUIAddon.cs similarity index 100% rename from .Deprecated/Blackout/Integrations/BTKUIAddon.cs rename to .DepricatedMods/Blackout/Integrations/BTKUIAddon.cs diff --git a/.Deprecated/Blackout/Integrations/UIExpansionKitAddon.cs b/.DepricatedMods/Blackout/Integrations/UIExpansionKitAddon.cs similarity index 100% rename from .Deprecated/Blackout/Integrations/UIExpansionKitAddon.cs rename to .DepricatedMods/Blackout/Integrations/UIExpansionKitAddon.cs diff --git a/.Deprecated/Blackout/Main.cs b/.DepricatedMods/Blackout/Main.cs similarity index 100% rename from .Deprecated/Blackout/Main.cs rename to .DepricatedMods/Blackout/Main.cs diff --git a/.Deprecated/Blackout/Properties/AssemblyInfo.cs b/.DepricatedMods/Blackout/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/Blackout/Properties/AssemblyInfo.cs rename to .DepricatedMods/Blackout/Properties/AssemblyInfo.cs diff --git a/.Deprecated/Blackout/Resource1.Designer.cs b/.DepricatedMods/Blackout/Resource1.Designer.cs similarity index 100% rename from .Deprecated/Blackout/Resource1.Designer.cs rename to .DepricatedMods/Blackout/Resource1.Designer.cs diff --git a/.Deprecated/Blackout/Resource1.resx b/.DepricatedMods/Blackout/Resource1.resx similarity index 100% rename from .Deprecated/Blackout/Resource1.resx rename to .DepricatedMods/Blackout/Resource1.resx diff --git a/.Deprecated/Blackout/format.json b/.DepricatedMods/Blackout/format.json similarity index 100% rename from .Deprecated/Blackout/format.json rename to .DepricatedMods/Blackout/format.json diff --git a/.Deprecated/Blackout/resources/blackout_controller.asset b/.DepricatedMods/Blackout/resources/blackout_controller.asset similarity index 100% rename from .Deprecated/Blackout/resources/blackout_controller.asset rename to .DepricatedMods/Blackout/resources/blackout_controller.asset diff --git a/.Deprecated/CameraFixes/CameraFixes.csproj b/.DepricatedMods/CameraFixes/CameraFixes.csproj similarity index 100% rename from .Deprecated/CameraFixes/CameraFixes.csproj rename to .DepricatedMods/CameraFixes/CameraFixes.csproj diff --git a/.Deprecated/CameraFixes/HarmonyPatches.cs b/.DepricatedMods/CameraFixes/HarmonyPatches.cs similarity index 100% rename from .Deprecated/CameraFixes/HarmonyPatches.cs rename to .DepricatedMods/CameraFixes/HarmonyPatches.cs diff --git a/.Deprecated/CameraFixes/Main.cs b/.DepricatedMods/CameraFixes/Main.cs similarity index 100% rename from .Deprecated/CameraFixes/Main.cs rename to .DepricatedMods/CameraFixes/Main.cs diff --git a/.Deprecated/CameraFixes/Properties/AssemblyInfo.cs b/.DepricatedMods/CameraFixes/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/CameraFixes/Properties/AssemblyInfo.cs rename to .DepricatedMods/CameraFixes/Properties/AssemblyInfo.cs diff --git a/.Deprecated/CameraFixes/format.json b/.DepricatedMods/CameraFixes/format.json similarity index 100% rename from .Deprecated/CameraFixes/format.json rename to .DepricatedMods/CameraFixes/format.json diff --git a/.Deprecated/ClearHudNotifications/ClearHudNotifications.csproj b/.DepricatedMods/ClearHudNotifications/ClearHudNotifications.csproj similarity index 100% rename from .Deprecated/ClearHudNotifications/ClearHudNotifications.csproj rename to .DepricatedMods/ClearHudNotifications/ClearHudNotifications.csproj diff --git a/.Deprecated/ClearHudNotifications/HarmonyPatches.cs b/.DepricatedMods/ClearHudNotifications/HarmonyPatches.cs similarity index 100% rename from .Deprecated/ClearHudNotifications/HarmonyPatches.cs rename to .DepricatedMods/ClearHudNotifications/HarmonyPatches.cs diff --git a/.Deprecated/ClearHudNotifications/Main.cs b/.DepricatedMods/ClearHudNotifications/Main.cs similarity index 100% rename from .Deprecated/ClearHudNotifications/Main.cs rename to .DepricatedMods/ClearHudNotifications/Main.cs diff --git a/.Deprecated/ClearHudNotifications/Properties/AssemblyInfo.cs b/.DepricatedMods/ClearHudNotifications/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/ClearHudNotifications/Properties/AssemblyInfo.cs rename to .DepricatedMods/ClearHudNotifications/Properties/AssemblyInfo.cs diff --git a/.Deprecated/ClearHudNotifications/format.json b/.DepricatedMods/ClearHudNotifications/format.json similarity index 100% rename from .Deprecated/ClearHudNotifications/format.json rename to .DepricatedMods/ClearHudNotifications/format.json diff --git a/.Deprecated/ControllerFreeze/ControllerFreeze.csproj b/.DepricatedMods/ControllerFreeze/ControllerFreeze.csproj similarity index 100% rename from .Deprecated/ControllerFreeze/ControllerFreeze.csproj rename to .DepricatedMods/ControllerFreeze/ControllerFreeze.csproj diff --git a/.Deprecated/ControllerFreeze/HarmonyPatches.cs b/.DepricatedMods/ControllerFreeze/HarmonyPatches.cs similarity index 100% rename from .Deprecated/ControllerFreeze/HarmonyPatches.cs rename to .DepricatedMods/ControllerFreeze/HarmonyPatches.cs diff --git a/.Deprecated/ControllerFreeze/Main.cs b/.DepricatedMods/ControllerFreeze/Main.cs similarity index 100% rename from .Deprecated/ControllerFreeze/Main.cs rename to .DepricatedMods/ControllerFreeze/Main.cs diff --git a/.Deprecated/ControllerFreeze/Properties/AssemblyInfo.cs b/.DepricatedMods/ControllerFreeze/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/ControllerFreeze/Properties/AssemblyInfo.cs rename to .DepricatedMods/ControllerFreeze/Properties/AssemblyInfo.cs diff --git a/.Deprecated/ControllerFreeze/README.md b/.DepricatedMods/ControllerFreeze/README.md similarity index 100% rename from .Deprecated/ControllerFreeze/README.md rename to .DepricatedMods/ControllerFreeze/README.md diff --git a/.Deprecated/ControllerFreeze/format.json b/.DepricatedMods/ControllerFreeze/format.json similarity index 100% rename from .Deprecated/ControllerFreeze/format.json rename to .DepricatedMods/ControllerFreeze/format.json diff --git a/.Deprecated/DesktopVRIK/DesktopVRIK.csproj b/.DepricatedMods/DesktopVRIK/DesktopVRIK.csproj similarity index 100% rename from .Deprecated/DesktopVRIK/DesktopVRIK.csproj rename to .DepricatedMods/DesktopVRIK/DesktopVRIK.csproj diff --git a/.Deprecated/DesktopVRIK/HarmonyPatches.cs b/.DepricatedMods/DesktopVRIK/HarmonyPatches.cs similarity index 100% rename from .Deprecated/DesktopVRIK/HarmonyPatches.cs rename to .DepricatedMods/DesktopVRIK/HarmonyPatches.cs diff --git a/.Deprecated/DesktopVRIK/IK/IKCalibrator.cs b/.DepricatedMods/DesktopVRIK/IK/IKCalibrator.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/IKCalibrator.cs rename to .DepricatedMods/DesktopVRIK/IK/IKCalibrator.cs diff --git a/.Deprecated/DesktopVRIK/IK/IKHandlers/IKHandler.cs b/.DepricatedMods/DesktopVRIK/IK/IKHandlers/IKHandler.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/IKHandlers/IKHandler.cs rename to .DepricatedMods/DesktopVRIK/IK/IKHandlers/IKHandler.cs diff --git a/.Deprecated/DesktopVRIK/IK/IKHandlers/IKHandlerDesktop.cs b/.DepricatedMods/DesktopVRIK/IK/IKHandlers/IKHandlerDesktop.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/IKHandlers/IKHandlerDesktop.cs rename to .DepricatedMods/DesktopVRIK/IK/IKHandlers/IKHandlerDesktop.cs diff --git a/.Deprecated/DesktopVRIK/IK/IKManager.cs b/.DepricatedMods/DesktopVRIK/IK/IKManager.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/IKManager.cs rename to .DepricatedMods/DesktopVRIK/IK/IKManager.cs diff --git a/.Deprecated/DesktopVRIK/IK/MusclePoses.cs b/.DepricatedMods/DesktopVRIK/IK/MusclePoses.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/MusclePoses.cs rename to .DepricatedMods/DesktopVRIK/IK/MusclePoses.cs diff --git a/.Deprecated/DesktopVRIK/IK/VRIKHelpers/VRIKLocomotionData.cs b/.DepricatedMods/DesktopVRIK/IK/VRIKHelpers/VRIKLocomotionData.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/VRIKHelpers/VRIKLocomotionData.cs rename to .DepricatedMods/DesktopVRIK/IK/VRIKHelpers/VRIKLocomotionData.cs diff --git a/.Deprecated/DesktopVRIK/IK/VRIKHelpers/VRIKUtils.cs b/.DepricatedMods/DesktopVRIK/IK/VRIKHelpers/VRIKUtils.cs similarity index 100% rename from .Deprecated/DesktopVRIK/IK/VRIKHelpers/VRIKUtils.cs rename to .DepricatedMods/DesktopVRIK/IK/VRIKHelpers/VRIKUtils.cs diff --git a/.Deprecated/DesktopVRIK/Integrations/AMTAddon.cs b/.DepricatedMods/DesktopVRIK/Integrations/AMTAddon.cs similarity index 100% rename from .Deprecated/DesktopVRIK/Integrations/AMTAddon.cs rename to .DepricatedMods/DesktopVRIK/Integrations/AMTAddon.cs diff --git a/.Deprecated/DesktopVRIK/Integrations/BTKUIAddon.cs b/.DepricatedMods/DesktopVRIK/Integrations/BTKUIAddon.cs similarity index 100% rename from .Deprecated/DesktopVRIK/Integrations/BTKUIAddon.cs rename to .DepricatedMods/DesktopVRIK/Integrations/BTKUIAddon.cs diff --git a/.Deprecated/DesktopVRIK/Main.cs b/.DepricatedMods/DesktopVRIK/Main.cs similarity index 100% rename from .Deprecated/DesktopVRIK/Main.cs rename to .DepricatedMods/DesktopVRIK/Main.cs diff --git a/.Deprecated/DesktopVRIK/ModSettings.cs b/.DepricatedMods/DesktopVRIK/ModSettings.cs similarity index 100% rename from .Deprecated/DesktopVRIK/ModSettings.cs rename to .DepricatedMods/DesktopVRIK/ModSettings.cs diff --git a/.Deprecated/DesktopVRIK/Properties/AssemblyInfo.cs b/.DepricatedMods/DesktopVRIK/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/DesktopVRIK/Properties/AssemblyInfo.cs rename to .DepricatedMods/DesktopVRIK/Properties/AssemblyInfo.cs diff --git a/.Deprecated/DesktopVRIK/README.md b/.DepricatedMods/DesktopVRIK/README.md similarity index 100% rename from .Deprecated/DesktopVRIK/README.md rename to .DepricatedMods/DesktopVRIK/README.md diff --git a/.Deprecated/DesktopVRIK/format.json b/.DepricatedMods/DesktopVRIK/format.json similarity index 100% rename from .Deprecated/DesktopVRIK/format.json rename to .DepricatedMods/DesktopVRIK/format.json diff --git a/.Deprecated/DesktopVRSwitch/DesktopVRSwitch.csproj b/.DepricatedMods/DesktopVRSwitch/DesktopVRSwitch.csproj similarity index 100% rename from .Deprecated/DesktopVRSwitch/DesktopVRSwitch.csproj rename to .DepricatedMods/DesktopVRSwitch/DesktopVRSwitch.csproj diff --git a/.Deprecated/DesktopVRSwitch/HarmonyPatches.cs b/.DepricatedMods/DesktopVRSwitch/HarmonyPatches.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/HarmonyPatches.cs rename to .DepricatedMods/DesktopVRSwitch/HarmonyPatches.cs diff --git a/.Deprecated/DesktopVRSwitch/Integrations/BTKUIAddon.cs b/.DepricatedMods/DesktopVRSwitch/Integrations/BTKUIAddon.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/Integrations/BTKUIAddon.cs rename to .DepricatedMods/DesktopVRSwitch/Integrations/BTKUIAddon.cs diff --git a/.Deprecated/DesktopVRSwitch/Main.cs b/.DepricatedMods/DesktopVRSwitch/Main.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/Main.cs rename to .DepricatedMods/DesktopVRSwitch/Main.cs diff --git a/.Deprecated/DesktopVRSwitch/ModSettings.cs b/.DepricatedMods/DesktopVRSwitch/ModSettings.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/ModSettings.cs rename to .DepricatedMods/DesktopVRSwitch/ModSettings.cs diff --git a/.Deprecated/DesktopVRSwitch/Patches/DestroySteamVRInstancesImmediate.cs b/.DepricatedMods/DesktopVRSwitch/Patches/DestroySteamVRInstancesImmediate.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/Patches/DestroySteamVRInstancesImmediate.cs rename to .DepricatedMods/DesktopVRSwitch/Patches/DestroySteamVRInstancesImmediate.cs diff --git a/.Deprecated/DesktopVRSwitch/Patches/ReferenceCameraPatch.cs b/.DepricatedMods/DesktopVRSwitch/Patches/ReferenceCameraPatch.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/Patches/ReferenceCameraPatch.cs rename to .DepricatedMods/DesktopVRSwitch/Patches/ReferenceCameraPatch.cs diff --git a/.Deprecated/DesktopVRSwitch/Properties/AssemblyInfo.cs b/.DepricatedMods/DesktopVRSwitch/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/Properties/AssemblyInfo.cs rename to .DepricatedMods/DesktopVRSwitch/Properties/AssemblyInfo.cs diff --git a/.Deprecated/DesktopVRSwitch/README.md b/.DepricatedMods/DesktopVRSwitch/README.md similarity index 100% rename from .Deprecated/DesktopVRSwitch/README.md rename to .DepricatedMods/DesktopVRSwitch/README.md diff --git a/.Deprecated/DesktopVRSwitch/Utils.cs b/.DepricatedMods/DesktopVRSwitch/Utils.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/Utils.cs rename to .DepricatedMods/DesktopVRSwitch/Utils.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeSwitchDebugger.cs b/.DepricatedMods/DesktopVRSwitch/VRModeSwitchDebugger.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeSwitchDebugger.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeSwitchDebugger.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeSwitchManager.cs b/.DepricatedMods/DesktopVRSwitch/VRModeSwitchManager.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeSwitchManager.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeSwitchManager.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CVRGestureRecognizerTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRGestureRecognizerTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CVRGestureRecognizerTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRGestureRecognizerTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CVRInputManagerTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRInputManagerTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CVRInputManagerTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRInputManagerTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CVRPickupObjectTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRPickupObjectTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CVRPickupObjectTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRPickupObjectTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CVRWorldTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRWorldTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CVRWorldTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVRWorldTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CVR_InteractableManagerTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVR_InteractableManagerTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CVR_InteractableManagerTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVR_InteractableManagerTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CVR_MenuManagerTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVR_MenuManagerTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CVR_MenuManagerTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CVR_MenuManagerTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CameraFacingObjectTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CameraFacingObjectTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CameraFacingObjectTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CameraFacingObjectTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CheckVRTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CheckVRTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CheckVRTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CheckVRTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/CohtmlHudTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/CohtmlHudTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/CohtmlHudTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/CohtmlHudTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/HudOperationsTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/HudOperationsTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/HudOperationsTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/HudOperationsTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/IKSystemTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/IKSystemTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/IKSystemTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/IKSystemTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/MetaPortTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/MetaPortTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/MetaPortTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/MetaPortTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/MovementSystemTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/MovementSystemTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/MovementSystemTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/MovementSystemTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/PlayerSetupTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/PlayerSetupTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/PlayerSetupTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/PlayerSetupTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/PortableCameraTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/PortableCameraTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/PortableCameraTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/PortableCameraTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/VRModeTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/VRModeTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/VRModeTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/VRModeTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/VRModeTrackers/ViewManagerTracker.cs b/.DepricatedMods/DesktopVRSwitch/VRModeTrackers/ViewManagerTracker.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/VRModeTrackers/ViewManagerTracker.cs rename to .DepricatedMods/DesktopVRSwitch/VRModeTrackers/ViewManagerTracker.cs diff --git a/.Deprecated/DesktopVRSwitch/XRHandler.cs b/.DepricatedMods/DesktopVRSwitch/XRHandler.cs similarity index 100% rename from .Deprecated/DesktopVRSwitch/XRHandler.cs rename to .DepricatedMods/DesktopVRSwitch/XRHandler.cs diff --git a/.Deprecated/DesktopVRSwitch/format.json b/.DepricatedMods/DesktopVRSwitch/format.json similarity index 100% rename from .Deprecated/DesktopVRSwitch/format.json rename to .DepricatedMods/DesktopVRSwitch/format.json diff --git a/.Deprecated/EzGrab/EzGrab.csproj b/.DepricatedMods/EzGrab/EzGrab.csproj similarity index 100% rename from .Deprecated/EzGrab/EzGrab.csproj rename to .DepricatedMods/EzGrab/EzGrab.csproj diff --git a/.Deprecated/EzGrab/Main.cs b/.DepricatedMods/EzGrab/Main.cs similarity index 100% rename from .Deprecated/EzGrab/Main.cs rename to .DepricatedMods/EzGrab/Main.cs diff --git a/.Deprecated/EzGrab/Properties/AssemblyInfo.cs b/.DepricatedMods/EzGrab/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/EzGrab/Properties/AssemblyInfo.cs rename to .DepricatedMods/EzGrab/Properties/AssemblyInfo.cs diff --git a/.Deprecated/EzGrab/format.json b/.DepricatedMods/EzGrab/format.json similarity index 100% rename from .Deprecated/EzGrab/format.json rename to .DepricatedMods/EzGrab/format.json diff --git a/.Deprecated/FuckCohtmlResourceHandler/FuckCohtmlResourceHandler.csproj b/.DepricatedMods/FuckCohtmlResourceHandler/FuckCohtmlResourceHandler.csproj similarity index 100% rename from .Deprecated/FuckCohtmlResourceHandler/FuckCohtmlResourceHandler.csproj rename to .DepricatedMods/FuckCohtmlResourceHandler/FuckCohtmlResourceHandler.csproj diff --git a/.Deprecated/FuckCohtmlResourceHandler/HarmonyPatches.cs b/.DepricatedMods/FuckCohtmlResourceHandler/HarmonyPatches.cs similarity index 100% rename from .Deprecated/FuckCohtmlResourceHandler/HarmonyPatches.cs rename to .DepricatedMods/FuckCohtmlResourceHandler/HarmonyPatches.cs diff --git a/.Deprecated/FuckCohtmlResourceHandler/Main.cs b/.DepricatedMods/FuckCohtmlResourceHandler/Main.cs similarity index 100% rename from .Deprecated/FuckCohtmlResourceHandler/Main.cs rename to .DepricatedMods/FuckCohtmlResourceHandler/Main.cs diff --git a/.Deprecated/FuckCohtmlResourceHandler/Properties/AssemblyInfo.cs b/.DepricatedMods/FuckCohtmlResourceHandler/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/FuckCohtmlResourceHandler/Properties/AssemblyInfo.cs rename to .DepricatedMods/FuckCohtmlResourceHandler/Properties/AssemblyInfo.cs diff --git a/.Deprecated/FuckCohtmlResourceHandler/README.md b/.DepricatedMods/FuckCohtmlResourceHandler/README.md similarity index 100% rename from .Deprecated/FuckCohtmlResourceHandler/README.md rename to .DepricatedMods/FuckCohtmlResourceHandler/README.md diff --git a/.Deprecated/FuckCohtmlResourceHandler/format.json b/.DepricatedMods/FuckCohtmlResourceHandler/format.json similarity index 100% rename from .Deprecated/FuckCohtmlResourceHandler/format.json rename to .DepricatedMods/FuckCohtmlResourceHandler/format.json diff --git a/.Deprecated/FuckMLA/FuckMLA.csproj b/.DepricatedMods/FuckMLA/FuckMLA.csproj similarity index 100% rename from .Deprecated/FuckMLA/FuckMLA.csproj rename to .DepricatedMods/FuckMLA/FuckMLA.csproj diff --git a/.Deprecated/FuckMLA/HarmonyPatches.cs b/.DepricatedMods/FuckMLA/HarmonyPatches.cs similarity index 100% rename from .Deprecated/FuckMLA/HarmonyPatches.cs rename to .DepricatedMods/FuckMLA/HarmonyPatches.cs diff --git a/.Deprecated/FuckMLA/Main.cs b/.DepricatedMods/FuckMLA/Main.cs similarity index 100% rename from .Deprecated/FuckMLA/Main.cs rename to .DepricatedMods/FuckMLA/Main.cs diff --git a/.Deprecated/FuckMLA/Properties/AssemblyInfo.cs b/.DepricatedMods/FuckMLA/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/FuckMLA/Properties/AssemblyInfo.cs rename to .DepricatedMods/FuckMLA/Properties/AssemblyInfo.cs diff --git a/.Deprecated/FuckMLA/format.json b/.DepricatedMods/FuckMLA/format.json similarity index 100% rename from .Deprecated/FuckMLA/format.json rename to .DepricatedMods/FuckMLA/format.json diff --git a/.Deprecated/FuckMetrics/FuckMetrics.csproj b/.DepricatedMods/FuckMetrics/FuckMetrics.csproj similarity index 100% rename from .Deprecated/FuckMetrics/FuckMetrics.csproj rename to .DepricatedMods/FuckMetrics/FuckMetrics.csproj diff --git a/.Deprecated/FuckMetrics/HarmonyPatches.cs b/.DepricatedMods/FuckMetrics/HarmonyPatches.cs similarity index 100% rename from .Deprecated/FuckMetrics/HarmonyPatches.cs rename to .DepricatedMods/FuckMetrics/HarmonyPatches.cs diff --git a/.Deprecated/FuckMetrics/Main.cs b/.DepricatedMods/FuckMetrics/Main.cs similarity index 100% rename from .Deprecated/FuckMetrics/Main.cs rename to .DepricatedMods/FuckMetrics/Main.cs diff --git a/.Deprecated/FuckMetrics/ManagedLibs/.keep b/.DepricatedMods/FuckMetrics/ManagedLibs/.keep similarity index 100% rename from .Deprecated/FuckMetrics/ManagedLibs/.keep rename to .DepricatedMods/FuckMetrics/ManagedLibs/.keep diff --git a/.Deprecated/FuckMetrics/Properties/AssemblyInfo.cs b/.DepricatedMods/FuckMetrics/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/FuckMetrics/Properties/AssemblyInfo.cs rename to .DepricatedMods/FuckMetrics/Properties/AssemblyInfo.cs diff --git a/.Deprecated/FuckMetrics/format.json b/.DepricatedMods/FuckMetrics/format.json similarity index 100% rename from .Deprecated/FuckMetrics/format.json rename to .DepricatedMods/FuckMetrics/format.json diff --git a/.Deprecated/FuckVivox/FuckVivox.csproj b/.DepricatedMods/FuckVivox/FuckVivox.csproj similarity index 100% rename from .Deprecated/FuckVivox/FuckVivox.csproj rename to .DepricatedMods/FuckVivox/FuckVivox.csproj diff --git a/.Deprecated/FuckVivox/HarmonyPatches.cs b/.DepricatedMods/FuckVivox/HarmonyPatches.cs similarity index 100% rename from .Deprecated/FuckVivox/HarmonyPatches.cs rename to .DepricatedMods/FuckVivox/HarmonyPatches.cs diff --git a/.Deprecated/FuckVivox/Main.cs b/.DepricatedMods/FuckVivox/Main.cs similarity index 100% rename from .Deprecated/FuckVivox/Main.cs rename to .DepricatedMods/FuckVivox/Main.cs diff --git a/.Deprecated/FuckVivox/Properties/AssemblyInfo.cs b/.DepricatedMods/FuckVivox/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/FuckVivox/Properties/AssemblyInfo.cs rename to .DepricatedMods/FuckVivox/Properties/AssemblyInfo.cs diff --git a/.Deprecated/FuckVivox/VivoxHelpers.cs b/.DepricatedMods/FuckVivox/VivoxHelpers.cs similarity index 100% rename from .Deprecated/FuckVivox/VivoxHelpers.cs rename to .DepricatedMods/FuckVivox/VivoxHelpers.cs diff --git a/.Deprecated/FuckVivox/WindowFocusManager.cs b/.DepricatedMods/FuckVivox/WindowFocusManager.cs similarity index 100% rename from .Deprecated/FuckVivox/WindowFocusManager.cs rename to .DepricatedMods/FuckVivox/WindowFocusManager.cs diff --git a/.Deprecated/FuckVivox/format.json b/.DepricatedMods/FuckVivox/format.json similarity index 100% rename from .Deprecated/FuckVivox/format.json rename to .DepricatedMods/FuckVivox/format.json diff --git a/.Deprecated/HeadBobbingFix/HarmonyPatches.cs b/.DepricatedMods/HeadBobbingFix/HarmonyPatches.cs similarity index 100% rename from .Deprecated/HeadBobbingFix/HarmonyPatches.cs rename to .DepricatedMods/HeadBobbingFix/HarmonyPatches.cs diff --git a/.Deprecated/HeadBobbingFix/HeadBobbingFix.csproj b/.DepricatedMods/HeadBobbingFix/HeadBobbingFix.csproj similarity index 100% rename from .Deprecated/HeadBobbingFix/HeadBobbingFix.csproj rename to .DepricatedMods/HeadBobbingFix/HeadBobbingFix.csproj diff --git a/.Deprecated/HeadBobbingFix/Main.cs b/.DepricatedMods/HeadBobbingFix/Main.cs similarity index 100% rename from .Deprecated/HeadBobbingFix/Main.cs rename to .DepricatedMods/HeadBobbingFix/Main.cs diff --git a/.Deprecated/HeadBobbingFix/Properties/AssemblyInfo.cs b/.DepricatedMods/HeadBobbingFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/HeadBobbingFix/Properties/AssemblyInfo.cs rename to .DepricatedMods/HeadBobbingFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/HeadBobbingFix/README.md b/.DepricatedMods/HeadBobbingFix/README.md similarity index 100% rename from .Deprecated/HeadBobbingFix/README.md rename to .DepricatedMods/HeadBobbingFix/README.md diff --git a/.Deprecated/HeadBobbingFix/format.json b/.DepricatedMods/HeadBobbingFix/format.json similarity index 100% rename from .Deprecated/HeadBobbingFix/format.json rename to .DepricatedMods/HeadBobbingFix/format.json diff --git a/.Deprecated/IKFixes/HarmonyPatches.cs b/.DepricatedMods/IKFixes/HarmonyPatches.cs similarity index 100% rename from .Deprecated/IKFixes/HarmonyPatches.cs rename to .DepricatedMods/IKFixes/HarmonyPatches.cs diff --git a/.Deprecated/IKFixes/IKFixes.csproj b/.DepricatedMods/IKFixes/IKFixes.csproj similarity index 100% rename from .Deprecated/IKFixes/IKFixes.csproj rename to .DepricatedMods/IKFixes/IKFixes.csproj diff --git a/.Deprecated/IKFixes/Integrations/UIExKitAddon.cs b/.DepricatedMods/IKFixes/Integrations/UIExKitAddon.cs similarity index 100% rename from .Deprecated/IKFixes/Integrations/UIExKitAddon.cs rename to .DepricatedMods/IKFixes/Integrations/UIExKitAddon.cs diff --git a/.Deprecated/IKFixes/Main.cs b/.DepricatedMods/IKFixes/Main.cs similarity index 100% rename from .Deprecated/IKFixes/Main.cs rename to .DepricatedMods/IKFixes/Main.cs diff --git a/.Deprecated/IKFixes/Properties/AssemblyInfo.cs b/.DepricatedMods/IKFixes/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/IKFixes/Properties/AssemblyInfo.cs rename to .DepricatedMods/IKFixes/Properties/AssemblyInfo.cs diff --git a/.Deprecated/IKFixes/README.md b/.DepricatedMods/IKFixes/README.md similarity index 100% rename from .Deprecated/IKFixes/README.md rename to .DepricatedMods/IKFixes/README.md diff --git a/.Deprecated/IKFixes/format.json b/.DepricatedMods/IKFixes/format.json similarity index 100% rename from .Deprecated/IKFixes/format.json rename to .DepricatedMods/IKFixes/format.json diff --git a/.Deprecated/InteractionTest/AutoArmIK.cs b/.DepricatedMods/InteractionTest/AutoArmIK.cs similarity index 100% rename from .Deprecated/InteractionTest/AutoArmIK.cs rename to .DepricatedMods/InteractionTest/AutoArmIK.cs diff --git a/.Deprecated/InteractionTest/ColliderTest/AvatarColliderStruct.cs b/.DepricatedMods/InteractionTest/ColliderTest/AvatarColliderStruct.cs similarity index 100% rename from .Deprecated/InteractionTest/ColliderTest/AvatarColliderStruct.cs rename to .DepricatedMods/InteractionTest/ColliderTest/AvatarColliderStruct.cs diff --git a/.Deprecated/InteractionTest/ColliderTest/AvatarColliders.cs b/.DepricatedMods/InteractionTest/ColliderTest/AvatarColliders.cs similarity index 100% rename from .Deprecated/InteractionTest/ColliderTest/AvatarColliders.cs rename to .DepricatedMods/InteractionTest/ColliderTest/AvatarColliders.cs diff --git a/.Deprecated/InteractionTest/ColliderTest/LineColliderTest.cs b/.DepricatedMods/InteractionTest/ColliderTest/LineColliderTest.cs similarity index 100% rename from .Deprecated/InteractionTest/ColliderTest/LineColliderTest.cs rename to .DepricatedMods/InteractionTest/ColliderTest/LineColliderTest.cs diff --git a/.Deprecated/InteractionTest/GrabbableAvatar.cs b/.DepricatedMods/InteractionTest/GrabbableAvatar.cs similarity index 100% rename from .Deprecated/InteractionTest/GrabbableAvatar.cs rename to .DepricatedMods/InteractionTest/GrabbableAvatar.cs diff --git a/.Deprecated/InteractionTest/GrabbingAvatar.cs b/.DepricatedMods/InteractionTest/GrabbingAvatar.cs similarity index 100% rename from .Deprecated/InteractionTest/GrabbingAvatar.cs rename to .DepricatedMods/InteractionTest/GrabbingAvatar.cs diff --git a/.Deprecated/InteractionTest/HarmonyPatches.cs b/.DepricatedMods/InteractionTest/HarmonyPatches.cs similarity index 100% rename from .Deprecated/InteractionTest/HarmonyPatches.cs rename to .DepricatedMods/InteractionTest/HarmonyPatches.cs diff --git a/.Deprecated/InteractionTest/InteractionTest.csproj b/.DepricatedMods/InteractionTest/InteractionTest.csproj similarity index 100% rename from .Deprecated/InteractionTest/InteractionTest.csproj rename to .DepricatedMods/InteractionTest/InteractionTest.csproj diff --git a/.Deprecated/InteractionTest/Main.cs b/.DepricatedMods/InteractionTest/Main.cs similarity index 100% rename from .Deprecated/InteractionTest/Main.cs rename to .DepricatedMods/InteractionTest/Main.cs diff --git a/.Deprecated/InteractionTest/Properties/AssemblyInfo.cs b/.DepricatedMods/InteractionTest/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/InteractionTest/Properties/AssemblyInfo.cs rename to .DepricatedMods/InteractionTest/Properties/AssemblyInfo.cs diff --git a/.Deprecated/InteractionTest/README.md b/.DepricatedMods/InteractionTest/README.md similarity index 100% rename from .Deprecated/InteractionTest/README.md rename to .DepricatedMods/InteractionTest/README.md diff --git a/.Deprecated/AvatarScaleMod/format.json b/.DepricatedMods/InteractionTest/format.json similarity index 100% rename from .Deprecated/AvatarScaleMod/format.json rename to .DepricatedMods/InteractionTest/format.json diff --git a/.Deprecated/JumpPatch/HarmonyPatches.cs b/.DepricatedMods/JumpPatch/HarmonyPatches.cs similarity index 100% rename from .Deprecated/JumpPatch/HarmonyPatches.cs rename to .DepricatedMods/JumpPatch/HarmonyPatches.cs diff --git a/.Deprecated/JumpPatch/JumpPatch.csproj b/.DepricatedMods/JumpPatch/JumpPatch.csproj similarity index 100% rename from .Deprecated/JumpPatch/JumpPatch.csproj rename to .DepricatedMods/JumpPatch/JumpPatch.csproj diff --git a/.Deprecated/JumpPatch/Main.cs b/.DepricatedMods/JumpPatch/Main.cs similarity index 100% rename from .Deprecated/JumpPatch/Main.cs rename to .DepricatedMods/JumpPatch/Main.cs diff --git a/.Deprecated/JumpPatch/Properties/AssemblyInfo.cs b/.DepricatedMods/JumpPatch/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/JumpPatch/Properties/AssemblyInfo.cs rename to .DepricatedMods/JumpPatch/Properties/AssemblyInfo.cs diff --git a/.Deprecated/JumpPatch/format.json b/.DepricatedMods/JumpPatch/format.json similarity index 100% rename from .Deprecated/JumpPatch/format.json rename to .DepricatedMods/JumpPatch/format.json diff --git a/.Deprecated/LateInitComponentHelperHack/LateInitComponentHelperHack.csproj b/.DepricatedMods/LateInitComponentHelperHack/LateInitComponentHelperHack.csproj similarity index 100% rename from .Deprecated/LateInitComponentHelperHack/LateInitComponentHelperHack.csproj rename to .DepricatedMods/LateInitComponentHelperHack/LateInitComponentHelperHack.csproj diff --git a/.Deprecated/LateInitComponentHelperHack/Main.cs b/.DepricatedMods/LateInitComponentHelperHack/Main.cs similarity index 100% rename from .Deprecated/LateInitComponentHelperHack/Main.cs rename to .DepricatedMods/LateInitComponentHelperHack/Main.cs diff --git a/.Deprecated/LateInitComponentHelperHack/Properties/AssemblyInfo.cs b/.DepricatedMods/LateInitComponentHelperHack/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/LateInitComponentHelperHack/Properties/AssemblyInfo.cs rename to .DepricatedMods/LateInitComponentHelperHack/Properties/AssemblyInfo.cs diff --git a/.Deprecated/LateInitComponentHelperHack/format.json b/.DepricatedMods/LateInitComponentHelperHack/format.json similarity index 100% rename from .Deprecated/LateInitComponentHelperHack/format.json rename to .DepricatedMods/LateInitComponentHelperHack/format.json diff --git a/.Deprecated/MenuScalePatch/HarmonyPatches.cs b/.DepricatedMods/MenuScalePatch/HarmonyPatches.cs similarity index 100% rename from .Deprecated/MenuScalePatch/HarmonyPatches.cs rename to .DepricatedMods/MenuScalePatch/HarmonyPatches.cs diff --git a/.Deprecated/MenuScalePatch/Helpers/MainMenuHelper.cs b/.DepricatedMods/MenuScalePatch/Helpers/MainMenuHelper.cs similarity index 100% rename from .Deprecated/MenuScalePatch/Helpers/MainMenuHelper.cs rename to .DepricatedMods/MenuScalePatch/Helpers/MainMenuHelper.cs diff --git a/.Deprecated/MenuScalePatch/Helpers/QuickMenuHelper.cs b/.DepricatedMods/MenuScalePatch/Helpers/QuickMenuHelper.cs similarity index 100% rename from .Deprecated/MenuScalePatch/Helpers/QuickMenuHelper.cs rename to .DepricatedMods/MenuScalePatch/Helpers/QuickMenuHelper.cs diff --git a/.Deprecated/MenuScalePatch/MSP_Menus.cs b/.DepricatedMods/MenuScalePatch/MSP_Menus.cs similarity index 100% rename from .Deprecated/MenuScalePatch/MSP_Menus.cs rename to .DepricatedMods/MenuScalePatch/MSP_Menus.cs diff --git a/.Deprecated/MenuScalePatch/Main.cs b/.DepricatedMods/MenuScalePatch/Main.cs similarity index 100% rename from .Deprecated/MenuScalePatch/Main.cs rename to .DepricatedMods/MenuScalePatch/Main.cs diff --git a/.Deprecated/MenuScalePatch/MenuScalePatch.csproj b/.DepricatedMods/MenuScalePatch/MenuScalePatch.csproj similarity index 100% rename from .Deprecated/MenuScalePatch/MenuScalePatch.csproj rename to .DepricatedMods/MenuScalePatch/MenuScalePatch.csproj diff --git a/.Deprecated/MenuScalePatch/Properties/AssemblyInfo.cs b/.DepricatedMods/MenuScalePatch/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/MenuScalePatch/Properties/AssemblyInfo.cs rename to .DepricatedMods/MenuScalePatch/Properties/AssemblyInfo.cs diff --git a/.Deprecated/MenuScalePatch/README.md b/.DepricatedMods/MenuScalePatch/README.md similarity index 100% rename from .Deprecated/MenuScalePatch/README.md rename to .DepricatedMods/MenuScalePatch/README.md diff --git a/.Deprecated/MenuScalePatch/format.json b/.DepricatedMods/MenuScalePatch/format.json similarity index 100% rename from .Deprecated/MenuScalePatch/format.json rename to .DepricatedMods/MenuScalePatch/format.json diff --git a/.Deprecated/NAK.CustomComponents/Components/NAKPointerTracker.cs b/.DepricatedMods/NAK.CustomComponents/Components/NAKPointerTracker.cs similarity index 100% rename from .Deprecated/NAK.CustomComponents/Components/NAKPointerTracker.cs rename to .DepricatedMods/NAK.CustomComponents/Components/NAKPointerTracker.cs diff --git a/.Deprecated/NAK.CustomComponents/CustomComponents.csproj b/.DepricatedMods/NAK.CustomComponents/CustomComponents.csproj similarity index 100% rename from .Deprecated/NAK.CustomComponents/CustomComponents.csproj rename to .DepricatedMods/NAK.CustomComponents/CustomComponents.csproj diff --git a/.Deprecated/NAK.CustomComponents/Main.cs b/.DepricatedMods/NAK.CustomComponents/Main.cs similarity index 100% rename from .Deprecated/NAK.CustomComponents/Main.cs rename to .DepricatedMods/NAK.CustomComponents/Main.cs diff --git a/.Deprecated/NAK.CustomComponents/Properties/AssemblyInfo.cs b/.DepricatedMods/NAK.CustomComponents/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/NAK.CustomComponents/Properties/AssemblyInfo.cs rename to .DepricatedMods/NAK.CustomComponents/Properties/AssemblyInfo.cs diff --git a/.Deprecated/NAK.CustomComponents/format.json b/.DepricatedMods/NAK.CustomComponents/format.json similarity index 100% rename from .Deprecated/NAK.CustomComponents/format.json rename to .DepricatedMods/NAK.CustomComponents/format.json diff --git a/.Deprecated/NoDepthOnlyFlat/Main.cs b/.DepricatedMods/NoDepthOnlyFlat/Main.cs similarity index 100% rename from .Deprecated/NoDepthOnlyFlat/Main.cs rename to .DepricatedMods/NoDepthOnlyFlat/Main.cs diff --git a/.Deprecated/NoDepthOnlyFlat/NoDepthOnlyFlat.csproj b/.DepricatedMods/NoDepthOnlyFlat/NoDepthOnlyFlat.csproj similarity index 100% rename from .Deprecated/NoDepthOnlyFlat/NoDepthOnlyFlat.csproj rename to .DepricatedMods/NoDepthOnlyFlat/NoDepthOnlyFlat.csproj diff --git a/.Deprecated/NoDepthOnlyFlat/Properties/AssemblyInfo.cs b/.DepricatedMods/NoDepthOnlyFlat/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/NoDepthOnlyFlat/Properties/AssemblyInfo.cs rename to .DepricatedMods/NoDepthOnlyFlat/Properties/AssemblyInfo.cs diff --git a/.Deprecated/NoDepthOnlyFlat/README.md b/.DepricatedMods/NoDepthOnlyFlat/README.md similarity index 100% rename from .Deprecated/NoDepthOnlyFlat/README.md rename to .DepricatedMods/NoDepthOnlyFlat/README.md diff --git a/.Deprecated/NoDepthOnlyFlat/format.json b/.DepricatedMods/NoDepthOnlyFlat/format.json similarity index 100% rename from .Deprecated/NoDepthOnlyFlat/format.json rename to .DepricatedMods/NoDepthOnlyFlat/format.json diff --git a/.Deprecated/PickupPushPull/HarmonyPatches.cs b/.DepricatedMods/PickupPushPull/HarmonyPatches.cs similarity index 100% rename from .Deprecated/PickupPushPull/HarmonyPatches.cs rename to .DepricatedMods/PickupPushPull/HarmonyPatches.cs diff --git a/.Deprecated/PickupPushPull/InputModules/PickupPushPull_Module.cs b/.DepricatedMods/PickupPushPull/InputModules/PickupPushPull_Module.cs similarity index 100% rename from .Deprecated/PickupPushPull/InputModules/PickupPushPull_Module.cs rename to .DepricatedMods/PickupPushPull/InputModules/PickupPushPull_Module.cs diff --git a/.Deprecated/PickupPushPull/Main.cs b/.DepricatedMods/PickupPushPull/Main.cs similarity index 100% rename from .Deprecated/PickupPushPull/Main.cs rename to .DepricatedMods/PickupPushPull/Main.cs diff --git a/.Deprecated/PickupPushPull/PickupPushPull.csproj b/.DepricatedMods/PickupPushPull/PickupPushPull.csproj similarity index 100% rename from .Deprecated/PickupPushPull/PickupPushPull.csproj rename to .DepricatedMods/PickupPushPull/PickupPushPull.csproj diff --git a/.Deprecated/PickupPushPull/Properties/AssemblyInfo.cs b/.DepricatedMods/PickupPushPull/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/PickupPushPull/Properties/AssemblyInfo.cs rename to .DepricatedMods/PickupPushPull/Properties/AssemblyInfo.cs diff --git a/.Deprecated/PickupPushPull/format.json b/.DepricatedMods/PickupPushPull/format.json similarity index 100% rename from .Deprecated/PickupPushPull/format.json rename to .DepricatedMods/PickupPushPull/format.json diff --git a/.Deprecated/PlaySpaceScaleFix/HarmonyPatches.cs b/.DepricatedMods/PlaySpaceScaleFix/HarmonyPatches.cs similarity index 100% rename from .Deprecated/PlaySpaceScaleFix/HarmonyPatches.cs rename to .DepricatedMods/PlaySpaceScaleFix/HarmonyPatches.cs diff --git a/.Deprecated/PlaySpaceScaleFix/Main.cs b/.DepricatedMods/PlaySpaceScaleFix/Main.cs similarity index 100% rename from .Deprecated/PlaySpaceScaleFix/Main.cs rename to .DepricatedMods/PlaySpaceScaleFix/Main.cs diff --git a/.Deprecated/PlaySpaceScaleFix/PlaySpaceScaleFix.csproj b/.DepricatedMods/PlaySpaceScaleFix/PlaySpaceScaleFix.csproj similarity index 100% rename from .Deprecated/PlaySpaceScaleFix/PlaySpaceScaleFix.csproj rename to .DepricatedMods/PlaySpaceScaleFix/PlaySpaceScaleFix.csproj diff --git a/.Deprecated/PlaySpaceScaleFix/Properties/AssemblyInfo.cs b/.DepricatedMods/PlaySpaceScaleFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/PlaySpaceScaleFix/Properties/AssemblyInfo.cs rename to .DepricatedMods/PlaySpaceScaleFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/PlaySpaceScaleFix/README.md b/.DepricatedMods/PlaySpaceScaleFix/README.md similarity index 100% rename from .Deprecated/PlaySpaceScaleFix/README.md rename to .DepricatedMods/PlaySpaceScaleFix/README.md diff --git a/.Deprecated/PlaySpaceScaleFix/format.json b/.DepricatedMods/PlaySpaceScaleFix/format.json similarity index 100% rename from .Deprecated/PlaySpaceScaleFix/format.json rename to .DepricatedMods/PlaySpaceScaleFix/format.json diff --git a/.DepricatedMods/SmoothRay/HarmonyPatches.cs b/.DepricatedMods/SmoothRay/HarmonyPatches.cs new file mode 100644 index 0000000..590def2 --- /dev/null +++ b/.DepricatedMods/SmoothRay/HarmonyPatches.cs @@ -0,0 +1,14 @@ +using ABI_RC.Core.Player; +using HarmonyLib; + +namespace NAK.SmoothRay.HarmonyPatches; + +internal class PlayerSetupPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))] + private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance) + { + + } +} \ No newline at end of file diff --git a/.DepricatedMods/SmoothRay/Main.cs b/.DepricatedMods/SmoothRay/Main.cs new file mode 100644 index 0000000..0a591d6 --- /dev/null +++ b/.DepricatedMods/SmoothRay/Main.cs @@ -0,0 +1,28 @@ +using MelonLoader; + +namespace NAK.SmoothRay; + +// ChilloutVR adaptation of: +// https://github.com/kinsi55/BeatSaber_SmoothedController +// https://github.com/kinsi55/BeatSaber_SmoothedController/blob/master/LICENSE + +public class SmoothRay : MelonMod +{ + public override void OnInitializeMelon() + { + ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches)); + } + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } +} \ No newline at end of file diff --git a/.Deprecated/FuckMagicaCloth2/Properties/AssemblyInfo.cs b/.DepricatedMods/SmoothRay/Properties/AssemblyInfo.cs similarity index 67% rename from .Deprecated/FuckMagicaCloth2/Properties/AssemblyInfo.cs rename to .DepricatedMods/SmoothRay/Properties/AssemblyInfo.cs index 91ab089..2becd53 100644 --- a/.Deprecated/FuckMagicaCloth2/Properties/AssemblyInfo.cs +++ b/.DepricatedMods/SmoothRay/Properties/AssemblyInfo.cs @@ -1,30 +1,33 @@ using MelonLoader; -using NAK.FuckOffMagicaCloth2.Properties; +using NAK.SmoothRay.Properties; using System.Reflection; [assembly: AssemblyVersion(AssemblyInfoParams.Version)] [assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] [assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.FuckOffMagicaCloth2))] +[assembly: AssemblyTitle(nameof(NAK.SmoothRay))] [assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.FuckOffMagicaCloth2))] +[assembly: AssemblyProduct(nameof(NAK.SmoothRay))] [assembly: MelonInfo( - typeof(NAK.FuckOffMagicaCloth2.FuckOffMagicaCloth2Mod), - nameof(NAK.FuckOffMagicaCloth2), + typeof(NAK.SmoothRay.SmoothRay), + nameof(NAK.SmoothRay), AssemblyInfoParams.Version, AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckOffMagicaCloth2" + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SmoothRay" )] [assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 220, 130, 5)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] -namespace NAK.FuckOffMagicaCloth2.Properties; +namespace NAK.SmoothRay.Properties; + internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.3"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/.Deprecated/FuckOffUICamera/README.md b/.DepricatedMods/SmoothRay/README.md similarity index 61% rename from .Deprecated/FuckOffUICamera/README.md rename to .DepricatedMods/SmoothRay/README.md index 1c4c5bc..7c670d3 100644 --- a/.Deprecated/FuckOffUICamera/README.md +++ b/.DepricatedMods/SmoothRay/README.md @@ -1,13 +1,13 @@ -# SearchWithSpacesFix +# SmoothRay -Fixes search terms that use spaces. +Smoothes your controller while using the Quick and Main menus to make it easier to navigate. --- -Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. +Here is the block of text where I tell you this mod is not affiliated 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. +> This mod is an independent creation and is 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. diff --git a/SmootherRay/SmootherRayer.cs b/.DepricatedMods/SmoothRay/SmoothRay.cs similarity index 65% rename from SmootherRay/SmootherRayer.cs rename to .DepricatedMods/SmoothRay/SmoothRay.cs index 6262824..ae562af 100644 --- a/SmootherRay/SmootherRayer.cs +++ b/.DepricatedMods/SmoothRay/SmoothRay.cs @@ -22,17 +22,15 @@ SOFTWARE. **/ -using ABI_RC.Core; using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.Savior; -using ABI_RC.Systems.InputManagement; using MelonLoader; using UnityEngine; using Valve.VR; -namespace NAK.SmootherRay; +namespace NAK.SmoothRay; -public class SmootherRayer : MonoBehaviour +public class SmoothRayer : MonoBehaviour { #region Variables @@ -72,10 +70,8 @@ public class SmootherRayer : MonoBehaviour UpdatePosesAction(true); } - foreach (MelonPreferences_Entry setting in SmootherRayMod.Category.Entries) + foreach (MelonPreferences_Entry setting in SmoothRay.Category.Entries) setting.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings); - - MetaPort.Instance.settings.settingBoolChanged.AddListener(OnSettingsBoolChanged); OnUpdateSettings(null, null); } @@ -131,41 +127,12 @@ public class SmootherRayer : MonoBehaviour private void OnUpdateSettings(object arg1, object arg2) { - _isEnabled = SmootherRayMod.EntryEnabled.Value; - _menuOnly = SmootherRayMod.EntryMenuOnly.Value; - _smallMovementThresholdAngle = SmootherRayMod.EntrySmallMovementThresholdAngle.Value; - + _isEnabled = SmoothRay.EntryEnabled.Value; + _menuOnly = SmoothRay.EntryMenuOnly.Value; + _smallMovementThresholdAngle = SmoothRay.EntrySmallMovementThresholdAngle.Value; // dont let value hit 0, itll freeze controllers - _positionSmoothingValue = Mathf.Max(20f - Mathf.Clamp(SmootherRayMod.EntryPositionSmoothing.Value, 0f, 20f), 0.1f); - _rotationSmoothingValue = Mathf.Max(20f - Mathf.Clamp(SmootherRayMod.EntryRotationSmoothing.Value, 0f, 20f), 0.1f); - - if (!_isEnabled) - return; // only care about setting being enabled - - ray._enableSmoothRay = false; // ensure built-in smoothing is disabled - - if (MetaPort.Instance.settings.GetSettingsBool("ControlSmoothRaycast")) - return; // disable saved setting once - - SmootherRayMod.Logger.Msg("Built-in SmootherRay setting found to be enabled. Disabling built-in SmootherRay implementation in favor of modded implementation."); - MetaPort.Instance.settings.SetSettingsBool("ControlSmoothRaycast", false); - ViewManager.SetGameSettingBool("ControlSmoothRaycast", false); - // ^ did you know the game doesn't even use this method native... - } - - private void OnSettingsBoolChanged(string key, bool value) - { - if (key != "ControlSmoothRaycast") - return; // only care about SmoothRaycast setting - - if (!value) - return; // only care about setting being enabled - - _isEnabled = false; // ensure modded SmootherRay is disabled - - if (!SmootherRayMod.EntryEnabled.Value) return; // disable saved setting once - SmootherRayMod.Logger.Msg("Modded SmootherRay found to be enabled. Disabling modded SmootherRay implementation in favor of built-in implementation."); - SmootherRayMod.EntryEnabled.Value = false; + _positionSmoothingValue = Math.Max(20f - Mathf.Clamp(SmoothRay.EntryPositionSmoothing.Value, 0f, 20f), 0.1f); + _rotationSmoothingValue = Math.Max(20f - Mathf.Clamp(SmoothRay.EntryRotationSmoothing.Value, 0f, 20f), 0.1f); } private void OnAppliedPoses() @@ -181,13 +148,12 @@ public class SmootherRayer : MonoBehaviour private void SmoothTransform() { Transform controller = transform; - if (!CanSmoothRay()) - { - _smoothedPosition = controller.localPosition; - _smoothedRotation = controller.localRotation; - } - else + if (_isEnabled && ray.lineRenderer != null && ray.lineRenderer.enabled) { + if (_menuOnly && (!ray.uiActive || (ray.hitTransform != ViewManager.Instance.transform && + ray.hitTransform != CVR_MenuManager.Instance.quickMenu.transform))) + return; + var angDiff = Quaternion.Angle(_smoothedRotation, controller.localRotation); _angleVelocitySnap = Mathf.Min(_angleVelocitySnap + angDiff, 90f); @@ -210,29 +176,12 @@ public class SmootherRayer : MonoBehaviour controller.localRotation = _smoothedRotation; } } + else + { + _smoothedPosition = controller.localPosition; + _smoothedRotation = controller.localRotation; + } } - private bool CanSmoothRay() - { - bool canSmoothRay = _isEnabled && ray.lineRenderer != null && ray.lineRenderer.enabled; - - if (_menuOnly) - { - switch (ray.hand) - { - case CVRHand.Left when !CVRInputManager.Instance.leftControllerPointingMenu: - case CVRHand.Right when !CVRInputManager.Instance.rightControllerPointingMenu: - canSmoothRay = false; - break; - } - } - - if (SmootherRayMod.EntrySmoothWhenHoldingPickup.Value - && ray.grabbedObject) - canSmoothRay = true; - - return canSmoothRay; - } - - #endregion Private Methods + #endregion } \ No newline at end of file diff --git a/.Deprecated/FOVAdjustment/FOVAdjustment.csproj b/.DepricatedMods/SmoothRay/SmoothRay.csproj similarity index 100% rename from .Deprecated/FOVAdjustment/FOVAdjustment.csproj rename to .DepricatedMods/SmoothRay/SmoothRay.csproj diff --git a/.DepricatedMods/SmoothRay/format.json b/.DepricatedMods/SmoothRay/format.json new file mode 100644 index 0000000..2c91941 --- /dev/null +++ b/.DepricatedMods/SmoothRay/format.json @@ -0,0 +1,23 @@ +{ + "_id": 162, + "name": "SmoothRay", + "modversion": "1.0.3", + "gameversion": "2023r172", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Smoothes your controller while using the Quick and Main menus to make it easier to navigate.\nThis is a CVR adaptation of a Beat Saber mod: [BeatSaber_SmoothedController](https://github.com/kinsi55/BeatSaber_SmoothedController)\n\nSupport for TrackedControllerFix & DesktopVRSwitch.\n**Only supports OpenVR, not OpenXR.**", + "searchtags": [ + "vr", + "ray", + "controller", + "smoothing" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r21/SmoothRay.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SmoothRay/", + "changelog": "- Fixes for 2023r172. Literally just recompiled.", + "embedcolor": "#dc8105" +} \ No newline at end of file diff --git a/.Deprecated/TrackedControllerFix/HarmonyPatches.cs b/.DepricatedMods/TrackedControllerFix/HarmonyPatches.cs similarity index 100% rename from .Deprecated/TrackedControllerFix/HarmonyPatches.cs rename to .DepricatedMods/TrackedControllerFix/HarmonyPatches.cs diff --git a/.Deprecated/TrackedControllerFix/Main.cs b/.DepricatedMods/TrackedControllerFix/Main.cs similarity index 100% rename from .Deprecated/TrackedControllerFix/Main.cs rename to .DepricatedMods/TrackedControllerFix/Main.cs diff --git a/.Deprecated/TrackedControllerFix/Properties/AssemblyInfo.cs b/.DepricatedMods/TrackedControllerFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/TrackedControllerFix/Properties/AssemblyInfo.cs rename to .DepricatedMods/TrackedControllerFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/TrackedControllerFix/README.md b/.DepricatedMods/TrackedControllerFix/README.md similarity index 100% rename from .Deprecated/TrackedControllerFix/README.md rename to .DepricatedMods/TrackedControllerFix/README.md diff --git a/.Deprecated/TrackedControllerFix/TrackedControllerFix.csproj b/.DepricatedMods/TrackedControllerFix/TrackedControllerFix.csproj similarity index 100% rename from .Deprecated/TrackedControllerFix/TrackedControllerFix.csproj rename to .DepricatedMods/TrackedControllerFix/TrackedControllerFix.csproj diff --git a/.Deprecated/TrackedControllerFix/TrackedControllerFixer.cs b/.DepricatedMods/TrackedControllerFix/TrackedControllerFixer.cs similarity index 100% rename from .Deprecated/TrackedControllerFix/TrackedControllerFixer.cs rename to .DepricatedMods/TrackedControllerFix/TrackedControllerFixer.cs diff --git a/.Deprecated/TrackedControllerFix/format.json b/.DepricatedMods/TrackedControllerFix/format.json similarity index 100% rename from .Deprecated/TrackedControllerFix/format.json rename to .DepricatedMods/TrackedControllerFix/format.json diff --git a/.Deprecated/TrackedPointFix/HarmonyPatches.cs b/.DepricatedMods/TrackedPointFix/HarmonyPatches.cs similarity index 100% rename from .Deprecated/TrackedPointFix/HarmonyPatches.cs rename to .DepricatedMods/TrackedPointFix/HarmonyPatches.cs diff --git a/.Deprecated/TrackedPointFix/Main.cs b/.DepricatedMods/TrackedPointFix/Main.cs similarity index 100% rename from .Deprecated/TrackedPointFix/Main.cs rename to .DepricatedMods/TrackedPointFix/Main.cs diff --git a/.Deprecated/TrackedPointFix/Properties/AssemblyInfo.cs b/.DepricatedMods/TrackedPointFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/TrackedPointFix/Properties/AssemblyInfo.cs rename to .DepricatedMods/TrackedPointFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/TrackedPointFix/README.md b/.DepricatedMods/TrackedPointFix/README.md similarity index 100% rename from .Deprecated/TrackedPointFix/README.md rename to .DepricatedMods/TrackedPointFix/README.md diff --git a/.Deprecated/TrackedPointFix/TrackedPointFix.csproj b/.DepricatedMods/TrackedPointFix/TrackedPointFix.csproj similarity index 100% rename from .Deprecated/TrackedPointFix/TrackedPointFix.csproj rename to .DepricatedMods/TrackedPointFix/TrackedPointFix.csproj diff --git a/.Deprecated/TrackedPointFix/format.json b/.DepricatedMods/TrackedPointFix/format.json similarity index 100% rename from .Deprecated/TrackedPointFix/format.json rename to .DepricatedMods/TrackedPointFix/format.json diff --git a/.Experimental/CustomRichPresence/CustomRichPresence.csproj b/.Experimental/CustomRichPresence/CustomRichPresence.csproj deleted file mode 100644 index 4e44ed3..0000000 --- a/.Experimental/CustomRichPresence/CustomRichPresence.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net48 - - - - ..\.ManagedLibs\TheClapper.dll - - - diff --git a/.Experimental/CustomRichPresence/Main.cs b/.Experimental/CustomRichPresence/Main.cs deleted file mode 100644 index a2702d5..0000000 --- a/.Experimental/CustomRichPresence/Main.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Reflection; -using ABI_RC.Core.Networking; -using ABI_RC.Core.Savior; -using ABI_RC.Core.Util.AnimatorManager; -using ABI_RC.Helpers; -using HarmonyLib; -using MagicaCloth; -using MelonLoader; -using Steamworks; -using UnityEngine; - -namespace NAK.CustomRichPresence; - -public class CustomRichPresenceMod : MelonMod -{ - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(CustomRichPresence)); - - private static readonly MelonPreferences_Entry EntryUseCustomPresence = - Category.CreateEntry("use_custom_presence", true, - "Use Custom Presence", description: "Uses the custom rich presence setup."); - - // Discord Rich Presence Customization - private static readonly MelonPreferences_Entry DiscordStatusFormat = - Category.CreateEntry("discord_status_format", "Online using {mode}.", - "Discord Status Format", description: "Format for Discord status message. Available variables: {mode}, {instance_name}, {privacy}"); - - private static readonly MelonPreferences_Entry DiscordDetailsFormat = - Category.CreateEntry("discord_details_format", "{instance_name} [{privacy}]", - "Discord Details Format", description: "Format for Discord details. Available variables: {instance_name}, {privacy}, {world_name}, {mission_name}"); - - // Steam Rich Presence Customization - private static readonly MelonPreferences_Entry SteamStatusFormat = - Category.CreateEntry("steam_status_format", "Exploring ({mode}) {instance_name} [{privacy}].", - "Steam Status Format", description: "Format for Steam status. Available variables: {mode}, {instance_name}, {privacy}"); - - private static readonly MelonPreferences_Entry SteamGameStatusFormat = - Category.CreateEntry("steam_game_status_format", "Connected to a server, Privacy: {privacy}.", - "Steam Game Status Format", description: "Format for Steam game status. Available variables: {privacy}, {instance_name}, {world_name}"); - - private static readonly MelonPreferences_Entry SteamDisplayStatus = - Category.CreateEntry("steam_display_status", "#Status_Online", - "Steam Display Status", description: "Steam display status localization key."); - - #endregion Melon Preferences - - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(RichPresence).GetMethod(nameof(RichPresence.PopulateLastMessage), - BindingFlags.NonPublic | BindingFlags.Static), - prefix: new HarmonyMethod(typeof(CustomRichPresenceMod).GetMethod(nameof(OnPopulateLastMessage), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - private static string FormatPresenceText(string format, string mode) - { - return format - .Replace("{mode}", mode) - .Replace("{instance_name}", RichPresence.LastMsg.InstanceName) - .Replace("{privacy}", RichPresence.LastMsg.InstancePrivacy) - .Replace("{world_name}", RichPresence.LastMsg.InstanceWorldName) - .Replace("{mission_name}", RichPresence.LastMsg.InstanceMissionName) - .Replace("{current_players}", RichPresence.LastMsg.CurrentPlayers.ToString()) - .Replace("{max_players}", RichPresence.LastMsg.MaxPlayers.ToString()); - } - - private static bool OnPopulateLastMessage() - { - if (!EntryUseCustomPresence.Value) - return true; - - string mode = MetaPort.Instance.isUsingVr ? "VR Mode" : "Desktop Mode"; - - PresenceManager.ClearPresence(); - if (RichPresence.DiscordEnabled) - { - string status = FormatPresenceText(DiscordStatusFormat.Value, mode); - string details = FormatPresenceText(DiscordDetailsFormat.Value, mode); - - PresenceManager.UpdatePresence( - status, - details, - RichPresence.LastConnectedToServer, - 0L, - "discordrp-cvrmain", - null, - null, - null, - RichPresence.LastMsg.InstanceMeshId, - RichPresence.LastMsg.CurrentPlayers, - RichPresence.LastMsg.MaxPlayers - ); - } - - if (!CheckVR.Instance.skipSteamApiRegister && SteamManager.Initialized) - { - SteamFriends.ClearRichPresence(); - if (RichPresence.SteamEnabled) - { - string status = FormatPresenceText(SteamStatusFormat.Value, mode); - string gameStatus = FormatPresenceText(SteamGameStatusFormat.Value, mode); - - SteamFriends.SetRichPresence("status", status); - SteamFriends.SetRichPresence("gamestatus", gameStatus); - SteamFriends.SetRichPresence("steam_display", SteamDisplayStatus.Value); - SteamFriends.SetRichPresence("steam_player_group", RichPresence.LastMsg.InstanceMeshId); - SteamFriends.SetRichPresence("steam_player_group_size", RichPresence.LastMsg.CurrentPlayers.ToString()); - SteamFriends.SetRichPresence("gamemode", RichPresence.LastMsg.InstanceMissionName); - SteamFriends.SetRichPresence("worldname", RichPresence.LastMsg.InstanceWorldName); - SteamFriends.SetRichPresence("instancename", RichPresence.LastMsg.InstanceName); - SteamFriends.SetRichPresence("instanceprivacy", RichPresence.LastMsg.InstancePrivacy); - SteamFriends.SetRichPresence("currentplayer", RichPresence.LastMsg.CurrentPlayers.ToString()); - SteamFriends.SetRichPresence("maxplayer", RichPresence.LastMsg.MaxPlayers.ToString()); - } - } - - return false; - } -} \ No newline at end of file diff --git a/.Experimental/CustomRichPresence/Properties/AssemblyInfo.cs b/.Experimental/CustomRichPresence/Properties/AssemblyInfo.cs deleted file mode 100644 index 0e00c7b..0000000 --- a/.Experimental/CustomRichPresence/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.CustomRichPresence.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.CustomRichPresence))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.CustomRichPresence))] - -[assembly: MelonInfo( - typeof(NAK.CustomRichPresence.CustomRichPresenceMod), - nameof(NAK.CustomRichPresence), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CustomRichPresence" -)] - -[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.CustomRichPresence.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Experimental/CustomRichPresence/README.md b/.Experimental/CustomRichPresence/README.md deleted file mode 100644 index 1f37b48..0000000 --- a/.Experimental/CustomRichPresence/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Custom Rich Presence - -Lets you customize the Steam & Discord rich presence messages & values. - ---- - -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. diff --git a/.Experimental/CustomRichPresence/format.json b/.Experimental/CustomRichPresence/format.json deleted file mode 100644 index a599614..0000000 --- a/.Experimental/CustomRichPresence/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": -1, - "name": "DropPropTweak", - "modversion": "1.0.0", - "gameversion": "2024r175", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Gives the Drop Prop button more utility by allowing you to drop props in the air.", - "searchtags": [ - "prop", - "spawn", - "indicator", - "loading" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r34/DropPropTweak.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DropPropTweak/", - "changelog": "- Initial Release", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/LuaNetworkVariables.csproj b/.Experimental/LuaNetworkVariables/LuaNetworkVariables.csproj deleted file mode 100644 index 728edb7..0000000 --- a/.Experimental/LuaNetworkVariables/LuaNetworkVariables.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - net48 - - diff --git a/.Experimental/LuaNetworkVariables/Main.cs b/.Experimental/LuaNetworkVariables/Main.cs deleted file mode 100644 index ce59fa0..0000000 --- a/.Experimental/LuaNetworkVariables/Main.cs +++ /dev/null @@ -1,56 +0,0 @@ -using ABI_RC.Core.Player; -using MelonLoader; -using UnityEngine; - -namespace NAK.LuaNetworkVariables; - -public class LuaNetworkVariablesMod : MelonMod -{ - internal static MelonLogger.Instance Logger; - - #region Melon Preferences - - - #endregion Melon Preferences - - #region Melon Events - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - ApplyPatches(typeof(Patches.LuaScriptFactory_Patches)); - } - - // public override void OnUpdate() - // { - // // if (Input.GetKeyDown(KeyCode.F1)) - // // { - // // PlayerSetup.Instance.DropProp("be0b5acc-a987-48dc-a28b-62bd912fe3a0"); - // // } - // // - // // if (Input.GetKeyDown(KeyCode.F2)) - // // { - // // GameObject go = new("TestSyncedObject"); - // // go.AddComponent(); - // // } - // } - - #endregion Melon Events - - #region Melon Mod Utilities - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #endregion Melon Mod Utilities -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetLuaModule.cs b/.Experimental/LuaNetworkVariables/NetLuaModule.cs deleted file mode 100644 index 23855ed..0000000 --- a/.Experimental/LuaNetworkVariables/NetLuaModule.cs +++ /dev/null @@ -1,155 +0,0 @@ -using ABI_RC.Core.Base; -using ABI.Scripting.CVRSTL.Common; -using JetBrains.Annotations; -using MoonSharp.Interpreter; - -namespace NAK.LuaNetworkVariables.Modules; - -[PublicAPI] -public class LuaNetModule : BaseScriptedStaticWrapper -{ - public const string MODULE_ID = "NetworkModule"; - - private LuaNetVarController _controller; - - public LuaNetModule(CVRLuaContext context) : base(context) - { - _controller = context.behaviour.AddComponentIfMissing(); - } - - internal static object RegisterUserData(Script script, CVRLuaContext context) - { - // Register the LuaNetModule type with MoonSharp - UserData.RegisterType(InteropAccessMode.Default, MODULE_ID); - return new LuaNetModule(context); - } - - #region Module Instance Methods - - /// - /// Registers a network variable that can be synchronized over the network. - /// - /// The name of the variable to register. - public void RegisterNetworkVar(string varName) - { - CheckIfCanAccessMethod(nameof(RegisterNetworkVar), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return; - } - - _controller.RegisterNetworkVar(varName); - } - - /// - /// Registers a callback function to be called when the specified network variable changes. - /// - /// The name of the variable to watch. - /// The Lua function to call when the variable changes. - public void RegisterNotifyCallback(string varName, DynValue callback) - { - CheckIfCanAccessMethod(nameof(RegisterNotifyCallback), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return; - } - - _controller.RegisterNotifyCallback(varName, callback); - } - - /// - /// Registers a callback function to be called when the specified event is received. - /// - /// The name of the event to watch. - /// The Lua function to call when the event occurs. - public void RegisterEventCallback(string eventName, DynValue callback) - { - CheckIfCanAccessMethod(nameof(RegisterEventCallback), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return; - } - - _controller.RegisterEventCallback(eventName, callback); - } - - /// - /// Sends a Lua event to other clients. - /// - /// The name of the event to send. - /// Optional arguments to send with the event. - public void SendLuaEvent(string eventName, params DynValue[] args) - { - CheckIfCanAccessMethod(nameof(SendLuaEvent), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return; - } - - _controller.SendLuaEvent(eventName, args); - } - - /// - /// Sends a Lua event to other clients. - /// - /// The name of the event to send. - /// Optional arguments to send with the event. - public void SendLuaEventToUser(string eventName, string userId, params DynValue[] args) - { - CheckIfCanAccessMethod(nameof(SendLuaEventToUser), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return; - } - - _controller.SendLuaEventToUser(eventName, userId, args); - } - - /// - /// Checks if the current client is the owner of the synchronized object. - /// - public bool IsSyncOwner() - { - CheckIfCanAccessMethod(nameof(IsSyncOwner), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return false; - } - - return _controller.IsSyncOwner(); - } - - public string GetSyncOwner() - { - CheckIfCanAccessMethod(nameof(GetSyncOwner), false, - CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); - - if (_controller == null) - { - LuaNetworkVariablesMod.Logger.Error("LuaNetVarController is null."); - return string.Empty; - } - - return _controller.GetSyncOwner(); - } - - #endregion -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs deleted file mode 100644 index 59b385e..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs +++ /dev/null @@ -1,41 +0,0 @@ -using ABI_RC.Core.Networking; -using MoonSharp.Interpreter; -using ABI_RC.Core.Player; - -namespace NAK.LuaNetworkVariables; - -public struct LuaEventContext -{ - private string SenderId { get; set; } - public string SenderName { get; private set; } - private DateTime LastInvokeTime { get; set; } - private double TimeSinceLastInvoke { get; set; } - private bool IsLocal { get; set; } - - public static LuaEventContext Create(bool isLocal, string senderId, DateTime lastInvokeTime) - { - var playerName = isLocal ? AuthManager.Username : CVRPlayerManager.Instance.TryGetPlayerName(senderId); - - return new LuaEventContext - { - SenderId = senderId, - SenderName = playerName ?? "Unknown", - LastInvokeTime = lastInvokeTime, - TimeSinceLastInvoke = (DateTime.Now - lastInvokeTime).TotalSeconds, - IsLocal = isLocal - }; - } - - public Table ToLuaTable(Script script) - { - Table table = new(script) - { - ["SenderId"] = SenderId, - ["SenderName"] = SenderName, - ["LastInvokeTime"] = LastInvokeTime.ToUniversalTime().ToString("O"), - ["TimeSinceLastInvoke"] = TimeSinceLastInvoke, - ["IsLocal"] = IsLocal - }; - return table; - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs deleted file mode 100644 index 98f6322..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace NAK.LuaNetworkVariables; - -internal class LuaEventTracker -{ - private class EventMetadata - { - public DateTime LastInvokeTime { get; set; } - public Dictionary LastInvokeTimePerSender { get; } = new(); - } - - private readonly Dictionary _eventMetadata = new(); - - public DateTime GetLastInvokeTime(string eventName) - { - if (!_eventMetadata.TryGetValue(eventName, out var metadata)) - { - metadata = new EventMetadata(); - _eventMetadata[eventName] = metadata; - } - return metadata.LastInvokeTime; - } - - public DateTime GetLastInvokeTimeForSender(string eventName, string senderId) - { - if (!_eventMetadata.TryGetValue(eventName, out var metadata)) - { - metadata = new EventMetadata(); - _eventMetadata[eventName] = metadata; - } - - if (!metadata.LastInvokeTimePerSender.TryGetValue(senderId, out DateTime time)) - { - return DateTime.MinValue; - } - return time; - } - - public void UpdateInvokeTime(string eventName, string senderId) - { - if (!_eventMetadata.TryGetValue(eventName, out EventMetadata metadata)) - { - metadata = new EventMetadata(); - _eventMetadata[eventName] = metadata; - } - - DateTime now = DateTime.Now; - metadata.LastInvokeTime = now; - metadata.LastInvokeTimePerSender[senderId] = now; - } - - public void Clear() - { - _eventMetadata.Clear(); - } - - public void ClearEvent(string eventName) - { - _eventMetadata.Remove(eventName); - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs deleted file mode 100644 index 51e60e8..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs +++ /dev/null @@ -1,156 +0,0 @@ -using ABI_RC.Core.Savior; -using ABI_RC.Core.Util; -using ABI_RC.Systems.ModNetwork; -using ABI.CCK.Components; -using ABI.Scripting.CVRSTL.Common; -using MoonSharp.Interpreter; -using UnityEngine; -using Coroutine = UnityEngine.Coroutine; - -namespace NAK.LuaNetworkVariables; - -public partial class LuaNetVarController : MonoBehaviour -{ - private static readonly HashSet _hashes = new(); - - private const string MODULE_ID = "NAK.LNV:"; - - private int _uniquePathHash; - private string ModNetworkID { get; set; } - private CVRLuaClientBehaviour _luaClientBehaviour; - - private readonly Dictionary _registeredNetworkVars = new(); - private readonly Dictionary _registeredNotifyCallbacks = new(); - private readonly Dictionary _registeredEventCallbacks = new(); - private readonly HashSet _dirtyVariables = new(); - - private bool _requestInitialSync; - private CVRSpawnable _spawnable; - private CVRObjectSync _objectSync; - - private bool _isInitialized; - private Coroutine _syncCoroutine; - - #region Unity Events - - private void Awake() - => _isInitialized = Initialize(); - - private void OnDestroy() - => Cleanup(); - - private void OnEnable() - => StartStopVariableUpdatesCoroutine(true); - - private void OnDisable() - => StartStopVariableUpdatesCoroutine(false); - - #endregion Unity Events - - #region Private Methods - - private bool Initialize() - { - if (!TryGetComponent(out _luaClientBehaviour)) return false; - if (!TryGetUniqueNetworkID(out _uniquePathHash)) return false; - - ModNetworkID = MODULE_ID + _uniquePathHash.ToString("X8"); - - if (ModNetworkID.Length > ModNetworkManager.MaxMessageIdLength) - { - LuaNetworkVariablesMod.Logger.Error($"ModNetworkID ({ModNetworkID}) exceeds max length of {ModNetworkManager.MaxMessageIdLength} characters!"); - return false; - } - - _hashes.Add(_uniquePathHash); - ModNetworkManager.Subscribe(ModNetworkID, OnMessageReceived); - LuaNetworkVariablesMod.Logger.Msg($"Registered LuaNetVarController with ModNetworkID: {ModNetworkID}"); - - switch (_luaClientBehaviour.Context.objContext) - { - case CVRLuaObjectContext.AVATAR: - _requestInitialSync = !_luaClientBehaviour.Context.IsWornByMe; - break; - case CVRLuaObjectContext.PROP: - _spawnable = _luaClientBehaviour.Context.RootComponent as CVRSpawnable; - _requestInitialSync = !_luaClientBehaviour.Context.IsSpawnedByMe; - break; - case CVRLuaObjectContext.WORLD: - _objectSync = GetComponentInParent(); - _requestInitialSync = true; // idk probably works - break; - default: - _requestInitialSync = true; - break; - } - - return true; - } - - // TODO: evaluate if having dedicated globals is better behaviour (i think so) - // private void ConfigureLuaEnvironment() - // { - // _luaClientBehaviour.script.Globals["SendLuaEvent"] = DynValue.NewCallback(SendLuaEventCallback); - // } - - private void Cleanup() - { - _eventTracker.Clear(); - - if (_uniquePathHash == 0 || string.IsNullOrEmpty(ModNetworkID)) - return; - - ModNetworkManager.Unsubscribe(ModNetworkID); - LuaNetworkVariablesMod.Logger.Msg($"Unsubscribed LuaNetVarController with ModNetworkID: {ModNetworkID}"); - _hashes.Remove(_uniquePathHash); - } - - private void StartStopVariableUpdatesCoroutine(bool start) - { - if (_syncCoroutine != null) StopCoroutine(_syncCoroutine); - _syncCoroutine = null; - if (start) _syncCoroutine = StartCoroutine(SendVariableUpdatesCoroutine()); - } - - private System.Collections.IEnumerator SendVariableUpdatesCoroutine() - { - while (isActiveAndEnabled) - { - yield return new WaitForSeconds(0.1f); - if (IsSyncOwner()) SendVariableUpdates(); - if (!_requestInitialSync) continue; - _requestInitialSync = false; - RequestVariableSync(); - } - } - - #endregion Private Methods - - #region Ownership Methods - - public bool IsSyncOwner() - { - if (_objectSync) return _objectSync.SyncedByMe; // idk - if (_spawnable) - { - if (_spawnable.IsSyncedByMe()) return true; // is held / attached locally - CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(x => x.InstanceId == _spawnable.instanceId); - if (propData != null) return propData.syncedBy == MetaPort.Instance.ownerId; // last updated by me - return false; // not held / attached locally and not last updated by me - } - return false; - } - - public string GetSyncOwner() - { - if (_objectSync) return _objectSync.syncedBy; - if (_spawnable) - { - CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(x => x.InstanceId == _spawnable.instanceId); - return propData?.syncedBy ?? string.Empty; - } - return string.Empty; - } - - #endregion Ownership Methods -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs deleted file mode 100644 index a674987..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs +++ /dev/null @@ -1,239 +0,0 @@ -using ABI_RC.Core.Savior; -using ABI_RC.Systems.ModNetwork; -using MoonSharp.Interpreter; - -namespace NAK.LuaNetworkVariables -{ - public partial class LuaNetVarController - { - private enum MessageType : byte - { - LuaVariable = 0, - LuaEvent = 1, - SyncVariables = 2, - RequestSync = 3 - } - - private readonly LuaEventTracker _eventTracker = new(); - - #region Mod Network Events - - private void OnMessageReceived(ModNetworkMessage msg) - { - msg.Read(out byte msgTypeRaw); - if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) return; - - MessageType msgType = (MessageType)msgTypeRaw; - switch (msgType) - { - case MessageType.LuaVariable: - HandleLuaVariableUpdate(msg); - break; - case MessageType.LuaEvent: - HandleLuaEvent(msg); - break; - case MessageType.SyncVariables: - HandleSyncVariables(msg); - break; - case MessageType.RequestSync: - HandleRequestSyncVariables(msg); - break; - } - } - - private void HandleLuaVariableUpdate(ModNetworkMessage msg) - { - msg.Read(out string varName); - DynValue newValue = DeserializeDynValue(msg); - - LuaNetworkVariablesMod.Logger.Msg($"Received LuaVariable update: {varName} = {newValue}"); - - if (_registeredNetworkVars.TryGetValue(varName, out DynValue var)) - { - UpdateNetworkVariable(varName, var, newValue); - } - else - { - LuaNetworkVariablesMod.Logger.Warning($"Received update for unregistered variable {varName}"); - } - } - - private void HandleLuaEvent(ModNetworkMessage msg) - { - string senderId = msg.Sender; - msg.Read(out string eventName); - msg.Read(out int argsCount); - - DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); - LuaEventContext context = LuaEventContext.Create(false, senderId, lastInvokeTime); - - // Update tracking - _eventTracker.UpdateInvokeTime(eventName, senderId); - - // Read event arguments - var args = new DynValue[argsCount + 1]; // +1 for context - args[0] = DynValue.FromObject(_luaClientBehaviour.script, context.ToLuaTable(_luaClientBehaviour.script)); - - for (int i = 0; i < argsCount; i++) - { - args[i + 1] = DeserializeDynValue(msg); - } - - LuaNetworkVariablesMod.Logger.Msg($"Received LuaEvent: {eventName} from {context.SenderName} with {argsCount} args"); - - InvokeLuaEvent(eventName, args); - } - - private void HandleSyncVariables(ModNetworkMessage msg) - { - msg.Read(out int varCount); - for (int i = 0; i < varCount; i++) - { - msg.Read(out string varName); - DynValue newValue = DeserializeDynValue(msg); - - if (_registeredNetworkVars.TryGetValue(varName, out DynValue var)) - { - UpdateNetworkVariable(varName, var, newValue); - } - else - { - LuaNetworkVariablesMod.Logger.Warning($"Received sync for unregistered variable {varName}"); - } - } - } - - private void HandleRequestSyncVariables(ModNetworkMessage msg) - { - if (!IsSyncOwner()) return; - SendVariableSyncToUser(msg.Sender); - } - - #endregion - - #region Event Invocation - - private void InvokeLuaEvent(string eventName, DynValue[] args) - { - if (_registeredEventCallbacks.TryGetValue(eventName, out DynValue callback)) - { - _luaClientBehaviour.script.Call(callback, args); - } - else - { - LuaNetworkVariablesMod.Logger.Warning($"No registered callback for event {eventName}"); - } - } - - #endregion - - #region Sending Methods - - private void SendVariableUpdates() - { - if (_dirtyVariables.Count == 0) return; - - using ModNetworkMessage modMsg = new(ModNetworkID); // can pass target userids as params if needed - modMsg.Write((byte)MessageType.SyncVariables); - modMsg.Write(_dirtyVariables.Count); - modMsg.Send(); - - foreach (var varName in _dirtyVariables) - { - modMsg.Write(varName); - SerializeDynValue(modMsg, _registeredNetworkVars[varName]); - } - - _dirtyVariables.Clear(); - } - - private void SendVariableSyncToUser(string userId) - { - using ModNetworkMessage modMsg = new(ModNetworkID, userId); - modMsg.Write((byte)MessageType.SyncVariables); - modMsg.Write(_registeredNetworkVars.Count); - foreach (var kvp in _registeredNetworkVars) - { - modMsg.Write(kvp.Key); - SerializeDynValue(modMsg, kvp.Value); - } - modMsg.Send(); - - LuaNetworkVariablesMod.Logger.Msg($"Sent variable sync to {userId}"); - } - - private void RequestVariableSync() - { - using ModNetworkMessage modMsg = new(ModNetworkID); - modMsg.Write((byte)MessageType.RequestSync); - modMsg.Send(); - LuaNetworkVariablesMod.Logger.Msg("Requested variable sync"); - } - - // private DynValue SendLuaEventCallback(ScriptExecutionContext context, CallbackArguments args) - // { - // if (args.Count < 1) return DynValue.Nil; - // - // var eventName = args[0].CastToString(); - // var eventArgs = args.GetArray().Skip(1).ToArray(); - // - // SendLuaEvent(eventName, eventArgs); - // - // return DynValue.Nil; - // } - - internal void SendLuaEvent(string eventName, DynValue[] args) - { - string senderId = MetaPort.Instance.ownerId; - DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); - LuaEventContext context = LuaEventContext.Create(true, senderId, lastInvokeTime); - - // Update tracking - _eventTracker.UpdateInvokeTime(eventName, senderId); - - var argsWithContext = new DynValue[args.Length + 1]; - argsWithContext[0] = DynValue.FromObject(_luaClientBehaviour.script, context.ToLuaTable(_luaClientBehaviour.script)); - Array.Copy(args, 0, argsWithContext, 1, args.Length); - - InvokeLuaEvent(eventName, argsWithContext); - - using ModNetworkMessage modMsg = new(ModNetworkID); - modMsg.Write((byte)MessageType.LuaEvent); - modMsg.Write(eventName); - modMsg.Write(args.Length); - - foreach (DynValue arg in args) - SerializeDynValue(modMsg, arg); - - modMsg.Send(); - } - - internal void SendLuaEventToUser(string eventName, string userId, DynValue[] args) - { - string senderId = MetaPort.Instance.ownerId; - DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); - LuaEventContext context = LuaEventContext.Create(true, senderId, lastInvokeTime); - - // Update tracking - _eventTracker.UpdateInvokeTime(eventName, senderId); - - var argsWithContext = new DynValue[args.Length + 1]; - argsWithContext[0] = DynValue.FromObject(_luaClientBehaviour.script, context.ToLuaTable(_luaClientBehaviour.script)); - Array.Copy(args, 0, argsWithContext, 1, args.Length); - - InvokeLuaEvent(eventName, argsWithContext); - - using ModNetworkMessage modMsg = new(ModNetworkID, userId); - modMsg.Write((byte)MessageType.LuaEvent); - modMsg.Write(eventName); - modMsg.Write(args.Length); - - foreach (DynValue arg in args) - SerializeDynValue(modMsg, arg); - - modMsg.Send(); - } - - #endregion - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs deleted file mode 100644 index fde4fdf..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs +++ /dev/null @@ -1,101 +0,0 @@ -using MoonSharp.Interpreter; - -namespace NAK.LuaNetworkVariables; - -public partial class LuaNetVarController -{ - internal void RegisterNetworkVar(string varName) - { - if (_registeredNetworkVars.ContainsKey(varName)) - { - LuaNetworkVariablesMod.Logger.Warning($"Network variable {varName} already registered!"); - return; - } - - _registeredNetworkVars[varName] = DynValue.Nil; - _luaClientBehaviour.script.Globals[varName] = DynValue.Nil; - - RegisterGetterFunction(varName); - RegisterSetterFunction(varName); - - LuaNetworkVariablesMod.Logger.Msg($"Registered network variable {varName}"); - } - - private void RegisterGetterFunction(string varName) - { - _luaClientBehaviour.script.Globals["Get" + varName] = DynValue.NewCallback((context, args) => - { - return _registeredNetworkVars.TryGetValue(varName, out var value) ? value : DynValue.Nil; - }); - } - - private void RegisterSetterFunction(string varName) - { - _luaClientBehaviour.script.Globals["Set" + varName] = DynValue.NewCallback((context, args) => - { - if (args.Count < 1) return DynValue.Nil; - - var newValue = args[0]; - if (!IsSupportedDynValue(newValue)) - { - LuaNetworkVariablesMod.Logger.Error($"Unsupported DynValue type: {newValue.Type} for variable {varName}"); - return DynValue.Nil; - } - - if (_registeredNetworkVars.TryGetValue(varName, out var oldValue)) - { - UpdateNetworkVariable(varName, oldValue, newValue); - } - - return DynValue.Nil; - }); - } - - private void UpdateNetworkVariable(string varName, DynValue oldValue, DynValue newValue) - { - _registeredNetworkVars[varName] = newValue; - _luaClientBehaviour.script.Globals[varName] = newValue; - _dirtyVariables.Add(varName); - - if (_registeredNotifyCallbacks.TryGetValue(varName, out var callback)) - { - _luaClientBehaviour.script.Call(callback, DynValue.NewString(varName), oldValue, newValue); - } - } - - internal void RegisterNotifyCallback(string varName, DynValue callback) - { - if (!ValidateCallback(callback) || !ValidateNetworkVar(varName)) return; - - if (_registeredNotifyCallbacks.ContainsKey(varName)) - LuaNetworkVariablesMod.Logger.Warning($"Overwriting notify callback for {varName}"); - - _registeredNotifyCallbacks[varName] = callback; - LuaNetworkVariablesMod.Logger.Msg($"Registered notify callback for {varName}"); - } - - internal void RegisterEventCallback(string eventName, DynValue callback) - { - if (!ValidateCallback(callback)) return; - - if (_registeredEventCallbacks.ContainsKey(eventName)) - LuaNetworkVariablesMod.Logger.Warning($"Overwriting event callback for {eventName}"); - - _registeredEventCallbacks[eventName] = callback; - LuaNetworkVariablesMod.Logger.Msg($"Registered event callback for {eventName}"); - } - - private bool ValidateCallback(DynValue callback) - { - if (callback?.Function != null) return true; - LuaNetworkVariablesMod.Logger.Error("Passed DynValue must be a function"); - return false; - } - - private bool ValidateNetworkVar(string varName) - { - if (_registeredNetworkVars.ContainsKey(varName)) return true; - LuaNetworkVariablesMod.Logger.Error($"Attempted to register notify callback for non-registered variable {varName}."); - return false; - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs deleted file mode 100644 index ba20565..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs +++ /dev/null @@ -1,62 +0,0 @@ -using ABI_RC.Systems.ModNetwork; -using MoonSharp.Interpreter; - -namespace NAK.LuaNetworkVariables; - -public partial class LuaNetVarController -{ - private static DynValue DeserializeDynValue(ModNetworkMessage msg) - { - msg.Read(out byte dataTypeByte); - DataType dataType = (DataType)dataTypeByte; - - switch (dataType) - { - case DataType.Boolean: - msg.Read(out bool boolValue); - return DynValue.NewBoolean(boolValue); - case DataType.Number: - msg.Read(out double numberValue); - return DynValue.NewNumber(numberValue); - case DataType.String: - msg.Read(out string stringValue); - return DynValue.NewString(stringValue); - case DataType.Nil: - return DynValue.Nil; - default: - LuaNetworkVariablesMod.Logger.Error($"Unsupported data type received: {dataType}"); - return DynValue.Nil; - } - } - - private static void SerializeDynValue(ModNetworkMessage msg, DynValue value) - { - switch (value.Type) - { - case DataType.Boolean: - msg.Write((byte)DataType.Boolean); - msg.Write(value.Boolean); - break; - case DataType.Number: - msg.Write((byte)DataType.Number); - msg.Write(value.Number); - break; - case DataType.String: - msg.Write((byte)DataType.String); - msg.Write(value.String); - break; - case DataType.Nil: - msg.Write((byte)DataType.Nil); - break; - default: - LuaNetworkVariablesMod.Logger.Error($"Unsupported DynValue type: {value.Type}"); - msg.Write((byte)DataType.Nil); - break; - } - } - - private static bool IsSupportedDynValue(DynValue value) - { - return value.Type is DataType.Boolean or DataType.Number or DataType.String or DataType.Nil; - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs deleted file mode 100644 index d52d504..0000000 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs +++ /dev/null @@ -1,57 +0,0 @@ -using ABI_RC.Core.Savior; -using UnityEngine; - -namespace NAK.LuaNetworkVariables; - -public partial class LuaNetVarController -{ - private bool TryGetUniqueNetworkID(out int hash) - { - string path = GetGameObjectPath(transform); - hash = path.GetHashCode(); - - // Check if it already exists (this **should** only matter in worlds) - if (_hashes.Contains(hash)) - { - LuaNetworkVariablesMod.Logger.Warning($"Duplicate RelativeSyncMarker found at path {path}"); - if (!FindAvailableHash(ref hash)) // Super lazy fix idfc - { - LuaNetworkVariablesMod.Logger.Error($"Failed to find available hash for RelativeSyncMarker after 16 tries! {path}"); - return false; - } - } - - _hashes.Add(hash); - - return true; - - static bool FindAvailableHash(ref int hash) - { - for (int i = 0; i < 16; i++) - { - hash += 1; - if (!_hashes.Contains(hash)) return true; - } - return false; // Failed to find a hash in 16 tries - } - } - - private static string GetGameObjectPath(Transform transform) - { - string path = transform.name; - while (transform.parent != null) - { - transform = transform.parent; - - // Only true at root of local player object - if (transform.CompareTag("Player")) - { - path = MetaPort.Instance.ownerId + "/" + path; - break; - } - - path = transform.name + "/" + path; - } - return path; - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/Patches.cs b/.Experimental/LuaNetworkVariables/Patches.cs deleted file mode 100644 index a442013..0000000 --- a/.Experimental/LuaNetworkVariables/Patches.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ABI.Scripting.CVRSTL.Client; -using ABI.Scripting.CVRSTL.Common; -using HarmonyLib; -using MoonSharp.Interpreter; -using NAK.LuaNetworkVariables.Modules; - -namespace NAK.LuaNetworkVariables.Patches; - -internal static class LuaScriptFactory_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(LuaScriptFactory.CVRRequireModule), nameof(LuaScriptFactory.CVRRequireModule.Require))] - private static void Postfix_CVRRequireModule_require( - string moduleFriendlyName, - ref LuaScriptFactory.CVRRequireModule __instance, - ref object __result, - ref Script ____script, - ref CVRLuaContext ____context) - { - if (LuaNetModule.MODULE_ID != moduleFriendlyName) - return; // not our module - - __result = LuaNetModule.RegisterUserData(____script, ____context); - __instance.RegisteredModules[LuaNetModule.MODULE_ID] = __result; // add module to cache - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs b/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs deleted file mode 100644 index 66b2858..0000000 --- a/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.LuaNetworkVariables.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.LuaNetworkVariables))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.LuaNetworkVariables))] - -[assembly: MelonInfo( - typeof(NAK.LuaNetworkVariables.LuaNetworkVariablesMod), - nameof(NAK.LuaNetworkVariables), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LuaNetworkVariables" -)] - -[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.LuaNetworkVariables.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.2"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/README.md b/.Experimental/LuaNetworkVariables/README.md deleted file mode 100644 index 5ad6ee2..0000000 --- a/.Experimental/LuaNetworkVariables/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# LuaNetworkVariables - -Adds a simple module for creating network variables & events *kinda* similar to Garry's Mod. - -Example Usage: -```lua --- Requires UnityEngine and NetworkModule -UnityEngine = require("UnityEngine") -NetworkModule = require("NetworkModule") - --- Unity Events -- - -function Start() - - if NetworkModule == nil then - print("NetworkModule did not load.") - return - end - - -- Registers "AvatarHeight" as a network variable - -- This creates Get and Set functions (GetAvatarHeight() and SetAvatarHeight()) - NetworkModule:RegisterNetworkVar("AvatarHeight") - - -- Registers a callback for when "AvatarHeight" is changed. - NetworkModule:RegisterNotifyCallback("AvatarHeight", function(varName, oldValue, newValue) - print(varName .. " changed from " .. tostring(oldValue) .. " to " .. tostring(newValue)) - end) - - -- Registers "ButtonClickedEvent" as a networked event. This provides context alongside the arguments passed. - NetworkModule:RegisterEventCallback("ButtonClickedEvent", function(context, message) - print("ButtonClickedEvent triggered by " .. tostring(context.SenderName) .. " with message: " .. tostring(message)) - print("Context details:") - print(" SenderId: " .. tostring(context.SenderId)) - print(" SenderName: " .. tostring(context.SenderName)) - print(" LastInvokeTime: " .. tostring(context.LastInvokeTime)) - print(" TimeSinceLastInvoke: " .. tostring(context.TimeSinceLastInvoke)) - print(" IsLocal: " .. tostring(context.IsLocal)) - end) - - -- Secondry example - NetworkModule:RegisterEventCallback("CoolEvent", OnCoolEventOccured) -end - -function Update() - if not NetworkModule:IsSyncOwner() then - return - end - - SetAvatarHeight(PlayerAPI.LocalPlayer:GetViewPointPosition().y) -end - --- Global Functions -- - -function SendClickEvent() - NetworkModule:SendLuaEvent("ButtonClickedEvent", "The button was clicked!") - print("Sent ButtonClickedEvent") -end - -function SendCoolEvent() - NetworkModule:SendLuaEvent("CoolEvent", 1, 2) -end - --- Listener Functions -- - -function OnCoolEventOccured(context, value, value2) - print("CoolEvent triggered by " .. tostring(context.SenderName)) - print("Received values: " .. tostring(value) .. ", " .. tostring(value2)) - print("Context details:") - print(" SenderId: " .. tostring(context.SenderId)) - print(" LastInvokeTime: " .. tostring(context.LastInvokeTime)) - print(" TimeSinceLastInvoke: " .. tostring(context.TimeSinceLastInvoke)) - print(" IsLocal: " .. tostring(context.IsLocal)) -end -``` - ---- - -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. diff --git a/.Experimental/LuaNetworkVariables/format.json b/.Experimental/LuaNetworkVariables/format.json deleted file mode 100644 index 654911a..0000000 --- a/.Experimental/LuaNetworkVariables/format.json +++ /dev/null @@ -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" -} \ No newline at end of file diff --git a/.Experimental/LuaTTS/Main.cs b/.Experimental/LuaTTS/Main.cs deleted file mode 100644 index 6242c04..0000000 --- a/.Experimental/LuaTTS/Main.cs +++ /dev/null @@ -1,54 +0,0 @@ -using ABI_RC.Scripting.ScriptNetwork; -using ABI_RC.Systems.ModNetwork; -using MelonLoader; -using NAK.LuaTTS.Patches; - -namespace NAK.LuaTTS; - -public class LuaTTSMod : MelonMod -{ - public override void OnInitializeMelon() - { - ApplyPatches(typeof(LuaScriptFactoryPatches)); - - ModNetworkMessage.AddConverter(ReadScriptID, WriteScriptID); - ModNetworkMessage.AddConverter(ReadScriptInstanceID, WriteScriptInstanceID); - } - - private static ScriptID ReadScriptID(ModNetworkMessage msg) - { - msg.Read(out byte[] value); - ScriptID scriptID = new(value); - return scriptID; - } - - private static void WriteScriptID(ModNetworkMessage msg, ScriptID scriptID) - { - msg.Write(scriptID.value); - } - - private static ScriptInstanceID ReadScriptInstanceID(ModNetworkMessage msg) - { - msg.Read(out byte[] value); - ScriptInstanceID scriptInstanceID = new(value); - return scriptInstanceID; - } - - private static void WriteScriptInstanceID(ModNetworkMessage msg, ScriptInstanceID scriptInstanceID) - { - msg.Write(scriptInstanceID.value); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } -} \ No newline at end of file diff --git a/.Experimental/LuaTTS/Patches.cs b/.Experimental/LuaTTS/Patches.cs deleted file mode 100644 index c45cec1..0000000 --- a/.Experimental/LuaTTS/Patches.cs +++ /dev/null @@ -1,27 +0,0 @@ -using ABI.Scripting.CVRSTL.Client; -using ABI.Scripting.CVRSTL.Common; -using HarmonyLib; -using MoonSharp.Interpreter; -using NAK.LuaTTS.Modules; - -namespace NAK.LuaTTS.Patches; - -internal static class LuaScriptFactoryPatches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(LuaScriptFactory.CVRRequireModule), nameof(LuaScriptFactory.CVRRequireModule.Require))] - private static void Postfix_CVRRequireModule_require( - string moduleFriendlyName, - ref LuaScriptFactory.CVRRequireModule __instance, - ref object __result, - ref Script ____script, - ref CVRLuaContext ____context) - { - const string TTSModuleID = "TextToSpeech"; - if (TTSModuleID != moduleFriendlyName) - return; // not our module - - __result = TTSLuaModule.RegisterUserData(____script, ____context); - __instance.RegisteredModules[TTSModuleID] = __result; // add module to cache - } -} \ No newline at end of file diff --git a/.Experimental/LuaTTS/format.json b/.Experimental/LuaTTS/format.json deleted file mode 100644 index 75db4ec..0000000 --- a/.Experimental/LuaTTS/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": -1, - "name": "LuaTTS", - "modversion": "1.0.2", - "gameversion": "2024r176", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak.", - "searchtags": [ - "lua", - "tts", - "texttospeech", - "tomato" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r28/LuaTTS.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LuaTTS/", - "changelog": "- Initial release.", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftEventReceiver.cs b/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftEventReceiver.cs deleted file mode 100644 index 11fe6fc..0000000 --- a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftEventReceiver.cs +++ /dev/null @@ -1,87 +0,0 @@ -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; - -public class OriginShiftEventReceiver : MonoBehaviour -{ - #region Serialized Fields - - [SerializeField] private UnityEvent _onOriginShifted = new(); - [SerializeField] private bool _filterChunkBoundary; - [SerializeField] private Vector3 _chunkBoundaryMin = Vector3.zero; - [SerializeField] private Vector3 _chunkBoundaryMax = Vector3.one; - - #endregion Serialized Fields - -#if !UNITY_EDITOR - - #region Private Fields - - private bool _isInitialized; - - #endregion Private Fields - - #region Unity Events - - private void OnEnable() - { - if (!_isInitialized) - { - //SharedFilter.SanitizeUnityEvents("OriginShiftEventReceiver", _onOriginShifted); - - UnityEventsHelper.SanitizeUnityEvents("OriginShiftEventReceiver", - UnityEventsHelper.EventSource.Unknown, - this, - CVRWorld.Instance, - _onOriginShifted); - - _isInitialized = true; - } - - OriginShiftManager.OnOriginShifted += HandleOriginShifted; - } - - private void OnDisable() - { - OriginShiftManager.OnOriginShifted -= HandleOriginShifted; - } - - #endregion Unity Events - - #region Origin Shift Events - - private void HandleOriginShifted(Vector3 shift) - { - if (_filterChunkBoundary && !IsWithinChunkBoundary(shift)) - return; - - // wrap user-defined event because the user can't be trusted - try - { - _onOriginShifted.Invoke(); - } - catch (Exception e) - { - OriginShiftMod.Logger.Error("OriginShiftEventReceiver: Exception invoking OnOriginShifted event: " + e, this); - } - } - - private bool IsWithinChunkBoundary(Vector3 shift) - { - return shift.x >= _chunkBoundaryMin.x && shift.x <= _chunkBoundaryMax.x && - shift.y >= _chunkBoundaryMin.y && shift.y <= _chunkBoundaryMax.y && - shift.z >= _chunkBoundaryMin.z && shift.z <= _chunkBoundaryMax.z; - } - - #endregion Origin Shift Events - -#endif -} \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftTransformReceiver.cs b/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftTransformReceiver.cs deleted file mode 100644 index 1e98ce7..0000000 --- a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftTransformReceiver.cs +++ /dev/null @@ -1,33 +0,0 @@ -using UnityEngine; - -namespace NAK.OriginShift.Components; - -public class OriginShiftTransformReceiver : MonoBehaviour -{ -#if !UNITY_EDITOR - - #region Unity Events - - private void OnEnable() - { - OriginShiftManager.OnOriginShifted += OnOriginShifted; - } - - private void OnDisable() - { - OriginShiftManager.OnOriginShifted -= OnOriginShifted; - } - - #endregion Unity Events - - #region Origin Shift Events - - private void OnOriginShifted(Vector3 shift) - { - transform.position += shift; - } - - #endregion Origin Shift Events - -#endif -} \ No newline at end of file diff --git a/.github/scripts/update-modlist.js b/.github/scripts/update-modlist.js deleted file mode 100644 index 8ed69bf..0000000 --- a/.github/scripts/update-modlist.js +++ /dev/null @@ -1,185 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const https = require('https'); - -// Configuration -const ROOT = '.'; -const EXPERIMENTAL = '.Experimental'; -const README_PATH = 'README.md'; -const MARKER_START = ''; -const MARKER_END = ''; -const REPO_OWNER = process.env.REPO_OWNER || 'NotAKidoS'; -const REPO_NAME = process.env.REPO_NAME || 'NAK_CVR_Mods'; - -// Function to get latest release info from GitHub API -async function getLatestRelease() { - return new Promise((resolve, reject) => { - const options = { - hostname: 'api.github.com', - path: `/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`, - method: 'GET', - headers: { - 'User-Agent': 'Node.js GitHub Release Checker', - 'Accept': 'application/vnd.github.v3+json' - } - }; - - const req = https.request(options, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - if (res.statusCode === 200) { - try { - resolve(JSON.parse(data)); - } catch (e) { - reject(new Error(`Failed to parse GitHub API response: ${e.message}`)); - } - } else { - reject(new Error(`GitHub API request failed with status code: ${res.statusCode}`)); - } - }); - }); - - req.on('error', (e) => { - reject(new Error(`GitHub API request error: ${e.message}`)); - }); - - req.end(); - }); -} - -function getModFolders(baseDir) { - const entries = fs.readdirSync(baseDir, { withFileTypes: true }); - return entries - .filter(entry => entry.isDirectory()) - .map(entry => path.join(baseDir, entry.name)) - .filter(dir => fs.existsSync(path.join(dir, 'README.md'))); -} - -function extractDescription(readmePath) { - try { - const content = fs.readFileSync(readmePath, 'utf8'); - const lines = content.split('\n'); - - // Find the first header (# something) - let headerIndex = -1; - for (let i = 0; i < lines.length; i++) { - if (lines[i].trim().startsWith('# ')) { - headerIndex = i; - break; - } - } - - // If we found a header, look for the first non-empty line after it - if (headerIndex !== -1) { - for (let i = headerIndex + 1; i < lines.length; i++) { - const line = lines[i].trim(); - if (line && !line.startsWith('#')) { - return line; - } - } - } - - return 'No description available'; - } catch (error) { - console.error(`Error reading ${readmePath}:`, error); - return 'No description available'; - } -} - -async function formatTable(mods, baseDir) { - if (mods.length === 0) return ''; - - try { - // Get the latest release info from GitHub - const latestRelease = await getLatestRelease(); - const releaseAssets = latestRelease.assets || []; - - // Create a map of available files in the release - const availableFiles = {}; - releaseAssets.forEach(asset => { - availableFiles[asset.name] = asset.browser_download_url; - }); - - let rows = mods.map(modPath => { - const modName = path.basename(modPath); - const readmeLink = path.join(modPath, 'README.md'); - const readmePath = path.join(modPath, 'README.md'); - const description = extractDescription(readmePath); - - // Check if the DLL exists in the latest release - const dllFilename = `${modName}.dll`; - let downloadSection; - - if (availableFiles[dllFilename]) { - downloadSection = `[Download](${availableFiles[dllFilename]})`; - } else { - downloadSection = 'No Download'; - } - - return `| [${modName}](${readmeLink}) | ${description} | ${downloadSection} |`; - }); - - return [ - `### ${baseDir === EXPERIMENTAL ? 'Experimental Mods' : 'Released Mods'}`, - '', - '| Name | Description | Download |', - '|------|-------------|----------|', - ...rows, - '' - ].join('\n'); - } catch (error) { - console.error('Error fetching release information:', error); - - // Fallback to showing "No Download" for all mods if we can't fetch release info - let rows = mods.map(modPath => { - const modName = path.basename(modPath); - const readmeLink = path.join(modPath, 'README.md'); - const readmePath = path.join(modPath, 'README.md'); - const description = extractDescription(readmePath); - - return `| [${modName}](${readmeLink}) | ${description} | No Download |`; - }); - - return [ - `### ${baseDir === EXPERIMENTAL ? 'Experimental Mods' : 'Released Mods'}`, - '', - '| Name | Description | Download |', - '|------|-------------|----------|', - ...rows, - '' - ].join('\n'); - } -} - -function updateReadme(modListSection) { - const readme = fs.readFileSync(README_PATH, 'utf8'); - const before = readme.split(MARKER_START)[0]; - const after = readme.split(MARKER_END)[1]; - const newReadme = `${before}${MARKER_START}\n\n${modListSection}\n${MARKER_END}${after}`; - fs.writeFileSync(README_PATH, newReadme); -} - -async function main() { - try { - const mainMods = getModFolders(ROOT).filter(dir => !dir.startsWith(EXPERIMENTAL)); - const experimentalMods = getModFolders(EXPERIMENTAL); - - const mainModsTable = await formatTable(mainMods, ROOT); - const experimentalModsTable = await formatTable(experimentalMods, EXPERIMENTAL); - - const tableContent = [mainModsTable, experimentalModsTable].join('\n'); - updateReadme(tableContent); - - console.log('README.md updated successfully!'); - } catch (error) { - console.error('Error updating README:', error); - process.exit(1); - } -} - -main(); \ No newline at end of file diff --git a/.github/workflows/update-modlist.yml b/.github/workflows/update-modlist.yml deleted file mode 100644 index 69c4426..0000000 --- a/.github/workflows/update-modlist.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Update Mod List - -on: - push: - paths: - - '.github/scripts/update-modlist.js' - - '.github/workflows/update-modlist.yml' - - 'README.md' - - '**/README.md' - workflow_dispatch: - -jobs: - update-modlist: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: '20' - - - name: Run mod list updater - run: node .github/scripts/update-modlist.js - - - name: Commit and push changes - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git remote set-url origin https://x-access-token:${{ secrets.GH_TOKEN }}@github.com/${{ github.repository }} - git add README.md - git commit -m "[NAK_CVR_Mods] Update mod list in README" || echo "No changes to commit" - git push \ No newline at end of file diff --git a/.gitignore b/.gitignore index e3092f6..7a3e958 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore -# NAK -.Blackbox/ - # Nstrip & ManagedLibs stuff NStrip.exe .ManagedLibs/*.dll diff --git a/.Deprecated/AASDefaultProfileFix/AASDefaultProfileFix.csproj b/AASDefaultProfileFix/AASDefaultProfileFix.csproj similarity index 100% rename from .Deprecated/AASDefaultProfileFix/AASDefaultProfileFix.csproj rename to AASDefaultProfileFix/AASDefaultProfileFix.csproj diff --git a/.Deprecated/AASDefaultProfileFix/Main.cs b/AASDefaultProfileFix/Main.cs similarity index 100% rename from .Deprecated/AASDefaultProfileFix/Main.cs rename to AASDefaultProfileFix/Main.cs diff --git a/.Deprecated/AASDefaultProfileFix/Properties/AssemblyInfo.cs b/AASDefaultProfileFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/AASDefaultProfileFix/Properties/AssemblyInfo.cs rename to AASDefaultProfileFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/AASDefaultProfileFix/README.md b/AASDefaultProfileFix/README.md similarity index 100% rename from .Deprecated/AASDefaultProfileFix/README.md rename to AASDefaultProfileFix/README.md diff --git a/.Deprecated/AASDefaultProfileFix/format.json b/AASDefaultProfileFix/format.json similarity index 100% rename from .Deprecated/AASDefaultProfileFix/format.json rename to AASDefaultProfileFix/format.json diff --git a/ASTExtension/Extensions/PlayerSetupExtensions.cs b/ASTExtension/Extensions/PlayerSetupExtensions.cs index 661a94a..241cd1e 100644 --- a/ASTExtension/Extensions/PlayerSetupExtensions.cs +++ b/ASTExtension/Extensions/PlayerSetupExtensions.cs @@ -1,5 +1,4 @@ -using ABI_RC.Core; -using ABI_RC.Core.Player; +using ABI_RC.Core.Player; using UnityEngine; namespace NAK.ASTExtension.Extensions; @@ -9,16 +8,16 @@ public static class PlayerSetupExtensions // immediate measurement of the player's avatar height public static float GetCurrentAvatarHeight(this PlayerSetup playerSetup) { - if (!playerSetup.IsAvatarLoaded) + if (playerSetup._avatar == null) { ASTExtensionMod.Logger.Error("GetCurrentAvatarHeight: Avatar is null"); return 0f; } - Vector3 localScale = playerSetup.AvatarTransform.localScale; + Vector3 localScale = playerSetup._avatar.transform.localScale; Vector3 initialScale = playerSetup.initialScale; float initialHeight = playerSetup._initialAvatarHeight; - Vector3 scaleDifference = CVRTools.DivideVectors(localScale - initialScale, initialScale); + Vector3 scaleDifference = PlayerSetup.DivideVectors(localScale - initialScale, initialScale); return initialHeight + initialHeight * scaleDifference.y; } } \ No newline at end of file diff --git a/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs b/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs index e075e88..8c57cb7 100644 --- a/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs +++ b/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs @@ -1,98 +1,56 @@ using ABI_RC.Core.Player; -using ABI_RC.Core.Util.AnimatorManager; using BTKUILib; using BTKUILib.UIObjects; using BTKUILib.UIObjects.Components; -using UnityEngine; -namespace NAK.ASTExtension.Integrations; - -public static partial class BtkUiAddon +namespace NAK.ASTExtension.Integrations { - public static void Initialize() + public static partial class BtkUiAddon { + public static void Initialize() + { Prepare_Icons(); Setup_PlayerSelectPage(); } - private static void Prepare_Icons() - { + private static void Prepare_Icons() + { QuickMenuAPI.PrepareIcon(ASTExtensionMod.ModName, "ASM_Icon_AvatarHeightCopy", GetIconStream("ASM_Icon_AvatarHeightCopy.png")); } - #region Player Select Page + #region Player Select Page - private static string _selectedPlayer; + private static string _selectedPlayer; - private static void Setup_PlayerSelectPage() - { + private static void Setup_PlayerSelectPage() + { QuickMenuAPI.OnPlayerSelected += OnPlayerSelected; Category category = QuickMenuAPI.PlayerSelectPage.AddCategory(ASTExtensionMod.ModName, ASTExtensionMod.ModName); Button button = category.AddButton("Copy Height", "ASM_Icon_AvatarHeightCopy", "Copy selected players Eye Height."); button.OnPress += OnCopyPlayerHeight; - - Button button2 = category.AddButton("Copy AAS", string.Empty, "Copy selected players AAS."); - button2.OnPress += OnCopyPlayerAAS; } - private static void OnPlayerSelected(string _, string id) - { + private static void OnPlayerSelected(string _, string id) + { _selectedPlayer = id; } - private static void OnCopyPlayerHeight() - { - if (string.IsNullOrEmpty(_selectedPlayer)) - return; - - if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(_selectedPlayer, out PuppetMaster player)) - return; - - if (!player.IsAvatarLoaded) - return; - - float height = player.netIkController.GetRemoteHeight(); - ASTExtensionMod.Instance.SetAvatarHeight(height); - } - - private static void OnCopyPlayerAAS() - { - if (string.IsNullOrEmpty(_selectedPlayer)) - return; - - if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(_selectedPlayer, out PuppetMaster player)) - return; - - AvatarAnimatorManager localAnimator = PlayerSetup.Instance.AnimatorManager; - AvatarAnimatorManager remoteAnimator = player.AnimatorManager; - if (!localAnimator.IsInitialized - || !remoteAnimator.IsInitialized) - return; - - // Copy AAS - foreach ((var parameterName, CVRAnimatorManager.ParamDef paramDef) in remoteAnimator.Parameters) + private static void OnCopyPlayerHeight() { - switch (paramDef.type) - { - case AnimatorControllerParameterType.Trigger: - case AnimatorControllerParameterType.Bool: - remoteAnimator.GetParameter(parameterName, out bool value); - localAnimator.SetParameter(parameterName, value); - break; - case AnimatorControllerParameterType.Float: - remoteAnimator.GetParameter(parameterName, out float value2); - localAnimator.SetParameter(parameterName, value2); - break; - case AnimatorControllerParameterType.Int: - remoteAnimator.GetParameter(parameterName, out int value3); - localAnimator.SetParameter(parameterName, value3); - break; - } + if (string.IsNullOrEmpty(_selectedPlayer)) + return; + + if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(_selectedPlayer, out PuppetMaster player)) + return; + + if (player._avatar == null) + return; + + float height = player.netIkController.GetRemoteHeight(); + ASTExtensionMod.Instance.SetAvatarHeight(height); } - + #endregion Player Select Page } - - #endregion Player Select Page } \ No newline at end of file diff --git a/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs b/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs index 7b3f569..3dd1c30 100644 --- a/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs +++ b/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs @@ -1,17 +1,18 @@ using System.Reflection; -namespace NAK.ASTExtension.Integrations; - -public static partial class BtkUiAddon +namespace NAK.ASTExtension.Integrations { - #region Icon Utils - - private static Stream GetIconStream(string iconName) + public static partial class BtkUiAddon { + #region Icon Utils + + private static Stream GetIconStream(string iconName) + { Assembly assembly = Assembly.GetExecutingAssembly(); string assemblyName = assembly.GetName().Name; return assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}"); } - #endregion Icon Utils + #endregion Icon Utils + } } \ No newline at end of file diff --git a/ASTExtension/Main.cs b/ASTExtension/Main.cs index 4ddf871..f7b7115 100644 --- a/ASTExtension/Main.cs +++ b/ASTExtension/Main.cs @@ -81,7 +81,7 @@ public class ASTExtensionMod : MelonMod ); HarmonyInstance.Patch( - typeof(PlayerBase).GetMethod(nameof(PlayerBase.ClearAvatar), + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar), BindingFlags.Public | BindingFlags.Instance), prefix: new HarmonyMethod(typeof(ASTExtensionMod).GetMethod(nameof(OnClearAvatar), BindingFlags.NonPublic | BindingFlags.Static)) @@ -112,11 +112,8 @@ public class ASTExtensionMod : MelonMod Instance.OnLocalAvatarLoad(); } - private static void OnClearAvatar(ref PlayerBase __instance) - { - if (!__instance.IsLocalPlayer) return; - Instance.OnLocalAvatarClear(__instance.AvatarDescriptor); - } + private static void OnClearAvatar(ref CVRAvatar ____avatarDescriptor) + => Instance.OnLocalAvatarClear(____avatarDescriptor); #endregion Harmony Patches @@ -124,8 +121,7 @@ public class ASTExtensionMod : MelonMod private void OnLocalAvatarLoad() { - _currentAvatarSupported = FindSupportedParameter(out string parameterName); - if (!_currentAvatarSupported) + if (!FindSupportedParameter(out string parameterName)) return; if (!AttemptCalibrateParameter(parameterName, out float minHeight, out float maxHeight, out float modifier)) @@ -230,7 +226,7 @@ public class ASTExtensionMod : MelonMod { parameterName = null; - AvatarAnimatorManager animatorManager = PlayerSetup.Instance.AnimatorManager; + AvatarAnimatorManager animatorManager = PlayerSetup.Instance.animatorManager; if (!animatorManager.IsInitialized) { Logger.Error("AnimatorManager is not initialized!"); @@ -257,7 +253,7 @@ public class ASTExtensionMod : MelonMod maxHeight = 0f; modifier = 1f; - AvatarAnimatorManager animatorManager = PlayerSetup.Instance.AnimatorManager; + AvatarAnimatorManager animatorManager = PlayerSetup.Instance.animatorManager; if (!animatorManager.IsInitialized) { Logger.Error("AnimatorManager is not initialized!"); @@ -322,7 +318,7 @@ public class ASTExtensionMod : MelonMod if (!_currentAvatarSupported) return; - AvatarAnimatorManager animatorManager = PlayerSetup.Instance.AnimatorManager; + AvatarAnimatorManager animatorManager = PlayerSetup.Instance.animatorManager; if (!animatorManager.IsInitialized) { Logger.Error("AnimatorManager is not initialized!"); diff --git a/ASTExtension/Properties/AssemblyInfo.cs b/ASTExtension/Properties/AssemblyInfo.cs index 2e4d7b4..faa1dc6 100644 --- a/ASTExtension/Properties/AssemblyInfo.cs +++ b/ASTExtension/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ASTExtension" )] -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[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 @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.ASTExtension.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.4"; + public const string Version = "1.0.1"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/ASTExtension/format.json b/ASTExtension/format.json index abcdbfc..55315d6 100644 --- a/ASTExtension/format.json +++ b/ASTExtension/format.json @@ -1,9 +1,9 @@ { "_id": 223, "name": "ASTExtension", - "modversion": "1.0.4", - "gameversion": "2025r180", - "loaderversion": "0.7.2", + "modversion": "1.0.1", + "gameversion": "2024r175", + "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", "description": "Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool):\n- VR Gesture to scale\n- Persistent height\n- Copy height from others\n\nBest used with Avatar Scale Tool, but will attempt to work with found scaling setups.\nRequires already having Avatar Scaling on the avatar. This is **not** Universal Scaling.", @@ -17,8 +17,8 @@ "requirements": [ "BTKUILib" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ASTExtension.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r35/ASTExtension.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ASTExtension/", - "changelog": "- Fixes for 2025r180", + "changelog": "- Fixed an issue where the parameter calibration process would sometimes result in the mouth pointer being misplaced when generated.\n- Fixed IsGripping check for Knuckles controllers not requiring *both* hands as intended.", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/AvatarQueueSystemTweaks/Properties/AssemblyInfo.cs b/AvatarQueueSystemTweaks/Properties/AssemblyInfo.cs index 821459b..1273c2c 100644 --- a/AvatarQueueSystemTweaks/Properties/AssemblyInfo.cs +++ b/AvatarQueueSystemTweaks/Properties/AssemblyInfo.cs @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.AvatarQueueSystemTweaks.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/AvatarQueueSystemTweaks/format.json b/AvatarQueueSystemTweaks/format.json index 84dadfc..bb6a269 100644 --- a/AvatarQueueSystemTweaks/format.json +++ b/AvatarQueueSystemTweaks/format.json @@ -1,8 +1,8 @@ { - "_id": 226, + "_id": -1, "name": "AvatarQueueSystemTweaks", - "modversion": "1.0.1", - "gameversion": "2025r179", + "modversion": "1.0.0", + "gameversion": "2024r175", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", @@ -17,8 +17,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/AvatarQueueSystemTweaks.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r36/AvatarQueueSystemTweaks.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/AvatarQueueSystemTweaks/", - "changelog": "- Recompiled for 2025r179", + "changelog": "- Initial Release", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/AvatarScaleMod.csproj b/AvatarScale/AvatarScaleMod.csproj similarity index 100% rename from .Deprecated/AvatarScaleMod/AvatarScaleMod.csproj rename to AvatarScale/AvatarScaleMod.csproj diff --git a/.Deprecated/AvatarScaleMod/AvatarScaling/AvatarScaleManager.cs b/AvatarScale/AvatarScaling/AvatarScaleManager.cs similarity index 99% rename from .Deprecated/AvatarScaleMod/AvatarScaling/AvatarScaleManager.cs rename to AvatarScale/AvatarScaling/AvatarScaleManager.cs index f3c1da4..38d6a9a 100644 --- a/.Deprecated/AvatarScaleMod/AvatarScaling/AvatarScaleManager.cs +++ b/AvatarScale/AvatarScaling/AvatarScaleManager.cs @@ -211,7 +211,7 @@ public class AvatarScaleManager : MonoBehaviour public float GetHeight() { if (_localAvatarScaler == null) - return PlayerAvatarPoint.DefaultAvatarHeight; + return PlayerAvatarPoint.defaultAvatarHeight; if (!_localAvatarScaler.IsForcingHeight()) return PlayerSetup.Instance.GetAvatarHeight(); @@ -222,7 +222,7 @@ public class AvatarScaleManager : MonoBehaviour public float GetAnimationClipHeight() { if (_localAvatarScaler == null) - return PlayerAvatarPoint.DefaultAvatarHeight; + return PlayerAvatarPoint.defaultAvatarHeight; if (!_localAvatarScaler.IsForcingHeight()) return PlayerSetup.Instance.GetAvatarHeight(); diff --git a/.Deprecated/AvatarScaleMod/AvatarScaling/Components/BaseScaler.cs b/AvatarScale/AvatarScaling/Components/BaseScaler.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/AvatarScaling/Components/BaseScaler.cs rename to AvatarScale/AvatarScaling/Components/BaseScaler.cs diff --git a/.Deprecated/AvatarScaleMod/AvatarScaling/Components/LocalScaler.cs b/AvatarScale/AvatarScaling/Components/LocalScaler.cs similarity index 94% rename from .Deprecated/AvatarScaleMod/AvatarScaling/Components/LocalScaler.cs rename to AvatarScale/AvatarScaling/Components/LocalScaler.cs index 7a076d8..e6d79f4 100644 --- a/.Deprecated/AvatarScaleMod/AvatarScaling/Components/LocalScaler.cs +++ b/AvatarScale/AvatarScaling/Components/LocalScaler.cs @@ -1,5 +1,4 @@ -using ABI_RC.Core; -using ABI_RC.Core.Player; +using ABI_RC.Core.Player; using ABI_RC.Core.UI; using NAK.AvatarScaleMod.AvatarScaling; using UnityEngine; @@ -73,7 +72,7 @@ public class LocalScaler : BaseScaler } // animation scale changed, record it! - Vector3 scaleDifference = CVRTools.DivideVectors(localScale - _initialScale, _initialScale); + Vector3 scaleDifference = PlayerSetup.DivideVectors(localScale - _initialScale, _initialScale); _animatedScaleFactor = scaleDifference.y; _animatedHeight = (_initialHeight * _animatedScaleFactor) + _initialHeight; _animatedScale = localScale; diff --git a/.Deprecated/AvatarScaleMod/AvatarScaling/Components/NetworkScaler.cs b/AvatarScale/AvatarScaling/Components/NetworkScaler.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/AvatarScaling/Components/NetworkScaler.cs rename to AvatarScale/AvatarScaling/Components/NetworkScaler.cs diff --git a/.Deprecated/AvatarScaleMod/AvatarScaling/Events/AvatarScaleEvents.cs b/AvatarScale/AvatarScaling/Events/AvatarScaleEvents.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/AvatarScaling/Events/AvatarScaleEvents.cs rename to AvatarScale/AvatarScaling/Events/AvatarScaleEvents.cs diff --git a/.Deprecated/AvatarScaleMod/AvatarScaling/ScaledComponents.cs b/AvatarScale/AvatarScaling/ScaledComponents.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/AvatarScaling/ScaledComponents.cs rename to AvatarScale/AvatarScaling/ScaledComponents.cs diff --git a/.Deprecated/AvatarScaleMod/HarmonyPatches.cs b/AvatarScale/HarmonyPatches.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/HarmonyPatches.cs rename to AvatarScale/HarmonyPatches.cs diff --git a/.Deprecated/AvatarScaleMod/Input/DebugKeybinds.cs b/AvatarScale/Input/DebugKeybinds.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/Input/DebugKeybinds.cs rename to AvatarScale/Input/DebugKeybinds.cs diff --git a/.Deprecated/AvatarScaleMod/Input/ScaleReconizer.cs b/AvatarScale/Input/ScaleReconizer.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/Input/ScaleReconizer.cs rename to AvatarScale/Input/ScaleReconizer.cs diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon.cs similarity index 71% rename from .Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon.cs rename to AvatarScale/Integrations/BTKUI/BtkUiAddon.cs index acf9fab..09a5ff4 100644 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon.cs +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon.cs @@ -3,32 +3,32 @@ using BTKUILib; using BTKUILib.UIObjects; using NAK.AvatarScaleMod.AvatarScaling; -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.AvatarScaleMod.Integrations { - private static Page _asmRootPage; - private static string _rootPageElementID; - - public static void Initialize() + public static partial class BtkUiAddon { + private static Page _asmRootPage; + private static string _rootPageElementID; + + public static void Initialize() + { Prepare_Icons(); Setup_AvatarScaleModTab(); Setup_PlayerSelectPage(); } - #region Initialization + #region Initialization - private static void Prepare_Icons() - { + private static void Prepare_Icons() + { QuickMenuAPI.PrepareIcon(ModSettings.ModName, "ASM_Icon_AvatarHeightConfig", GetIconStream("ASM_Icon_AvatarHeightConfig.png")); QuickMenuAPI.PrepareIcon(ModSettings.ModName, "ASM_Icon_AvatarHeightCopy", GetIconStream("ASM_Icon_AvatarHeightCopy.png")); } - private static void Setup_AvatarScaleModTab() - { + private static void Setup_AvatarScaleModTab() + { _asmRootPage = new Page(ModSettings.ModName, ModSettings.ASM_SettingsCategory, true, "ASM_Icon_AvatarHeightConfig") { MenuTitle = ModSettings.ASM_SettingsCategory, @@ -54,18 +54,18 @@ public static partial class BtkUiAddon Setup_DebugOptionsCategory(_asmRootPage); } - #endregion + #endregion - #region Player Count Display + #region Player Count Display - private static void OnWorldLeave() - => UpdatePlayerCountDisplay(); + private static void OnWorldLeave() + => UpdatePlayerCountDisplay(); - private static void OnUserJoinLeave(CVRPlayerEntity _) - => UpdatePlayerCountDisplay(); + private static void OnUserJoinLeave(CVRPlayerEntity _) + => UpdatePlayerCountDisplay(); - private static void UpdatePlayerCountDisplay() - { + private static void UpdatePlayerCountDisplay() + { if (_asmRootPage == null) return; @@ -74,14 +74,14 @@ public static partial class BtkUiAddon _asmRootPage.MenuSubtitle = $"Everything Avatar Scaling! :: ({modUserCount}/{playerCount} players using ASM)"; } - #endregion + #endregion - #region Double-Click Reset Height + #region Double-Click Reset Height - private static DateTime lastTime = DateTime.Now; + private static DateTime lastTime = DateTime.Now; - private static void OnTabChange(string newTab, string previousTab) - { + private static void OnTabChange(string newTab, string previousTab) + { if (newTab == _rootPageElementID) { UpdatePlayerCountDisplay(); @@ -95,5 +95,6 @@ public static partial class BtkUiAddon lastTime = DateTime.Now; } - #endregion + #endregion + } } \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleMod.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleMod.cs similarity index 76% rename from .Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleMod.cs rename to AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleMod.cs index 9ddc1c2..875668c 100644 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleMod.cs +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleMod.cs @@ -1,11 +1,11 @@ using BTKUILib.UIObjects; -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.AvatarScaleMod.Integrations { - private static void Setup_AvatarScaleModCategory(Page page) + public static partial class BtkUiAddon { + private static void Setup_AvatarScaleModCategory(Page page) + { Category avScaleModCategory = AddMelonCategory(ref page, ModSettings.Hidden_Foldout_ASM_SettingsCategory); AddMelonToggle(ref avScaleModCategory, ModSettings.EntryScaleGestureEnabled); @@ -13,4 +13,5 @@ public static partial class BtkUiAddon AddMelonToggle(ref avScaleModCategory, ModSettings.EntryPersistentHeight); AddMelonToggle(ref avScaleModCategory, ModSettings.EntryPersistThroughRestart); } + } } \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleTool.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleTool.cs similarity index 83% rename from .Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleTool.cs rename to AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleTool.cs index d3820df..2eb6c57 100644 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleTool.cs +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_AvatarScaleTool.cs @@ -1,12 +1,12 @@ using BTKUILib.UIObjects; using BTKUILib.UIObjects.Components; -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.AvatarScaleMod.Integrations { - private static void Setup_AvatarScaleToolCategory(Page page) + public static partial class BtkUiAddon { + private static void Setup_AvatarScaleToolCategory(Page page) + { Category avScaleToolCategory = AddMelonCategory(ref page, ModSettings.Hidden_Foldout_AST_SettingsCategory); AddMelonStringInput(ref avScaleToolCategory, ModSettings.EntryASTScaleParameter, "icon"); @@ -19,4 +19,5 @@ public static partial class BtkUiAddon ModSettings.EntryASTMaxHeight.ResetToDefault(); }; } + } } \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_DebugOptions.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_DebugOptions.cs similarity index 71% rename from .Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_DebugOptions.cs rename to AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_DebugOptions.cs index 088aafd..b05dd94 100644 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_DebugOptions.cs +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_DebugOptions.cs @@ -1,15 +1,16 @@ using BTKUILib.UIObjects; -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.AvatarScaleMod.Integrations { - private static void Setup_DebugOptionsCategory(Page page) + public static partial class BtkUiAddon { + private static void Setup_DebugOptionsCategory(Page page) + { Category debugCategory = AddMelonCategory(ref page, ModSettings.Hidden_Foldout_DEBUG_SettingsCategory); AddMelonToggle(ref debugCategory, ModSettings.Debug_NetworkInbound); AddMelonToggle(ref debugCategory, ModSettings.Debug_NetworkOutbound); AddMelonToggle(ref debugCategory, ModSettings.Debug_ComponentSearchTime); } + } } \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_UniversalScalingSettings.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_UniversalScalingSettings.cs similarity index 83% rename from .Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_UniversalScalingSettings.cs rename to AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_UniversalScalingSettings.cs index 1437582..216fa48 100644 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_CAT_UniversalScalingSettings.cs +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon_CAT_UniversalScalingSettings.cs @@ -4,14 +4,14 @@ using BTKUILib.UIObjects.Components; using NAK.AvatarScaleMod.AvatarScaling; using System.Collections.Generic; // Added for list support -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.AvatarScaleMod.Integrations { - private static readonly List USM_QmUiElements = new(); - - private static void Setup_UniversalScalingSettings(Page page) + public static partial class BtkUiAddon { + private static readonly List USM_QmUiElements = new(); + + private static void Setup_UniversalScalingSettings(Page page) + { Category uniScalingCategory = AddMelonCategory(ref page, ModSettings.Hidden_Foldout_USM_SettingsCategory); SliderFloat scaleSlider = AddMelonSlider(ref uniScalingCategory, ModSettings.EntryHiddenAvatarHeight, AvatarScaleManager.DefaultMinHeight, AvatarScaleManager.DefaultMaxHeight); @@ -52,23 +52,24 @@ public static partial class BtkUiAddon ModSettings.EntryUseUniversalScaling.OnEntryValueChanged.Subscribe((_, newValue) => OnUniversalScalingChanged(newValue)); } - private static void OnUniversalScalingChanged(bool value) - { + private static void OnUniversalScalingChanged(bool value) + { foreach (QMUIElement uiElement in USM_QmUiElements) uiElement.Disabled = !value; } - #region Slider Events + #region Slider Events - private static void OnAvatarHeightSliderChanged(float height) - { + private static void OnAvatarHeightSliderChanged(float height) + { AvatarScaleManager.Instance.SetTargetHeight(height); } - private static void OnAvatarHeightSliderReset() - { + private static void OnAvatarHeightSliderReset() + { AvatarScaleManager.Instance.Setting_UniversalScaling = false; } - #endregion + #endregion + } } \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_PSP_AvatarScaleMod.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon_PSP_AvatarScaleMod.cs similarity index 76% rename from .Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_PSP_AvatarScaleMod.cs rename to AvatarScale/Integrations/BTKUI/BtkUiAddon_PSP_AvatarScaleMod.cs index ab54a69..c83796d 100644 --- a/.Deprecated/AvatarScaleMod/Integrations/BTKUI/BtkUiAddon_PSP_AvatarScaleMod.cs +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon_PSP_AvatarScaleMod.cs @@ -3,15 +3,15 @@ using BTKUILib.UIObjects; using BTKUILib.UIObjects.Components; using NAK.AvatarScaleMod.AvatarScaling; -namespace NAK.AvatarScaleMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.AvatarScaleMod.Integrations { - private static Button _playerHasModElement; - private static string _selectedPlayer; - - private static void Setup_PlayerSelectPage() + public static partial class BtkUiAddon { + private static Button _playerHasModElement; + private static string _selectedPlayer; + + private static void Setup_PlayerSelectPage() + { QuickMenuAPI.OnPlayerSelected += OnPlayerSelected; Category category = QuickMenuAPI.PlayerSelectPage.AddCategory(ModSettings.ASM_SettingsCategory, ModSettings.ModName); @@ -22,27 +22,27 @@ public static partial class BtkUiAddon button.OnPress += OnCopyPlayerHeight; } - #region QM Events + #region QM Events - private static void OnPlayerSelected(string _, string id) - { + private static void OnPlayerSelected(string _, string id) + { _selectedPlayer = id; UpdatePlayerHasModIcon(); } - private static void OnCopyPlayerHeight() - { + private static void OnCopyPlayerHeight() + { float networkHeight = AvatarScaleManager.Instance.GetNetworkHeight(_selectedPlayer); if (networkHeight < 0) return; AvatarScaleManager.Instance.SetTargetHeight(networkHeight); } - #endregion + #endregion - #region Private Methods + #region Private Methods - private static void UpdatePlayerHasModIcon() - { + private static void UpdatePlayerHasModIcon() + { if (_playerHasModElement == null) return; @@ -60,5 +60,6 @@ public static partial class BtkUiAddon } } - #endregion + #endregion + } } \ No newline at end of file diff --git a/AvatarScale/Integrations/BTKUI/BtkUiAddon_Utils.cs b/AvatarScale/Integrations/BTKUI/BtkUiAddon_Utils.cs new file mode 100644 index 0000000..473dc45 --- /dev/null +++ b/AvatarScale/Integrations/BTKUI/BtkUiAddon_Utils.cs @@ -0,0 +1,78 @@ +using System.Reflection; +using BTKUILib; +using BTKUILib.UIObjects; +using BTKUILib.UIObjects.Components; +using MelonLoader; +using UnityEngine; + +namespace NAK.AvatarScaleMod.Integrations +{ + public static partial class BtkUiAddon + { + #region Melon Preference Helpers + + private static ToggleButton AddMelonToggle(ref Category category, MelonPreferences_Entry entry) + { + ToggleButton toggle = category.AddToggle(entry.DisplayName, entry.Description, entry.Value); + toggle.OnValueUpdated += b => entry.Value = b; + return toggle; + } + + private static SliderFloat AddMelonSlider(ref Category category, MelonPreferences_Entry entry, float min, + float max, int decimalPlaces = 2, bool allowReset = true) + { + SliderFloat slider = category.AddSlider(entry.DisplayName, entry.Description, + Mathf.Clamp(entry.Value, min, max), min, max, decimalPlaces, entry.DefaultValue, allowReset); + slider.OnValueUpdated += f => entry.Value = f; + return slider; + } + + private static Button AddMelonStringInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) + { + Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); + button.OnPress += () => QuickMenuAPI.OpenKeyboard(entry.Value, s => entry.Value = s); + return button; + } + + private static Button AddMelonNumberInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) + { + Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); + button.OnPress += () => QuickMenuAPI.OpenNumberInput(entry.DisplayName, entry.Value, f => entry.Value = f); + return button; + } + + // private static SliderFloat AddMelonSlider(ref Page page, MelonPreferences_Entry entry, float min, float max, int decimalPlaces = 2, bool allowReset = true) + // { + // SliderFloat slider = page.AddSlider(entry.DisplayName, entry.Description, Mathf.Clamp(entry.Value, min, max), min, max, decimalPlaces, entry.DefaultValue, allowReset); + // slider.OnValueUpdated += f => entry.Value = f; + // return slider; + // } + + /// + /// Helper method to create a category that saves its collapsed state to a MelonPreferences entry. + /// + /// + /// + /// + /// + private static Category AddMelonCategory(ref Page page, MelonPreferences_Entry entry, bool showHeader = true) + { + Category category = page.AddCategory(entry.DisplayName, showHeader, true, entry.Value); + category.OnCollapse += b => entry.Value = b; + return category; + } + + #endregion + + #region Icon Utils + + private static Stream GetIconStream(string iconName) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + string assemblyName = assembly.GetName().Name; + return assembly.GetManifestResourceStream($"{assemblyName}.resources.{iconName}"); + } + + #endregion + } +} \ No newline at end of file diff --git a/.Deprecated/AvatarScaleMod/Main.cs b/AvatarScale/Main.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/Main.cs rename to AvatarScale/Main.cs diff --git a/.Deprecated/AvatarScaleMod/ModSettings.cs b/AvatarScale/ModSettings.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/ModSettings.cs rename to AvatarScale/ModSettings.cs diff --git a/.Deprecated/AvatarScaleMod/Networking/ModNetwork.cs b/AvatarScale/Networking/ModNetwork.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/Networking/ModNetwork.cs rename to AvatarScale/Networking/ModNetwork.cs diff --git a/.Deprecated/AvatarScaleMod/Properties/AssemblyInfo.cs b/AvatarScale/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/AvatarScaleMod/Properties/AssemblyInfo.cs rename to AvatarScale/Properties/AssemblyInfo.cs diff --git a/.Deprecated/AvatarScaleMod/README.md b/AvatarScale/README.md similarity index 100% rename from .Deprecated/AvatarScaleMod/README.md rename to AvatarScale/README.md diff --git a/.Deprecated/AvatarScaleMod/Scripts.cs b/AvatarScale/Scripts.cs similarity index 83% rename from .Deprecated/AvatarScaleMod/Scripts.cs rename to AvatarScale/Scripts.cs index 16d7319..5980b65 100644 --- a/.Deprecated/AvatarScaleMod/Scripts.cs +++ b/AvatarScale/Scripts.cs @@ -3,12 +3,12 @@ using System.IO; using System.Reflection; // https://github.com/SDraw/ml_mods_cvr/blob/master/ml_amt/Scripts.cs -namespace NAK.AvatarScaleMod; - -static class Scripts +namespace NAK.AvatarScaleMod { - public static string GetEmbeddedScript(string p_name) + static class Scripts { + public static string GetEmbeddedScript(string p_name) + { string l_result = ""; Assembly l_assembly = Assembly.GetExecutingAssembly(); string l_assemblyName = l_assembly.GetName().Name; @@ -23,4 +23,5 @@ static class Scripts return l_result; } + } } \ No newline at end of file diff --git a/.Deprecated/InteractionTest/format.json b/AvatarScale/format.json similarity index 100% rename from .Deprecated/InteractionTest/format.json rename to AvatarScale/format.json diff --git a/.Deprecated/AvatarScaleMod/resources/ASM_Icon_AvatarHeightConfig.png b/AvatarScale/resources/ASM_Icon_AvatarHeightConfig.png similarity index 100% rename from .Deprecated/AvatarScaleMod/resources/ASM_Icon_AvatarHeightConfig.png rename to AvatarScale/resources/ASM_Icon_AvatarHeightConfig.png diff --git a/.Deprecated/AvatarScaleMod/resources/ASM_Icon_AvatarHeightCopy.png b/AvatarScale/resources/ASM_Icon_AvatarHeightCopy.png similarity index 100% rename from .Deprecated/AvatarScaleMod/resources/ASM_Icon_AvatarHeightCopy.png rename to AvatarScale/resources/ASM_Icon_AvatarHeightCopy.png diff --git a/.Deprecated/AvatarScaleMod/resources/menu.js b/AvatarScale/resources/menu.js similarity index 100% rename from .Deprecated/AvatarScaleMod/resources/menu.js rename to AvatarScale/resources/menu.js diff --git a/BetterShadowClone/BetterShadowClone.csproj b/BetterShadowClone/BetterShadowClone.csproj new file mode 100644 index 0000000..09ffc35 --- /dev/null +++ b/BetterShadowClone/BetterShadowClone.csproj @@ -0,0 +1,8 @@ + + + + + bettershadowclone.assets + + + diff --git a/BetterShadowClone/Koneko/BoneHider.cs b/BetterShadowClone/Koneko/BoneHider.cs new file mode 100644 index 0000000..0e0c57d --- /dev/null +++ b/BetterShadowClone/Koneko/BoneHider.cs @@ -0,0 +1,130 @@ +// using System.Collections.Generic; +// using System.Linq; +// using NAK.BetterShadowClone; +// using UnityEngine; +// using UnityEngine.Serialization; +// +// namespace Koneko.BetterHeadHider; +// +// public class BoneHider : MonoBehaviour { +// public static ComputeShader shader; +// +// public SkinnedMeshRenderer[] targets; +// public Transform shrinkBone; +// private ComputeBuffer[] weightedBuffers; +// private int[] weightedCounts; +// private int[] bufferLayouts; +// private int[] threadGroups; +// private bool Initialized; +// +// +// #region Public Methods +// public static void SetupAvatar(GameObject avatar, Transform shrinkBone, SkinnedMeshRenderer[] targets) { +// BoneHider shrink = avatar.AddComponent(); +// shrink.shrinkBone = shrinkBone; +// shrink.targets = targets; +// } +// #endregion +// +// #region Private Methods +// private void Initialize() { +// Dispose(); +// +// weightedBuffers = new ComputeBuffer[targets.Length]; +// weightedCounts = new int[targets.Length]; +// bufferLayouts = new int[targets.Length]; +// threadGroups = new int[targets.Length]; +// +// for (int i = 0; i < targets.Length; i++) { +// List weighted = FindHeadVertices(targets[i]); +// if (weighted.Count == 0) continue; +// +// targets[i].sharedMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw; +// weightedBuffers[i] = new(weighted.Count, sizeof(int)); +// weightedBuffers[i].SetData(weighted.ToArray()); +// weightedCounts[i] = weighted.Count; +// +// int bufferLayout = 0; +// if (targets[i].sharedMesh.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.Position)) bufferLayout += 3; +// if (targets[i].sharedMesh.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.Normal)) bufferLayout += 3; +// if (targets[i].sharedMesh.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.Tangent)) bufferLayout += 4; +// bufferLayouts[i] = bufferLayout; +// +// threadGroups[i] = Mathf.CeilToInt(weighted.Count / 64.0f); +// Debug.Log(threadGroups[i]); +// } +// +// Initialized = true; +// } +// +// private List FindHeadVertices(SkinnedMeshRenderer target) { +// List headVertices = new(); +// BoneWeight[] boneWeights = target.sharedMesh.boneWeights; +// HashSet bones = new(); +// +// bones = shrinkBone.GetComponentsInChildren(true).ToHashSet(); +// +// //get indexs of child bones +// HashSet weights = new(); +// for (int i = 0; i < target.bones.Length; i++) { +// if (bones.Contains(target.bones[i])) weights.Add(i); +// } +// +// for (int i = 0; i < boneWeights.Length; i++) { +// BoneWeight weight = boneWeights[i]; +// if (weights.Contains(weight.boneIndex0) || weights.Contains(weight.boneIndex1) || +// weights.Contains(weight.boneIndex2) || weights.Contains(weight.boneIndex3)) { +// headVertices.Add(i); +// } +// } +// return headVertices; +// } +// +// private void MyOnPreRender(Camera cam) { +// if (!Initialized) +// return; +// +// // NOTE: We only hide head once, so any camera rendered after wont see the head! +// +// if (cam != ShadowCloneMod.PlayerCamera // only hide in player cam, or in portable cam if debug is on +// && (!ModSettings.EntryHideInPortableCamera.Value || cam != ShadowCloneMod.HandCamera)) +// return; +// +// if (!ShadowCloneMod.CheckWantsToHideHead(cam)) +// return; // listener said no (Third Person, etc) +// +// for (int i = 0; i < targets.Length; i++) { +// SkinnedMeshRenderer target = targets[i]; +// +// if (target == null +// || !target.gameObject.activeInHierarchy +// || weightedBuffers[i] == null) continue; +// +// GraphicsBuffer vertexBuffer = targets[i].GetVertexBuffer(); +// if(vertexBuffer == null) continue; +// +// shader.SetVector(s_Pos, Vector3.positiveInfinity); // todo: fix +// shader.SetInt(s_WeightedCount, weightedCounts[i]); +// shader.SetInt(s_BufferLayout, bufferLayouts[i]); +// shader.SetBuffer(0, s_WeightedVertices, weightedBuffers[i]); +// shader.SetBuffer(0, s_VertexBuffer, vertexBuffer); +// shader.Dispatch(0, threadGroups[i], 1, 1); +// vertexBuffer.Dispose(); +// } +// } +// +// private void Dispose() { +// Initialized = false; +// if (weightedBuffers == null) return; +// foreach (ComputeBuffer c in weightedBuffers) +// c?.Dispose(); +// } +// #endregion +// +// #region Unity Events +// private void OnEnable() => Camera.onPreRender += MyOnPreRender; +// private void OnDisable() => Camera.onPreRender -= MyOnPreRender; +// private void OnDestroy() => Dispose(); +// private void Start() => Initialize(); +// #endregion +// } \ No newline at end of file diff --git a/BetterShadowClone/Main.cs b/BetterShadowClone/Main.cs new file mode 100644 index 0000000..81591fd --- /dev/null +++ b/BetterShadowClone/Main.cs @@ -0,0 +1,144 @@ +using System; +using System.IO; +using MelonLoader; +using System.Reflection; +using ABI_RC.Core.Player; +using ABI_RC.Core.Util; +using ABI_RC.Core.Util.AssetFiltering; +using ABI_RC.Systems.Camera; +using UnityEngine; + +namespace NAK.BetterShadowClone; + +public class ShadowCloneMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + ModSettings.Initialize(); + //VRModeSwitchEvents.OnCompletedVRModeSwitch.AddListener(_ => FindCameras()); + + SharedFilter._avatarWhitelist.Add(typeof(FPRExclusion)); + SharedFilter._localComponentWhitelist.Add(typeof(FPRExclusion)); + + try + { + LoadAssetBundle(); + InitializePatches(); + } + catch (Exception e) + { + Logger.Error(e); + } + } + + #region Hide Head Override + + /// + /// Return false to prevent the head from being hidden. + /// + public static WantsToHideHeadDelegate wantsToHideHead; + public delegate bool WantsToHideHeadDelegate(Camera cam); + + public static bool CheckWantsToHideHead(Camera cam) + { + if (wantsToHideHead == null) + return true; + + foreach (Delegate @delegate in wantsToHideHead.GetInvocationList()) + { + WantsToHideHeadDelegate method = (WantsToHideHeadDelegate)@delegate; + if (!method(cam)) return false; + } + + return true; + } + + #endregion + + #region Asset Bundle Loading + + private const string BetterShadowCloneAssets = "bettershadowclone.assets"; + + private const string BoneHiderComputePath = "Assets/Koneko/ComputeShaders/BoneHider.compute"; + //private const string MeshCopyComputePath = "Assets/Koneko/ComputeShaders/MeshCopy.compute"; + + private const string ShadowCloneComputePath = "Assets/NotAKid/Shaders/ShadowClone.compute"; + private const string ShadowCloneShaderPath = "Assets/NotAKid/Shaders/ShadowClone.shader"; + private const string DummyCloneShaderPath = "Assets/NotAKid/Shaders/DummyClone.shader"; + + private void LoadAssetBundle() + { + Logger.Msg($"Loading required asset bundle..."); + using Stream resourceStream = MelonAssembly.Assembly.GetManifestResourceStream(BetterShadowCloneAssets); + using MemoryStream memoryStream = new(); + if (resourceStream == null) { + Logger.Error($"Failed to load {BetterShadowCloneAssets}!"); + return; + } + + resourceStream.CopyTo(memoryStream); + AssetBundle assetBundle = AssetBundle.LoadFromMemory(memoryStream.ToArray()); + if (assetBundle == null) { + Logger.Error($"Failed to load {BetterShadowCloneAssets}! Asset bundle is null!"); + return; + } + + // load shaders + ComputeShader shader = assetBundle.LoadAsset(BoneHiderComputePath); + shader.hideFlags |= HideFlags.DontUnloadUnusedAsset; + TransformHiderManager.shader = shader; + Logger.Msg($"Loaded {BoneHiderComputePath}!"); + + // load shadow clone shader + ComputeShader shadowCloneCompute = assetBundle.LoadAsset(ShadowCloneComputePath); + shadowCloneCompute.hideFlags |= HideFlags.DontUnloadUnusedAsset; + ShadowCloneHelper.shader = shadowCloneCompute; + Logger.Msg($"Loaded {ShadowCloneComputePath}!"); + + // load shadow clone material + Shader shadowCloneShader = assetBundle.LoadAsset(ShadowCloneShaderPath); + shadowCloneShader.hideFlags |= HideFlags.DontUnloadUnusedAsset; + ShadowCloneHelper.shadowMaterial = new Material(shadowCloneShader); + Logger.Msg($"Loaded {ShadowCloneShaderPath}!"); + + Logger.Msg("Asset bundle successfully loaded!"); + } + + #endregion + + #region Harmony Patches + + private void InitializePatches() + { + HarmonyInstance.Patch( + typeof(TransformHiderForMainCamera).GetMethod(nameof(TransformHiderForMainCamera.ProcessHierarchy)), + prefix: new HarmonyLib.HarmonyMethod(typeof(ShadowCloneMod).GetMethod(nameof(OnTransformHiderForMainCamera_ProcessHierarchy_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + prefix: new HarmonyLib.HarmonyMethod(typeof(ShadowCloneMod).GetMethod(nameof(OnPlayerSetup_ClearAvatar_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + } + + private static void OnPlayerSetup_ClearAvatar_Prefix() + { + TransformHiderManager.Instance.OnAvatarCleared(); + ShadowCloneManager.Instance.OnAvatarCleared(); + } + + private static void OnTransformHiderForMainCamera_ProcessHierarchy_Prefix(ref bool __runOriginal) + { + if (!__runOriginal || (__runOriginal = !ModSettings.EntryEnabled.Value)) + return; // if something else disabled, or we are disabled, don't run + + ShadowCloneHelper.SetupAvatar(PlayerSetup.Instance._avatar); + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/ModSettings.cs b/BetterShadowClone/ModSettings.cs new file mode 100644 index 0000000..de687fa --- /dev/null +++ b/BetterShadowClone/ModSettings.cs @@ -0,0 +1,60 @@ +using MelonLoader; +using UnityEngine; + +namespace NAK.BetterShadowClone; + +public static class ModSettings +{ + #region Melon Prefs + + private const string SettingsCategory = nameof(ShadowCloneMod); + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(SettingsCategory); + + internal static readonly MelonPreferences_Entry EntryEnabled = + Category.CreateEntry("Enabled", true, + description: "Enable Mirror Clone."); + + internal static readonly MelonPreferences_Entry EntryUseShadowClone = + Category.CreateEntry("Use Shadow Clone", true, + description: "Should you have shadow clones?"); + + internal static readonly MelonPreferences_Entry EntryCopyMaterialToShadow = + Category.CreateEntry("Copy Material to Shadow", true, + description: "Should the shadow clone copy the material from the original mesh? Note: This can have a slight performance hit."); + + internal static readonly MelonPreferences_Entry EntryDontRespectFPR = + Category.CreateEntry("Dont Respect FPR", false, + description: "Should the transform hider not respect FPR?"); + + internal static readonly MelonPreferences_Entry EntryDebugHeadHide = + Category.CreateEntry("Debug Head Hide", false, + description: "Should head be hidden for first render?"); + + internal static readonly MelonPreferences_Entry EntryDebugShowShadow = + Category.CreateEntry("Debug Show Shadow", false, + description: "Should the shadow clone be shown?"); + + internal static readonly MelonPreferences_Entry EntryDebugShowInFront = + Category.CreateEntry("Debug Show in Front", false, + description: "Should the shadow clone be shown in front?"); + + + #endregion + + internal static void Initialize() + { + foreach (MelonPreferences_Entry setting in Category.Entries) + setting.OnEntryValueChangedUntyped.Subscribe(OnSettingsChanged); + } + + private static void OnSettingsChanged(object oldValue = null, object newValue = null) + { + TransformHiderManager.s_DisallowFprExclusions = EntryDontRespectFPR.Value; + TransformHiderManager.s_DebugHeadHide = EntryDebugHeadHide.Value; + ShadowCloneManager.s_CopyMaterialsToShadow = EntryCopyMaterialToShadow.Value; + ShadowCloneManager.s_DebugShowShadow = EntryDebugShowShadow.Value; + ShadowCloneManager.s_DebugShowInFront = EntryDebugShowInFront.Value; + } +} \ No newline at end of file diff --git a/BetterShadowClone/Properties/AssemblyInfo.cs b/BetterShadowClone/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..58368ed --- /dev/null +++ b/BetterShadowClone/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using MelonLoader; +using NAK.BetterShadowClone.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.BetterShadowClone))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.BetterShadowClone))] + +[assembly: MelonInfo( + typeof(NAK.BetterShadowClone.ShadowCloneMod), + nameof(NAK.BetterShadowClone), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShadowCloneMod" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] + +namespace NAK.BetterShadowClone.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS & Exterrata"; +} \ No newline at end of file diff --git a/.Deprecated/EzCurls/README.md b/BetterShadowClone/README.md similarity index 100% rename from .Deprecated/EzCurls/README.md rename to BetterShadowClone/README.md diff --git a/BetterShadowClone/Resources/bettershadowclone.assets b/BetterShadowClone/Resources/bettershadowclone.assets new file mode 100644 index 0000000..42c1734 Binary files /dev/null and b/BetterShadowClone/Resources/bettershadowclone.assets differ diff --git a/BetterShadowClone/Resources/bettershadowclone.assets_OLD b/BetterShadowClone/Resources/bettershadowclone.assets_OLD new file mode 100644 index 0000000..a80c900 Binary files /dev/null and b/BetterShadowClone/Resources/bettershadowclone.assets_OLD differ diff --git a/BetterShadowClone/ShadowClone/IShadowClone/IShadowClone.cs b/BetterShadowClone/ShadowClone/IShadowClone/IShadowClone.cs new file mode 100644 index 0000000..e6cc161 --- /dev/null +++ b/BetterShadowClone/ShadowClone/IShadowClone/IShadowClone.cs @@ -0,0 +1,12 @@ +using System; + +namespace NAK.BetterShadowClone; + +public interface IShadowClone : IDisposable +{ + bool IsValid { get; } + bool Process(); + void RenderForShadow(); + void RenderForUiCulling(); + void ResetMainMesh(); +} \ No newline at end of file diff --git a/BetterShadowClone/ShadowClone/IShadowClone/MeshShadowClone.cs b/BetterShadowClone/ShadowClone/IShadowClone/MeshShadowClone.cs new file mode 100644 index 0000000..d27883b --- /dev/null +++ b/BetterShadowClone/ShadowClone/IShadowClone/MeshShadowClone.cs @@ -0,0 +1,158 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NAK.BetterShadowClone; + +public struct MeshShadowClone : IShadowClone +{ + // We technically don't need a clone mesh for MeshRenderer shadow clone handling, + // but as the shadows are also utilized for UI culling, we need to have a clone mesh. + // If we don't stick with UI culling, we can just set the shadowCastingMode to ShadowsOnly when player camera renders. + + // lame 2 frame init stuff + private const int FrameInitCount = 0; + private int _frameInitCounter; + private bool _hasInitialized; + + // shadow is used to cull ui, clone always exists + private readonly bool _shouldCastShadows; + private readonly MeshRenderer _mainMesh; + private readonly MeshRenderer _shadowMesh; + private readonly MeshFilter _shadowMeshFilter; + + // material copying (unity is shit) + private bool _hasShadowMaterials; + private readonly Material[] _shadowMaterials; + private readonly MaterialPropertyBlock _shadowMaterialBlock; + + #region IShadowClone Methods + + public void ResetMainMesh(){} + + public bool IsValid => _mainMesh != null && _shadowMesh != null; + + public MeshShadowClone(MeshRenderer meshRenderer) + { + _mainMesh = meshRenderer; + MeshFilter _mainMeshFilter = meshRenderer.GetComponent(); + + if (_mainMesh == null + || _mainMesh.sharedMaterials == null + || _mainMesh.sharedMaterials.Length == 0 + || _mainMeshFilter == null + || _mainMeshFilter.sharedMesh == null) + { + Dispose(); + return; // no mesh! + } + + _shouldCastShadows = _mainMesh.shadowCastingMode != ShadowCastingMode.Off; + _mainMesh.shadowCastingMode = ShadowCastingMode.Off; // visual mesh doesn't cast shadows + + (_shadowMesh, _shadowMeshFilter) = ShadowCloneManager.InstantiateShadowClone(_mainMesh); + _shadowMesh.forceRenderingOff = true; + + // material copying shit + int materialCount = _mainMesh.sharedMaterials.Length; + Material shadowMaterial = ShadowCloneHelper.shadowMaterial; + + _shadowMaterialBlock = new MaterialPropertyBlock(); + _shadowMaterials = new Material[materialCount]; + for (int i = 0; i < materialCount; i++) _shadowMaterials[i] = shadowMaterial; + } + + public bool Process() + { + bool shouldRender = _mainMesh.enabled && _mainMesh.gameObject.activeInHierarchy; + + // copying behaviour of SkinnedShadowClone, to visually be the same when a mesh toggles + if (!shouldRender) + { + _frameInitCounter = 0; + _hasInitialized = false; + _shadowMesh.forceRenderingOff = true; + return false; + } + + if (_frameInitCounter >= FrameInitCount) + { + if (_hasInitialized) + return true; + + _hasInitialized = true; + return true; + } + + _frameInitCounter++; + return false; + } + + public void RenderForShadow() + { + _shadowMesh.shadowCastingMode = ShadowCloneManager.s_DebugShowShadow + ? ShadowCastingMode.On : ShadowCastingMode.ShadowsOnly; + + _shadowMesh.forceRenderingOff = !_shouldCastShadows; + + // shadow casting needs clone to have original materials (uv discard) + // we also want to respect material swaps... but this is fucking slow :( + + if (!ShadowCloneManager.s_CopyMaterialsToShadow) + return; + + if (_hasShadowMaterials) + { + // NOTE: will not handle material swaps unless Avatar Overrender Ui is on + _shadowMesh.sharedMaterials = _mainMesh.sharedMaterials; + _hasShadowMaterials = false; + } + + UpdateCloneMaterialProperties(); + } + + public void RenderForUiCulling() + { + _shadowMesh.shadowCastingMode = ShadowCastingMode.On; + _shadowMesh.forceRenderingOff = false; + + // UI culling needs clone to have write-to-depth shader + if (_hasShadowMaterials) return; + _shadowMesh.sharedMaterials = _shadowMaterials; + _hasShadowMaterials = true; + + // Not needed- MaterialPropertyBlock applied to renderer in RenderForShadow + //UpdateCloneMaterialProperties(); + } + + public void Dispose() + { + if (_shadowMesh == null) + return; // uh oh + + // Cleanup instanced Mesh & Materials + GameObject shadowMeshObject = _shadowMesh.gameObject; + UnityEngine.Object.Destroy(_shadowMeshFilter.sharedMesh); + UnityEngine.Object.Destroy(_shadowMeshFilter); + if (!_hasShadowMaterials) + { + var materials = _shadowMesh.sharedMaterials; + foreach (Material mat in materials) UnityEngine.Object.Destroy(mat); + } + UnityEngine.Object.Destroy(_shadowMesh); + UnityEngine.Object.Destroy(shadowMeshObject); + } + + #endregion + + #region Private Methods + + private void UpdateCloneMaterialProperties() + { + // copy material properties to shadow clone materials + _mainMesh.GetPropertyBlock(_shadowMaterialBlock); + _shadowMesh.SetPropertyBlock(_shadowMaterialBlock); + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/ShadowClone/IShadowClone/SkinnedShadowClone.cs b/BetterShadowClone/ShadowClone/IShadowClone/SkinnedShadowClone.cs new file mode 100644 index 0000000..89d7494 --- /dev/null +++ b/BetterShadowClone/ShadowClone/IShadowClone/SkinnedShadowClone.cs @@ -0,0 +1,296 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NAK.BetterShadowClone; + +public class SkinnedShadowClone : IShadowClone +{ + private static readonly int s_SourceBufferId = Shader.PropertyToID("_sourceBuffer"); + private static readonly int s_TargetBufferId = Shader.PropertyToID("_targetBuffer"); + private static readonly int s_HiddenVerticiesId = Shader.PropertyToID("_hiddenVertices"); + private static readonly int s_HiddenVertexPos = Shader.PropertyToID("_hiddenVertexPos"); + private static readonly int s_SourceBufferLayoutId = Shader.PropertyToID("_sourceBufferLayout"); + private static readonly int s_SourceRootMatrix = Shader.PropertyToID("_rootBoneMatrix"); + + // lame 2 frame init stuff + private const int FrameInitCount = 0; + private int _frameInitCounter; + private bool _hasInitialized; + + // shadow is used to cull ui, clone always exists + private readonly bool _shouldCastShadows; + private readonly SkinnedMeshRenderer _mainMesh; + private readonly MeshRenderer _shadowMesh; + private readonly MeshFilter _shadowMeshFilter; + private readonly Transform _rootBone; + + // clone copying + private GraphicsBuffer _graphicsBuffer; + private GraphicsBuffer _targetBuffer; + private ComputeBuffer _computeBuffer; + private int _threadGroups; + private int _bufferLayout; + + // material copying (unity is shit) + private bool _hasShadowMaterials; + private readonly Material[] _shadowMaterials; + private readonly MaterialPropertyBlock _shadowMaterialBlock; + + #region IShadowClone Methods + + // anything player can touch is suspect to death + public bool IsValid => _mainMesh != null && _shadowMesh != null && _rootBone != null; + + internal SkinnedShadowClone(SkinnedMeshRenderer renderer, FPRExclusion exclusion) + { + _mainMesh = renderer; + + if (_mainMesh == null + || _mainMesh.sharedMesh == null + || _mainMesh.sharedMaterials == null + || _mainMesh.sharedMaterials.Length == 0) + { + Dispose(); + return; // no mesh! + } + + + FindExclusionVertList(_mainMesh, exclusion); + + if (exclusion.affectedVertexIndices.Count == 0) + { + Dispose(); + return; // no affected verts! + } + + _computeBuffer = new ComputeBuffer(_mainMesh.sharedMesh.vertexCount, sizeof(int)); + _computeBuffer.SetData(exclusion.affectedVertexIndices.ToArray()); + exclusion.affectedVertexIndices.Clear(); + + + + _shouldCastShadows = _mainMesh.shadowCastingMode != ShadowCastingMode.Off; + //_mainMesh.shadowCastingMode = ShadowCastingMode.On; // visual mesh doesn't cast shadows + + (_shadowMesh, _shadowMeshFilter) = ShadowCloneManager.InstantiateShadowClone(_mainMesh); + _shadowMesh.shadowCastingMode = ShadowCastingMode.Off; // shadow mesh doesn't cast shadows + _shadowMesh.forceRenderingOff = false; + + + _rootBone = _mainMesh.rootBone; + _rootBone ??= _mainMesh.transform; // fallback to transform if no root bone + + // material copying shit + int materialCount = _mainMesh.sharedMaterials.Length; + Material shadowMaterial = ShadowCloneHelper.shadowMaterial; + + _shadowMaterialBlock = new MaterialPropertyBlock(); // TODO: check if we need one per material on renderer, idk if this is only first index + _shadowMaterials = new Material[materialCount]; + for (int i = 0; i < materialCount; i++) _shadowMaterials[i] = shadowMaterial; + } + + public bool Process() + { + // some people animate renderer.enabled instead of gameObject.activeInHierarchy + // do not disable shadow clone game object, it causes a flicker when re-enabled! + bool shouldRender = _mainMesh.enabled && _mainMesh.gameObject.activeInHierarchy; + + // GraphicsBuffer becomes stale when mesh is disabled + if (!shouldRender) + { + _frameInitCounter = 0; + _hasInitialized = false; + _shadowMesh.forceRenderingOff = true; // force off if mesh is disabled + return false; // TODO: dispose stale buffers + } + + // Unity is weird, so we need to wait 2 frames before we can get the graphics buffer + if (_frameInitCounter >= FrameInitCount) + { + if (_hasInitialized) + return true; + + _hasInitialized = true; + SetupGraphicsBuffer(); + return true; + } + + _frameInitCounter++; + return false; + } + + public void RenderForShadow() + { + ResetShadowClone(); + RenderShadowClone(); + } + + public void RenderForUiCulling() + { + ConfigureShadowCloneForUiCulling(); + RenderShadowClone(); + } + + public void Dispose() + { + if (_shadowMesh != null) + { + // Cleanup instanced Mesh & Materials + GameObject shadowMeshObject = _shadowMesh.gameObject; + UnityEngine.Object.Destroy(_shadowMeshFilter.mesh); + UnityEngine.Object.Destroy(_shadowMeshFilter); + + // explain why this works + if (_hasShadowMaterials) _shadowMesh.sharedMaterials = _mainMesh.sharedMaterials; + foreach (Material mat in _shadowMesh.sharedMaterials) UnityEngine.Object.Destroy(mat); + + UnityEngine.Object.Destroy(_shadowMesh); + UnityEngine.Object.Destroy(shadowMeshObject); + } + + _graphicsBuffer?.Dispose(); + _graphicsBuffer = null; + _targetBuffer?.Dispose(); + _targetBuffer = null; + _computeBuffer?.Dispose(); + _computeBuffer = null; + } + + #endregion + + #region Private Methods + + // Unity is weird, so we need to wait 2 frames before we can get the graphics buffer + private void SetupGraphicsBuffer() + { + Mesh mesh = _mainMesh.sharedMesh; + Mesh shadowMesh = _shadowMesh.GetComponent().mesh; + + _bufferLayout = 0; + if (mesh.HasVertexAttribute(VertexAttribute.Position)) _bufferLayout += 3; + if (mesh.HasVertexAttribute(VertexAttribute.Normal)) _bufferLayout += 3; + if (mesh.HasVertexAttribute(VertexAttribute.Tangent)) _bufferLayout += 4; + _bufferLayout *= 4; // 4 bytes per float + + const float xThreadGroups = 32f; + _threadGroups = Mathf.CeilToInt(mesh.vertexCount / xThreadGroups); + + _mainMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw; + shadowMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw; + + _targetBuffer = shadowMesh.GetVertexBuffer(0); + + //Debug.Log($"Initialized! BufferLayout: {_bufferLayout}, GraphicsBuffer: {_graphicsBuffer != null}, TargetBuffer: {_targetBuffer != null}"); + } + + public static void FindExclusionVertList(SkinnedMeshRenderer renderer, FPRExclusion exclusion) + { + var boneWeights = renderer.sharedMesh.boneWeights; + + HashSet weights = new(); + for (int i = 0; i < renderer.bones.Length; i++) + { + if (exclusion.affectedChildren.Contains(renderer.bones[i])) + weights.Add(i); + } + + for (int i = 0; i < boneWeights.Length; i++) + { + BoneWeight weight = boneWeights[i]; + + Transform bone = null; + const float minWeightThreshold = 0.2f; + if (weights.Contains(weight.boneIndex0) && weight.weight0 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex0]; + else if (weights.Contains(weight.boneIndex1) && weight.weight1 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex1]; + else if (weights.Contains(weight.boneIndex2) && weight.weight2 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex2]; + else if (weights.Contains(weight.boneIndex3) && weight.weight3 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex3]; + + exclusion.affectedVertexIndices.Add(bone != null ? i : -1); + } + } + + public void ResetMainMesh() + { + _mainMesh.shadowCastingMode = ShadowCastingMode.On; + _mainMesh.forceRenderingOff = false; + + _shadowMesh.transform.position = Vector3.positiveInfinity; // nan + } + + private void ResetShadowClone() + { + if (ShadowCloneManager.s_DebugShowShadow) + { + _mainMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly; + _mainMesh.forceRenderingOff = false; + + _shadowMesh.shadowCastingMode = ShadowCastingMode.On; + _shadowMesh.forceRenderingOff = false; + + _shadowMesh.transform.localPosition = Vector3.zero; + } + else + { + _mainMesh.shadowCastingMode = ShadowCastingMode.On; + _mainMesh.forceRenderingOff = false; + + _shadowMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly; + _shadowMesh.forceRenderingOff = !_shouldCastShadows; + } + + //_shadowMesh.enabled = true; + + // shadow casting needs clone to have original materials (uv discard) + // we also want to respect material swaps... but this is fucking slow :( + + if (!ShadowCloneManager.s_CopyMaterialsToShadow) + return; + + _shadowMesh.sharedMaterials = _mainMesh.sharedMaterials; + UpdateCloneMaterialProperties(); + } + + private void ConfigureShadowCloneForUiCulling() + { + _shadowMesh.shadowCastingMode = ShadowCastingMode.On; + _shadowMesh.forceRenderingOff = false; + + // UI culling needs clone to have write-to-depth shader + _shadowMesh.sharedMaterials = _shadowMaterials; + + // Not needed- MaterialPropertyBlock applied to renderer in RenderForShadow + UpdateCloneMaterialProperties(); + } + + private void RenderShadowClone() + { + // thanks sdraw, i suck at matrix math + Matrix4x4 rootMatrix = _mainMesh.localToWorldMatrix.inverse * Matrix4x4.TRS(_rootBone.position, _rootBone.rotation, Vector3.one); + + _graphicsBuffer = _mainMesh.GetVertexBuffer(); + ShadowCloneHelper.shader.SetMatrix(s_SourceRootMatrix, rootMatrix); + ShadowCloneHelper.shader.SetBuffer(0, s_SourceBufferId, _graphicsBuffer); + ShadowCloneHelper.shader.SetBuffer(0, s_TargetBufferId, _targetBuffer); + + ShadowCloneHelper.shader.SetBuffer(0, s_HiddenVerticiesId, _computeBuffer); + ShadowCloneHelper.shader.SetVector(s_HiddenVertexPos, Vector4.positiveInfinity); // temp + + ShadowCloneHelper.shader.SetInt(s_SourceBufferLayoutId, _bufferLayout); + ShadowCloneHelper.shader.Dispatch(0, _threadGroups, 1, 1); + _graphicsBuffer.Release(); + } + + private void UpdateCloneMaterialProperties() + { + // copy material properties to shadow clone materials + _mainMesh.GetPropertyBlock(_shadowMaterialBlock); + _shadowMesh.SetPropertyBlock(_shadowMaterialBlock); + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/ShadowClone/ShadowCloneManager.cs b/BetterShadowClone/ShadowClone/ShadowCloneManager.cs new file mode 100644 index 0000000..4443efd --- /dev/null +++ b/BetterShadowClone/ShadowClone/ShadowCloneManager.cs @@ -0,0 +1,251 @@ +using System.Collections.Generic; +using ABI_RC.Core; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using MagicaCloth; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NAK.BetterShadowClone; + +public class ShadowCloneManager : MonoBehaviour +{ + #region Singleton Implementation + + private static ShadowCloneManager _instance; + public static ShadowCloneManager Instance + { + get + { + if (_instance != null) return _instance; + _instance = new GameObject("NAK.ShadowCloneManager").AddComponent(); + DontDestroyOnLoad(_instance.gameObject); + return _instance; + } + } + + #endregion + + private const string ShadowClonePostfix = "_ShadowClone"; + //public const string CVRIgnoreForUiCulling = "CVRIgnoreForUiCulling"; // TODO: Shader Tag to ignore for UI culling? + + // Game cameras + private static Camera s_MainCamera; + private static Camera s_UiCamera; + + // Settings + internal static bool s_CopyMaterialsToShadow = true; + internal static bool s_DebugShowShadow = false; + internal static bool s_DebugShowInFront = false; + private static bool s_UseShadowToCullUi; + private const string ShadowCullUiSettingName = "ExperimentalAvatarOverrenderUI"; + + // Implementation + private bool _hasRenderedThisFrame; + public static readonly List s_Exclusions = new(); + + // Shadow Clones + private readonly List s_ShadowClones = new(); + public void AddShadowClone(IShadowClone clone) + => s_ShadowClones.Add(clone); + + // Debug + private bool _debugShadowProcessingTime; + private readonly StopWatch _stopWatch = new(); + + #region Unity Events + + private void Start() + { + if (Instance != null + && Instance != this) + { + Destroy(this); + return; + } + + UpdatePlayerCameras(); + + s_CopyMaterialsToShadow = ModSettings.EntryCopyMaterialToShadow.Value; + s_DebugShowShadow = ModSettings.EntryDebugShowShadow.Value; + s_DebugShowInFront = ModSettings.EntryDebugShowInFront.Value; + s_UseShadowToCullUi = MetaPort.Instance.settings.GetSettingsBool(ShadowCullUiSettingName); + MetaPort.Instance.settings.settingBoolChanged.AddListener(OnSettingsBoolChanged); + } + + private void OnEnable() + => Camera.onPreRender += MyOnPreCull; + + private void OnDisable() + => Camera.onPreRender -= MyOnPreCull; + + private void OnDestroy() + { + MetaPort.Instance.settings.settingBoolChanged.RemoveListener(OnSettingsBoolChanged); + } + + #endregion + + #region Shadow Clone Managment + + private void Update() + { + _hasRenderedThisFrame = false; + + for (int i = s_ShadowClones.Count - 1; i >= 0; i--) + { + IShadowClone clone = s_ShadowClones[i]; + if (clone is not { IsValid: true }) + { + clone?.Dispose(); + s_ShadowClones.RemoveAt(i); + continue; // invalid or dead + } + + clone.ResetMainMesh(); + } + } + + private void MyOnPreCull(Camera cam) + { + //bool forceRenderForUiCull = s_UseShadowToCullUi && cam == s_UiCamera; + if (cam != s_MainCamera) + return; + + _hasRenderedThisFrame = true; + + _stopWatch.Start(); + + for (int i = s_ShadowClones.Count - 1; i >= 0; i--) + { + IShadowClone clone = s_ShadowClones[i]; + if (clone is not { IsValid: true }) + { + clone?.Dispose(); + s_ShadowClones.RemoveAt(i); + continue; // invalid or dead + } + + if (!clone.Process()) continue; // not ready yet or disabled + + clone.RenderForShadow(); // first cam to render + } + + _stopWatch.Stop(); + } + + #endregion + + #region Game Events + + public void OnAvatarCleared() + { + // Dispose all shadow clones BEFORE game unloads avatar + // Otherwise we memory leak the shadow clones mesh & material instances!!! + foreach (IShadowClone clone in s_ShadowClones) + clone.Dispose(); + s_ShadowClones.Clear(); + } + + private void OnSettingsBoolChanged(string settingName, bool settingValue) + { + if (settingName == ShadowCullUiSettingName) + { + s_UseShadowToCullUi = settingValue; + s_UiCamera.cullingMask = settingValue // make UI camera not see CVRLayers.PlayerClone + ? s_UiCamera.cullingMask | (1 << CVRLayers.PlayerClone) + : s_UiCamera.cullingMask & ~(1 << CVRLayers.PlayerClone); + } + } + + private void OnVRModeSwitchCompleted(bool _, Camera __) + { + UpdatePlayerCameras(); + } + + #endregion + + #region Private Methods + + private static void UpdatePlayerCameras() + { + s_MainCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent(); + s_UiCamera = s_MainCamera.transform.Find("_UICamera").GetComponent(); + + //s_PortableCamera = PortableCamera.Instance.cameraComponent; + } + + #endregion + + #region Static Helpers + + internal static IShadowClone CreateShadowClone(Renderer renderer, FPRExclusion exclusion) + { + return renderer switch + { + SkinnedMeshRenderer skinnedMeshRenderer => new SkinnedShadowClone(skinnedMeshRenderer, exclusion), + MeshRenderer meshRenderer => new MeshShadowClone(meshRenderer), + _ => null + }; + } + + internal static (MeshRenderer, MeshFilter) InstantiateShadowClone(SkinnedMeshRenderer meshRenderer) + { + GameObject shadowClone = new (meshRenderer.name + ShadowClonePostfix) { layer = CVRLayers.PlayerClone }; + shadowClone.transform.SetParent(meshRenderer.transform, false); + shadowClone.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + shadowClone.transform.localScale = Vector3.one; + + if (s_DebugShowShadow && s_DebugShowInFront) + { + float scale = PlayerSetup.Instance.GetPlaySpaceScale(); + Transform playerTransform = PlayerSetup.Instance.transform; + shadowClone.transform.position += playerTransform.forward * scale * 1f; + shadowClone.transform.rotation = Quaternion.AngleAxis(180f, playerTransform.up) * shadowClone.transform.rotation; + } + + MeshRenderer newMesh = shadowClone.AddComponent(); + MeshFilter newMeshFilter = shadowClone.AddComponent(); + + ShadowCloneHelper.ConfigureRenderer(newMesh, true); + + // only shadow clone should cast shadows + newMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly; + + // copy mesh and materials + newMeshFilter.sharedMesh = meshRenderer.sharedMesh; + newMesh.sharedMaterials = meshRenderer.sharedMaterials; + + // copy probe anchor + newMesh.probeAnchor = meshRenderer.probeAnchor; + + return (newMesh, newMeshFilter); + } + + internal static (MeshRenderer, MeshFilter) InstantiateShadowClone(MeshRenderer meshRenderer) + { + GameObject shadowClone = new (meshRenderer.name + ShadowClonePostfix) { layer = CVRLayers.PlayerClone }; + shadowClone.transform.SetParent(meshRenderer.transform, false); + shadowClone.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + shadowClone.transform.localScale = Vector3.one; + + MeshRenderer newMesh = shadowClone.AddComponent(); + MeshFilter newMeshFilter = shadowClone.AddComponent(); + + ShadowCloneHelper.ConfigureRenderer(newMesh, true); + + // only shadow clone should cast shadows + newMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly; + + // copy mesh and materials + newMeshFilter.sharedMesh = meshRenderer.GetComponent().sharedMesh; + newMesh.sharedMaterials = meshRenderer.sharedMaterials; + + // copy probe anchor + newMesh.probeAnchor = meshRenderer.probeAnchor; + + return (newMesh, newMeshFilter); + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/ShadowCloneHelper.cs b/BetterShadowClone/ShadowCloneHelper.cs new file mode 100644 index 0000000..797ebb7 --- /dev/null +++ b/BetterShadowClone/ShadowCloneHelper.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ABI_RC.Core; +using ABI.CCK.Components; +using UnityEngine; +using UnityEngine.Rendering; +using Object = UnityEngine.Object; + +namespace NAK.BetterShadowClone; + +public static class ShadowCloneHelper +{ + public static ComputeShader shader; + public static Material shadowMaterial; + + #region Avatar Setup + + public static void SetupAvatar(GameObject avatar) + { + Animator animator = avatar.GetComponent(); + if (animator == null || animator.avatar == null || animator.avatar.isHuman == false) + { + ShadowCloneMod.Logger.Warning("Avatar is not humanoid!"); + return; + } + + Transform headBone = animator.GetBoneTransform(HumanBodyBones.Head); + if (headBone == null) + { + ShadowCloneMod.Logger.Warning("Head bone not found!"); + return; + } + + var renderers = avatar.GetComponentsInChildren(true); + if (renderers == null || renderers.Length == 0) + { + ShadowCloneMod.Logger.Warning("No renderers found!"); + return; + } + + ShadowCloneMod.Logger.Msg($"Found {renderers.Length} renderers. Processing..."); + + // create shadow clones + ProcessRenderers(renderers, avatar.transform, headBone); + } + + private static void ProcessRenderers(IEnumerable renderers, Transform root, Transform headBone) + { + Stopwatch sw = Stopwatch.StartNew(); + + IReadOnlyDictionary exclusions = CollectTransformToExclusionMap(root, headBone); + var exclusion = headBone.gameObject.GetComponent(); + + // log current time + ShadowCloneMod.Logger.Msg($"CollectTransformToExclusionMap in {sw.ElapsedMilliseconds}ms"); + + foreach (Renderer renderer in renderers) + { + ConfigureRenderer(renderer); + + if (ModSettings.EntryUseShadowClone.Value) + { + IShadowClone clone = ShadowCloneManager.CreateShadowClone(renderer, exclusion); + if (clone != null) ShadowCloneManager.Instance.AddShadowClone(clone); + } + + // ITransformHider hider = TransformHiderManager.CreateTransformHider(renderer, exclusions); + // if (hider != null) TransformHiderManager.Instance.AddTransformHider(hider); + } + + sw.Stop(); + + // log current time + ShadowCloneMod.Logger.Msg($"ProcessRenderers in {sw.ElapsedMilliseconds}ms"); + } + + #endregion + + #region FPR Exclusion Processing + + private static Dictionary CollectTransformToExclusionMap(Component root, Transform headBone) + { + // add an fpr exclusion to the head bone + headBone.gameObject.AddComponent().target = headBone; + + // add an FPRExclusion for all target entries on CVRAvatar (Experimental feature) + CVRAvatar avatar = root.GetComponent(); + if (avatar != null) + { + foreach (CVRAvatarFPREntry fprEntry in avatar.fprSettingsList.Where(fprEntry => fprEntry.transform != null)) + fprEntry.transform.gameObject.AddComponent().target = fprEntry.transform; + } + + // get all FPRExclusions + var fprExclusions = root.GetComponentsInChildren(true).ToList(); + + // get all valid exclusion targets, and destroy invalid exclusions + Dictionary exclusionTargets = new(); + for (int i = fprExclusions.Count - 1; i >= 0; i--) + { + FPRExclusion exclusion = fprExclusions[i]; + if (exclusion.target == null) + { + Object.Destroy(exclusion); + continue; + } + + // first to add wins + exclusionTargets.TryAdd(exclusion.target, exclusion); + } + + // process each FPRExclusion (recursive) + foreach (FPRExclusion exclusion in fprExclusions) + ProcessExclusion(exclusion, exclusion.target); + + // log totals + ShadowCloneMod.Logger.Msg($"Exclusions: {fprExclusions.Count}"); + return exclusionTargets; + + void ProcessExclusion(FPRExclusion exclusion, Transform transform) + { + if (exclusionTargets.ContainsKey(transform) + && exclusionTargets[transform] != exclusion) return; // found other exclusion root + + exclusion.affectedChildren.Add(transform); // associate with the exclusion + exclusionTargets.TryAdd(transform, exclusion); // add to the dictionary (yes its wasteful) + + foreach (Transform child in transform) + ProcessExclusion(exclusion, child); // process children + } + } + + #endregion + + #region Generic Renderer Configuration + + internal static void ConfigureRenderer(Renderer renderer, bool isShadowClone = false) + { + // generic optimizations + renderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; + + // don't let visual/shadow mesh cull in weird worlds + renderer.allowOcclusionWhenDynamic = false; // (third person stripped local player naked when camera was slightly occluded) + + // shadow clone optimizations (always MeshRenderer) + if (isShadowClone) + { + // renderer.receiveShadows = false; + // renderer.lightProbeUsage = LightProbeUsage.Off; + // renderer.reflectionProbeUsage = ReflectionProbeUsage.Off; + return; + } + + if (renderer is not SkinnedMeshRenderer skinnedMeshRenderer) + return; + + // GraphicsBuffer becomes stale randomly otherwise ??? + //skinnedMeshRenderer.updateWhenOffscreen = true; + + // skin mesh renderer optimizations + skinnedMeshRenderer.skinnedMotionVectors = false; + skinnedMeshRenderer.forceMatrixRecalculationPerRender = false; // expensive + skinnedMeshRenderer.quality = SkinQuality.Bone4; + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/FPRExclusion.cs b/BetterShadowClone/TransformHider/FPRExclusion.cs new file mode 100644 index 0000000..b07aeea --- /dev/null +++ b/BetterShadowClone/TransformHider/FPRExclusion.cs @@ -0,0 +1,35 @@ +using UnityEngine; + +namespace NAK.BetterShadowClone; + +/// +/// Manual exclusion component for the TransformHider (FPR) system. +/// Allows you to manually hide and show a transform that would otherwise be hidden. +/// +public class FPRExclusion : MonoBehaviour +{ + public Transform target; + + internal readonly List affectedVertexIndices = new(); + + internal readonly List affectedChildren = new(); + internal readonly List relatedTasks = new(); + + private void OnEnable() + => SetFPRState(true); + + private void OnDisable() + => SetFPRState(false); + + private void SetFPRState(bool state) + { + if (relatedTasks == null) return; // no hiders to set + foreach (IFPRExclusionTask task in relatedTasks) + task.IsActive = state; + } +} + +public interface IFPRExclusionTask +{ + public bool IsActive { get; set; } +} \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/ITransformHider/ITransformHider.cs b/BetterShadowClone/TransformHider/ITransformHider/ITransformHider.cs new file mode 100644 index 0000000..4922adf --- /dev/null +++ b/BetterShadowClone/TransformHider/ITransformHider/ITransformHider.cs @@ -0,0 +1,11 @@ +namespace NAK.BetterShadowClone; + +public interface ITransformHider : IDisposable +{ + bool IsActive { get; set; } + bool IsValid { get; } + bool Process(); + bool PostProcess(); + void HideTransform(bool forced = false); + void ShowTransform(); +} \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs b/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs new file mode 100644 index 0000000..523aab3 --- /dev/null +++ b/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs @@ -0,0 +1,98 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NAK.BetterShadowClone; + +public class MeshTransformHider : ITransformHider, IFPRExclusionTask +{ + // lame 2 frame init stuff + private const int FrameInitCount = 0; + private int _frameInitCounter; + private bool _hasInitialized; + private bool _markedForDeath; + + // mesh + private readonly MeshRenderer _mainMesh; + private bool _enabledState; + + #region ITransformHider Methods + + public bool IsActive { get; set; } = true; // default hide, but FPRExclusion can override + + // anything player can touch is suspect to death + public bool IsValid => _mainMesh != null && !_markedForDeath; + + public MeshTransformHider(MeshRenderer renderer, IReadOnlyDictionary exclusions) + { + Transform rootBone = renderer.transform; + + // if no key found, dispose + if (!exclusions.TryGetValue(rootBone, out FPRExclusion exclusion)) + { + Dispose(); + return; + } + + exclusion.relatedTasks.Add(this); + + _mainMesh = renderer; + + if (_mainMesh == null + || _mainMesh.sharedMaterials == null + || _mainMesh.sharedMaterials.Length == 0) + { + Dispose(); + } + } + + public bool Process() + { + bool shouldRender = _mainMesh.enabled && _mainMesh.gameObject.activeInHierarchy; + + // GraphicsBuffer becomes stale when mesh is disabled + if (!shouldRender) + { + _frameInitCounter = 0; + _hasInitialized = false; + return false; + } + + // Unity is weird, so we need to wait 2 frames before we can get the graphics buffer + if (_frameInitCounter >= FrameInitCount) + { + if (_hasInitialized) + return true; + + _hasInitialized = true; + return true; + } + + _frameInitCounter++; + return false; + } + + public bool PostProcess() + => true; + + public void HideTransform(bool forced = false) + { + if (!forced && !IsActive) + return; + + _enabledState = _mainMesh.enabled; + _mainMesh.enabled = false; + } + + public void ShowTransform() + { + _mainMesh.enabled = _enabledState; + } + + public void Dispose() + { + _markedForDeath = true; + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs b/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs new file mode 100644 index 0000000..10a32b3 --- /dev/null +++ b/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs @@ -0,0 +1,253 @@ +using System; +using System.Diagnostics; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NAK.BetterShadowClone; + +public class SkinnedTransformHider : ITransformHider +{ + private static readonly int s_Pos = Shader.PropertyToID("pos"); + private static readonly int s_BufferLayout = Shader.PropertyToID("bufferLayout"); + private static readonly int s_WeightedCount = Shader.PropertyToID("weightedCount"); + private static readonly int s_WeightedVertices = Shader.PropertyToID("weightedVertices"); + private static readonly int s_VertexBuffer = Shader.PropertyToID("VertexBuffer"); + + // lame 2 frame init stuff + private const int FrameInitCount = 0; + private int _frameInitCounter; + private bool _hasInitialized; + private bool _markedForDeath; + + // mesh & bone + private readonly SkinnedMeshRenderer _mainMesh; + private readonly Transform _rootBone; + + // main hider stuff + private GraphicsBuffer _graphicsBuffer; + private int _bufferLayout; + + // subtasks + private readonly List _subTasks = new(); + + #region ITransformHider Methods + + public bool IsActive { get; set; } = true; // default hide, but FPRExclusion can override + + // anything player can touch is suspect to death + public bool IsValid => !_markedForDeath && _mainMesh != null && _rootBone != null; + + public SkinnedTransformHider(SkinnedMeshRenderer renderer, IReadOnlyDictionary exclusions) + { + Stopwatch sw = Stopwatch.StartNew(); + + _mainMesh = renderer; + + if (_mainMesh == null + || _mainMesh.sharedMesh == null + || _mainMesh.sharedMaterials == null + || _mainMesh.sharedMaterials.Length == 0) + { + Dispose(); + return; // no mesh or bone! + } + + _rootBone = _mainMesh.rootBone; + _rootBone ??= _mainMesh.transform; // fallback to transform if no root bone + + // log current time + ShadowCloneMod.Logger.Msg($"SkinnedTransformHider part 1 in {sw.ElapsedMilliseconds}ms"); + + SubTask.FindExclusionVertList(renderer, exclusions); + + foreach (var exclusion in exclusions) + { + FPRExclusion fprExclusion = exclusion.Value; + if (fprExclusion.affectedVertexIndices.Count == 0) + continue; // no affected verts + + SubTask subTask = new(this, fprExclusion, fprExclusion.affectedVertexIndices); + _subTasks.Add(subTask); + fprExclusion.relatedTasks.Add(subTask); + fprExclusion.affectedVertexIndices.Clear(); // clear list for next SkinnedTransformHider + } + + // log current time + ShadowCloneMod.Logger.Msg($"SkinnedTransformHider part 3 in {sw.ElapsedMilliseconds}ms"); + + if (_subTasks.Count == 0) + { + Dispose(); // had the bones, but not the weights :? + ShadowCloneMod.Logger.Warning("SkinnedTransformHider No valid exclusions found!"); + } + + sw.Stop(); + + ShadowCloneMod.Logger.Msg($"SkinnedTransformHider created in {sw.ElapsedMilliseconds}ms"); + } + + public bool Process() + { + bool shouldRender = _mainMesh.enabled && _mainMesh.gameObject.activeInHierarchy; + + // GraphicsBuffer becomes stale when mesh is disabled + if (!shouldRender) + { + _frameInitCounter = 0; + _hasInitialized = false; + return false; + } + + // Unity is weird, so we need to wait 2 frames before we can get the graphics buffer + if (_frameInitCounter >= FrameInitCount) + { + if (_hasInitialized) + return true; + + _hasInitialized = true; + SetupGraphicsBuffer(); + return true; + } + + _mainMesh.forceRenderingOff = true; // force off if mesh is disabled + + _frameInitCounter++; + return false; + } + + public bool PostProcess() + => false; // not needed + + public void HideTransform(bool forced = false) + { + _mainMesh.forceRenderingOff = false; + + _graphicsBuffer = _mainMesh.GetVertexBuffer(); + + foreach (SubTask subTask in _subTasks) + if ((forced || subTask.IsActive) && subTask.IsValid) + subTask.Dispatch(); + + _graphicsBuffer.Release(); + } + + public void ShowTransform() + { + // not needed + } + + public void Dispose() + { + _markedForDeath = true; + foreach (SubTask subTask in _subTasks) + subTask.Dispose(); + + _graphicsBuffer?.Dispose(); + _graphicsBuffer = null; + } + + #endregion + + #region Private Methods + + // Unity is weird, so we need to wait 2 frames before we can get the graphics buffer + private void SetupGraphicsBuffer() + { + Mesh mesh = _mainMesh.sharedMesh; + + _bufferLayout = 0; + if (mesh.HasVertexAttribute(VertexAttribute.Position)) _bufferLayout += 3; + if (mesh.HasVertexAttribute(VertexAttribute.Normal)) _bufferLayout += 3; + if (mesh.HasVertexAttribute(VertexAttribute.Tangent)) _bufferLayout += 4; + // ComputeShader is doing bitshift so we dont need to multiply by 4 + + _mainMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw; + } + + #endregion + + #region Sub Task Class + + private class SubTask : IFPRExclusionTask + { + public bool IsActive { get; set; } = true; + public bool IsValid => _computeBuffer != null; // TODO: cleanup dead tasks + + private readonly SkinnedTransformHider _parent; + private readonly Transform _shrinkBone; + private readonly int _vertexCount; + private readonly ComputeBuffer _computeBuffer; + private readonly int _threadGroups; + + public SubTask(SkinnedTransformHider parent, FPRExclusion exclusion, List exclusionVerts) + { + _parent = parent; + _shrinkBone = exclusion.target; + + _vertexCount = exclusionVerts.Count; + _computeBuffer = new ComputeBuffer(_vertexCount, sizeof(int)); + _computeBuffer.SetData(exclusionVerts.ToArray()); + + const float xThreadGroups = 64f; + _threadGroups = Mathf.CeilToInt(_vertexCount / xThreadGroups); + } + + public void Dispatch() + { + Vector3 pos = _parent._rootBone.transform.InverseTransformPoint(_shrinkBone.position) * _parent._rootBone.lossyScale.y; + TransformHiderManager.shader.SetVector(s_Pos, pos); + TransformHiderManager.shader.SetInt(s_WeightedCount, _vertexCount); + TransformHiderManager.shader.SetInt(s_BufferLayout, _parent._bufferLayout); + TransformHiderManager.shader.SetBuffer(0, s_WeightedVertices, _computeBuffer); + TransformHiderManager.shader.SetBuffer(0, s_VertexBuffer, _parent._graphicsBuffer); + TransformHiderManager.shader.Dispatch(0, _threadGroups, 1, 1); + } + + public void Dispose() + { + _computeBuffer?.Dispose(); + } + + #region Private Methods + + public static void FindExclusionVertList(SkinnedMeshRenderer renderer, IReadOnlyDictionary exclusions) + { + var boneWeights = renderer.sharedMesh.boneWeights; + + HashSet weights = new(); + for (int i = 0; i < renderer.bones.Length; i++) + { + // if bone == any key in exclusions, add to weights + if (!exclusions.TryGetValue(renderer.bones[i], out FPRExclusion _)) + continue; + + weights.Add(i); + } + + for (int i = 0; i < boneWeights.Length; i++) + { + BoneWeight weight = boneWeights[i]; + + Transform bone = null; + const float minWeightThreshold = 0.2f; + if (weights.Contains(weight.boneIndex0) && weight.weight0 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex0]; + else if (weights.Contains(weight.boneIndex1) && weight.weight1 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex1]; + else if (weights.Contains(weight.boneIndex2) && weight.weight2 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex2]; + else if (weights.Contains(weight.boneIndex3) && weight.weight3 > minWeightThreshold) + bone = renderer.bones[weight.boneIndex3]; + + if (bone == null) continue; // no bone found + + // add vertex to exclusion list + exclusions[bone].affectedVertexIndices.Add(i); + } + } + + #endregion + } + + #endregion +} \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/TransformHiderManager.cs b/BetterShadowClone/TransformHider/TransformHiderManager.cs new file mode 100644 index 0000000..2c14854 --- /dev/null +++ b/BetterShadowClone/TransformHider/TransformHiderManager.cs @@ -0,0 +1,213 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.VRModeSwitch; +using MagicaCloth; +using UnityEngine; + +namespace NAK.BetterShadowClone; + +// Built on top of Koneko's BoneHider but to mimic the ShadowCloneManager + +public class TransformHiderManager : MonoBehaviour +{ + public static ComputeShader shader; + + #region Singleton Implementation + + private static TransformHiderManager _instance; + public static TransformHiderManager Instance + { + get + { + if (_instance != null) return _instance; + _instance = new GameObject("Koneko.TransformHiderManager").AddComponent(); + DontDestroyOnLoad(_instance.gameObject); + return _instance; + } + } + + #endregion + + // Game cameras + private static Camera s_MainCamera; + private static Camera s_UiCamera; + + // Settings + internal static bool s_DebugHeadHide; + internal static bool s_DisallowFprExclusions = true; + + // Implementation + private bool _hasRenderedThisFrame; + + // Shadow Clones + private readonly List s_TransformHider = new(); + public void AddTransformHider(ITransformHider clone) + => s_TransformHider.Add(clone); + + // Debug + private bool _debugHeadHiderProcessingTime; + private readonly StopWatch _stopWatch = new(); + + #region Unity Events + + private void Start() + { + if (Instance != null + && Instance != this) + { + Destroy(this); + return; + } + + UpdatePlayerCameras(); + + s_DisallowFprExclusions = ModSettings.EntryDontRespectFPR.Value; + s_DebugHeadHide = ModSettings.EntryDebugHeadHide.Value; + + VRModeSwitchEvents.OnCompletedVRModeSwitch.AddListener(OnVRModeSwitchCompleted); + } + + private void OnEnable() + { + Camera.onPreRender += MyOnPreRender; + Camera.onPostRender += MyOnPostRender; + } + + private void OnDisable() + { + Camera.onPreRender -= MyOnPreRender; + Camera.onPostRender -= MyOnPostRender; + } + + private void OnDestroy() + { + VRModeSwitchEvents.OnCompletedVRModeSwitch.RemoveListener(OnVRModeSwitchCompleted); + OnAvatarCleared(); + } + + #endregion + + #region Transform Hider Managment + + private void Update() + { + _hasRenderedThisFrame = false; + } + + private void MyOnPreRender(Camera cam) + { + if (_hasRenderedThisFrame) + return; // can only hide head once per frame + + if (cam != s_MainCamera // only hide in player cam, or if debug is on + && !s_DebugHeadHide) + return; + + if (!CheckPlayerCamWithinRange()) + return; // player is too far away (likely HoloPort or Sitting) + + if (!ShadowCloneMod.CheckWantsToHideHead(cam)) + return; // listener said no (Third Person, etc) + + _hasRenderedThisFrame = true; + + _stopWatch.Start(); + + for (int i = s_TransformHider.Count - 1; i >= 0; i--) + { + ITransformHider hider = s_TransformHider[i]; + if (hider is not { IsValid: true }) + { + hider?.Dispose(); + s_TransformHider.RemoveAt(i); + continue; // invalid or dead + } + + if (!hider.Process()) continue; // not ready yet or disabled + + hider.HideTransform(s_DisallowFprExclusions); + } + + _stopWatch.Stop(); + if (_debugHeadHiderProcessingTime) Debug.Log($"TransformHiderManager.MyOnPreRender({s_DebugHeadHide}) took {_stopWatch.ElapsedMilliseconds}ms"); + } + + private void MyOnPostRender(Camera cam) + { + if (cam != s_UiCamera) return; // ui camera is expected to render last + + for (int i = s_TransformHider.Count - 1; i >= 0; i--) + { + ITransformHider hider = s_TransformHider[i]; + if (hider is not { IsValid: true }) + { + hider?.Dispose(); + s_TransformHider.RemoveAt(i); + continue; // invalid or dead + } + + if (!hider.PostProcess()) continue; // does not need post processing + + hider.ShowTransform(); + } + } + + #endregion + + #region Game Events + + public void OnAvatarCleared() + { + // Dispose all shadow clones BEFORE game unloads avatar + // Otherwise we memory leak the shadow clones mesh & material instances!!! + foreach (ITransformHider hider in s_TransformHider) + hider.Dispose(); + s_TransformHider.Clear(); + } + + private void OnVRModeSwitchCompleted(bool _) + => UpdatePlayerCameras(); + + #endregion + + #region Private Methods + + private static void UpdatePlayerCameras() + { + s_MainCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent(); + s_UiCamera = s_MainCamera.transform.Find("_UICamera").GetComponent(); + } + + private static bool CheckPlayerCamWithinRange() + { + if (PlayerSetup.Instance == null) + return false; // hack + + const float MinHeadHidingRange = 0.5f; + Vector3 playerHeadPos = PlayerSetup.Instance.GetViewWorldPosition(); + Vector3 playerCamPos = s_MainCamera.transform.position; + float scaleModifier = PlayerSetup.Instance.GetPlaySpaceScale(); + return (Vector3.Distance(playerHeadPos, playerCamPos) < (MinHeadHidingRange * scaleModifier)); + } + + #endregion + + #region Static Helpers + + internal static bool IsLegacyFPRExcluded(Component renderer) + => renderer.gameObject.name.Contains("[FPR]"); + + internal static ITransformHider CreateTransformHider(Component renderer, IReadOnlyDictionary exclusions) + { + if (IsLegacyFPRExcluded(renderer)) + return null; + + return renderer switch + { + SkinnedMeshRenderer skinnedMeshRenderer => new SkinnedTransformHider(skinnedMeshRenderer, exclusions), + MeshRenderer meshRenderer => new MeshTransformHider(meshRenderer, exclusions), + _ => null + }; + } + + #endregion +} \ No newline at end of file diff --git a/.Deprecated/EzCurls/format.json b/BetterShadowClone/format.json similarity index 100% rename from .Deprecated/EzCurls/format.json rename to BetterShadowClone/format.json diff --git a/.Deprecated/CVRGizmos/CVRGizmoManager.cs b/CVRGizmos/CVRGizmoManager.cs similarity index 76% rename from .Deprecated/CVRGizmos/CVRGizmoManager.cs rename to CVRGizmos/CVRGizmoManager.cs index b1d55b5..fd03eb0 100644 --- a/.Deprecated/CVRGizmos/CVRGizmoManager.cs +++ b/CVRGizmos/CVRGizmoManager.cs @@ -2,18 +2,18 @@ using UnityEngine; using Gizmos = Popcron.Gizmos; -namespace NAK.CVRGizmos; - -public class CVRGizmoManager : MonoBehaviour +namespace NAK.CVRGizmos { - public static CVRGizmoManager Instance; + public class CVRGizmoManager : MonoBehaviour + { + public static CVRGizmoManager Instance; - public bool g_enabled = false; - public bool g_localOnly = false; + public bool g_enabled = false; + public bool g_localOnly = false; - public MonoBehaviour[] managed; + public MonoBehaviour[] managed; - public System.Type[] GizmoTypes = { + public System.Type[] GizmoTypes = { typeof(CVRGizmos_Pointer), typeof(CVRGizmos_AdvancedAvatarSettingsTrigger), typeof(CVRGizmos_SpawnableTrigger), @@ -30,8 +30,8 @@ public class CVRGizmoManager : MonoBehaviour typeof(CVRGizmos_CapsuleCollider), }; - private void Start() - { + private void Start() + { CVRGizmoManager.Instance = this; managed = new MonoBehaviour[GizmoTypes.Count()]; for (int i = 0; i < GizmoTypes.Count(); i++) @@ -40,8 +40,8 @@ public class CVRGizmoManager : MonoBehaviour } } - public void EnableGizmos(bool able) - { + public void EnableGizmos(bool able) + { for (int i = 0; i < GizmoTypes.Count(); i++) { managed[i].enabled = able; @@ -50,11 +50,12 @@ public class CVRGizmoManager : MonoBehaviour RefreshGizmos(); } - public void RefreshGizmos() - { + public void RefreshGizmos() + { for (int i = 0; i < GizmoTypes.Count(); i++) { managed[i].Invoke("CacheGizmos", 0f); } } + } } \ No newline at end of file diff --git a/.Deprecated/CVRGizmos/CVRGizmos.csproj b/CVRGizmos/CVRGizmos.csproj similarity index 100% rename from .Deprecated/CVRGizmos/CVRGizmos.csproj rename to CVRGizmos/CVRGizmos.csproj diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsPointer.cs b/CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsPointer.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsPointer.cs rename to CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsPointer.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsTrigger.cs b/CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsTrigger.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsTrigger.cs rename to CVRGizmos/GizmoTypes/CVRAdvancedAvatarSettingsTrigger.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRAvatar.cs b/CVRGizmos/GizmoTypes/CVRAvatar.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRAvatar.cs rename to CVRGizmos/GizmoTypes/CVRAvatar.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRAvatarPickupMarker.cs b/CVRGizmos/GizmoTypes/CVRAvatarPickupMarker.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRAvatarPickupMarker.cs rename to CVRGizmos/GizmoTypes/CVRAvatarPickupMarker.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRDistanceConstrain.cs b/CVRGizmos/GizmoTypes/CVRDistanceConstrain.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRDistanceConstrain.cs rename to CVRGizmos/GizmoTypes/CVRDistanceConstrain.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRDistanceLod.cs b/CVRGizmos/GizmoTypes/CVRDistanceLod.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRDistanceLod.cs rename to CVRGizmos/GizmoTypes/CVRDistanceLod.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRGizmoBase.cs b/CVRGizmos/GizmoTypes/CVRGizmoBase.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRGizmoBase.cs rename to CVRGizmos/GizmoTypes/CVRGizmoBase.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRHapticAreaChest.cs b/CVRGizmos/GizmoTypes/CVRHapticAreaChest.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRHapticAreaChest.cs rename to CVRGizmos/GizmoTypes/CVRHapticAreaChest.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRHapticZone.cs b/CVRGizmos/GizmoTypes/CVRHapticZone.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRHapticZone.cs rename to CVRGizmos/GizmoTypes/CVRHapticZone.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRPointer.cs b/CVRGizmos/GizmoTypes/CVRPointer.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRPointer.cs rename to CVRGizmos/GizmoTypes/CVRPointer.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRSpawnableTrigger.cs b/CVRGizmos/GizmoTypes/CVRSpawnableTrigger.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRSpawnableTrigger.cs rename to CVRGizmos/GizmoTypes/CVRSpawnableTrigger.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/CVRToggleStateTrigger.cs b/CVRGizmos/GizmoTypes/CVRToggleStateTrigger.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/CVRToggleStateTrigger.cs rename to CVRGizmos/GizmoTypes/CVRToggleStateTrigger.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/Unity_BoxCollider.cs b/CVRGizmos/GizmoTypes/Unity_BoxCollider.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/Unity_BoxCollider.cs rename to CVRGizmos/GizmoTypes/Unity_BoxCollider.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/Unity_CapsuleCollider.cs b/CVRGizmos/GizmoTypes/Unity_CapsuleCollider.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/Unity_CapsuleCollider.cs rename to CVRGizmos/GizmoTypes/Unity_CapsuleCollider.cs diff --git a/.Deprecated/CVRGizmos/GizmoTypes/Unity_SphereCollider.cs b/CVRGizmos/GizmoTypes/Unity_SphereCollider.cs similarity index 100% rename from .Deprecated/CVRGizmos/GizmoTypes/Unity_SphereCollider.cs rename to CVRGizmos/GizmoTypes/Unity_SphereCollider.cs diff --git a/.Deprecated/CVRGizmos/Main.cs b/CVRGizmos/Main.cs similarity index 100% rename from .Deprecated/CVRGizmos/Main.cs rename to CVRGizmos/Main.cs diff --git a/CVRGizmos/Popcron.Gizmos/Constants.cs b/CVRGizmos/Popcron.Gizmos/Constants.cs new file mode 100644 index 0000000..e5884c9 --- /dev/null +++ b/CVRGizmos/Popcron.Gizmos/Constants.cs @@ -0,0 +1,8 @@ +namespace Popcron +{ + public class Constants + { + public const string UniqueIdentifier = "Popcron.Gizmos"; + public const string EnabledKey = UniqueIdentifier + ".Enabled"; + } +} \ No newline at end of file diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawer.cs b/CVRGizmos/Popcron.Gizmos/Drawer.cs similarity index 86% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/Drawer.cs rename to CVRGizmos/Popcron.Gizmos/Drawer.cs index 729445f..8e42ae8 100644 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawer.cs +++ b/CVRGizmos/Popcron.Gizmos/Drawer.cs @@ -1,21 +1,21 @@ using System.Reflection; using UnityEngine; -namespace Popcron; - -public abstract class Drawer +namespace Popcron { - private static Dictionary typeToDrawer = null; - - public abstract int Draw(ref Vector3[] buffer, params object[] args); - - public Drawer() + public abstract class Drawer { + private static Dictionary typeToDrawer = null; + + public abstract int Draw(ref Vector3[] buffer, params object[] args); + + public Drawer() + { } - public static Drawer Get() where T : class - { + public static Drawer Get() where T : class + { //find all drawers if (typeToDrawer == null) { @@ -64,4 +64,5 @@ public abstract class Drawer return null; } } -} \ No newline at end of file + } +} diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/CubeDrawer.cs b/CVRGizmos/Popcron.Gizmos/Drawers/CubeDrawer.cs similarity index 93% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/CubeDrawer.cs rename to CVRGizmos/Popcron.Gizmos/Drawers/CubeDrawer.cs index 78ab2bf..478a0b6 100644 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/CubeDrawer.cs +++ b/CVRGizmos/Popcron.Gizmos/Drawers/CubeDrawer.cs @@ -1,16 +1,16 @@ using UnityEngine; -namespace Popcron; - -public class CubeDrawer : Drawer +namespace Popcron { - public CubeDrawer() + public class CubeDrawer : Drawer { + public CubeDrawer() + { } - public override int Draw(ref Vector3[] buffer, params object[] values) - { + public override int Draw(ref Vector3[] buffer, params object[] values) + { Vector3 position = (Vector3)values[0]; Quaternion rotation = (Quaternion)values[1]; Vector3 size = (Vector3)values[2]; @@ -92,4 +92,5 @@ public class CubeDrawer : Drawer return 24; } -} \ No newline at end of file + } +} diff --git a/CVRGizmos/Popcron.Gizmos/Drawers/LineDrawer.cs b/CVRGizmos/Popcron.Gizmos/Drawers/LineDrawer.cs new file mode 100644 index 0000000..f9193cf --- /dev/null +++ b/CVRGizmos/Popcron.Gizmos/Drawers/LineDrawer.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace Popcron +{ + public class LineDrawer : Drawer + { + public LineDrawer() + { + + } + + public override int Draw(ref Vector3[] buffer, params object[] args) + { + buffer[0] = (Vector3)args[0]; + buffer[1] = (Vector3)args[1]; + return 2; + } + } +} diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/PolygonDrawer.cs b/CVRGizmos/Popcron.Gizmos/Drawers/PolygonDrawer.cs similarity index 84% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/PolygonDrawer.cs rename to CVRGizmos/Popcron.Gizmos/Drawers/PolygonDrawer.cs index ab66d29..ad3b1b2 100644 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/PolygonDrawer.cs +++ b/CVRGizmos/Popcron.Gizmos/Drawers/PolygonDrawer.cs @@ -1,16 +1,16 @@ using UnityEngine; -namespace Popcron; - -public class PolygonDrawer : Drawer +namespace Popcron { - public PolygonDrawer() + public class PolygonDrawer : Drawer { + public PolygonDrawer() + { } - public override int Draw(ref Vector3[] buffer, params object[] values) - { + public override int Draw(ref Vector3[] buffer, params object[] values) + { Vector3 position = (Vector3)values[0]; int points = (int)values[1]; float radius = (float)values[2]; @@ -36,4 +36,5 @@ public class PolygonDrawer : Drawer return points * 2; } -} \ No newline at end of file + } +} diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/SquareDrawer.cs b/CVRGizmos/Popcron.Gizmos/Drawers/SquareDrawer.cs similarity index 89% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/SquareDrawer.cs rename to CVRGizmos/Popcron.Gizmos/Drawers/SquareDrawer.cs index 7b0643d..c46160a 100644 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Drawers/SquareDrawer.cs +++ b/CVRGizmos/Popcron.Gizmos/Drawers/SquareDrawer.cs @@ -1,16 +1,16 @@ using UnityEngine; -namespace Popcron; - -public class SquareDrawer : Drawer +namespace Popcron { - public SquareDrawer() + public class SquareDrawer : Drawer { + public SquareDrawer() + { } - public override int Draw(ref Vector3[] buffer, params object[] values) - { + public override int Draw(ref Vector3[] buffer, params object[] values) + { Vector2 position = default; if (values[0] is Vector2 p2) { @@ -68,4 +68,5 @@ public class SquareDrawer : Drawer return 8; } -} \ No newline at end of file + } +} diff --git a/CVRGizmos/Popcron.Gizmos/Element.cs b/CVRGizmos/Popcron.Gizmos/Element.cs new file mode 100644 index 0000000..361fd59 --- /dev/null +++ b/CVRGizmos/Popcron.Gizmos/Element.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace Popcron +{ + internal class Element + { + public Vector3[] points = { }; + public Color color = Color.white; + public bool dashed = false; + public Matrix4x4 matrix = Matrix4x4.identity; + } +} \ No newline at end of file diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/Gizmos.cs b/CVRGizmos/Popcron.Gizmos/Gizmos.cs similarity index 52% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/Gizmos.cs rename to CVRGizmos/Popcron.Gizmos/Gizmos.cs index cf86940..a897991 100644 --- a/.Deprecated/CVRGizmos/Popcron.Gizmos/Gizmos.cs +++ b/CVRGizmos/Popcron.Gizmos/Gizmos.cs @@ -1,29 +1,29 @@ using UnityEngine; -namespace Popcron; - -public class Gizmos +namespace Popcron { - private static string _prefsKey = null; - private static int? _bufferSize = null; - private static bool? _enabled = null; - private static float? _dashGap = null; - private static bool? _cull = null; - private static int? _pass = null; - private static Vector3? _offset = null; - - private static Vector3[] buffer = new Vector3[BufferSize]; - - /// - /// By default, it will always render to scene view camera and the main camera. - /// Subscribing to this allows you to whitelist your custom cameras. - /// - public static Func CameraFilter = cam => false; - - private static string PrefsKey + public class Gizmos { - get + private static string _prefsKey = null; + private static int? _bufferSize = null; + private static bool? _enabled = null; + private static float? _dashGap = null; + private static bool? _cull = null; + private static int? _pass = null; + private static Vector3? _offset = null; + + private static Vector3[] buffer = new Vector3[BufferSize]; + + /// + /// By default, it will always render to scene view camera and the main camera. + /// Subscribing to this allows you to whitelist your custom cameras. + /// + public static Func CameraFilter = cam => false; + + private static string PrefsKey { + get + { if (string.IsNullOrEmpty(_prefsKey)) { _prefsKey = $"{SystemInfo.deviceUniqueIdentifier}.{Application.companyName}.{Application.productName}.{Constants.UniqueIdentifier}"; @@ -31,34 +31,34 @@ public class Gizmos return _prefsKey; } - } + } - /// - /// The matrix to use. - /// - public static Matrix4x4 Matrix - { - get => GizmosInstance.Matrix; - set => GizmosInstance.Matrix = value; - } - - /// - /// The matrix to use. - /// - public static Color Color - { - get => GizmosInstance.Color; - set => GizmosInstance.Color = value; - } - - /// - /// The size of the total gizmos buffer. - /// Default is 4096. - /// - public static int BufferSize - { - get + /// + /// The matrix to use. + /// + public static Matrix4x4 Matrix { + get => GizmosInstance.Matrix; + set => GizmosInstance.Matrix = value; + } + + /// + /// The matrix to use. + /// + public static Color Color + { + get => GizmosInstance.Color; + set => GizmosInstance.Color = value; + } + + /// + /// The size of the total gizmos buffer. + /// Default is 4096. + /// + public static int BufferSize + { + get + { if (_bufferSize == null) { _bufferSize = PlayerPrefs.GetInt($"{PrefsKey}.BufferSize", 4096); @@ -66,8 +66,8 @@ public class Gizmos return _bufferSize.Value; } - set - { + set + { value = Mathf.Clamp(value, 0, int.MaxValue); if (_bufferSize != value) { @@ -78,15 +78,15 @@ public class Gizmos buffer = new Vector3[value]; } } - } + } - /// - /// Toggles wether the gizmos could be drawn or not. - /// - public static bool Enabled - { - get + /// + /// Toggles wether the gizmos could be drawn or not. + /// + public static bool Enabled { + get + { if (_enabled == null) { _enabled = PlayerPrefs.GetInt($"{PrefsKey}.Enabled", 1) == 1; @@ -94,24 +94,24 @@ public class Gizmos return _enabled.Value; } - set - { + set + { if (_enabled != value) { _enabled = value; PlayerPrefs.SetInt($"{PrefsKey}.Enabled", value ? 1 : 0); } } - } + } - /// - /// The size of the gap when drawing dashed elements. - /// Default gap size is 0.1 - /// - public static float DashGap - { - get + /// + /// The size of the gap when drawing dashed elements. + /// Default gap size is 0.1 + /// + public static float DashGap { + get + { if (_dashGap == null) { _dashGap = PlayerPrefs.GetFloat($"{PrefsKey}.DashGap", 0.1f); @@ -119,46 +119,46 @@ public class Gizmos return _dashGap.Value; } - set - { + set + { if (_dashGap != value) { _dashGap = value; PlayerPrefs.SetFloat($"{PrefsKey}.DashGap", value); } } - } + } - [Obsolete("This property is obsolete. Use FrustumCulling instead.", false)] - public static bool Cull - { - get + [Obsolete("This property is obsolete. Use FrustumCulling instead.", false)] + public static bool Cull { + get + { return FrustumCulling; } - set - { + set + { FrustumCulling = value; } - } + } - [Obsolete("This property is obsolete. Subscribe to CameraFilter predicate instead and return true for your custom camera.", false)] - public static Camera Camera - { - get => null; - set + [Obsolete("This property is obsolete. Subscribe to CameraFilter predicate instead and return true for your custom camera.", false)] + public static Camera Camera { + get => null; + set + { } - } + } - /// - /// Should the camera not draw elements that are not visible? - /// - public static bool FrustumCulling - { - get + /// + /// Should the camera not draw elements that are not visible? + /// + public static bool FrustumCulling { + get + { if (_cull == null) { _cull = PlayerPrefs.GetInt($"{PrefsKey}.FrustumCulling", 1) == 1; @@ -166,32 +166,32 @@ public class Gizmos return _cull.Value; } - set - { + set + { if (_cull != value) { _cull = value; PlayerPrefs.SetInt($"{PrefsKey}.FrustumCulling", value ? 1 : 0); } } - } + } - /// - /// The material being used to render. - /// - public static Material Material - { - get => GizmosInstance.Material; - set => GizmosInstance.Material = value; - } - - /// - /// Rendering pass to activate. - /// - public static int Pass - { - get + /// + /// The material being used to render. + /// + public static Material Material { + get => GizmosInstance.Material; + set => GizmosInstance.Material = value; + } + + /// + /// Rendering pass to activate. + /// + public static int Pass + { + get + { if (_pass == null) { _pass = PlayerPrefs.GetInt($"{PrefsKey}.Pass", 0); @@ -199,23 +199,23 @@ public class Gizmos return _pass.Value; } - set - { + set + { if (_pass != value) { _pass = value; PlayerPrefs.SetInt($"{PrefsKey}.Pass", value); } } - } + } - /// - /// Global offset for all points. Default is (0, 0, 0). - /// - public static Vector3 Offset - { - get + /// + /// Global offset for all points. Default is (0, 0, 0). + /// + public static Vector3 Offset { + get + { const string Delim = ","; if (_offset == null) { @@ -235,8 +235,8 @@ public class Gizmos return _offset.Value; } - set - { + set + { const string Delim = ","; if (_offset != value) { @@ -244,13 +244,13 @@ public class Gizmos PlayerPrefs.SetString($"{PrefsKey}.Offset", value.x + Delim + value.y + Delim + value.y); } } - } + } - /// - /// Draws an element onto the screen. - /// - public static void Draw(Color? color, bool dashed, params object[] args) where T : Drawer - { + /// + /// Draws an element onto the screen. + /// + public static void Draw(Color? color, bool dashed, params object[] args) where T : Drawer + { if (!Enabled) { return; @@ -268,11 +268,11 @@ public class Gizmos } } - /// - /// Draws an array of lines. Useful for things like paths. - /// - public static void Lines(Vector3[] lines, Color? color = null, bool dashed = false) - { + /// + /// Draws an array of lines. Useful for things like paths. + /// + public static void Lines(Vector3[] lines, Color? color = null, bool dashed = false) + { if (!Enabled) { return; @@ -281,69 +281,69 @@ public class Gizmos GizmosInstance.Submit(lines, color, dashed); } - /// - /// Draw line in world space. - /// - public static void Line(Vector3 a, Vector3 b, Color? color = null, bool dashed = false) - { + /// + /// Draw line in world space. + /// + public static void Line(Vector3 a, Vector3 b, Color? color = null, bool dashed = false) + { Draw(color, dashed, a, b); } - /// - /// Draw square in world space. - /// - public static void Square(Vector2 position, Vector2 size, Color? color = null, bool dashed = false) - { + /// + /// Draw square in world space. + /// + public static void Square(Vector2 position, Vector2 size, Color? color = null, bool dashed = false) + { Square(position, Quaternion.identity, size, color, dashed); } - /// - /// Draw square in world space with float diameter parameter. - /// - public static void Square(Vector2 position, float diameter, Color? color = null, bool dashed = false) - { + /// + /// Draw square in world space with float diameter parameter. + /// + public static void Square(Vector2 position, float diameter, Color? color = null, bool dashed = false) + { Square(position, Quaternion.identity, Vector2.one * diameter, color, dashed); } - /// - /// Draw square in world space with a rotation parameter. - /// - public static void Square(Vector2 position, Quaternion rotation, Vector2 size, Color? color = null, bool dashed = false) - { + /// + /// Draw square in world space with a rotation parameter. + /// + public static void Square(Vector2 position, Quaternion rotation, Vector2 size, Color? color = null, bool dashed = false) + { Draw(color, dashed, position, rotation, size); } - /// - /// Draws a cube in world space. - /// - public static void Cube(Vector3 position, Quaternion rotation, Vector3 size, Color? color = null, bool dashed = false) - { + /// + /// Draws a cube in world space. + /// + public static void Cube(Vector3 position, Quaternion rotation, Vector3 size, Color? color = null, bool dashed = false) + { Draw(color, dashed, position, rotation, size); } - /// - /// Draws a rectangle in screen space. - /// - public static void Rect(Rect rect, Camera camera, Color? color = null, bool dashed = false) - { + /// + /// Draws a rectangle in screen space. + /// + public static void Rect(Rect rect, Camera camera, Color? color = null, bool dashed = false) + { rect.y = Screen.height - rect.y; Vector2 corner = camera.ScreenToWorldPoint(new Vector2(rect.x, rect.y - rect.height)); Draw(color, dashed, corner + rect.size * 0.5f, Quaternion.identity, rect.size); } - /// - /// Draws a representation of a bounding box. - /// - public static void Bounds(Bounds bounds, Color? color = null, bool dashed = false) - { + /// + /// Draws a representation of a bounding box. + /// + public static void Bounds(Bounds bounds, Color? color = null, bool dashed = false) + { Draw(color, dashed, bounds.center, Quaternion.identity, bounds.size); } - /// - /// Draws a cone similar to the one that spot lights draw. - /// - public static void Cone(Vector3 position, Quaternion rotation, float length, float angle, Color? color = null, bool dashed = false, int pointsCount = 16) - { + /// + /// Draws a cone similar to the one that spot lights draw. + /// + public static void Cone(Vector3 position, Quaternion rotation, float length, float angle, Color? color = null, bool dashed = false, int pointsCount = 16) + { //draw the end of the cone float endAngle = Mathf.Tan(angle * 0.5f * Mathf.Deg2Rad) * length; Vector3 forward = rotation * Vector3.forward; @@ -360,33 +360,34 @@ public class Gizmos } } - /// - /// Draws a sphere at position with specified radius. - /// - public static void Sphere(Vector3 position, float radius, Color? color = null, bool dashed = false, int pointsCount = 16) - { + /// + /// Draws a sphere at position with specified radius. + /// + public static void Sphere(Vector3 position, float radius, Color? color = null, bool dashed = false, int pointsCount = 16) + { float offset = 0f; Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(0f, 0f, 0f)); Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(90f, 0f, 0f)); Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(0f, 90f, 90f)); } - /// - /// Draws a circle in world space and billboards towards the camera. - /// - public static void Circle(Vector3 position, float radius, Camera camera, Color? color = null, bool dashed = false, int pointsCount = 16) - { + /// + /// Draws a circle in world space and billboards towards the camera. + /// + public static void Circle(Vector3 position, float radius, Camera camera, Color? color = null, bool dashed = false, int pointsCount = 16) + { float offset = 0f; Quaternion rotation = Quaternion.LookRotation(position - camera.transform.position); Draw(color, dashed, position, pointsCount, radius, offset, rotation); } - /// - /// Draws a circle in world space with a specified rotation. - /// - public static void Circle(Vector3 position, float radius, Quaternion rotation, Color? color = null, bool dashed = false, int pointsCount = 16) - { + /// + /// Draws a circle in world space with a specified rotation. + /// + public static void Circle(Vector3 position, float radius, Quaternion rotation, Color? color = null, bool dashed = false, int pointsCount = 16) + { float offset = 0f; Draw(color, dashed, position, pointsCount, radius, offset, rotation); } -} \ No newline at end of file + } +} diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/GizmosInstance.cs b/CVRGizmos/Popcron.Gizmos/GizmosInstance.cs similarity index 100% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/GizmosInstance.cs rename to CVRGizmos/Popcron.Gizmos/GizmosInstance.cs diff --git a/.Deprecated/CVRGizmos/Popcron.Gizmos/LICENSE.md b/CVRGizmos/Popcron.Gizmos/LICENSE.md similarity index 100% rename from .Deprecated/CVRGizmos/Popcron.Gizmos/LICENSE.md rename to CVRGizmos/Popcron.Gizmos/LICENSE.md diff --git a/.Deprecated/CVRGizmos/Properties/AssemblyInfo.cs b/CVRGizmos/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/CVRGizmos/Properties/AssemblyInfo.cs rename to CVRGizmos/Properties/AssemblyInfo.cs diff --git a/.Deprecated/CVRGizmos/format.json b/CVRGizmos/format.json similarity index 100% rename from .Deprecated/CVRGizmos/format.json rename to CVRGizmos/format.json diff --git a/.Experimental/CVRLuaToolsExtension/CVRLuaToolsExtension.csproj b/CVRLuaToolsExtension/CVRLuaToolsExtension.csproj similarity index 100% rename from .Experimental/CVRLuaToolsExtension/CVRLuaToolsExtension.csproj rename to CVRLuaToolsExtension/CVRLuaToolsExtension.csproj diff --git a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs b/CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs rename to CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs diff --git a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs rename to CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs diff --git a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs similarity index 91% rename from .Experimental/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs rename to CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs index 5dc7b86..6f77a61 100644 --- a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs +++ b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs @@ -1,23 +1,18 @@ using ABI.CCK.Components; using ABI.Scripting.CVRSTL.Client; using System.Diagnostics; -using ABI_RC.Scripting.Persistence; using MTJobSystem; -using UnityEngine; namespace NAK.CVRLuaToolsExtension; public static class CVRLuaClientBehaviourExtensions { internal static readonly Dictionary _isRestarting = new(); - private static string PersistentDataPath; #region Public Methods public static void Restart(this CVRLuaClientBehaviour behaviour) { - PersistentDataPath ??= Application.persistentDataPath; // needs to be set on main - if (_isRestarting.TryGetValue(behaviour, out bool isRestarting) && isRestarting) { CVRLuaToolsExtensionMod.Logger.Warning($"Restart is already in progress for {behaviour.ScriptName}."); @@ -109,9 +104,6 @@ public static class CVRLuaClientBehaviourExtensions behaviour._startupMessageQueue.Clear(); // will be repopulated behaviour.LogInfo("[CVRLuaToolsExtension] Resetting script...\n"); - // remove the script from the persistence manager, as the storage needs to be reinitialized - PersistenceManager.HandleUnsubscribe(behaviour.Storage, behaviour.script, behaviour.Context.ParentContent.ContentType, behaviour.Context.AssetID); - behaviour.script = null; behaviour.script = LuaScriptFactory.ForLuaBehaviour(behaviour, boundObjectEntries, behaviour.gameObject, behaviour.transform); diff --git a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs b/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs rename to CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs diff --git a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs b/CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs rename to CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs diff --git a/.Experimental/CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs b/CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs rename to CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs diff --git a/.Experimental/CVRLuaToolsExtension/Main.cs b/CVRLuaToolsExtension/Main.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/Main.cs rename to CVRLuaToolsExtension/Main.cs diff --git a/.Experimental/CVRLuaToolsExtension/Properties/AssemblyInfo.cs b/CVRLuaToolsExtension/Properties/AssemblyInfo.cs similarity index 100% rename from .Experimental/CVRLuaToolsExtension/Properties/AssemblyInfo.cs rename to CVRLuaToolsExtension/Properties/AssemblyInfo.cs diff --git a/.Experimental/CVRLuaToolsExtension/README.md b/CVRLuaToolsExtension/README.md similarity index 100% rename from .Experimental/CVRLuaToolsExtension/README.md rename to CVRLuaToolsExtension/README.md diff --git a/.Experimental/CVRLuaToolsExtension/format.json b/CVRLuaToolsExtension/format.json similarity index 100% rename from .Experimental/CVRLuaToolsExtension/format.json rename to CVRLuaToolsExtension/format.json diff --git a/.Deprecated/ChatBoxExtensions/ChatBoxExtensions.csproj b/ChatBoxExtensions/ChatBoxExtensions.csproj similarity index 100% rename from .Deprecated/ChatBoxExtensions/ChatBoxExtensions.csproj rename to ChatBoxExtensions/ChatBoxExtensions.csproj diff --git a/.Deprecated/ChatBoxExtensions/HarmonyPatches.cs b/ChatBoxExtensions/HarmonyPatches.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/HarmonyPatches.cs rename to ChatBoxExtensions/HarmonyPatches.cs diff --git a/.Deprecated/ChatBoxExtensions/InputModules/InputModuleChatBoxExtensions.cs b/ChatBoxExtensions/InputModules/InputModuleChatBoxExtensions.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/InputModules/InputModuleChatBoxExtensions.cs rename to ChatBoxExtensions/InputModules/InputModuleChatBoxExtensions.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/Base.cs b/ChatBoxExtensions/Integrations/Base.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/Base.cs rename to ChatBoxExtensions/Integrations/Base.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/ChatBox.cs b/ChatBoxExtensions/Integrations/ChatBox.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/ChatBox.cs rename to ChatBoxExtensions/Integrations/ChatBox.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRAAS.cs b/ChatBoxExtensions/Integrations/ChilloutVRAAS.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/ChilloutVRAAS.cs rename to ChatBoxExtensions/Integrations/ChilloutVRAAS.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRBase.cs b/ChatBoxExtensions/Integrations/ChilloutVRBase.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/ChilloutVRBase.cs rename to ChatBoxExtensions/Integrations/ChilloutVRBase.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRInput.cs b/ChatBoxExtensions/Integrations/ChilloutVRInput.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/ChilloutVRInput.cs rename to ChatBoxExtensions/Integrations/ChilloutVRInput.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/Commands.cs b/ChatBoxExtensions/Integrations/Commands.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/Commands.cs rename to ChatBoxExtensions/Integrations/Commands.cs diff --git a/.Deprecated/ChatBoxExtensions/Integrations/PlayerRagdollMod.cs b/ChatBoxExtensions/Integrations/PlayerRagdollMod.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Integrations/PlayerRagdollMod.cs rename to ChatBoxExtensions/Integrations/PlayerRagdollMod.cs diff --git a/.Deprecated/ChatBoxExtensions/Main.cs b/ChatBoxExtensions/Main.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Main.cs rename to ChatBoxExtensions/Main.cs diff --git a/.Deprecated/ChatBoxExtensions/Properties/AssemblyInfo.cs b/ChatBoxExtensions/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/ChatBoxExtensions/Properties/AssemblyInfo.cs rename to ChatBoxExtensions/Properties/AssemblyInfo.cs diff --git a/.Deprecated/ChatBoxExtensions/format.json b/ChatBoxExtensions/format.json similarity index 100% rename from .Deprecated/ChatBoxExtensions/format.json rename to ChatBoxExtensions/format.json diff --git a/ConfigureCalibrationPose/ConfigureCalibrationPose.csproj b/ConfigureCalibrationPose/ConfigureCalibrationPose.csproj deleted file mode 100644 index 5a8badc..0000000 --- a/ConfigureCalibrationPose/ConfigureCalibrationPose.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - YouAreMineNow - - diff --git a/ConfigureCalibrationPose/Main.cs b/ConfigureCalibrationPose/Main.cs deleted file mode 100644 index df677fd..0000000 --- a/ConfigureCalibrationPose/Main.cs +++ /dev/null @@ -1,543 +0,0 @@ -using System.Reflection; -using ABI_RC.Core.Player; -using ABI_RC.Systems.IK; -using ABI_RC.Systems.IK.SubSystems; -using ABI_RC.Systems.InputManagement; -using ABI_RC.Systems.Movement; -using HarmonyLib; -using MelonLoader; -using RootMotion.FinalIK; -using UnityEngine; - -namespace NAK.ConfigureCalibrationPose; - -public class ConfigureCalibrationPoseMod : MelonMod -{ - #region Enums - - private enum CalibrationPose - { - TPose, - APose, - IKPose, - BikePose, - RacushSit, - CCKSitting, - CCKCrouch, - CCKProne, - } - - #endregion Enums - - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(ConfigureCalibrationPose)); - - private static readonly MelonPreferences_Entry EntryCalibrationPose = - Category.CreateEntry("calibration_pose", CalibrationPose.APose, display_name: "Calibration Pose", - description: "What pose to use for FBT calibration."); - - #endregion Melon Preferences - - #region Melon Events - - public override void OnInitializeMelon() - { - #region BodySystem Patches - - HarmonyInstance.Patch( - typeof(BodySystem).GetMethod(nameof(BodySystem.MuscleUpdate), - BindingFlags.Public | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(ConfigureCalibrationPoseMod).GetMethod(nameof(OnPreBodySystemMuscleUpdate), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion BodySystem Patches - } - - #endregion Melon Events - - #region Harmony Patches - - private static bool OnPreBodySystemMuscleUpdate(ref float[] muscles) - { - PlayerSetup playerSetup = PlayerSetup.Instance; - IKSystem ikSystem = IKSystem.Instance; - ref HumanPose humanPose = ref ikSystem._humanPose; - - if (BodySystem.isCalibrating) - { - switch (EntryCalibrationPose.Value) - { - default: - case CalibrationPose.TPose: - for (int i = 0; i < MusclePoses.TPoseMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, MusclePoses.TPoseMuscles[i], ref muscles); - break; - case CalibrationPose.APose: - for (int i = 0; i < MusclePoses.APoseMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, MusclePoses.APoseMuscles[i], ref muscles); - break; - case CalibrationPose.IKPose: - for (int i = 0; i < MusclePoses.IKPoseMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, MusclePoses.IKPoseMuscles[i], ref muscles); - break; - case CalibrationPose.BikePose: - for (int i = 0; i < MusclePoses.TPoseMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, 0f, ref muscles); - break; - case CalibrationPose.CCKSitting: - for (int i = 0; i < CCKSittingMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, CCKSittingMuscles[i], ref muscles); - break; - case CalibrationPose.CCKCrouch: - for (int i = 0; i < CCKCrouchMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, CCKCrouchMuscles[i], ref muscles); - break; - case CalibrationPose.CCKProne: - for (int i = 0; i < CCKProneMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex) i, CCKProneMuscles[i], ref muscles); - break; - case CalibrationPose.RacushSit: - for (int i = 0; i < RacushSitMuscles.Length; i++) - ikSystem.ApplyMuscleValue((MuscleIndex)i, RacushSitMuscles[i], ref muscles); - break; - } - - humanPose.bodyPosition = Vector3.up; - humanPose.bodyRotation = Quaternion.identity; - } - else if (BodySystem.isCalibratedAsFullBody && BodySystem.TrackingPositionWeight > 0f) - { - BetterBetterCharacterController characterController = playerSetup.CharacterController; - - bool isRunning = characterController.IsMoving(); - bool isGrounded = characterController.IsGrounded(); - bool isFlying = characterController.IsFlying(); - bool isSwimming = characterController.IsSwimming(); - - if ((BodySystem.PlayRunningAnimationInFullBody - && (isRunning || !isGrounded && !isFlying && !isSwimming))) - { - ikSystem.applyOriginalHipPosition = true; - ikSystem.applyOriginalHipRotation = true; - - IKSolverVR solver = IKSystem.vrik.solver; - BodySystem.SetPelvisWeight(solver.spine, 0f); - BodySystem.SetLegWeight(solver.leftLeg, 0f); - BodySystem.SetLegWeight(solver.rightLeg, 0f); - } - else - { - ikSystem.applyOriginalHipPosition = true; - ikSystem.applyOriginalHipRotation = false; - humanPose.bodyRotation = Quaternion.identity; - } - } - - return false; // dont run original - } - - #endregion Harmony Patches - - #region Custom Pose Arrays - - private static readonly float[] CCKSittingMuscles = - [ - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - -0.8000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - -0.8000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - -1.0000f, - 0.0000f, - -0.3000f, - 0.3000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - -1.0000f, - 0.0000f, - -0.3000f, - 0.3000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f - ]; - - private static readonly float[] CCKCrouchMuscles = - [ - -1.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.5000f, - 0.0000f, - 0.0000f, - 0.5000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - -0.6279f, - 0.0000f, - 0.0000f, - -0.8095f, - 0.0000f, - -1.0091f, - 0.0000f, - 0.0000f, - -0.4126f, - 0.0013f, - -0.0860f, - -0.9331f, - -0.0869f, - -1.3586f, - 0.1791f, - 0.0000f, - 0.0000f, - 0.0000f, - -0.1998f, - -0.2300f, - 0.1189f, - 0.3479f, - 0.1364f, - -0.3737f, - 0.0069f, - 0.0000f, - 0.0000f, - -0.1994f, - -0.2301f, - 0.0267f, - 0.7532f, - 0.1922f, - 0.0009f, - -0.0005f, - -1.4747f, - -0.0443f, - -0.3347f, - -0.3062f, - -0.7596f, - -1.2067f, - -0.7329f, - -0.7329f, - -0.5984f, - -2.7162f, - -0.7439f, - -0.7439f, - -0.5812f, - 1.8528f, - -0.7520f, - -0.7520f, - -0.7242f, - 0.5912f, - -0.7632f, - -0.7632f, - -1.4747f, - -0.0443f, - -0.3347f, - -0.3062f, - -0.7596f, - -1.2067f, - -0.7329f, - -0.7329f, - -0.5984f, - -2.7162f, - -0.7439f, - -0.7439f, - -0.5812f, - 1.8528f, - -0.7520f, - 0.8104f, - -0.7242f, - 0.5912f, - -0.7632f, - 0.8105f - ]; - - private static readonly float[] CCKProneMuscles = - [ - 0.6604f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.7083f, - 0.0000f, - 0.0000f, - 0.7083f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.2444f, - -0.0554f, - -0.8192f, - 0.9301f, - 0.5034f, - 1.0274f, - -0.1198f, - 0.5849f, - 0.2360f, - -0.0837f, - -1.1803f, - 0.9676f, - 0.7390f, - 0.9944f, - -0.1717f, - 0.5849f, - 0.0000f, - 0.0000f, - 0.2823f, - -0.6297f, - 0.3200f, - -0.3376f, - 0.0714f, - 0.9260f, - -1.5768f, - 0.0000f, - 0.0000f, - 0.1561f, - -0.6712f, - 0.2997f, - -0.3392f, - 0.0247f, - 0.7672f, - -1.5269f, - -1.1422f, - 0.0392f, - 0.6457f, - 0.0000f, - 0.6185f, - -0.5393f, - 0.8104f, - 0.8104f, - 0.6223f, - -0.8225f, - 0.8104f, - 0.8104f, - 0.6218f, - -0.3961f, - 0.8104f, - 0.8104f, - 0.6160f, - -0.3721f, - 0.8105f, - 0.8105f, - -1.1422f, - 0.0392f, - 0.6457f, - 0.0000f, - 0.6185f, - -0.5393f, - 0.8104f, - 0.8104f, - 0.6223f, - -0.8226f, - 0.8104f, - 0.8104f, - 0.6218f, - -0.3961f, - 0.8104f, - 0.8104f, - 0.6160f, - -0.3721f, - 0.8105f, - 0.8105f - ]; - - public static readonly float[] RacushSitMuscles = - [ - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - -0.7600f, - 0.1000f, - 0.0600f, - -0.1800f, - -0.0991f, - 0.1300f, - 0.0001f, - 0.0000f, - -0.7600f, - 0.1000f, - 0.0600f, - -0.1800f, - -0.0991f, - 0.1300f, - 0.0001f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.3927f, - 0.3115f, - 0.0931f, - 0.9650f, - -0.0662f, - 0.0026f, - 0.0006f, - 0.0000f, - 0.0000f, - 0.3927f, - 0.3115f, - 0.0931f, - 0.9650f, - -0.0662f, - 0.0026f, - 0.0006f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f, - 0.0000f - ]; - - #endregion Custom Pose Arrays -} \ No newline at end of file diff --git a/ConfigureCalibrationPose/Properties/AssemblyInfo.cs b/ConfigureCalibrationPose/Properties/AssemblyInfo.cs deleted file mode 100644 index 1fdc09d..0000000 --- a/ConfigureCalibrationPose/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.ConfigureCalibrationPose.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.ConfigureCalibrationPose))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.ConfigureCalibrationPose))] - -[assembly: MelonInfo( - typeof(NAK.ConfigureCalibrationPose.ConfigureCalibrationPoseMod), - nameof(NAK.ConfigureCalibrationPose), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ConfigureCalibrationPose" -)] - -[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.ConfigureCalibrationPose.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/ConfigureCalibrationPose/README.md b/ConfigureCalibrationPose/README.md deleted file mode 100644 index f17ba48..0000000 --- a/ConfigureCalibrationPose/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# ConfigureCalibrationPose - -Select FBT calibration pose. - ---- - -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. diff --git a/ConfigureCalibrationPose/format.json b/ConfigureCalibrationPose/format.json deleted file mode 100644 index 6324d2b..0000000 --- a/ConfigureCalibrationPose/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": -1, - "name": "ConfigureCalibrationPose", - "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/ConfigureCalibrationPose.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ConfigureCalibrationPose/", - "changelog": "- Initial Release", - "embedcolor": "#00FFFF" -} \ No newline at end of file diff --git a/CustomSpawnPoint/Properties/AssemblyInfo.cs b/CustomSpawnPoint/Properties/AssemblyInfo.cs index d1887b3..867796d 100644 --- a/CustomSpawnPoint/Properties/AssemblyInfo.cs +++ b/CustomSpawnPoint/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CustomSpawnPoint" )] -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[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 @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.CustomSpawnPoint.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.3"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/CustomSpawnPoint/SpawnPointManager.cs b/CustomSpawnPoint/SpawnPointManager.cs index 78c81dd..0aa5504 100644 --- a/CustomSpawnPoint/SpawnPointManager.cs +++ b/CustomSpawnPoint/SpawnPointManager.cs @@ -7,54 +7,54 @@ using Object = UnityEngine.Object; using ABI_RC.Core; using Newtonsoft.Json; -namespace NAK.CustomSpawnPoint; - -internal static class SpawnPointManager +namespace NAK.CustomSpawnPoint { - #region Fields - - private static string currentWorldId = string.Empty; - private static SpawnPointData? currentSpawnPoint; - - private static string requestedWorldId = string.Empty; - private static SpawnPointData? requestedSpawnPoint; - - private static Dictionary spawnPoints = new(); - private static readonly string jsonFilePath = Path.Combine("UserData", "customspawnpoints.json"); - - private static GameObject[] customSpawnPointsArray; - private static GameObject[] originalSpawnPointsArray; - - #endregion Fields - - #region Initialization - - internal static void Init() + internal static class SpawnPointManager { + #region Fields + + private static string currentWorldId = string.Empty; + private static SpawnPointData? currentSpawnPoint; + + private static string requestedWorldId = string.Empty; + private static SpawnPointData? requestedSpawnPoint; + + private static Dictionary spawnPoints = new(); + private static readonly string jsonFilePath = Path.Combine("UserData", "customspawnpoints.json"); + + private static GameObject[] customSpawnPointsArray; + private static GameObject[] originalSpawnPointsArray; + + #endregion Fields + + #region Initialization + + internal static void Init() + { LoadSpawnpoints(); CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoaded); CVRGameEventSystem.World.OnUnload.AddListener(OnWorldUnloaded); MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); } - private static System.Collections.IEnumerator WaitMainMenuUi() - { + private static System.Collections.IEnumerator WaitMainMenuUi() + { while (ViewManager.Instance == null) yield return null; - while (ViewManager.Instance.cohtmlView == null) + while (ViewManager.Instance.gameMenuView == null) yield return null; - while (ViewManager.Instance.cohtmlView.Listener == null) + while (ViewManager.Instance.gameMenuView.Listener == null) yield return null; ViewManager.Instance.OnUiConfirm.AddListener(OnClearSpawnpointConfirm); - ViewManager.Instance.cohtmlView.Listener.FinishLoad += (_) => + ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => { - ViewManager.Instance.cohtmlView.View._view.ExecuteScript(spawnpointJs); + ViewManager.Instance.gameMenuView.View._view.ExecuteScript(spawnpointJs); }; - ViewManager.Instance.cohtmlView.Listener.ReadyForBindings += () => + ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => { // listen for setting the spawn point on our custom button - ViewManager.Instance.cohtmlView.View.BindCall("NAKCallSetSpawnpoint", SetSpawnPoint); + ViewManager.Instance.gameMenuView.View.BindCall("NAKCallSetSpawnpoint", SetSpawnPoint); }; // create our custom spawn point object @@ -65,12 +65,12 @@ internal static class SpawnPointManager customSpawnPointsArray = new[] { customSpawnPointObject }; } - #endregion Initialization + #endregion Initialization - #region Game Events + #region Game Events - private static void OnWorldLoaded(string worldId) - { + private static void OnWorldLoaded(string worldId) + { CVRWorld world = CVRWorld.Instance; if (world == null) return; @@ -90,14 +90,14 @@ internal static class SpawnPointManager } } - private static void OnWorldUnloaded(string worldId) - { + private static void OnWorldUnloaded(string worldId) + { ClearCurrentWorldState(); } - internal static void OnRequestWorldDetailsPage(string worldId) - { - //CustomSpawnPointMod.Logger.Msg("Requesting world details page for world: " + worldId); + internal static void OnRequestWorldDetailsPage(string worldId) + { + CustomSpawnPointMod.Logger.Msg("Requesting world details page for world: " + worldId); requestedWorldId = worldId; requestedSpawnPoint = spawnPoints.TryGetValue(requestedWorldId, out SpawnPointData spawnPoint) ? spawnPoint : null; @@ -106,24 +106,24 @@ internal static class SpawnPointManager UpdateMenuButtonState(hasSpawnpoint, worldId == currentWorldId && CVRWorld.Instance != null && CVRWorld.Instance.allowFlying); } - private static void OnClearSpawnpointConfirm(string id, string value, string data) - { + private static void OnClearSpawnpointConfirm(string id, string value, string data) + { if (id != "nak_clear_spawnpoint") return; if (value == "true") ClearSpawnPoint(); } - #endregion Game Events + #endregion Game Events - #region Spawnpoint Management + #region Spawnpoint Management - public static void SetSpawnPoint() - => SetSpawnPointForWorld(currentWorldId); + public static void SetSpawnPoint() + => SetSpawnPointForWorld(currentWorldId); - public static void ClearSpawnPoint() - => ClearSpawnPointForWorld(currentWorldId); + public static void ClearSpawnPoint() + => ClearSpawnPointForWorld(currentWorldId); - private static void SetSpawnPointForWorld(string worldId) - { + private static void SetSpawnPointForWorld(string worldId) + { CustomSpawnPointMod.Logger.Msg("Setting spawn point for world: " + worldId); Vector3 playerPosition = PlayerSetup.Instance.GetPlayerPosition(); @@ -152,8 +152,8 @@ internal static class SpawnPointManager UpdateMenuButtonState(true, worldId == currentWorldId); } - private static void ClearSpawnPointForWorld(string worldId) - { + private static void ClearSpawnPointForWorld(string worldId) + { CustomSpawnPointMod.Logger.Msg("Clearing spawn point for world: " + worldId); if (spawnPoints.ContainsKey(worldId)) @@ -172,29 +172,29 @@ internal static class SpawnPointManager UpdateMenuButtonState(false, worldId == currentWorldId); } - private static void UpdateCustomSpawnPointTransform(SpawnPointData spawnPoint) - { + private static void UpdateCustomSpawnPointTransform(SpawnPointData spawnPoint) + { customSpawnPointsArray[0].transform.SetPositionAndRotation(spawnPoint.Position, Quaternion.Euler(spawnPoint.Rotation)); } - private static void UpdateMenuButtonState(bool hasSpawnpoint, bool isInWorld) - { - ViewManager.Instance.cohtmlView.View.TriggerEvent("NAKUpdateSpawnpointStatus", hasSpawnpoint.ToString(), isInWorld.ToString()); + private static void UpdateMenuButtonState(bool hasSpawnpoint, bool isInWorld) + { + ViewManager.Instance.gameMenuView.View.TriggerEvent("NAKUpdateSpawnpointStatus", hasSpawnpoint.ToString(), isInWorld.ToString()); } - private static void ClearCurrentWorldState() - { + private static void ClearCurrentWorldState() + { currentWorldId = string.Empty; currentSpawnPoint = null; originalSpawnPointsArray = null; } - #endregion Spawnpoint Management + #endregion Spawnpoint Management - #region JSON Management + #region JSON Management - private static void LoadSpawnpoints() - { + private static void LoadSpawnpoints() + { if (File.Exists(jsonFilePath)) { string json = File.ReadAllText(jsonFilePath); @@ -206,18 +206,18 @@ internal static class SpawnPointManager } } - private static void SaveSpawnpoints() - { + private static void SaveSpawnpoints() + { File.WriteAllText(jsonFilePath, JsonConvert.SerializeObject(spawnPoints, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore } // death )); } - #endregion JSON Management + #endregion JSON Management - #region Spawnpoint JS + #region Spawnpoint JS - private const string spawnpointJs = @" + private const string spawnpointJs = @" let hasSpawnpointForThisWorld = false; let spawnpointButton = null; @@ -249,16 +249,17 @@ engine.on('NAKUpdateSpawnpointStatus', function (hasSpawnpoint, isInWorld) { }); "; - #endregion Spawnpoint JS -} + #endregion Spawnpoint JS + } -#region Serializable + #region Serializable -[Serializable] -public struct SpawnPointData -{ - public Vector3 Position; - public Vector3 Rotation; -} + [Serializable] + public struct SpawnPointData + { + public Vector3 Position; + public Vector3 Rotation; + } -#endregion Serializable \ No newline at end of file + #endregion Serializable +} \ No newline at end of file diff --git a/CustomSpawnPoint/format.json b/CustomSpawnPoint/format.json index 52fe5e3..c5541ff 100644 --- a/CustomSpawnPoint/format.json +++ b/CustomSpawnPoint/format.json @@ -1,9 +1,9 @@ { "_id": 228, "name": "CustomSpawnPoint", - "modversion": "1.0.3", - "gameversion": "2025r180", - "loaderversion": "0.7.2", + "modversion": "1.0.0", + "gameversion": "2024r175", + "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", "description": "Replaces the unused Images button in the World Details page with a button to set a custom spawn point.", @@ -17,8 +17,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/CustomSpawnPoint.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r37/CustomSpawnPoint.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CustomSpawnPoint/", - "changelog": "- Fixes for 2025r180", + "changelog": "- Initial Release", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/DoubleTapJumpToExitSeat/DoubleTapJumpToExitSeat.csproj b/DoubleTapJumpToExitSeat/DoubleTapJumpToExitSeat.csproj deleted file mode 100644 index 5a8badc..0000000 --- a/DoubleTapJumpToExitSeat/DoubleTapJumpToExitSeat.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - YouAreMineNow - - diff --git a/DoubleTapJumpToExitSeat/Main.cs b/DoubleTapJumpToExitSeat/Main.cs deleted file mode 100644 index 18dd9fb..0000000 --- a/DoubleTapJumpToExitSeat/Main.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Reflection; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Systems.InputManagement; -using ABI_RC.Systems.Movement; -using HarmonyLib; -using MelonLoader; -using UnityEngine; - -namespace NAK.DoubleTapJumpToExitSeat; - -public class DoubleTapJumpToExitSeatMod : MelonMod -{ - #region Melon Events - - public override void OnInitializeMelon() - { - #region CVRSeat Patches - - HarmonyInstance.Patch( - typeof(CVRSeat).GetMethod(nameof(CVRSeat.Update), - BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(DoubleTapJumpToExitSeatMod).GetMethod(nameof(OnPreCVRSeatUpdate), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRSeat Patches - - #region ViewManager Patches - - HarmonyInstance.Patch( - typeof(ViewManager).GetMethod(nameof(ViewManager.Update), - BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(DoubleTapJumpToExitSeatMod).GetMethod(nameof(OnPreViewManagerUpdate), - BindingFlags.NonPublic | BindingFlags.Static)), - postfix: new HarmonyMethod(typeof(DoubleTapJumpToExitSeatMod).GetMethod(nameof(OnPostViewManagerUpdate), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion ViewManager Patches - } - - #endregion Melon Events - - #region Harmony Patches - - private static float _lastJumpTime = -1f; - private static bool _wasJumping; - - private static bool OnPreCVRSeatUpdate(CVRSeat __instance) - { - if (!__instance.occupied) return false; - - // Crazy? - bool jumped = CVRInputManager.Instance.jump; - bool justJumped = jumped && !_wasJumping; - _wasJumping = jumped; - if (justJumped) - { - float t = Time.time; - if (t - _lastJumpTime <= BetterBetterCharacterController.DoubleJumpFlightTimeOut) - { - _lastJumpTime = -1f; - __instance.ExitSeat(); - return false; - } - _lastJumpTime = t; - } - - // Double update this frame (this ensures Extrapolate / Every Frame Updated objects are seated correctly) - if (__instance.vrSitPosition.position != __instance._lastPosition || __instance.vrSitPosition.rotation != __instance._lastRotation) - __instance.MovePlayerToSeat(__instance.vrSitPositionReady ? __instance.vrSitPosition : __instance.transform); - - // Steal sync - if (__instance.lockControls) - { - if (__instance._spawnable) __instance._spawnable.ForceUpdate(4); - if (__instance._objectSync) __instance._objectSync.ForceUpdate(4); - } - - return false; // don't call original method - } - - // ReSharper disable once RedundantAssignment - private static void OnPreViewManagerUpdate(ref bool __state) - => (__state, BetterBetterCharacterController.Instance._isSitting) - = (BetterBetterCharacterController.Instance._isSitting, false); - - private static void OnPostViewManagerUpdate(ref bool __state) - => BetterBetterCharacterController.Instance._isSitting = __state; - - #endregion Harmony Patches -} \ No newline at end of file diff --git a/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs b/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs deleted file mode 100644 index 2e85366..0000000 --- a/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.DoubleTapJumpToExitSeat.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.DoubleTapJumpToExitSeat))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.DoubleTapJumpToExitSeat))] - -[assembly: MelonInfo( - typeof(NAK.DoubleTapJumpToExitSeat.DoubleTapJumpToExitSeatMod), - nameof(NAK.DoubleTapJumpToExitSeat), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DoubleTapJumpToExitSeat" -)] - -[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.DoubleTapJumpToExitSeat.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.1"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/DoubleTapJumpToExitSeat/README.md b/DoubleTapJumpToExitSeat/README.md deleted file mode 100644 index c154674..0000000 --- a/DoubleTapJumpToExitSeat/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# DoubleTapJumpToExitSeat - -Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu. - ---- - -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. diff --git a/DoubleTapJumpToExitSeat/format.json b/DoubleTapJumpToExitSeat/format.json deleted file mode 100644 index e14365c..0000000 --- a/DoubleTapJumpToExitSeat/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 255, - "name": "DoubleTapJumpToExitSeat", - "modversion": "1.0.1", - "gameversion": "2025r180", - "loaderversion": "0.7.2", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu.", - "searchtags": [ - "double", - "jump", - "chair", - "seat" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/DoubleTapJumpToExitSeat.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DoubleTapJumpToExitSeat/", - "changelog": "- Fixes for 2025r180", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Deprecated/DropPropTweak/DropPropTweak.csproj b/DropPropTweak/DropPropTweak.csproj similarity index 100% rename from .Deprecated/DropPropTweak/DropPropTweak.csproj rename to DropPropTweak/DropPropTweak.csproj diff --git a/.Deprecated/DropPropTweak/Main.cs b/DropPropTweak/Main.cs similarity index 99% rename from .Deprecated/DropPropTweak/Main.cs rename to DropPropTweak/Main.cs index b72f652..023ce21 100644 --- a/.Deprecated/DropPropTweak/Main.cs +++ b/DropPropTweak/Main.cs @@ -44,4 +44,6 @@ public class DropPropTweakMod : MelonMod __instance.CharacterController.gravity = ogGravity; // restore gravity return false; } + + } \ No newline at end of file diff --git a/.Deprecated/DropPropTweak/Properties/AssemblyInfo.cs b/DropPropTweak/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/DropPropTweak/Properties/AssemblyInfo.cs rename to DropPropTweak/Properties/AssemblyInfo.cs diff --git a/.Deprecated/DropPropTweak/README.md b/DropPropTweak/README.md similarity index 100% rename from .Deprecated/DropPropTweak/README.md rename to DropPropTweak/README.md diff --git a/.Deprecated/DropPropTweak/format.json b/DropPropTweak/format.json similarity index 100% rename from .Deprecated/DropPropTweak/format.json rename to DropPropTweak/format.json diff --git a/.Deprecated/EzCurls/EzCurls.csproj b/EzCurls/EzCurls.csproj similarity index 100% rename from .Deprecated/EzCurls/EzCurls.csproj rename to EzCurls/EzCurls.csproj diff --git a/.Deprecated/EzCurls/InputModules/InputModuleCurlAdjuster.cs b/EzCurls/InputModules/InputModuleCurlAdjuster.cs similarity index 100% rename from .Deprecated/EzCurls/InputModules/InputModuleCurlAdjuster.cs rename to EzCurls/InputModules/InputModuleCurlAdjuster.cs diff --git a/.Deprecated/EzCurls/Main.cs b/EzCurls/Main.cs similarity index 100% rename from .Deprecated/EzCurls/Main.cs rename to EzCurls/Main.cs diff --git a/.Deprecated/EzCurls/ModSettings.cs b/EzCurls/ModSettings.cs similarity index 100% rename from .Deprecated/EzCurls/ModSettings.cs rename to EzCurls/ModSettings.cs diff --git a/.Deprecated/EzCurls/Properties/AssemblyInfo.cs b/EzCurls/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/EzCurls/Properties/AssemblyInfo.cs rename to EzCurls/Properties/AssemblyInfo.cs diff --git a/EzCurls/README.md b/EzCurls/README.md new file mode 100644 index 0000000..af9d39c --- /dev/null +++ b/EzCurls/README.md @@ -0,0 +1,16 @@ +# EzCurls + +A mod that allows you to tune your finger curls to your liking. Supposedly can help with VR sign language, as raw finger curls are not that great for quick and precise gestures. + +The settings are not too coherent, it is mostly a bunch of things thrown at the wall, but it works for me. I hope it works for you too. + +--- + +Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation and is 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. diff --git a/EzCurls/format.json b/EzCurls/format.json new file mode 100644 index 0000000..a2374f1 --- /dev/null +++ b/EzCurls/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "EzCurls", + "modversion": "1.0.0", + "gameversion": "2023r173", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "A mod that allows you to tune your finger curls to your liking. Supposedly can help with VR sign language, as raw finger curls are not that great for quick and precise gestures.\n\nThe settings are not too coherent, it is mostly a bunch of things thrown at the wall, but it works for me. I hope it works for you too.", + "searchtags": [ + "curls", + "fingers", + "index", + "knuckles" + ], + "requirements": [ + "UIExpansionKit" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r24/EzCurls.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/EzCurls/", + "changelog": "- Initial CVRMG release", + "embedcolor": "7d7d7d" +} \ No newline at end of file diff --git a/.Deprecated/GestureLock/GestureLock.csproj b/FOVAdjustment/FOVAdjustment.csproj similarity index 100% rename from .Deprecated/GestureLock/GestureLock.csproj rename to FOVAdjustment/FOVAdjustment.csproj diff --git a/.Deprecated/FOVAdjustment/Main.cs b/FOVAdjustment/Main.cs similarity index 94% rename from .Deprecated/FOVAdjustment/Main.cs rename to FOVAdjustment/Main.cs index bafb6ae..a593ab0 100644 --- a/.Deprecated/FOVAdjustment/Main.cs +++ b/FOVAdjustment/Main.cs @@ -55,10 +55,10 @@ public class FOVAdjustment : MelonMod private static void UpdateDesktopCameraControllerFov(float value) { - // if (CVRWorld.Instance != null && Mathf.Approximately(CVRWorld.Instance.fov, 60f)) - // { + if (CVRWorld.Instance != null && Mathf.Approximately(CVRWorld.Instance.fov, 60f)) + { CVR_DesktopCameraController.defaultFov = Mathf.Clamp(value, 60f, 120f); CVR_DesktopCameraController.zoomFov = CVR_DesktopCameraController.defaultFov * 0.5f; - //} + } } } \ No newline at end of file diff --git a/.Deprecated/FOVAdjustment/Properties/AssemblyInfo.cs b/FOVAdjustment/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/FOVAdjustment/Properties/AssemblyInfo.cs rename to FOVAdjustment/Properties/AssemblyInfo.cs diff --git a/.Deprecated/FOVAdjustment/README.md b/FOVAdjustment/README.md similarity index 100% rename from .Deprecated/FOVAdjustment/README.md rename to FOVAdjustment/README.md diff --git a/.Deprecated/FOVAdjustment/format.json b/FOVAdjustment/format.json similarity index 100% rename from .Deprecated/FOVAdjustment/format.json rename to FOVAdjustment/format.json diff --git a/.Deprecated/FuckOffUICamera/FuckOffUICamera.csproj b/FuckCameraIndicator/FuckCameraIndicator.csproj similarity index 100% rename from .Deprecated/FuckOffUICamera/FuckOffUICamera.csproj rename to FuckCameraIndicator/FuckCameraIndicator.csproj diff --git a/FuckCameraIndicator/Main.cs b/FuckCameraIndicator/Main.cs new file mode 100644 index 0000000..a0c7957 --- /dev/null +++ b/FuckCameraIndicator/Main.cs @@ -0,0 +1,34 @@ +using MelonLoader; +using System.Reflection; +using ABI_RC.Core.Player; +using UnityEngine; + +namespace NAK.FuckCameraIndicator; + +public class FuckCameraIndicator : MelonMod +{ + public override void OnInitializeMelon() + { + HarmonyInstance.Patch( + typeof(PuppetMaster).GetMethod(nameof(PuppetMaster.Start), BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyLib.HarmonyMethod(typeof(FuckCameraIndicator).GetMethod(nameof(OnPuppetMasterStart_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + } + + private static void OnPuppetMasterStart_Postfix(PuppetMaster __instance) + { + // thanks for not making it modular, fucking spaghetti + // and why leave it a skinned mesh... lazy fucking implementation + + GameObject indicator = __instance.cameraIndicator; + GameObject lens = __instance.cameraIndicatorLense; + + // Disable NamePlate child object + const string c_CanvasPath = "[NamePlate]/Canvas"; + GameObject canvas = indicator.transform.Find(c_CanvasPath).gameObject; + canvas.SetActive(false); + + // Disable lens renderer + lens.GetComponent().forceRenderingOff = true; + } +} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/Properties/AssemblyInfo.cs b/FuckCameraIndicator/Properties/AssemblyInfo.cs similarity index 64% rename from .Deprecated/SuperAwesomeMod/Properties/AssemblyInfo.cs rename to FuckCameraIndicator/Properties/AssemblyInfo.cs index f1590e2..05525a7 100644 --- a/.Deprecated/SuperAwesomeMod/Properties/AssemblyInfo.cs +++ b/FuckCameraIndicator/Properties/AssemblyInfo.cs @@ -1,30 +1,27 @@ using MelonLoader; -using NAK.SuperAwesomeMod.Properties; +using NAK.FuckCameraIndicator.Properties; using System.Reflection; [assembly: AssemblyVersion(AssemblyInfoParams.Version)] [assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] [assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.SuperAwesomeMod))] +[assembly: AssemblyTitle(nameof(NAK.FuckCameraIndicator))] [assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.SuperAwesomeMod))] +[assembly: AssemblyProduct(nameof(NAK.FuckCameraIndicator))] [assembly: MelonInfo( - typeof(NAK.SuperAwesomeMod.SuperAwesomeModMod), - nameof(NAK.SuperAwesomeMod), + typeof(NAK.FuckCameraIndicator.FuckCameraIndicator), + nameof(NAK.FuckCameraIndicator), AssemblyInfoParams.Version, AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SuperAwesomeMod" + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckCameraIndicator" )] [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.SuperAwesomeMod.Properties; +namespace NAK.FuckCameraIndicator.Properties; internal static class AssemblyInfoParams { public const string Version = "1.0.0"; diff --git a/FuckCameraIndicator/README.md b/FuckCameraIndicator/README.md new file mode 100644 index 0000000..af9d39c --- /dev/null +++ b/FuckCameraIndicator/README.md @@ -0,0 +1,16 @@ +# EzCurls + +A mod that allows you to tune your finger curls to your liking. Supposedly can help with VR sign language, as raw finger curls are not that great for quick and precise gestures. + +The settings are not too coherent, it is mostly a bunch of things thrown at the wall, but it works for me. I hope it works for you too. + +--- + +Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation and is 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. diff --git a/FuckCameraIndicator/format.json b/FuckCameraIndicator/format.json new file mode 100644 index 0000000..a2374f1 --- /dev/null +++ b/FuckCameraIndicator/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "EzCurls", + "modversion": "1.0.0", + "gameversion": "2023r173", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "A mod that allows you to tune your finger curls to your liking. Supposedly can help with VR sign language, as raw finger curls are not that great for quick and precise gestures.\n\nThe settings are not too coherent, it is mostly a bunch of things thrown at the wall, but it works for me. I hope it works for you too.", + "searchtags": [ + "curls", + "fingers", + "index", + "knuckles" + ], + "requirements": [ + "UIExpansionKit" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r24/EzCurls.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/EzCurls/", + "changelog": "- Initial CVRMG release", + "embedcolor": "7d7d7d" +} \ No newline at end of file diff --git a/FuckToes/Main.cs b/FuckToes/Main.cs index 7fc6892..c98b8e0 100644 --- a/FuckToes/Main.cs +++ b/FuckToes/Main.cs @@ -3,66 +3,54 @@ using ABI_RC.Systems.IK; using MelonLoader; using RootMotion.FinalIK; using System.Reflection; -using ABI_RC.Core; namespace NAK.FuckToes; -public class FuckToesMod : MelonMod +public class FuckToes : MelonMod { - #region Melon Preferences + internal static MelonLogger.Instance Logger; - private static readonly MelonPreferences_Category Category = + public static readonly MelonPreferences_Category Category = MelonPreferences.CreateCategory(nameof(FuckToes)); - private static readonly MelonPreferences_Entry EntryEnabledVR = - Category.CreateEntry("use_in_halfbody", true, display_name:"No Toes in Halfbody", description: "Nuke VRIK toes when in Halfbody."); + public static readonly MelonPreferences_Entry EntryEnabledVR = + Category.CreateEntry("Enabled in HalfBody", true, description: "Nuke VRIK toes when in Halfbody."); - private static readonly MelonPreferences_Entry EntryEnabledFBT = - Category.CreateEntry("use_in_fbt", true, display_name:"No Toes in Fullbody", description: "Nuke VRIK toes when in FBT."); + public static readonly MelonPreferences_Entry EntryEnabledFBT = + Category.CreateEntry("Enabled in FBT", true, description: "Nuke VRIK toes when in FBT."); - #endregion Melon Preferences - - #region Melon Events - public override void OnInitializeMelon() { + Logger = LoggerInstance; HarmonyInstance.Patch( typeof(VRIK).GetMethod(nameof(VRIK.AutoDetectReferences)), - prefix: new HarmonyLib.HarmonyMethod(typeof(FuckToesMod).GetMethod(nameof(OnVRIKAutoDetectReferences_Prefix), - BindingFlags.NonPublic | BindingFlags.Static)) + prefix: new HarmonyLib.HarmonyMethod(typeof(FuckToes).GetMethod(nameof(OnVRIKAutoDetectReferences_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) ); } - - #endregion Melon Events - #region Harmony Patches - private static void OnVRIKAutoDetectReferences_Prefix(ref VRIK __instance) { try { // Must be PlayerLocal layer and in VR - if (__instance.gameObject.layer != CVRLayers.PlayerLocal - || !MetaPort.Instance.isUsingVr) + if (__instance.gameObject.layer != 8 || !MetaPort.Instance.isUsingVr) return; - switch (IKSystem.Instance.BodySystem.FullBodyActive) - { - case false when !EntryEnabledVR.Value: // Not in FBT, and not enabled, perish - case true when !EntryEnabledFBT.Value: // In FBT, and not enabled in fbt, perish - return; - default: - __instance.references.leftToes = null; - __instance.references.rightToes = null; - break; - } + // Not in FBT, and not enabled, perish + if (!IKSystem.Instance.BodySystem.FBTActive() && !EntryEnabledVR.Value) + return; + + // In FBT, and not enabled in fbt, perish + if (IKSystem.Instance.BodySystem.FBTActive() && !EntryEnabledFBT.Value) + return; + + __instance.references.leftToes = null; + __instance.references.rightToes = null; } catch (Exception e) { - MelonLogger.Error($"Error during the patched method {nameof(OnVRIKAutoDetectReferences_Prefix)}"); - MelonLogger.Error(e); + Logger.Error($"Error during the patched method {nameof(OnVRIKAutoDetectReferences_Prefix)}"); + Logger.Error(e); } } - - #endregion Harmony Patches } \ No newline at end of file diff --git a/FuckToes/Properties/AssemblyInfo.cs b/FuckToes/Properties/AssemblyInfo.cs index c308a51..68c3504 100644 --- a/FuckToes/Properties/AssemblyInfo.cs +++ b/FuckToes/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Reflection; [assembly: AssemblyProduct(nameof(NAK.FuckToes))] [assembly: MelonInfo( - typeof(NAK.FuckToes.FuckToesMod), + typeof(NAK.FuckToes.FuckToes), nameof(NAK.FuckToes), AssemblyInfoParams.Version, AssemblyInfoParams.Author, @@ -20,13 +20,13 @@ using System.Reflection; [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: MelonColor(255, 255, 200, 0)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] namespace NAK.FuckToes.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.4"; + public const string Version = "1.0.2"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/FuckToes/README.md b/FuckToes/README.md index 6caf1f4..e9b9e4d 100644 --- a/FuckToes/README.md +++ b/FuckToes/README.md @@ -1,6 +1,7 @@ # FuckToes +Prevents VRIK from autodetecting toes in HalfbodyIK. -Prevents VRIK from autodetecting toes in Halfbody or Fullbody. +Optionally can be applied in FBT, but toes in FBT are nice so you are a monster if so. ![fuckthetoes](https://user-images.githubusercontent.com/37721153/216518012-ae3b1dde-17ea-419a-a875-48d57e13f3dd.png) @@ -13,4 +14,5 @@ https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games > 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. \ No newline at end of file +> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. + diff --git a/FuckToes/format.json b/FuckToes/format.json index c7c42ee..de762bf 100644 --- a/FuckToes/format.json +++ b/FuckToes/format.json @@ -1,12 +1,12 @@ { "_id": 129, "name": "FuckToes", - "modversion": "1.0.4", - "gameversion": "2025r179", + "modversion": "1.0.2", + "gameversion": "2023r171", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", - "description": "Prevents VRIK from using toe bones in HalfBody or FBT.\n\nVRIK calculates weird center of mass when toes are mapped, so it is sometimes desired to unmap toes to prevent an avatars feet from resting far back.\n\nPlease see the [README](https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckToes/README.md) for relevant imagery detailing the problem.", + "description": "Prevents VRIK from using toe bones in HalfBody or FBT.\n\nVRIK calculates weird center of mass when toes are mapped, so it is sometimes desired to unmap toes to prevent an avatars feet from resting far back.\n\nPlease see the README for relevant imagery detailing the problem.", "searchtags": [ "toes", "vrik", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/FuckToes.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r14/FuckToes.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckToes/", - "changelog": "- Recompiled for 2025r179", - "embedcolor": "#f61963" + "changelog": "- Fixes for 2023r171.", + "embedcolor": "#ffc800" } \ No newline at end of file diff --git a/.Deprecated/MoreMenuOptions/MoreMenuOptions.csproj b/GestureLock/GestureLock.csproj similarity index 100% rename from .Deprecated/MoreMenuOptions/MoreMenuOptions.csproj rename to GestureLock/GestureLock.csproj diff --git a/.Deprecated/GestureLock/HarmonyPatches.cs b/GestureLock/HarmonyPatches.cs similarity index 100% rename from .Deprecated/GestureLock/HarmonyPatches.cs rename to GestureLock/HarmonyPatches.cs diff --git a/.Deprecated/GestureLock/Main.cs b/GestureLock/Main.cs similarity index 100% rename from .Deprecated/GestureLock/Main.cs rename to GestureLock/Main.cs diff --git a/.Deprecated/GestureLock/Properties/AssemblyInfo.cs b/GestureLock/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/GestureLock/Properties/AssemblyInfo.cs rename to GestureLock/Properties/AssemblyInfo.cs diff --git a/.Deprecated/GestureLock/README.md b/GestureLock/README.md similarity index 100% rename from .Deprecated/GestureLock/README.md rename to GestureLock/README.md diff --git a/.Deprecated/GestureLock/format.json b/GestureLock/format.json similarity index 100% rename from .Deprecated/GestureLock/format.json rename to GestureLock/format.json diff --git a/.Deprecated/HeadLookLockingInputFix/HeadLookLockingInputFix.csproj b/HeadLookLockingInputFix/HeadLookLockingInputFix.csproj similarity index 100% rename from .Deprecated/HeadLookLockingInputFix/HeadLookLockingInputFix.csproj rename to HeadLookLockingInputFix/HeadLookLockingInputFix.csproj diff --git a/.Deprecated/HeadLookLockingInputFix/Main.cs b/HeadLookLockingInputFix/Main.cs similarity index 100% rename from .Deprecated/HeadLookLockingInputFix/Main.cs rename to HeadLookLockingInputFix/Main.cs diff --git a/.Deprecated/HeadLookLockingInputFix/Properties/AssemblyInfo.cs b/HeadLookLockingInputFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/HeadLookLockingInputFix/Properties/AssemblyInfo.cs rename to HeadLookLockingInputFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/HeadLookLockingInputFix/README.md b/HeadLookLockingInputFix/README.md similarity index 100% rename from .Deprecated/HeadLookLockingInputFix/README.md rename to HeadLookLockingInputFix/README.md diff --git a/.Deprecated/HeadLookLockingInputFix/format.json b/HeadLookLockingInputFix/format.json similarity index 100% rename from .Deprecated/HeadLookLockingInputFix/format.json rename to HeadLookLockingInputFix/format.json diff --git a/.Deprecated/IKAdjustments/HarmonyPatches.cs b/IKAdjustments/HarmonyPatches.cs similarity index 100% rename from .Deprecated/IKAdjustments/HarmonyPatches.cs rename to IKAdjustments/HarmonyPatches.cs diff --git a/.Deprecated/IKAdjustments/IKAdjuster.cs b/IKAdjustments/IKAdjuster.cs similarity index 100% rename from .Deprecated/IKAdjustments/IKAdjuster.cs rename to IKAdjustments/IKAdjuster.cs diff --git a/.Deprecated/IKAdjustments/IKAdjustments.csproj b/IKAdjustments/IKAdjustments.csproj similarity index 100% rename from .Deprecated/IKAdjustments/IKAdjustments.csproj rename to IKAdjustments/IKAdjustments.csproj diff --git a/.Deprecated/IKAdjustments/Integrations/BTKUIAddon.cs b/IKAdjustments/Integrations/BTKUIAddon.cs similarity index 100% rename from .Deprecated/IKAdjustments/Integrations/BTKUIAddon.cs rename to IKAdjustments/Integrations/BTKUIAddon.cs diff --git a/.Deprecated/IKAdjustments/Main.cs b/IKAdjustments/Main.cs similarity index 100% rename from .Deprecated/IKAdjustments/Main.cs rename to IKAdjustments/Main.cs diff --git a/.Deprecated/IKAdjustments/Properties/AssemblyInfo.cs b/IKAdjustments/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/IKAdjustments/Properties/AssemblyInfo.cs rename to IKAdjustments/Properties/AssemblyInfo.cs diff --git a/.Deprecated/IKSimulatedRootAngleFix/IKSimulatedRootAngleFix.csproj b/IKSimulatedRootAngleFix/IKSimulatedRootAngleFix.csproj similarity index 100% rename from .Deprecated/IKSimulatedRootAngleFix/IKSimulatedRootAngleFix.csproj rename to IKSimulatedRootAngleFix/IKSimulatedRootAngleFix.csproj diff --git a/.Deprecated/IKSimulatedRootAngleFix/Main.cs b/IKSimulatedRootAngleFix/Main.cs similarity index 100% rename from .Deprecated/IKSimulatedRootAngleFix/Main.cs rename to IKSimulatedRootAngleFix/Main.cs diff --git a/.Deprecated/IKSimulatedRootAngleFix/Properties/AssemblyInfo.cs b/IKSimulatedRootAngleFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/IKSimulatedRootAngleFix/Properties/AssemblyInfo.cs rename to IKSimulatedRootAngleFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/FuckMagicaCloth2/README.md b/IKSimulatedRootAngleFix/README.md similarity index 100% rename from .Deprecated/FuckMagicaCloth2/README.md rename to IKSimulatedRootAngleFix/README.md diff --git a/.Deprecated/IKSimulatedRootAngleFix/format.json b/IKSimulatedRootAngleFix/format.json similarity index 100% rename from .Deprecated/IKSimulatedRootAngleFix/format.json rename to IKSimulatedRootAngleFix/format.json diff --git a/InteractionTest/Components/InteractionTracker.cs b/InteractionTest/Components/InteractionTracker.cs new file mode 100644 index 0000000..f543aa6 --- /dev/null +++ b/InteractionTest/Components/InteractionTracker.cs @@ -0,0 +1,187 @@ +using System.Collections; +using ABI_RC.Core; +using ABI_RC.Systems.GameEventSystem; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.Movement; +using ABI.CCK.Components; +using RootMotion.FinalIK; +using UnityEngine; + +namespace NAK.InteractionTest.Components; + +public class InteractionTracker : MonoBehaviour +{ + #region Setup + + public static void Setup(GameObject parentObject, bool isLeft = true) + { + // LeapMotion: RotationTarget + + GameObject trackerObject = new("NAK.InteractionTracker"); + trackerObject.transform.SetParent(parentObject.transform); + trackerObject.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + + GameObject ikObject = new("NAK.InteractionTracker.IK"); + ikObject.transform.SetParent(trackerObject.transform); + ikObject.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + + SphereCollider sphereCol = trackerObject.AddComponent(); + sphereCol.radius = 0f; + sphereCol.isTrigger = true; + + BetterBetterCharacterController.QueueRemovePlayerCollision(sphereCol); + trackerObject.AddComponent().isLeft = isLeft; + } + + #endregion Setup + + #region Actions + + public Action OnPenetrationDetected; // called on start of penetration + public Action OnPenetrationLost; // called on end of penetration + public Action OnPenetrationNormalChanged; // called when penetration normal changes after 2 degree threshold + + #endregion Actions + + public bool isLeft; + + public bool IsColliding => _isColliding; + public Vector3 ClosestPoint { get; private set; } + public Vector3 LastPenetrationNormal => _lastPenetrationNormal; + + private bool _isColliding; + private bool _wasPenetrating; + public Vector3 _lastPenetrationNormal = Vector3.forward; + private Collider _selfCollider; + private const float NormalChangeThreshold = 0.2f; + + #region Unity Events + + private void Awake() + { + _selfCollider = GetComponent(); + CVRGameEventSystem.Avatar.OnLocalAvatarLoad.AddListener(OnLocalAvatarLoaded); + } + + private void OnDestroy() + { + CVRGameEventSystem.Avatar.OnLocalAvatarLoad.RemoveListener(OnLocalAvatarLoaded); + } + + private void OnLocalAvatarLoaded(CVRAvatar _) + { + StartCoroutine(FrameLateInit()); + } + + private IEnumerator FrameLateInit() + { + yield return null; + IKSystem.vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdate); + IKSystem.vrik.onPostSolverUpdate.AddListener(OnPostSolverUpdate); + } + + private void OnTriggerStay(Collider other) + { + if (other.gameObject.layer == CVRLayers.PlayerLocal) + return; + + if (_selfCollider == null) + return; + + Transform selfTransform = transform; + Transform otherTransform = other.transform; + + bool isPenetrating = Physics.ComputePenetration( + _selfCollider, selfTransform.position, selfTransform.rotation, + other, otherTransform.position, otherTransform.rotation, + out Vector3 direction, out float distance); + + if (isPenetrating) + { + ClosestPoint = selfTransform.position + direction * distance; + Debug.DrawRay(ClosestPoint, direction * 10, Color.red); + + if (!_wasPenetrating) + { + OnPenetrationDetected?.Invoke(); + _wasPenetrating = true; + _lastPenetrationNormal = direction; + } + + float angleChange = Vector3.Angle(_lastPenetrationNormal, direction); + Debug.Log("Angle change: " + angleChange); + if (angleChange > NormalChangeThreshold) + { + _lastPenetrationNormal = direction; + OnPenetrationNormalChanged?.Invoke(); + } + } + else + { + if (_wasPenetrating) + { + OnPenetrationLost?.Invoke(); + _wasPenetrating = false; + } + } + } + + private void OnTriggerEnter(Collider other) + { + if (other.gameObject.layer == CVRLayers.PlayerLocal) + return; + + Debug.Log("Triggered with " + other.gameObject.name); + _isColliding = true; + } + + private void OnTriggerExit(Collider other) + { + if (other.gameObject.layer == CVRLayers.PlayerLocal) + return; + + Debug.Log("Exited trigger with " + other.gameObject.name); + _isColliding = false; + + if (_wasPenetrating) + { + OnPenetrationLost?.Invoke(); + _wasPenetrating = false; + } + } + + #endregion Unity Events + + private Transform _oldTarget; + + private void OnPreSolverUpdate() + { + if (!IsColliding) return; + + var solverArms = IKSystem.vrik.solver.arms; + IKSolverVR.Arm arm = isLeft ? solverArms[0] : solverArms[1]; + + _oldTarget = arm.target; + arm.target = transform.GetChild(0); + + arm.target.position = ClosestPoint; + arm.target.rotation = Quaternion.LookRotation(_lastPenetrationNormal, _oldTarget.rotation * Vector3.up); + + arm.positionWeight = 1f; + arm.rotationWeight = 1f; + } + + private void OnPostSolverUpdate() + { + if (!_oldTarget) + return; + + var solverArms = IKSystem.vrik.solver.arms; + IKSolverVR.Arm arm = isLeft ? solverArms[0] : solverArms[1]; + arm.target = _oldTarget; + _oldTarget = null; + + arm.positionWeight = 0f; + arm.rotationWeight = 0f; + } +} \ No newline at end of file diff --git a/.Deprecated/SuperAwesomeMod/SuperAwesomeMod.csproj b/InteractionTest/InteractionTest.csproj similarity index 52% rename from .Deprecated/SuperAwesomeMod/SuperAwesomeMod.csproj rename to InteractionTest/InteractionTest.csproj index fa113da..1b7a95f 100644 --- a/.Deprecated/SuperAwesomeMod/SuperAwesomeMod.csproj +++ b/InteractionTest/InteractionTest.csproj @@ -1,12 +1,13 @@ - - ASTExtension - ..\.ManagedLibs\BTKUILib.dll - False + + + + + diff --git a/RCCVirtualSteeringWheel/Main.cs b/InteractionTest/Main.cs similarity index 53% rename from RCCVirtualSteeringWheel/Main.cs rename to InteractionTest/Main.cs index 79fae31..a88cbfd 100644 --- a/RCCVirtualSteeringWheel/Main.cs +++ b/InteractionTest/Main.cs @@ -1,26 +1,22 @@ using MelonLoader; -using NAK.RCCVirtualSteeringWheel.Patches; -namespace NAK.RCCVirtualSteeringWheel; +namespace NAK.InteractionTest; -public class RCCVirtualSteeringWheelMod : MelonMod +public class InteractionTestMod : MelonMod { - private static MelonLogger.Instance Logger; - - #region Melon Events + internal static MelonLogger.Instance Logger; + + #region Melon Mod Overrides public override void OnInitializeMelon() { Logger = LoggerInstance; - ApplyPatches(typeof(RCCCarControllerV3_Patches)); - ApplyPatches(typeof(CVRInputManager_Patches)); - - Logger.Msg(ModSettings.EntryCustomSteeringRange); + ApplyPatches(typeof(Patches.ControllerRayPatches)); } - #endregion Melon Events - + #endregion Melon Mod Overrides + #region Melon Mod Utilities private void ApplyPatches(Type type) @@ -37,4 +33,4 @@ public class RCCVirtualSteeringWheelMod : MelonMod } #endregion Melon Mod Utilities -} \ No newline at end of file +} diff --git a/InteractionTest/ModSettings.cs b/InteractionTest/ModSettings.cs new file mode 100644 index 0000000..010f81b --- /dev/null +++ b/InteractionTest/ModSettings.cs @@ -0,0 +1,7 @@ +using MelonLoader; + +namespace NAK.InteractionTest; + +internal static class ModSettings +{ +} \ No newline at end of file diff --git a/InteractionTest/Patches.cs b/InteractionTest/Patches.cs new file mode 100644 index 0000000..1fa65fd --- /dev/null +++ b/InteractionTest/Patches.cs @@ -0,0 +1,17 @@ + +using ABI_RC.Core; +using ABI_RC.Core.InteractionSystem; +using HarmonyLib; +using NAK.InteractionTest.Components; + +namespace NAK.InteractionTest.Patches; + +internal static class ControllerRayPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.Start))] + private static void Postfix_BetterCharacterController_Start(ref ControllerRay __instance) + { + InteractionTracker.Setup(__instance.gameObject, __instance.hand == CVRHand.Left); + } +} \ No newline at end of file diff --git a/.Deprecated/BullshitWatcher/Properties/AssemblyInfo.cs b/InteractionTest/Properties/AssemblyInfo.cs similarity index 67% rename from .Deprecated/BullshitWatcher/Properties/AssemblyInfo.cs rename to InteractionTest/Properties/AssemblyInfo.cs index c78d2a8..ffc8f5e 100644 --- a/.Deprecated/BullshitWatcher/Properties/AssemblyInfo.cs +++ b/InteractionTest/Properties/AssemblyInfo.cs @@ -1,30 +1,30 @@ -using NAK.BullshitWatcher.Properties; +using NAK.InteractionTest.Properties; using MelonLoader; using System.Reflection; [assembly: AssemblyVersion(AssemblyInfoParams.Version)] [assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] [assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.BullshitWatcher))] +[assembly: AssemblyTitle(nameof(NAK.InteractionTest))] [assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.BullshitWatcher))] +[assembly: AssemblyProduct(nameof(NAK.InteractionTest))] [assembly: MelonInfo( - typeof(NAK.BullshitWatcher.BullshitWatcherMod), - nameof(NAK.BullshitWatcher), + typeof(NAK.InteractionTest.InteractionTestMod), + nameof(NAK.InteractionTest), AssemblyInfoParams.Version, AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/BullshitWatcher" + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/InteractionTest" )] [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: MelonColor(255, 125, 126, 129)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] -namespace NAK.BullshitWatcher.Properties; +namespace NAK.InteractionTest.Properties; internal static class AssemblyInfoParams { public const string Version = "1.0.0"; diff --git a/.Experimental/OriginShift/README.md b/InteractionTest/README.md similarity index 100% rename from .Experimental/OriginShift/README.md rename to InteractionTest/README.md diff --git a/.Experimental/OriginShift/format.json b/InteractionTest/format.json similarity index 100% rename from .Experimental/OriginShift/format.json rename to InteractionTest/format.json diff --git a/KeepVelocityOnExitFlight/Properties/AssemblyInfo.cs b/KeepVelocityOnExitFlight/Properties/AssemblyInfo.cs index e51a838..9ad16f8 100644 --- a/KeepVelocityOnExitFlight/Properties/AssemblyInfo.cs +++ b/KeepVelocityOnExitFlight/Properties/AssemblyInfo.cs @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.KeepVelocityOnExitFlight.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/KeepVelocityOnExitFlight/format.json b/KeepVelocityOnExitFlight/format.json index 94d3b8a..ae56509 100644 --- a/KeepVelocityOnExitFlight/format.json +++ b/KeepVelocityOnExitFlight/format.json @@ -1,8 +1,8 @@ { - "_id": 222, + "_id": -1, "name": "KeepVelocityOnExitFlight", - "modversion": "1.0.1", - "gameversion": "2025r179", + "modversion": "1.0.0", + "gameversion": "2024r175", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", @@ -17,8 +17,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/KeepVelocityOnExitFlight.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r34/KeepVelocityOnExitFlight.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/KeepVelocityOnExitFlight/", - "changelog": "- Recompiled for 2025r179", + "changelog": "- Initial release", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4480281 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 NotAKidoS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LazyPrune/Properties/AssemblyInfo.cs b/LazyPrune/Properties/AssemblyInfo.cs index 169f34e..48fb1ee 100644 --- a/LazyPrune/Properties/AssemblyInfo.cs +++ b/LazyPrune/Properties/AssemblyInfo.cs @@ -20,13 +20,11 @@ using System.Reflection; [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.LazyPrune.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.3"; + public const string Version = "1.0.2"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/LazyPrune/format.json b/LazyPrune/format.json index 5125073..d954f95 100644 --- a/LazyPrune/format.json +++ b/LazyPrune/format.json @@ -1,8 +1,8 @@ { "_id": 214, "name": "LazyPrune", - "modversion": "1.0.3", - "gameversion": "2025r179", + "modversion": "1.0.2", + "gameversion": "2024r175", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", @@ -17,8 +17,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/LazyPrune.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r32/LazyPrune.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LazyPrune/", - "changelog": "- Recompiled for 2025r179", + "changelog": "- Fixed killtime check, would needlessly check known non-eligible objects for pruning.\n- Moved away from using GameEventSystem as it proved unreliable for tracking remote Avatar destruction, now patching Object Loader directly as loadedObject is not assigned when object wrappers are enabled.\n- Fixed scheduled prune job being nuked as it was created before initial scene load.\n- Patched two race conditions in the game that would cause the object loader to lock up.", "embedcolor": "#1c75f1" } \ No newline at end of file diff --git a/.Experimental/LuaTTS/LuaTTS.csproj b/LuaTTS/LuaTTS.csproj similarity index 100% rename from .Experimental/LuaTTS/LuaTTS.csproj rename to LuaTTS/LuaTTS.csproj diff --git a/RelativeSyncJitterFix/Main.cs b/LuaTTS/Main.cs similarity index 67% rename from RelativeSyncJitterFix/Main.cs rename to LuaTTS/Main.cs index 19863d1..2035bb9 100644 --- a/RelativeSyncJitterFix/Main.cs +++ b/LuaTTS/Main.cs @@ -1,13 +1,13 @@ using MelonLoader; +using NAK.LuaTTS.Patches; -namespace NAK.RelativeSyncJitterFix; +namespace NAK.LuaTTS; -public class RelativeSyncJitterFixMod : MelonMod +public class LuaTTSMod : MelonMod { public override void OnInitializeMelon() { - // Experimental sync hack - ApplyPatches(typeof(Patches.CVRSpawnablePatches)); + ApplyPatches(typeof(LuaScriptFactoryPatches)); } private void ApplyPatches(Type type) diff --git a/LuaTTS/Patches.cs b/LuaTTS/Patches.cs new file mode 100644 index 0000000..c81a0d6 --- /dev/null +++ b/LuaTTS/Patches.cs @@ -0,0 +1,20 @@ + +using ABI.Scripting.CVRSTL.Client; +using ABI.Scripting.CVRSTL.Common; +using HarmonyLib; +using MoonSharp.Interpreter; +using NAK.LuaTTS.Modules; + +namespace NAK.LuaTTS.Patches; + +internal static class LuaScriptFactoryPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(LuaScriptFactory.CVRRequireModule), nameof(LuaScriptFactory.CVRRequireModule.require))] + private static void Postfix_CVRRequireModule_require(string modid, + ref object __result, ref Script ___script, CVRLuaContext ___context) + { + if (modid == "TextToSpeech") + __result = TTSLuaModule.RegisterUserData(___script, ___context); + } +} \ No newline at end of file diff --git a/.Experimental/LuaTTS/Properties/AssemblyInfo.cs b/LuaTTS/Properties/AssemblyInfo.cs similarity index 87% rename from .Experimental/LuaTTS/Properties/AssemblyInfo.cs rename to LuaTTS/Properties/AssemblyInfo.cs index 13b932d..435ab9a 100644 --- a/.Experimental/LuaTTS/Properties/AssemblyInfo.cs +++ b/LuaTTS/Properties/AssemblyInfo.cs @@ -20,13 +20,13 @@ using System.Reflection; [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: MelonColor(255, 125, 126, 129)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] namespace NAK.LuaTTS.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/.Experimental/LuaTTS/README.md b/LuaTTS/README.md similarity index 100% rename from .Experimental/LuaTTS/README.md rename to LuaTTS/README.md diff --git a/.Experimental/LuaTTS/TTSLuaModule.cs b/LuaTTS/TTSLuaModule.cs similarity index 100% rename from .Experimental/LuaTTS/TTSLuaModule.cs rename to LuaTTS/TTSLuaModule.cs diff --git a/LuaTTS/format.json b/LuaTTS/format.json new file mode 100644 index 0000000..9ec9f5f --- /dev/null +++ b/LuaTTS/format.json @@ -0,0 +1,23 @@ +{ + "_id": 211, + "name": "RelativeSync", + "modversion": "1.0.2", + "gameversion": "2024r175", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network.\n\nProvides some Experimental settings to also fix local jitter on movement parents.", + "searchtags": [ + "relative", + "sync", + "movement", + "chair" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r28/RelativeSync.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSync/", + "changelog": "- Fixed RelativeSyncMarker not generating correct path hash for local player\n - This fixes relative sync on local avatar movement parents\n- Fixed initial calculated interpolation interval when assigning a new RelativeSyncMarker for a remote user\n - Would calculate from time was last on a movement parent, which could lead to seconds long interpolation\n- Added Network Debug settings\n- Added experimental settings to fix **local** jitter on movement parents\n - These options are disabled by default as they have not been heavily tested", + "embedcolor": "#507e64" +} \ No newline at end of file diff --git a/MirrorClone/Main.cs b/MirrorClone/Main.cs new file mode 100644 index 0000000..87afe20 --- /dev/null +++ b/MirrorClone/Main.cs @@ -0,0 +1,80 @@ +using MelonLoader; +using System.Reflection; +using ABI_RC.Core.Player; +using ABI_RC.Core.Util; +using ABI_RC.Systems.IK; +using UnityEngine; + +namespace NAK.BetterShadowClone; + +public class MirrorCloneMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + ModSettings.Initialize(); + + try + { + InitializePatches(); + } + catch (Exception e) + { + Logger.Error(e); + } + } + + #region Harmony Patches + + private void InitializePatches() + { + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.Awake), BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyLib.HarmonyMethod(typeof(MirrorCloneMod).GetMethod(nameof(OnPlayerSetup_Awake_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + prefix: new HarmonyLib.HarmonyMethod(typeof(MirrorCloneMod).GetMethod(nameof(OnPlayerSetup_SetupAvatar_Prefix), BindingFlags.NonPublic | BindingFlags.Static)), + postfix: new HarmonyLib.HarmonyMethod(typeof(MirrorCloneMod).GetMethod(nameof(OnPlayerSetup_SetupAvatar_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + prefix: new HarmonyLib.HarmonyMethod(typeof(MirrorCloneMod).GetMethod(nameof(OnPlayerSetup_ClearAvatar_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod(nameof(IKSystem.OnPostSolverUpdateGeneral), BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyLib.HarmonyMethod(typeof(MirrorCloneMod).GetMethod(nameof(OnIKSystem_OnPostSolverUpdateGeneral_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(TransformHiderForMainCamera).GetMethod(nameof(TransformHiderForMainCamera.ProcessHierarchy)), + prefix: new HarmonyLib.HarmonyMethod(typeof(MirrorCloneMod).GetMethod(nameof(OnTransformHiderForMainCamera_ProcessHierarchy_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + } + + private static void OnPlayerSetup_Awake_Postfix() + => MirrorCloneManager.OnPlayerSetupAwake(); + + private static void OnPlayerSetup_SetupAvatar_Prefix(GameObject inAvatar) + => MirrorCloneManager.Instance.OnAvatarInitialized(inAvatar); + + private static void OnPlayerSetup_SetupAvatar_Postfix() + => MirrorCloneManager.Instance.OnAvatarConfigured(); + + private static void OnPlayerSetup_ClearAvatar_Prefix() + => MirrorCloneManager.Instance.OnAvatarDestroyed(); + + private static void OnIKSystem_OnPostSolverUpdateGeneral_Postfix() + => MirrorCloneManager.Instance.OnPostSolverUpdateGeneral(); + + private static void OnTransformHiderForMainCamera_ProcessHierarchy_Prefix(ref bool __runOriginal) + => __runOriginal = !ModSettings.EntryEnabled.Value; + + #endregion +} \ No newline at end of file diff --git a/.Deprecated/SearchWithSpacesFix/SearchWithSpacesFix.csproj b/MirrorClone/MirrorClone.csproj similarity index 100% rename from .Deprecated/SearchWithSpacesFix/SearchWithSpacesFix.csproj rename to MirrorClone/MirrorClone.csproj diff --git a/MirrorClone/MirrorClone/MirrorCloneManager.cs b/MirrorClone/MirrorClone/MirrorCloneManager.cs new file mode 100644 index 0000000..035c9c0 --- /dev/null +++ b/MirrorClone/MirrorClone/MirrorCloneManager.cs @@ -0,0 +1,247 @@ +using ABI_RC.Core; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.IK; +using ABI.CCK.Components; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NAK.BetterShadowClone; + +public class MirrorCloneManager : MonoBehaviour +{ + #region Static Instance + + public static MirrorCloneManager Instance { get; private set; } + + #endregion + + private bool _isAvatarConfigured; + + private GameObject _avatar; + private GameObject _mirrorClone; + private GameObject _initializationTarget; + + private CVRAnimatorManager _animatorManager; + private Animator _mirrorAnimator; + + #region Unity Events + + private void Awake() + { + if (Instance != null + && Instance != this) + { + DestroyImmediate(this); + return; + } + + Instance = this; + + MirrorCloneMod.Logger.Msg("Mirror Clone Manager initialized."); + + _animatorManager = PlayerSetup.Instance.animatorManager; + + // Create initialization target (so no components are initialized before we're ready) + _initializationTarget = new GameObject(nameof(MirrorCloneManager) + " Initialization Target"); + _initializationTarget.transform.SetParent(transform); + _initializationTarget.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + _initializationTarget.transform.localScale = Vector3.one; + _initializationTarget.SetActive(false); + } + + private void OnDestroy() + { + if (Instance == this) + Instance = null; + } + + #endregion + + #region Game Events + + public static void OnPlayerSetupAwake() + { + if (Instance != null) + return; + + GameObject manager = new (nameof(MirrorCloneManager), typeof(MirrorCloneManager)); + DontDestroyOnLoad(manager); + } + + public void OnAvatarInitialized(GameObject avatar) + { + if (!ModSettings.EntryEnabled.Value) + return; + + if (avatar == null + || _isAvatarConfigured) + return; + + _isAvatarConfigured = true; + + _avatar = avatar; + _mirrorClone = InstantiateMirrorCopy(_avatar); + } + + public void OnAvatarConfigured() + { + if (!_isAvatarConfigured) + return; + + Animator baseAnimator = _avatar.GetComponent(); + + if (!_mirrorClone.TryGetComponent(out _mirrorAnimator)) + _mirrorAnimator = gameObject.AddComponent(); + _mirrorAnimator.runtimeAnimatorController = baseAnimator.runtimeAnimatorController; + + _animatorManager._copyAnimator = _mirrorAnimator; // thank you for existing + + var cameras = PlayerSetup.Instance.GetComponentsInChildren(true); + foreach (var camera in cameras) + { + // hide PlayerClone layer from all cameras + camera.cullingMask &= ~(1 << CVRLayers.PlayerClone); + } + + var mirrors = Resources.FindObjectsOfTypeAll(); + foreach (CVRMirror mirror in mirrors) + { + // hide PlayerLocal layer from all mirrors + mirror.m_ReflectLayers &= ~(1 << CVRLayers.PlayerLocal); + } + + // scale avatar head bone to 0 0 0 + Transform headBone = baseAnimator.GetBoneTransform(HumanBodyBones.Head); + headBone.localScale = Vector3.zero; + + CleanupAvatar(); + CleanupMirrorClone(); + SetupHumanPoseHandler(); + + _initializationTarget.SetActive(true); + } + + public void OnAvatarDestroyed() + { + if (!_isAvatarConfigured) + return; + + _avatar = null; + _mirrorAnimator = null; + if (_mirrorClone != null) + Destroy(_mirrorClone); + + _initializationTarget.SetActive(false); + + _isAvatarConfigured = false; + } + + public void OnPostSolverUpdateGeneral() + { + if (!_isAvatarConfigured) + return; + + StealTransforms(); + } + + #endregion + + #region Private Methods + + private GameObject InstantiateMirrorCopy(GameObject original) + { + GameObject clone = Instantiate(original, _initializationTarget.transform); + clone.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + clone.name = original.name + " (Mirror Clone)"; + clone.SetLayerRecursive(CVRLayers.PlayerClone); + return clone; + } + + private void CleanupAvatar() + { + // set local avatar mesh to shadow off + var avatarMeshes = _avatar.GetComponentsInChildren(true); + foreach (SkinnedMeshRenderer avatarMesh in avatarMeshes) + { + avatarMesh.shadowCastingMode = ShadowCastingMode.Off; + avatarMesh.forceMatrixRecalculationPerRender = false; + } + } + + private void CleanupMirrorClone() + { + // destroy unneeded components + // only keep Animator + + var components = _mirrorClone.GetComponentsInChildren(true); + foreach (Component component in components) + { + if (component == null) + continue; + + // skip basic unity components + if (component is Animator + or Transform + or SkinnedMeshRenderer + or MeshRenderer + or MeshFilter) + continue; + + // skip basic CVR components + if (component is CVRAvatar or CVRAssetInfo) + { + (component as MonoBehaviour).enabled = false; + continue; + } + + Destroy(component); + } + } + + #endregion + + #region Job System + + private HumanPoseHandler _humanPoseHandler; + private Transform _hipTransform; + + private void SetupHumanPoseHandler() + { + _hipTransform = _mirrorAnimator.GetBoneTransform(HumanBodyBones.Hips); + + _humanPoseHandler?.Dispose(); + _humanPoseHandler = new HumanPoseHandler(_mirrorAnimator.avatar, _mirrorAnimator.transform); + } + + private void StealTransforms() + { + // copy transforms from avatar to mirror clone + // var avatarTransforms = _avatar.GetComponentsInChildren(true); + // var mirrorCloneTransforms = _mirrorClone.GetComponentsInChildren(true); + // for (int i = 0; i < avatarTransforms.Length; i++) + // { + // Transform avatarTransform = avatarTransforms[i]; + // Transform mirrorCloneTransform = mirrorCloneTransforms[i]; + // + // mirrorCloneTransform.SetLocalPositionAndRotation( + // avatarTransform.localPosition, + // avatarTransform.localRotation); + // } + + if (!IKSystem.Instance.IsAvatarCalibrated()) + return; + + IKSystem.Instance._humanPoseHandler.GetHumanPose(ref IKSystem.Instance._humanPose); + _humanPoseHandler.SetHumanPose(ref IKSystem.Instance._humanPose); + + if (!MetaPort.Instance.isUsingVr) + _mirrorAnimator.transform.SetPositionAndRotation(PlayerSetup.Instance.GetPlayerPosition(), PlayerSetup.Instance.GetPlayerRotation()); + else + _mirrorAnimator.transform.SetPositionAndRotation(_avatar.transform.position, _avatar.transform.rotation); + + _hipTransform.SetPositionAndRotation(IKSystem.Instance._hipTransform.position, IKSystem.Instance._hipTransform.rotation); + } + + #endregion +} \ No newline at end of file diff --git a/MirrorClone/ModSettings.cs b/MirrorClone/ModSettings.cs new file mode 100644 index 0000000..05c753a --- /dev/null +++ b/MirrorClone/ModSettings.cs @@ -0,0 +1,31 @@ +using MelonLoader; +using UnityEngine; + +namespace NAK.BetterShadowClone; + +public static class ModSettings +{ + #region Melon Prefs + + private const string SettingsCategory = nameof(MirrorCloneMod); + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(SettingsCategory); + + internal static readonly MelonPreferences_Entry EntryEnabled = + Category.CreateEntry("Enabled", true, + description: "Enable Mirror Clone."); + + #endregion + + internal static void Initialize() + { + foreach (MelonPreferences_Entry setting in Category.Entries) + setting.OnEntryValueChangedUntyped.Subscribe(OnSettingsChanged); + } + + internal static void OnSettingsChanged(object oldValue = null, object newValue = null) + { + + } +} \ No newline at end of file diff --git a/MirrorClone/Properties/AssemblyInfo.cs b/MirrorClone/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..611ba51 --- /dev/null +++ b/MirrorClone/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using MelonLoader; +using NAK.BetterShadowClone.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.BetterShadowClone))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.BetterShadowClone))] + +[assembly: MelonInfo( + typeof(NAK.BetterShadowClone.MirrorCloneMod), + nameof(NAK.BetterShadowClone), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/MirrorCloneMod" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] + +namespace NAK.BetterShadowClone.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/MirrorClone/README.md b/MirrorClone/README.md new file mode 100644 index 0000000..af9d39c --- /dev/null +++ b/MirrorClone/README.md @@ -0,0 +1,16 @@ +# EzCurls + +A mod that allows you to tune your finger curls to your liking. Supposedly can help with VR sign language, as raw finger curls are not that great for quick and precise gestures. + +The settings are not too coherent, it is mostly a bunch of things thrown at the wall, but it works for me. I hope it works for you too. + +--- + +Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation and is 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. diff --git a/MirrorClone/format.json b/MirrorClone/format.json new file mode 100644 index 0000000..a2374f1 --- /dev/null +++ b/MirrorClone/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "EzCurls", + "modversion": "1.0.0", + "gameversion": "2023r173", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "A mod that allows you to tune your finger curls to your liking. Supposedly can help with VR sign language, as raw finger curls are not that great for quick and precise gestures.\n\nThe settings are not too coherent, it is mostly a bunch of things thrown at the wall, but it works for me. I hope it works for you too.", + "searchtags": [ + "curls", + "fingers", + "index", + "knuckles" + ], + "requirements": [ + "UIExpansionKit" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r24/EzCurls.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/EzCurls/", + "changelog": "- Initial CVRMG release", + "embedcolor": "7d7d7d" +} \ No newline at end of file diff --git a/.Deprecated/MoreMenuOptions/Main.cs b/MoreMenuOptions/Main.cs similarity index 100% rename from .Deprecated/MoreMenuOptions/Main.cs rename to MoreMenuOptions/Main.cs diff --git a/.Deprecated/MoreMenuOptions/ModSettings.cs b/MoreMenuOptions/ModSettings.cs similarity index 100% rename from .Deprecated/MoreMenuOptions/ModSettings.cs rename to MoreMenuOptions/ModSettings.cs diff --git a/.Deprecated/Nevermind/Nevermind.csproj b/MoreMenuOptions/MoreMenuOptions.csproj similarity index 100% rename from .Deprecated/Nevermind/Nevermind.csproj rename to MoreMenuOptions/MoreMenuOptions.csproj diff --git a/.Deprecated/MoreMenuOptions/Properties/AssemblyInfo.cs b/MoreMenuOptions/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/MoreMenuOptions/Properties/AssemblyInfo.cs rename to MoreMenuOptions/Properties/AssemblyInfo.cs diff --git a/.Deprecated/MoreMenuOptions/README.md b/MoreMenuOptions/README.md similarity index 100% rename from .Deprecated/MoreMenuOptions/README.md rename to MoreMenuOptions/README.md diff --git a/.Deprecated/MoreMenuOptions/format.json b/MoreMenuOptions/format.json similarity index 100% rename from .Deprecated/MoreMenuOptions/format.json rename to MoreMenuOptions/format.json diff --git a/.Deprecated/MuteSFX/AudioModuleManager.cs b/MuteSFX/AudioModuleManager.cs similarity index 100% rename from .Deprecated/MuteSFX/AudioModuleManager.cs rename to MuteSFX/AudioModuleManager.cs diff --git a/.Deprecated/MuteSFX/Main.cs b/MuteSFX/Main.cs similarity index 100% rename from .Deprecated/MuteSFX/Main.cs rename to MuteSFX/Main.cs diff --git a/.Deprecated/MuteSFX/MuteSFX.csproj b/MuteSFX/MuteSFX.csproj similarity index 67% rename from .Deprecated/MuteSFX/MuteSFX.csproj rename to MuteSFX/MuteSFX.csproj index 5eba218..21b218c 100644 --- a/.Deprecated/MuteSFX/MuteSFX.csproj +++ b/MuteSFX/MuteSFX.csproj @@ -5,7 +5,7 @@ - - + + diff --git a/.Deprecated/MuteSFX/Properties/AssemblyInfo.cs b/MuteSFX/Properties/AssemblyInfo.cs similarity index 96% rename from .Deprecated/MuteSFX/Properties/AssemblyInfo.cs rename to MuteSFX/Properties/AssemblyInfo.cs index 452c973..75cb9f3 100644 --- a/.Deprecated/MuteSFX/Properties/AssemblyInfo.cs +++ b/MuteSFX/Properties/AssemblyInfo.cs @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.MuteSFX.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.3"; + public const string Version = "1.0.2"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/.Deprecated/MuteSFX/README.md b/MuteSFX/README.md similarity index 100% rename from .Deprecated/MuteSFX/README.md rename to MuteSFX/README.md diff --git a/.Deprecated/MuteSFX/SFX/sfx_mute.wav b/MuteSFX/SFX/sfx_mute.wav similarity index 100% rename from .Deprecated/MuteSFX/SFX/sfx_mute.wav rename to MuteSFX/SFX/sfx_mute.wav diff --git a/.Deprecated/MuteSFX/SFX/sfx_unmute.wav b/MuteSFX/SFX/sfx_unmute.wav similarity index 100% rename from .Deprecated/MuteSFX/SFX/sfx_unmute.wav rename to MuteSFX/SFX/sfx_unmute.wav diff --git a/.Deprecated/MuteSFX/format.json b/MuteSFX/format.json similarity index 96% rename from .Deprecated/MuteSFX/format.json rename to MuteSFX/format.json index 877e3e0..3601dd3 100644 --- a/.Deprecated/MuteSFX/format.json +++ b/MuteSFX/format.json @@ -1,7 +1,7 @@ { "_id": 172, "name": "MuteSFX", - "modversion": "1.0.3", + "modversion": "1.0.2", "gameversion": "2023r171", "loaderversion": "0.6.1", "modtype": "Mod", diff --git a/NAK_CVR_Mods.sln b/NAK_CVR_Mods.sln index 1541fc7..ccdda68 100644 --- a/NAK_CVR_Mods.sln +++ b/NAK_CVR_Mods.sln @@ -3,8 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32630.192 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CVRGizmos", "CVRGizmos\CVRGizmos.csproj", "{CF9BC79E-4FB6-429A-8C19-DF31F040BD4A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FuckToes", "FuckToes\FuckToes.csproj", "{79B2A7C4-348D-4A8E-94D1-BA22FDD5FEED}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GestureLock", "GestureLock\GestureLock.csproj", "{45A65AEB-4BFC-4E47-B181-BBB43BD81283}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PathCamDisabler", "PathCamDisabler\PathCamDisabler.csproj", "{98169FD2-5CEB-46D1-A320-D7E06F82C9E0}" EndProject @@ -14,69 +17,80 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PropUndoButton", "PropUndoB EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThirdPerson", "ThirdPerson\ThirdPerson.csproj", "{675CEC0E-3E8A-4970-98EA-9B79277A7252}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarScaleMod", "AvatarScale\AvatarScaleMod.csproj", "{A6DF0D98-428C-4FE2-BA7F-756312122B1E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKAdjustments", "IKAdjustments\IKAdjustments.csproj", "{CCD510BF-1A32-441F-B52B-8A937BF75CE3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FOVAdjustment", "FOVAdjustment\FOVAdjustment.csproj", "{EE552804-30B1-49CF-BBDE-3B312895AFF7}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MuteSFX", "MuteSFX\MuteSFX.csproj", "{77D222FC-4AEC-4672-A87A-B860B4C39E17}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatBoxExtensions", "ChatBoxExtensions\ChatBoxExtensions.csproj", "{0E1DD746-33A1-4179-AE70-8FB83AC40ABC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EzCurls", "EzCurls\EzCurls.csproj", "{9D39E900-8F38-485B-8B67-9F75D8FF2C5E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhysicsGunMod", "PhysicsGunMod\PhysicsGunMod.csproj", "{F94DDB73-9041-4F5C-AD43-6960701E8417}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MirrorClone", "MirrorClone\MirrorClone.csproj", "{D5E81123-9D3B-4420-9CCD-1861657BE00B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterShadowClone", "BetterShadowClone\BetterShadowClone.csproj", "{D0C40987-AF16-490A-9304-F99D5A5A774C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FuckCameraIndicator", "FuckCameraIndicator\FuckCameraIndicator.csproj", "{0BE10630-EA6A-40FB-B3AF-5C2018F22BD3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nevermind", "Nevermind\Nevermind.csproj", "{AC4857DD-F6D9-436D-A3EE-D148A518E642}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShadowCloneFallback", "ShadowCloneFallback\ShadowCloneFallback.csproj", "{69AF3C10-1BB1-4746-B697-B5A81D78C8D9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StopClosingMyMenuOnWorldLoad", "StopClosingMyMenuOnWorldLoad\StopClosingMyMenuOnWorldLoad.csproj", "{9FA83514-13F8-412C-9790-C2B750E0E7E7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwitchToDesktopOnSteamVRExit", "SwitchToDesktopOnSteamVRExit\SwitchToDesktopOnSteamVRExit.csproj", "{F7F4B840-6FF5-46C4-AAFD-95362D1D666C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoreMenuOptions", "MoreMenuOptions\MoreMenuOptions.csproj", "{AB07B31E-A930-4CCC-8E02-F2A4D6C52C8F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RelativeSync", "RelativeSync\RelativeSync.csproj", "{B48C8F19-9451-4EE2-999F-82C0033CDE2C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptingSpoofer", "ScriptingSpoofer\ScriptingSpoofer.csproj", "{6B4396C7-B451-4FFD-87B6-3ED8377AC308}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaTTS", "LuaTTS\LuaTTS.csproj", "{24A069F4-4D69-4ABD-AA16-77765469245B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LazyPrune", "LazyPrune\LazyPrune.csproj", "{8FA6D481-5801-4E4C-822E-DE561155D22B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReconnectionSystemFix", "ReconnectionSystemFix\ReconnectionSystemFix.csproj", "{05C427DD-1261-4AAD-B316-A551FC126F2C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AASDefaultProfileFix", "AASDefaultProfileFix\AASDefaultProfileFix.csproj", "{C6794B18-E785-4F91-A517-3A2A8006E008}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OriginShift", "OriginShift\OriginShift.csproj", "{F381F604-9C16-4870-AD49-4BD7CA3F36DC}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScrollFlight", "ScrollFlight\ScrollFlight.csproj", "{1B5D7DCB-01A4-4988-8B25-211948AEED76}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Portals", "Portals\Portals.csproj", "{BE9629C2-8461-481C-B267-1B8A1805DCD7}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PropLoadingHexagon", "PropLoadingHexagon\PropLoadingHexagon.csproj", "{642A2BC7-C027-4F8F-969C-EF0F867936FD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IKSimulatedRootAngleFix", "IKSimulatedRootAngleFix\IKSimulatedRootAngleFix.csproj", "{D11214B0-94FE-4008-8D1B-3DC8614466B3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DropPropTweak", "DropPropTweak\DropPropTweak.csproj", "{2CC1F7C6-A953-4008-8C10-C7592EB401E8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualCloneFix", "VisualCloneFix\VisualCloneFix.csproj", "{39915C4C-B555-4CB9-890F-26DE1388BC2E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteractionTest", "InteractionTest\InteractionTest.csproj", "{7C675E64-0A2D-4B34-B6D1-5D6AA369A520}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeepVelocityOnExitFlight", "KeepVelocityOnExitFlight\KeepVelocityOnExitFlight.csproj", "{0BB3D187-BBBA-4C58-B246-102342BE5E8C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASTExtension", "ASTExtension\ASTExtension.csproj", "{6580AA87-6A95-438E-A5D3-70E583CCD77B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeadLookLockingInputFix", "HeadLookLockingInputFix\HeadLookLockingInputFix.csproj", "{AAE8A952-2764-43CF-A5D7-515924CA2D9F}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvatarQueueSystemTweaks", "AvatarQueueSystemTweaks\AvatarQueueSystemTweaks.csproj", "{D178E422-283B-4FB3-89A6-AA4FB9F87E2F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomSpawnPoint", "CustomSpawnPoint\CustomSpawnPoint.csproj", "{51CA34CA-7684-4819-AC9E-89DFAD63E9AB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NukePostPresentHandoff", "NukePostPresentHandoff\NukePostPresentHandoff.csproj", "{77F332CD-019A-472F-9269-CFDEB087B3F9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CVRLuaToolsExtension", "CVRLuaToolsExtension\CVRLuaToolsExtension.csproj", "{FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stickers", "Stickers\Stickers.csproj", "{E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}" EndProject -EndProject -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmootherRay", "SmootherRay\SmootherRay.csproj", "{99F9D60D-9A2D-4DBE-AA52-13D8A0838696}" -EndProject -EndProject -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShareBubbles", "ShareBubbles\ShareBubbles.csproj", "{ADD6205B-67A4-4BA8-8BED-DF7D0E857A6A}" -EndProject -EndProject -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCCVirtualSteeringWheel", "RCCVirtualSteeringWheel\RCCVirtualSteeringWheel.csproj", "{4A378F81-3805-41E8-9565-A8A89A8C00D6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YouAreMyPropNowWeAreHavingSoftTacosLater", "YouAreMyPropNowWeAreHavingSoftTacosLater\YouAreMyPropNowWeAreHavingSoftTacosLater.csproj", "{8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoubleTapJumpToExitSeat", "DoubleTapJumpToExitSeat\DoubleTapJumpToExitSeat.csproj", "{36BF2B8B-F444-4886-AA4C-0EDF7540F1CE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FuckToes", "FuckToes\FuckToes.csproj", "{751E4140-2F4D-4550-A4A9-65ABA9F7893A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaNetworkVariables", ".Experimental\LuaNetworkVariables\LuaNetworkVariables.csproj", "{6E7857D9-07AC-419F-B111-0DB0348D1C92}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchySquishy", ".Blackbox\TouchySquishy\TouchySquishy.csproj", "{FF4BF0E7-698D-49A0-96E9-0E2646FEAFFA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CVRLuaToolsExtension", ".Experimental\CVRLuaToolsExtension\CVRLuaToolsExtension.csproj", "{3D221A25-007F-4764-98CD-CEEF2EB92165}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigureCalibrationPose", "ConfigureCalibrationPose\ConfigureCalibrationPose.csproj", "{31667A36-D069-4708-9DCA-E3446009941B}" -EndProject -EndProject -EndProject -EndProject -EndProject -EndProject -EndProject -EndProject -EndProject -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -111,18 +125,46 @@ Global {675CEC0E-3E8A-4970-98EA-9B79277A7252}.Debug|Any CPU.Build.0 = Debug|Any CPU {675CEC0E-3E8A-4970-98EA-9B79277A7252}.Release|Any CPU.ActiveCfg = Release|Any CPU {675CEC0E-3E8A-4970-98EA-9B79277A7252}.Release|Any CPU.Build.0 = Release|Any CPU + {A6DF0D98-428C-4FE2-BA7F-756312122B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6DF0D98-428C-4FE2-BA7F-756312122B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6DF0D98-428C-4FE2-BA7F-756312122B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6DF0D98-428C-4FE2-BA7F-756312122B1E}.Release|Any CPU.Build.0 = Release|Any CPU + {CCD510BF-1A32-441F-B52B-8A937BF75CE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCD510BF-1A32-441F-B52B-8A937BF75CE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCD510BF-1A32-441F-B52B-8A937BF75CE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCD510BF-1A32-441F-B52B-8A937BF75CE3}.Release|Any CPU.Build.0 = Release|Any CPU {EE552804-30B1-49CF-BBDE-3B312895AFF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE552804-30B1-49CF-BBDE-3B312895AFF7}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE552804-30B1-49CF-BBDE-3B312895AFF7}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE552804-30B1-49CF-BBDE-3B312895AFF7}.Release|Any CPU.Build.0 = Release|Any CPU + {77D222FC-4AEC-4672-A87A-B860B4C39E17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77D222FC-4AEC-4672-A87A-B860B4C39E17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77D222FC-4AEC-4672-A87A-B860B4C39E17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77D222FC-4AEC-4672-A87A-B860B4C39E17}.Release|Any CPU.Build.0 = Release|Any CPU {0E1DD746-33A1-4179-AE70-8FB83AC40ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E1DD746-33A1-4179-AE70-8FB83AC40ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E1DD746-33A1-4179-AE70-8FB83AC40ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E1DD746-33A1-4179-AE70-8FB83AC40ABC}.Release|Any CPU.Build.0 = Release|Any CPU + {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D39E900-8F38-485B-8B67-9F75D8FF2C5E}.Release|Any CPU.Build.0 = Release|Any CPU {F94DDB73-9041-4F5C-AD43-6960701E8417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F94DDB73-9041-4F5C-AD43-6960701E8417}.Debug|Any CPU.Build.0 = Debug|Any CPU {F94DDB73-9041-4F5C-AD43-6960701E8417}.Release|Any CPU.ActiveCfg = Release|Any CPU {F94DDB73-9041-4F5C-AD43-6960701E8417}.Release|Any CPU.Build.0 = Release|Any CPU + {D5E81123-9D3B-4420-9CCD-1861657BE00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5E81123-9D3B-4420-9CCD-1861657BE00B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5E81123-9D3B-4420-9CCD-1861657BE00B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5E81123-9D3B-4420-9CCD-1861657BE00B}.Release|Any CPU.Build.0 = Release|Any CPU + {D0C40987-AF16-490A-9304-F99D5A5A774C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C40987-AF16-490A-9304-F99D5A5A774C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C40987-AF16-490A-9304-F99D5A5A774C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C40987-AF16-490A-9304-F99D5A5A774C}.Release|Any CPU.Build.0 = Release|Any CPU + {0BE10630-EA6A-40FB-B3AF-5C2018F22BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BE10630-EA6A-40FB-B3AF-5C2018F22BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BE10630-EA6A-40FB-B3AF-5C2018F22BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BE10630-EA6A-40FB-B3AF-5C2018F22BD3}.Release|Any CPU.Build.0 = Release|Any CPU {AC4857DD-F6D9-436D-A3EE-D148A518E642}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AC4857DD-F6D9-436D-A3EE-D148A518E642}.Debug|Any CPU.Build.0 = Debug|Any CPU {AC4857DD-F6D9-436D-A3EE-D148A518E642}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -135,6 +177,14 @@ Global {9FA83514-13F8-412C-9790-C2B750E0E7E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {9FA83514-13F8-412C-9790-C2B750E0E7E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9FA83514-13F8-412C-9790-C2B750E0E7E7}.Release|Any CPU.Build.0 = Release|Any CPU + {F7F4B840-6FF5-46C4-AAFD-95362D1D666C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7F4B840-6FF5-46C4-AAFD-95362D1D666C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7F4B840-6FF5-46C4-AAFD-95362D1D666C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7F4B840-6FF5-46C4-AAFD-95362D1D666C}.Release|Any CPU.Build.0 = Release|Any CPU + {AB07B31E-A930-4CCC-8E02-F2A4D6C52C8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB07B31E-A930-4CCC-8E02-F2A4D6C52C8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB07B31E-A930-4CCC-8E02-F2A4D6C52C8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB07B31E-A930-4CCC-8E02-F2A4D6C52C8F}.Release|Any CPU.Build.0 = Release|Any CPU {B48C8F19-9451-4EE2-999F-82C0033CDE2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B48C8F19-9451-4EE2-999F-82C0033CDE2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {B48C8F19-9451-4EE2-999F-82C0033CDE2C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -199,6 +249,10 @@ Global {6580AA87-6A95-438E-A5D3-70E583CCD77B}.Debug|Any CPU.Build.0 = Debug|Any CPU {6580AA87-6A95-438E-A5D3-70E583CCD77B}.Release|Any CPU.ActiveCfg = Release|Any CPU {6580AA87-6A95-438E-A5D3-70E583CCD77B}.Release|Any CPU.Build.0 = Release|Any CPU + {AAE8A952-2764-43CF-A5D7-515924CA2D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AAE8A952-2764-43CF-A5D7-515924CA2D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AAE8A952-2764-43CF-A5D7-515924CA2D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AAE8A952-2764-43CF-A5D7-515924CA2D9F}.Release|Any CPU.Build.0 = Release|Any CPU {D178E422-283B-4FB3-89A6-AA4FB9F87E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D178E422-283B-4FB3-89A6-AA4FB9F87E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU {D178E422-283B-4FB3-89A6-AA4FB9F87E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -207,6 +261,10 @@ Global {51CA34CA-7684-4819-AC9E-89DFAD63E9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {51CA34CA-7684-4819-AC9E-89DFAD63E9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {51CA34CA-7684-4819-AC9E-89DFAD63E9AB}.Release|Any CPU.Build.0 = Release|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Release|Any CPU.Build.0 = Release|Any CPU {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -215,122 +273,6 @@ Global {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Release|Any CPU.Build.0 = Release|Any CPU - {3C992D0C-9729-438E-800C-496B7EFFFB25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3C992D0C-9729-438E-800C-496B7EFFFB25}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C992D0C-9729-438E-800C-496B7EFFFB25}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3C992D0C-9729-438E-800C-496B7EFFFB25}.Release|Any CPU.Build.0 = Release|Any CPU - {E285BCC9-D953-4066-8FA2-97EA28EB348E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E285BCC9-D953-4066-8FA2-97EA28EB348E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E285BCC9-D953-4066-8FA2-97EA28EB348E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E285BCC9-D953-4066-8FA2-97EA28EB348E}.Release|Any CPU.Build.0 = Release|Any CPU - {A38E687F-8B6B-499E-ABC9-BD95C53DD391}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A38E687F-8B6B-499E-ABC9-BD95C53DD391}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A38E687F-8B6B-499E-ABC9-BD95C53DD391}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A38E687F-8B6B-499E-ABC9-BD95C53DD391}.Release|Any CPU.Build.0 = Release|Any CPU - {99F9D60D-9A2D-4DBE-AA52-13D8A0838696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99F9D60D-9A2D-4DBE-AA52-13D8A0838696}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99F9D60D-9A2D-4DBE-AA52-13D8A0838696}.Release|Any CPU.ActiveCfg = Release|Any CPU - {99F9D60D-9A2D-4DBE-AA52-13D8A0838696}.Release|Any CPU.Build.0 = Release|Any CPU - {A3D97D1A-3099-49C5-85AD-D8C79CC5FDE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A3D97D1A-3099-49C5-85AD-D8C79CC5FDE3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A3D97D1A-3099-49C5-85AD-D8C79CC5FDE3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A3D97D1A-3099-49C5-85AD-D8C79CC5FDE3}.Release|Any CPU.Build.0 = Release|Any CPU - {0640B2BF-1EF5-4FFE-A144-0368748FC48B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0640B2BF-1EF5-4FFE-A144-0368748FC48B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0640B2BF-1EF5-4FFE-A144-0368748FC48B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0640B2BF-1EF5-4FFE-A144-0368748FC48B}.Release|Any CPU.Build.0 = Release|Any CPU - {FC91FFFE-1E0A-4F59-8802-BFF99152AD07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC91FFFE-1E0A-4F59-8802-BFF99152AD07}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC91FFFE-1E0A-4F59-8802-BFF99152AD07}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC91FFFE-1E0A-4F59-8802-BFF99152AD07}.Release|Any CPU.Build.0 = Release|Any CPU - {ADD6205B-67A4-4BA8-8BED-DF7D0E857A6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ADD6205B-67A4-4BA8-8BED-DF7D0E857A6A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ADD6205B-67A4-4BA8-8BED-DF7D0E857A6A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ADD6205B-67A4-4BA8-8BED-DF7D0E857A6A}.Release|Any CPU.Build.0 = Release|Any CPU - {6E315182-CC9F-4F62-8385-5E26EFA3B98A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6E315182-CC9F-4F62-8385-5E26EFA3B98A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6E315182-CC9F-4F62-8385-5E26EFA3B98A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6E315182-CC9F-4F62-8385-5E26EFA3B98A}.Release|Any CPU.Build.0 = Release|Any CPU - {09238300-4583-45C6-A997-025CBDC44C24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09238300-4583-45C6-A997-025CBDC44C24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {09238300-4583-45C6-A997-025CBDC44C24}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09238300-4583-45C6-A997-025CBDC44C24}.Release|Any CPU.Build.0 = Release|Any CPU - {21FDAB94-5014-488D-86C7-A366F1902B24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {21FDAB94-5014-488D-86C7-A366F1902B24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {21FDAB94-5014-488D-86C7-A366F1902B24}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21FDAB94-5014-488D-86C7-A366F1902B24}.Release|Any CPU.Build.0 = Release|Any CPU - {4A378F81-3805-41E8-9565-A8A89A8C00D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A378F81-3805-41E8-9565-A8A89A8C00D6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A378F81-3805-41E8-9565-A8A89A8C00D6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A378F81-3805-41E8-9565-A8A89A8C00D6}.Release|Any CPU.Build.0 = Release|Any CPU - {FFCF6FA8-4F38-415E-AC2D-B576FFD5FED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FFCF6FA8-4F38-415E-AC2D-B576FFD5FED5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FFCF6FA8-4F38-415E-AC2D-B576FFD5FED5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FFCF6FA8-4F38-415E-AC2D-B576FFD5FED5}.Release|Any CPU.Build.0 = Release|Any CPU - {71CBD7CC-C787-4796-B05E-4F3BC3C28B48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {71CBD7CC-C787-4796-B05E-4F3BC3C28B48}.Debug|Any CPU.Build.0 = Debug|Any CPU - {71CBD7CC-C787-4796-B05E-4F3BC3C28B48}.Release|Any CPU.ActiveCfg = Release|Any CPU - {71CBD7CC-C787-4796-B05E-4F3BC3C28B48}.Release|Any CPU.Build.0 = Release|Any CPU - {42E626F7-9A7E-4F55-B02C-16EB56E2B540}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42E626F7-9A7E-4F55-B02C-16EB56E2B540}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42E626F7-9A7E-4F55-B02C-16EB56E2B540}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42E626F7-9A7E-4F55-B02C-16EB56E2B540}.Release|Any CPU.Build.0 = Release|Any CPU - {8BF2CBBF-6DAB-4D7A-87E0-AE643D6019AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8BF2CBBF-6DAB-4D7A-87E0-AE643D6019AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8BF2CBBF-6DAB-4D7A-87E0-AE643D6019AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8BF2CBBF-6DAB-4D7A-87E0-AE643D6019AB}.Release|Any CPU.Build.0 = Release|Any CPU - {262A8AE0-E610-405F-B4EC-DB714FB54C00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {262A8AE0-E610-405F-B4EC-DB714FB54C00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {262A8AE0-E610-405F-B4EC-DB714FB54C00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {262A8AE0-E610-405F-B4EC-DB714FB54C00}.Release|Any CPU.Build.0 = Release|Any CPU - {E5F07862-5715-470D-B324-19BDEBB2AA4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E5F07862-5715-470D-B324-19BDEBB2AA4D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E5F07862-5715-470D-B324-19BDEBB2AA4D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E5F07862-5715-470D-B324-19BDEBB2AA4D}.Release|Any CPU.Build.0 = Release|Any CPU - {21B591A0-F6E5-4645-BF2D-4E71F47394A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {21B591A0-F6E5-4645-BF2D-4E71F47394A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {21B591A0-F6E5-4645-BF2D-4E71F47394A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21B591A0-F6E5-4645-BF2D-4E71F47394A7}.Release|Any CPU.Build.0 = Release|Any CPU - {F093BDE5-1824-459E-B86E-B9F79B548E58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F093BDE5-1824-459E-B86E-B9F79B548E58}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F093BDE5-1824-459E-B86E-B9F79B548E58}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F093BDE5-1824-459E-B86E-B9F79B548E58}.Release|Any CPU.Build.0 = Release|Any CPU - {EDA96974-0BEA-404B-8EED-F19CCA2C95A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EDA96974-0BEA-404B-8EED-F19CCA2C95A8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EDA96974-0BEA-404B-8EED-F19CCA2C95A8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EDA96974-0BEA-404B-8EED-F19CCA2C95A8}.Release|Any CPU.Build.0 = Release|Any CPU - {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Release|Any CPU.Build.0 = Release|Any CPU - {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Release|Any CPU.Build.0 = Release|Any CPU - {36BF2B8B-F444-4886-AA4C-0EDF7540F1CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {36BF2B8B-F444-4886-AA4C-0EDF7540F1CE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {36BF2B8B-F444-4886-AA4C-0EDF7540F1CE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {36BF2B8B-F444-4886-AA4C-0EDF7540F1CE}.Release|Any CPU.Build.0 = Release|Any CPU - {751E4140-2F4D-4550-A4A9-65ABA9F7893A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {751E4140-2F4D-4550-A4A9-65ABA9F7893A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {751E4140-2F4D-4550-A4A9-65ABA9F7893A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {751E4140-2F4D-4550-A4A9-65ABA9F7893A}.Release|Any CPU.Build.0 = Release|Any CPU - {6E7857D9-07AC-419F-B111-0DB0348D1C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6E7857D9-07AC-419F-B111-0DB0348D1C92}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6E7857D9-07AC-419F-B111-0DB0348D1C92}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6E7857D9-07AC-419F-B111-0DB0348D1C92}.Release|Any CPU.Build.0 = Release|Any CPU - {FF4BF0E7-698D-49A0-96E9-0E2646FEAFFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF4BF0E7-698D-49A0-96E9-0E2646FEAFFA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF4BF0E7-698D-49A0-96E9-0E2646FEAFFA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF4BF0E7-698D-49A0-96E9-0E2646FEAFFA}.Release|Any CPU.Build.0 = Release|Any CPU - {3D221A25-007F-4764-98CD-CEEF2EB92165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3D221A25-007F-4764-98CD-CEEF2EB92165}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3D221A25-007F-4764-98CD-CEEF2EB92165}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3D221A25-007F-4764-98CD-CEEF2EB92165}.Release|Any CPU.Build.0 = Release|Any CPU - {31667A36-D069-4708-9DCA-E3446009941B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {31667A36-D069-4708-9DCA-E3446009941B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31667A36-D069-4708-9DCA-E3446009941B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {31667A36-D069-4708-9DCA-E3446009941B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/.Deprecated/Nevermind/Main.cs b/Nevermind/Main.cs similarity index 76% rename from .Deprecated/Nevermind/Main.cs rename to Nevermind/Main.cs index 935e2ba..c631850 100644 --- a/.Deprecated/Nevermind/Main.cs +++ b/Nevermind/Main.cs @@ -5,23 +5,23 @@ using UnityEngine; namespace NAK.Nevermind; -public class NevermindMod : MelonMod +public class Nevermind : MelonMod { - #region Melon Preferences + #region Mod Settings private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(NevermindMod)); + MelonPreferences.CreateCategory(nameof(Nevermind)); - private static readonly MelonPreferences_Entry Entry_CancelKeybind = - Category.CreateEntry("keybind", KeyCode.Home, description: "Key to cancel world join."); + private static readonly MelonPreferences_Entry Setting_NevermindKey = + Category.CreateEntry("Keybind", KeyCode.Home, description: "Key to cancel world join."); - #endregion Melon Preferences + #endregion #region Melon Events public override void OnUpdate() { - if (!Input.GetKeyDown(Entry_CancelKeybind.Value)) + if (!Input.GetKeyDown(Setting_NevermindKey.Value)) return; if (CVRObjectLoader.Instance == null @@ -35,6 +35,7 @@ public class NevermindMod : MelonMod return; // too late to cancel, world is being loaded // Cancel world join if still downloading + CVRDownloadManager.Instance.ActiveWorldDownload = false; foreach (var download in CVRDownloadManager.Instance._downloadTasks) download.Value.JoinOnComplete = false; @@ -48,5 +49,5 @@ public class NevermindMod : MelonMod ViewManager.Instance.NotifyUser("(Local) Client", "World Join Cancelled", 2f); } - #endregion Melon Events + #endregion } \ No newline at end of file diff --git a/.Deprecated/ShadowCloneFallback/ShadowCloneFallback.csproj b/Nevermind/Nevermind.csproj similarity index 100% rename from .Deprecated/ShadowCloneFallback/ShadowCloneFallback.csproj rename to Nevermind/Nevermind.csproj diff --git a/.Deprecated/Nevermind/Properties/AssemblyInfo.cs b/Nevermind/Properties/AssemblyInfo.cs similarity index 84% rename from .Deprecated/Nevermind/Properties/AssemblyInfo.cs rename to Nevermind/Properties/AssemblyInfo.cs index befa5b6..01933eb 100644 --- a/.Deprecated/Nevermind/Properties/AssemblyInfo.cs +++ b/Nevermind/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Reflection; [assembly: AssemblyProduct(nameof(NAK.Nevermind))] [assembly: MelonInfo( - typeof(NAK.Nevermind.NevermindMod), + typeof(NAK.Nevermind.Nevermind), nameof(NAK.Nevermind), AssemblyInfoParams.Version, AssemblyInfoParams.Author, @@ -20,13 +20,11 @@ using System.Reflection; [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.Nevermind.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/.Deprecated/Nevermind/format.json b/Nevermind/format.json similarity index 80% rename from .Deprecated/Nevermind/format.json rename to Nevermind/format.json index 6055a72..710a545 100644 --- a/.Deprecated/Nevermind/format.json +++ b/Nevermind/format.json @@ -1,8 +1,8 @@ { "_id": -1, "name": "Nevermind", - "modversion": "1.0.1", - "gameversion": "2024r176", + "modversion": "1.0.0", + "gameversion": "2024r174", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", @@ -18,6 +18,6 @@ ], "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r25/Nevermind.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Nevermind/", - "changelog": "- Initial release", - "embedcolor": "#f61963" + "changelog": "- Initial Release", + "embedcolor": "#b589ec" } \ No newline at end of file diff --git a/.Experimental/OriginShift/Integrations/BTKUI/BtkUiAddon_CAT_OriginShiftMod.cs b/OriginShift/Integrations/BTKUI/BtkUiAddon_CAT_OriginShiftMod.cs similarity index 79% rename from .Experimental/OriginShift/Integrations/BTKUI/BtkUiAddon_CAT_OriginShiftMod.cs rename to OriginShift/Integrations/BTKUI/BtkUiAddon_CAT_OriginShiftMod.cs index cd32e4f..543ea43 100644 --- a/.Experimental/OriginShift/Integrations/BTKUI/BtkUiAddon_CAT_OriginShiftMod.cs +++ b/OriginShift/Integrations/BTKUI/BtkUiAddon_CAT_OriginShiftMod.cs @@ -4,19 +4,19 @@ using BTKUILib.UIObjects; using BTKUILib.UIObjects.Components; using NAK.OriginShift; -namespace NAK.OriginShiftMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.OriginShiftMod.Integrations { - private static Category _ourCategory; - - private static Button _ourMainButton; - private static bool _isForcedMode; - - private static ToggleButton _ourToggle; - - private static void Setup_OriginShiftModCategory(Page page) + public static partial class BtkUiAddon { + private static Category _ourCategory; + + private static Button _ourMainButton; + private static bool _isForcedMode; + + private static ToggleButton _ourToggle; + + private static void Setup_OriginShiftModCategory(Page page) + { // dear category _ourCategory = page.AddCategory(ModSettings.OSM_SettingsCategory, ModSettings.ModName, true, true, false); @@ -38,21 +38,21 @@ public static partial class BtkUiAddon debugToggle.OnValueUpdated += OnDebugToggle; } - #region Category Actions + #region Category Actions - private static void UpdateCategoryModUserCount() - { + private static void UpdateCategoryModUserCount() + { int modUsers = 1; // we are always here :3 int playerCount = CVRPlayerManager.Instance.NetworkPlayers.Count + 1; // +1 for us :3 _ourCategory.CategoryName = $"{ModSettings.OSM_SettingsCategory} ({modUsers}/{playerCount})"; } - #endregion Category Actions + #endregion Category Actions - #region Button Actions + #region Button Actions - private static void SetButtonState(OriginShiftManager.OriginShiftState state) - { + private static void SetButtonState(OriginShiftManager.OriginShiftState state) + { switch (state) { default: @@ -74,8 +74,8 @@ public static partial class BtkUiAddon } } - private static void OnMainButtonClick() - { + private static void OnMainButtonClick() + { // if active, return as world is using Origin Shift if (OriginShiftManager.Instance.CurrentState is OriginShiftManager.OriginShiftState.Active) @@ -97,19 +97,19 @@ public static partial class BtkUiAddon } } - private static void OnOriginShiftStateChanged(OriginShiftManager.OriginShiftState state) - { + private static void OnOriginShiftStateChanged(OriginShiftManager.OriginShiftState state) + { _isForcedMode = state == OriginShiftManager.OriginShiftState.Forced; SetButtonState(state); SetToggleLocked(_isForcedMode); } - #endregion Button Actions + #endregion Button Actions - #region Toggle Actions + #region Toggle Actions - private static void SetToggleLocked(bool value) - { + private static void SetToggleLocked(bool value) + { if (value) { // lock the toggle @@ -126,19 +126,20 @@ public static partial class BtkUiAddon } } - private static void OnCompatibilityModeToggle(bool value) - { + private static void OnCompatibilityModeToggle(bool value) + { ModSettings.EntryCompatibilityMode.Value = value; } - #endregion Toggle Actions + #endregion Toggle Actions - #region Debug Toggle Actions + #region Debug Toggle Actions - private static void OnDebugToggle(bool value) - { + private static void OnDebugToggle(bool value) + { OriginShiftManager.Instance.ToggleDebugOverlay(value); } - #endregion Debug Toggle Actions + #endregion Debug Toggle Actions + } } \ No newline at end of file diff --git a/.Experimental/OriginShift/Integrations/BTKUI/BtkuiAddon.cs b/OriginShift/Integrations/BTKUI/BtkuiAddon.cs similarity index 68% rename from .Experimental/OriginShift/Integrations/BTKUI/BtkuiAddon.cs rename to OriginShift/Integrations/BTKUI/BtkuiAddon.cs index f980330..aa64098 100644 --- a/.Experimental/OriginShift/Integrations/BTKUI/BtkuiAddon.cs +++ b/OriginShift/Integrations/BTKUI/BtkuiAddon.cs @@ -3,23 +3,23 @@ using BTKUILib; using BTKUILib.UIObjects; using NAK.OriginShift; -namespace NAK.OriginShiftMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.OriginShiftMod.Integrations { - private static Page _miscTabPage; - private static string _miscTabElementID; - - public static void Initialize() + public static partial class BtkUiAddon { + private static Page _miscTabPage; + private static string _miscTabElementID; + + public static void Initialize() + { Prepare_Icons(); Setup_OriginShiftTab(); } - #region Initialization + #region Initialization - private static void Prepare_Icons() - { + private static void Prepare_Icons() + { QuickMenuAPI.PrepareIcon(ModSettings.ModName, "OriginShift-Icon-Active", GetIconStream("OriginShift-Icon-Active.png")); @@ -30,8 +30,8 @@ public static partial class BtkUiAddon GetIconStream("OriginShift-Icon-Forced.png")); } - private static void Setup_OriginShiftTab() - { + private static void Setup_OriginShiftTab() + { _miscTabPage = QuickMenuAPI.MiscTabPage; _miscTabElementID = _miscTabPage.ElementID; QuickMenuAPI.UserJoin += OnUserJoinLeave; @@ -51,15 +51,16 @@ public static partial class BtkUiAddon // Setup_DebugOptionsCategory(_miscTabPage); } - #endregion + #endregion - #region Player Count Display + #region Player Count Display - private static void OnWorldLeave() - => UpdateCategoryModUserCount(); + private static void OnWorldLeave() + => UpdateCategoryModUserCount(); - private static void OnUserJoinLeave(CVRPlayerEntity _) - => UpdateCategoryModUserCount(); + private static void OnUserJoinLeave(CVRPlayerEntity _) + => UpdateCategoryModUserCount(); - #endregion -} \ No newline at end of file + #endregion + } +} diff --git a/.Experimental/OriginShift/Integrations/BTKUI/BtkuiAddon_Utils.cs b/OriginShift/Integrations/BTKUI/BtkuiAddon_Utils.cs similarity index 58% rename from .Experimental/OriginShift/Integrations/BTKUI/BtkuiAddon_Utils.cs rename to OriginShift/Integrations/BTKUI/BtkuiAddon_Utils.cs index 823d41f..a5cec73 100644 --- a/.Experimental/OriginShift/Integrations/BTKUI/BtkuiAddon_Utils.cs +++ b/OriginShift/Integrations/BTKUI/BtkuiAddon_Utils.cs @@ -5,59 +5,60 @@ using BTKUILib.UIObjects.Components; using MelonLoader; using UnityEngine; -namespace NAK.OriginShiftMod.Integrations; - -public static partial class BtkUiAddon +namespace NAK.OriginShiftMod.Integrations { - #region Melon Preference Helpers - - private static ToggleButton AddMelonToggle(ref Category category, MelonPreferences_Entry entry) + public static partial class BtkUiAddon { + #region Melon Preference Helpers + + private static ToggleButton AddMelonToggle(ref Category category, MelonPreferences_Entry entry) + { ToggleButton toggle = category.AddToggle(entry.DisplayName, entry.Description, entry.Value); toggle.OnValueUpdated += b => entry.Value = b; return toggle; } - private static SliderFloat AddMelonSlider(ref Category category, MelonPreferences_Entry entry, float min, - float max, int decimalPlaces = 2, bool allowReset = true) - { + private static SliderFloat AddMelonSlider(ref Category category, MelonPreferences_Entry entry, float min, + float max, int decimalPlaces = 2, bool allowReset = true) + { SliderFloat slider = category.AddSlider(entry.DisplayName, entry.Description, Mathf.Clamp(entry.Value, min, max), min, max, decimalPlaces, entry.DefaultValue, allowReset); slider.OnValueUpdated += f => entry.Value = f; return slider; } - private static Button AddMelonStringInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) - { + private static Button AddMelonStringInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) + { Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); button.OnPress += () => QuickMenuAPI.OpenKeyboard(entry.Value, s => entry.Value = s); return button; } - private static Button AddMelonNumberInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) - { + private static Button AddMelonNumberInput(ref Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) + { Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); button.OnPress += () => QuickMenuAPI.OpenNumberInput(entry.DisplayName, entry.Value, f => entry.Value = f); return button; } - private static Category AddMelonCategory(ref Page page, MelonPreferences_Entry entry, bool showHeader = true) - { + private static Category AddMelonCategory(ref Page page, MelonPreferences_Entry entry, bool showHeader = true) + { Category category = page.AddCategory(entry.DisplayName, showHeader, true, entry.Value); category.OnCollapse += b => entry.Value = b; return category; } - #endregion Melon Preference Helpers + #endregion Melon Preference Helpers - #region Icon Utils + #region Icon Utils - private static Stream GetIconStream(string iconName) - { + private static Stream GetIconStream(string iconName) + { Assembly assembly = Assembly.GetExecutingAssembly(); string assemblyName = assembly.GetName().Name; return assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}"); } - #endregion Icon Utils + #endregion Icon Utils + } } \ No newline at end of file diff --git a/.Experimental/OriginShift/Integrations/Ragdoll/RagdollAddon.cs b/OriginShift/Integrations/Ragdoll/RagdollAddon.cs similarity index 100% rename from .Experimental/OriginShift/Integrations/Ragdoll/RagdollAddon.cs rename to OriginShift/Integrations/Ragdoll/RagdollAddon.cs diff --git a/.Experimental/OriginShift/Integrations/ThirdPerson/ThirdPersonAddon.cs b/OriginShift/Integrations/ThirdPerson/ThirdPersonAddon.cs similarity index 100% rename from .Experimental/OriginShift/Integrations/ThirdPerson/ThirdPersonAddon.cs rename to OriginShift/Integrations/ThirdPerson/ThirdPersonAddon.cs diff --git a/.Experimental/OriginShift/Main.cs b/OriginShift/Main.cs similarity index 97% rename from .Experimental/OriginShift/Main.cs rename to OriginShift/Main.cs index eb617e0..840caaf 100644 --- a/.Experimental/OriginShift/Main.cs +++ b/OriginShift/Main.cs @@ -20,14 +20,12 @@ namespace NAK.OriginShift; public class OriginShiftMod : MelonMod { internal static MelonLogger.Instance Logger; - internal static HarmonyLib.Harmony HarmonyInst; #region Melon Mod Overrides public override void OnInitializeMelon() { Logger = LoggerInstance; - HarmonyInst = HarmonyInstance; ModSettings.Initialize(); diff --git a/.Experimental/OriginShift/ModSettings.cs b/OriginShift/ModSettings.cs similarity index 100% rename from .Experimental/OriginShift/ModSettings.cs rename to OriginShift/ModSettings.cs diff --git a/.Experimental/OriginShift/Networking/ModNetwork.cs b/OriginShift/Networking/ModNetwork.cs similarity index 100% rename from .Experimental/OriginShift/Networking/ModNetwork.cs rename to OriginShift/Networking/ModNetwork.cs diff --git a/.Experimental/OriginShift/OriginShift.csproj b/OriginShift/OriginShift.csproj similarity index 100% rename from .Experimental/OriginShift/OriginShift.csproj rename to OriginShift/OriginShift.csproj diff --git a/.Experimental/OriginShift/OriginShift/Components/Chunk/ChunkController.cs b/OriginShift/OriginShift/Components/Chunk/ChunkController.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Components/Chunk/ChunkController.cs rename to OriginShift/OriginShift/Components/Chunk/ChunkController.cs diff --git a/.Experimental/OriginShift/OriginShift/Components/Chunk/ChunkCreator.cs b/OriginShift/OriginShift/Components/Chunk/ChunkCreator.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Components/Chunk/ChunkCreator.cs rename to OriginShift/OriginShift/Components/Chunk/ChunkCreator.cs diff --git a/.Experimental/OriginShift/OriginShift/Components/Chunk/ChunkListener.cs b/OriginShift/OriginShift/Components/Chunk/ChunkListener.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Components/Chunk/ChunkListener.cs rename to OriginShift/OriginShift/Components/Chunk/ChunkListener.cs diff --git a/.Experimental/OriginShift/OriginShift/Components/OriginShiftController.cs b/OriginShift/OriginShift/Components/OriginShiftController.cs similarity index 65% rename from .Experimental/OriginShift/OriginShift/Components/OriginShiftController.cs rename to OriginShift/OriginShift/Components/OriginShiftController.cs index 568409a..70784f3 100644 --- a/.Experimental/OriginShift/OriginShift/Components/OriginShiftController.cs +++ b/OriginShift/OriginShift/Components/OriginShiftController.cs @@ -7,45 +7,45 @@ using NAK.OriginShift.Utility; // Creator Exposed component -namespace NAK.OriginShift.Components; - -public class OriginShiftController : MonoBehaviour +namespace NAK.OriginShift.Components { - public static OriginShiftController Instance { get; private set; } + public class OriginShiftController : MonoBehaviour + { + public static OriginShiftController Instance { get; private set; } - #region Serialized Fields + #region Serialized Fields - [Header("Config / Shift Params")] + [Header("Config / Shift Params")] - [SerializeField] private bool _shiftVertical = true; - [SerializeField] [Range(10, 2500)] private int _shiftThreshold = 15; + [SerializeField] private bool _shiftVertical = true; + [SerializeField] [Range(10, 2500)] private int _shiftThreshold = 15; - [Header("Config / Scene Objects")] + [Header("Config / Scene Objects")] - [SerializeField] private bool _autoMoveSceneRoots = true; - [SerializeField] private Transform[] _toShiftTransforms = Array.Empty(); + [SerializeField] private bool _autoMoveSceneRoots = true; + [SerializeField] private Transform[] _toShiftTransforms = Array.Empty(); - [Header("Config / Additive Objects")] + [Header("Config / Additive Objects")] - [SerializeField] private bool _shiftRemotePlayers = true; - [SerializeField] private bool _shiftSpawnedObjects = true; + [SerializeField] private bool _shiftRemotePlayers = true; + [SerializeField] private bool _shiftSpawnedObjects = true; - #endregion Serialized Fields + #endregion Serialized Fields - #region Internal Fields + #region Internal Fields - internal bool IsForced { get; set; } + internal bool IsForced { get; set; } - #endregion Internal Fields + #endregion Internal Fields #if !UNITY_EDITOR - public static int ORIGIN_SHIFT_THRESHOLD = 15; + public static int ORIGIN_SHIFT_THRESHOLD = 15; - #region Unity Events + #region Unity Events - private void Awake() - { + private void Awake() + { if (Instance != null && Instance != this) { @@ -56,8 +56,8 @@ public class OriginShiftController : MonoBehaviour Instance = this; } - private void Start() - { + private void Start() + { // set threshold (we can not support dynamic threshold change) ORIGIN_SHIFT_THRESHOLD = IsForced ? 1000 : _shiftThreshold; @@ -73,26 +73,26 @@ public class OriginShiftController : MonoBehaviour AnchorAllStaticRenderers(); } - private void OnDestroy() - { + private void OnDestroy() + { OriginShiftManager.OnOriginShifted -= OnOriginShifted; OriginShiftManager.Instance.ResetManager(); } - #endregion Unity Events + #endregion Unity Events - #region Private Methods + #region Private Methods - private void GetAllSceneRootTransforms() - { + private void GetAllSceneRootTransforms() + { Scene scene = gameObject.scene; var sceneRoots = scene.GetRootGameObjects(); _toShiftTransforms = new Transform[sceneRoots.Length + 1]; // +1 for the static batch anchor for (var i = 0; i < sceneRoots.Length; i++) _toShiftTransforms[i] = sceneRoots[i].transform; } - private void AnchorAllStaticRenderers() - { + private void AnchorAllStaticRenderers() + { // create an anchor object at 0,0,0 Transform anchor = new GameObject("NAK.StaticBatchAnchor").transform; anchor.SetPositionAndRotation(Vector3.zero, Quaternion.identity); @@ -113,12 +113,12 @@ public class OriginShiftController : MonoBehaviour } } - #endregion Private Methods + #endregion Private Methods - #region Origin Shift Events + #region Origin Shift Events - private void OnOriginShifted(Vector3 shift) - { + private void OnOriginShifted(Vector3 shift) + { foreach (Transform toShiftTransform in _toShiftTransforms) { if (toShiftTransform == null) continue; // skip nulls @@ -126,7 +126,8 @@ public class OriginShiftController : MonoBehaviour } } - #endregion Origin Shift Events + #endregion Origin Shift Events #endif + } } \ No newline at end of file diff --git a/OriginShift/OriginShift/Components/Receivers/OriginShiftEventReceiver.cs b/OriginShift/OriginShift/Components/Receivers/OriginShiftEventReceiver.cs new file mode 100644 index 0000000..fd329bf --- /dev/null +++ b/OriginShift/OriginShift/Components/Receivers/OriginShiftEventReceiver.cs @@ -0,0 +1,79 @@ +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Events; + +#if !UNITY_EDITOR +using ABI_RC.Core.Util.AssetFiltering; +#endif + +namespace NAK.OriginShift.Components +{ + public class OriginShiftEventReceiver : MonoBehaviour + { + #region Serialized Fields + + [SerializeField] private UnityEvent _onOriginShifted = new(); + [SerializeField] private bool _filterChunkBoundary; + [SerializeField] private Vector3 _chunkBoundaryMin = Vector3.zero; + [SerializeField] private Vector3 _chunkBoundaryMax = Vector3.one; + + #endregion Serialized Fields + +#if !UNITY_EDITOR + + #region Private Fields + + private bool _isInitialized; + + #endregion Private Fields + + #region Unity Events + + private void OnEnable() + { + if (!_isInitialized) + { + SharedFilter.SanitizeUnityEvents("OriginShiftEventReceiver", _onOriginShifted); + _isInitialized = true; + } + + OriginShiftManager.OnOriginShifted += HandleOriginShifted; + } + + private void OnDisable() + { + OriginShiftManager.OnOriginShifted -= HandleOriginShifted; + } + + #endregion Unity Events + + #region Origin Shift Events + + private void HandleOriginShifted(Vector3 shift) + { + if (_filterChunkBoundary && !IsWithinChunkBoundary(shift)) + return; + + // wrap user-defined event because the user can't be trusted + try + { + _onOriginShifted.Invoke(); + } + catch (Exception e) + { + OriginShiftMod.Logger.Error("OriginShiftEventReceiver: Exception invoking OnOriginShifted event: " + e, this); + } + } + + private bool IsWithinChunkBoundary(Vector3 shift) + { + return shift.x >= _chunkBoundaryMin.x && shift.x <= _chunkBoundaryMax.x && + shift.y >= _chunkBoundaryMin.y && shift.y <= _chunkBoundaryMax.y && + shift.z >= _chunkBoundaryMin.z && shift.z <= _chunkBoundaryMax.z; + } + + #endregion Origin Shift Events + +#endif + } +} \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftParticleSystemReceiver.cs b/OriginShift/OriginShift/Components/Receivers/OriginShiftParticleSystemReceiver.cs similarity index 56% rename from .Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftParticleSystemReceiver.cs rename to OriginShift/OriginShift/Components/Receivers/OriginShiftParticleSystemReceiver.cs index c5f4c52..819b351 100644 --- a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftParticleSystemReceiver.cs +++ b/OriginShift/OriginShift/Components/Receivers/OriginShiftParticleSystemReceiver.cs @@ -1,20 +1,20 @@ using UnityEngine; -namespace NAK.OriginShift.Components; - -public class OriginShiftParticleSystemReceiver : MonoBehaviour +namespace NAK.OriginShift.Components { + public class OriginShiftParticleSystemReceiver : MonoBehaviour + { #if !UNITY_EDITOR - // max particles count cause i said so 2 - private static readonly ParticleSystem.Particle[] _tempParticles = new ParticleSystem.Particle[10000]; + // max particles count cause i said so 2 + private static readonly ParticleSystem.Particle[] _tempParticles = new ParticleSystem.Particle[10000]; - private ParticleSystem[] _particleSystems; + private ParticleSystem[] _particleSystems; - #region Unity Events + #region Unity Events - private void Start() - { + private void Start() + { _particleSystems = GetComponentsInChildren(true); if (_particleSystems.Length == 0) { @@ -23,34 +23,35 @@ public class OriginShiftParticleSystemReceiver : MonoBehaviour } } - private void OnEnable() - { + private void OnEnable() + { OriginShiftManager.OnOriginShifted += OnOriginShifted; } - private void OnDisable() - { + private void OnDisable() + { OriginShiftManager.OnOriginShifted -= OnOriginShifted; } - #endregion Unity Events + #endregion Unity Events - #region Origin Shift Events + #region Origin Shift Events - private void OnOriginShifted(Vector3 offset) - { + private void OnOriginShifted(Vector3 offset) + { foreach (ParticleSystem particleSystem in _particleSystems) ShiftParticleSystem(particleSystem, offset); } - private static void ShiftParticleSystem(ParticleSystem particleSystem, Vector3 offset) - { + private static void ShiftParticleSystem(ParticleSystem particleSystem, Vector3 offset) + { int particleCount = particleSystem.GetParticles(_tempParticles); for (int i = 0; i < particleCount; i++) _tempParticles[i].position += offset; particleSystem.SetParticles(_tempParticles, particleCount); } - #endregion Origin Shift Events + #endregion Origin Shift Events #endif + } } \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftRigidbodyReceiver.cs b/OriginShift/OriginShift/Components/Receivers/OriginShiftRigidbodyReceiver.cs similarity index 56% rename from .Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftRigidbodyReceiver.cs rename to OriginShift/OriginShift/Components/Receivers/OriginShiftRigidbodyReceiver.cs index 7fdc939..ae2ca54 100644 --- a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftRigidbodyReceiver.cs +++ b/OriginShift/OriginShift/Components/Receivers/OriginShiftRigidbodyReceiver.cs @@ -1,17 +1,17 @@ using UnityEngine; -namespace NAK.OriginShift.Components; - -public class OriginShiftRigidbodyReceiver : MonoBehaviour +namespace NAK.OriginShift.Components { + public class OriginShiftRigidbodyReceiver : MonoBehaviour + { #if !UNITY_EDITOR - private Rigidbody _rigidbody; + private Rigidbody _rigidbody; - #region Unity Events + #region Unity Events - private void Start() - { + private void Start() + { _rigidbody = GetComponentInChildren(); if (_rigidbody == null) { @@ -20,26 +20,27 @@ public class OriginShiftRigidbodyReceiver : MonoBehaviour } } - private void OnEnable() - { + private void OnEnable() + { OriginShiftManager.OnOriginShifted += OnOriginShifted; } - private void OnDisable() - { + private void OnDisable() + { OriginShiftManager.OnOriginShifted -= OnOriginShifted; } - #endregion Unity Events + #endregion Unity Events - #region Origin Shift Events + #region Origin Shift Events - private void OnOriginShifted(Vector3 shift) - { + private void OnOriginShifted(Vector3 shift) + { _rigidbody.position += shift; } - #endregion Origin Shift Events + #endregion Origin Shift Events #endif + } } \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftTrailRendererReceiver.cs b/OriginShift/OriginShift/Components/Receivers/OriginShiftTrailRendererReceiver.cs similarity index 57% rename from .Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftTrailRendererReceiver.cs rename to OriginShift/OriginShift/Components/Receivers/OriginShiftTrailRendererReceiver.cs index 4c3b34d..da1140c 100644 --- a/.Experimental/OriginShift/OriginShift/Components/Receivers/OriginShiftTrailRendererReceiver.cs +++ b/OriginShift/OriginShift/Components/Receivers/OriginShiftTrailRendererReceiver.cs @@ -1,20 +1,20 @@ using UnityEngine; -namespace NAK.OriginShift.Components; - -public class OriginShiftTrailRendererReceiver : MonoBehaviour +namespace NAK.OriginShift.Components { + public class OriginShiftTrailRendererReceiver : MonoBehaviour + { #if !UNITY_EDITOR - // max positions count cause i said so - private static readonly Vector3[] _tempPositions = new Vector3[10000]; + // max positions count cause i said so + private static readonly Vector3[] _tempPositions = new Vector3[10000]; - private TrailRenderer[] _trailRenderers; + private TrailRenderer[] _trailRenderers; - #region Unity Events + #region Unity Events - private void Start() - { + private void Start() + { _trailRenderers = GetComponentsInChildren(true); if (_trailRenderers.Length == 0) { @@ -23,34 +23,35 @@ public class OriginShiftTrailRendererReceiver : MonoBehaviour } } - private void OnEnable() - { + private void OnEnable() + { OriginShiftManager.OnOriginShifted += OnOriginShifted; } - private void OnDisable() - { + private void OnDisable() + { OriginShiftManager.OnOriginShifted -= OnOriginShifted; } - #endregion Unity Events + #endregion Unity Events - #region Origin Shift Events + #region Origin Shift Events - private void OnOriginShifted(Vector3 offset) - { + private void OnOriginShifted(Vector3 offset) + { foreach (TrailRenderer trailRenderer in _trailRenderers) ShiftTrailRenderer(trailRenderer, offset); } - private static void ShiftTrailRenderer(TrailRenderer trailRenderer, Vector3 offset) - { + private static void ShiftTrailRenderer(TrailRenderer trailRenderer, Vector3 offset) + { trailRenderer.GetPositions(_tempPositions); for (var i = 0; i < _tempPositions.Length; i++) _tempPositions[i] += offset; trailRenderer.SetPositions(_tempPositions); } - #endregion Origin Shift Events + #endregion Origin Shift Events #endif + } } \ No newline at end of file diff --git a/OriginShift/OriginShift/Components/Receivers/OriginShiftTransformReceiver.cs b/OriginShift/OriginShift/Components/Receivers/OriginShiftTransformReceiver.cs new file mode 100644 index 0000000..1370e34 --- /dev/null +++ b/OriginShift/OriginShift/Components/Receivers/OriginShiftTransformReceiver.cs @@ -0,0 +1,34 @@ +using UnityEngine; + +namespace NAK.OriginShift.Components +{ + public class OriginShiftTransformReceiver : MonoBehaviour + { +#if !UNITY_EDITOR + + #region Unity Events + + private void OnEnable() + { + OriginShiftManager.OnOriginShifted += OnOriginShifted; + } + + private void OnDisable() + { + OriginShiftManager.OnOriginShifted -= OnOriginShifted; + } + + #endregion Unity Events + + #region Origin Shift Events + + private void OnOriginShifted(Vector3 shift) + { + transform.position += shift; + } + + #endregion Origin Shift Events + +#endif + } +} \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Extensions/BetterBetterCharacterControllerExtensions.cs b/OriginShift/OriginShift/Extensions/BetterBetterCharacterControllerExtensions.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Extensions/BetterBetterCharacterControllerExtensions.cs rename to OriginShift/OriginShift/Extensions/BetterBetterCharacterControllerExtensions.cs diff --git a/.Experimental/OriginShift/OriginShift/Extensions/PlayerSetupExtensions.cs b/OriginShift/OriginShift/Extensions/PlayerSetupExtensions.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Extensions/PlayerSetupExtensions.cs rename to OriginShift/OriginShift/Extensions/PlayerSetupExtensions.cs diff --git a/.Experimental/OriginShift/OriginShift/Hacks/OcclusionCullingHack.cs b/OriginShift/OriginShift/Hacks/OcclusionCullingHack.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Hacks/OcclusionCullingHack.cs rename to OriginShift/OriginShift/Hacks/OcclusionCullingHack.cs diff --git a/.Experimental/OriginShift/OriginShift/Hacks/OriginShiftOcclusionCullingDisabler.cs b/OriginShift/OriginShift/Hacks/OriginShiftOcclusionCullingDisabler.cs similarity index 54% rename from .Experimental/OriginShift/OriginShift/Hacks/OriginShiftOcclusionCullingDisabler.cs rename to OriginShift/OriginShift/Hacks/OriginShiftOcclusionCullingDisabler.cs index 3fb90f1..2e78a6c 100644 --- a/.Experimental/OriginShift/OriginShift/Hacks/OriginShiftOcclusionCullingDisabler.cs +++ b/OriginShift/OriginShift/Hacks/OriginShiftOcclusionCullingDisabler.cs @@ -1,16 +1,16 @@ using UnityEngine; -namespace NAK.OriginShift.Hacks; - -public class OriginShiftOcclusionCullingDisabler : MonoBehaviour +namespace NAK.OriginShift.Hacks { - private Camera _camera; - private bool _originalCullingState; - - #region Unity Events - - private void Start() + public class OriginShiftOcclusionCullingDisabler : MonoBehaviour { + private Camera _camera; + private bool _originalCullingState; + + #region Unity Events + + private void Start() + { _camera = GetComponent(); if (_camera == null) { @@ -21,24 +21,25 @@ public class OriginShiftOcclusionCullingDisabler : MonoBehaviour _originalCullingState = _camera.useOcclusionCulling; } - private void Awake() // we want to execute even if the component is disabled - { + private void Awake() // we want to execute even if the component is disabled + { OriginShiftManager.OnStateChanged += OnOriginShiftStateChanged; } - private void OnDestroy() - { + private void OnDestroy() + { OriginShiftManager.OnStateChanged -= OnOriginShiftStateChanged; } - #endregion Unity Events + #endregion Unity Events - #region Origin Shift Events + #region Origin Shift Events - private void OnOriginShiftStateChanged(OriginShiftManager.OriginShiftState state) - { + private void OnOriginShiftStateChanged(OriginShiftManager.OriginShiftState state) + { _camera.useOcclusionCulling = state != OriginShiftManager.OriginShiftState.Forced && _originalCullingState; } - #endregion Origin Shift Events + #endregion Origin Shift Events + } } \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/OriginShiftManager.cs b/OriginShift/OriginShift/OriginShiftManager.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/OriginShiftManager.cs rename to OriginShift/OriginShift/OriginShiftManager.cs diff --git a/.Experimental/OriginShift/OriginShift/Player/Dynamics/OriginShiftDbAvatarReceiver.cs b/OriginShift/OriginShift/Player/Dynamics/OriginShiftDbAvatarReceiver.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Player/Dynamics/OriginShiftDbAvatarReceiver.cs rename to OriginShift/OriginShift/Player/Dynamics/OriginShiftDbAvatarReceiver.cs diff --git a/.Experimental/OriginShift/OriginShift/Player/OriginShiftMonitor.cs b/OriginShift/OriginShift/Player/OriginShiftMonitor.cs similarity index 61% rename from .Experimental/OriginShift/OriginShift/Player/OriginShiftMonitor.cs rename to OriginShift/OriginShift/Player/OriginShiftMonitor.cs index f2157a0..a812e08 100644 --- a/.Experimental/OriginShift/OriginShift/Player/OriginShiftMonitor.cs +++ b/OriginShift/OriginShift/Player/OriginShiftMonitor.cs @@ -9,35 +9,35 @@ using UnityEngine; using ABI_RC.Systems.Movement; #endif -namespace NAK.OriginShift; - -[DefaultExecutionOrder(int.MaxValue)] -public class OriginShiftMonitor : MonoBehaviour +namespace NAK.OriginShift { + [DefaultExecutionOrder(int.MaxValue)] + public class OriginShiftMonitor : MonoBehaviour + { #if !UNITY_EDITOR - private PlayerSetup _playerSetup; - private BetterBetterCharacterController _characterController; + private PlayerSetup _playerSetup; + private BetterBetterCharacterController _characterController; #endif - #region Unity Events + #region Unity Events - private void Start() - { + private void Start() + { #if !UNITY_EDITOR - _playerSetup = GetComponent(); - _characterController = GetComponent(); + _playerSetup = GetComponent(); + _characterController = GetComponent(); #endif - OriginShiftManager.OnPostOriginShifted += OnPostOriginShifted; - } + OriginShiftManager.OnPostOriginShifted += OnPostOriginShifted; + } - private void OnDestroy() - { + private void OnDestroy() + { OriginShiftManager.OnPostOriginShifted -= OnPostOriginShifted; StopAllCoroutines(); } - private void Update() - { + private void Update() + { // in CVR use GetPlayerPosition to account for VR offset Vector3 position = PlayerSetup.Instance.GetPlayerPosition(); @@ -56,20 +56,21 @@ public class OriginShiftMonitor : MonoBehaviour OriginShiftManager.Instance.ShiftOrigin(position); } - #endregion Unity Events + #endregion Unity Events - #region Origin Shift Events + #region Origin Shift Events - private void OnPostOriginShifted(Vector3 shift) - { + private void OnPostOriginShifted(Vector3 shift) + { #if UNITY_EDITOR // shift our transform back transform.position += shift; #else - _characterController.OffsetBy(shift); - _playerSetup.OffsetAvatarMovementData(shift); + _characterController.OffsetBy(shift); + _playerSetup.OffsetAvatarMovementData(shift); #endif - } + } - #endregion Origin Shift Events + #endregion Origin Shift Events + } } \ No newline at end of file diff --git a/.Experimental/OriginShift/OriginShift/Player/OriginShiftNetIkReceiver.cs b/OriginShift/OriginShift/Player/OriginShiftNetIkReceiver.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Player/OriginShiftNetIkReceiver.cs rename to OriginShift/OriginShift/Player/OriginShiftNetIkReceiver.cs diff --git a/.Experimental/OriginShift/OriginShift/Player/OriginShiftObjectSyncReceiver.cs b/OriginShift/OriginShift/Player/OriginShiftObjectSyncReceiver.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Player/OriginShiftObjectSyncReceiver.cs rename to OriginShift/OriginShift/Player/OriginShiftObjectSyncReceiver.cs diff --git a/.Experimental/OriginShift/OriginShift/Player/OriginShiftPickupObjectReceiver.cs b/OriginShift/OriginShift/Player/OriginShiftPickupObjectReceiver.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Player/OriginShiftPickupObjectReceiver.cs rename to OriginShift/OriginShift/Player/OriginShiftPickupObjectReceiver.cs diff --git a/.Experimental/OriginShift/OriginShift/Player/OriginShiftSpawnableReceiver.cs b/OriginShift/OriginShift/Player/OriginShiftSpawnableReceiver.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Player/OriginShiftSpawnableReceiver.cs rename to OriginShift/OriginShift/Player/OriginShiftSpawnableReceiver.cs diff --git a/.Experimental/OriginShift/OriginShift/Utility/DebugTextDisplay.cs b/OriginShift/OriginShift/Utility/DebugTextDisplay.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Utility/DebugTextDisplay.cs rename to OriginShift/OriginShift/Utility/DebugTextDisplay.cs diff --git a/.Experimental/OriginShift/OriginShift/Utility/RendererReflectionUtility.cs b/OriginShift/OriginShift/Utility/RendererReflectionUtility.cs similarity index 100% rename from .Experimental/OriginShift/OriginShift/Utility/RendererReflectionUtility.cs rename to OriginShift/OriginShift/Utility/RendererReflectionUtility.cs diff --git a/.Experimental/OriginShift/Patches.cs b/OriginShift/Patches.cs similarity index 99% rename from .Experimental/OriginShift/Patches.cs rename to OriginShift/Patches.cs index cd61791..8e17cb0 100644 --- a/.Experimental/OriginShift/Patches.cs +++ b/OriginShift/Patches.cs @@ -10,7 +10,6 @@ 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; diff --git a/.Experimental/OriginShift/Properties/AssemblyInfo.cs b/OriginShift/Properties/AssemblyInfo.cs similarity index 100% rename from .Experimental/OriginShift/Properties/AssemblyInfo.cs rename to OriginShift/Properties/AssemblyInfo.cs diff --git a/OriginShift/README.md b/OriginShift/README.md new file mode 100644 index 0000000..35bb285 --- /dev/null +++ b/OriginShift/README.md @@ -0,0 +1,52 @@ +# OriginShift + +Experimental mod that allows world origin to be shifted to prevent floating point precision issues. + +## Compromises +- Steam Audio data cannot be shifted. +- NavMesh data cannot be shifted. +- Light Probe data cannot be shifted (until [unity 2022](https://docs.unity3d.com/2022.3/Documentation/Manual/LightProbes-Moving.html)). +- Occlusion Culling data cannot be shifted. + - When using "Forced" mode, occlusion culling is disabled. +- Only 10k trail positions can be shifted per Trail Renderer (artificial limit). +- Only 10k particle positions can be shifted per Particle System (artificial limit). + - Potentially can fix by changing Particle System to Custom Simulation Space ? (untested) +- World Constraints are not shifted. + +## Known Issues +- Mod Network is not yet implemented, so Compatibility Mode is required to play with others. +- Portable Camera drone mode is not yet offset by the world origin shift. +- Chunk threshold past 10 units will break Voice Chat with remote players in some cases (without Compatibility Mode). + - This is because the voice server dictates who can hear who based on distance from each other and the world origin shift messes with that. +- Teleports past 50k units will not work. + - BetterBetterCharacterController prevents teleports past 50k units. +- Magica Cloth. + +## Mod Incompatibilities +- PlayerRagdollMod will freak out when you ragdoll between chunk boundaries. + +## Provided Components +- `OriginShiftController` - World script to configure origin shift. +- `OriginShiftEventReceiver` - Event receiver for OriginShift events. +- `OriginShiftTransformReceiver` - Shifts the transform of the GameObject it is attached to. +- `OriginShiftRigidbodyReceiver` - Shifts the rigidbody of the GameObject it is attached to. +- `OriginShiftTrailRendererReceiver` - Shifts the positions of the Trail Renderer of the GameObject it is attached to. +- `OriginShiftParticleSystemReceiver` - Shifts the positions of the Particle System of the GameObject it is attached to. + +The provided receiver components are automatically added to Props, Players, and Object Syncs. + +## Provided Shader Globals +- `_OriginShiftChunkOffset` - The current amount of chunks offset from the origin. +- `_OriginShiftChunkThreshold` - The size of a chunk in world units. +- `_OriginShiftChunkPosition` - The chunk offset multiplied by the chunk threshold. + +--- + +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. diff --git a/.Experimental/OriginShift/Resources/OriginShift-Icon-Active.png b/OriginShift/Resources/OriginShift-Icon-Active.png similarity index 100% rename from .Experimental/OriginShift/Resources/OriginShift-Icon-Active.png rename to OriginShift/Resources/OriginShift-Icon-Active.png diff --git a/.Experimental/OriginShift/Resources/OriginShift-Icon-Forced.png b/OriginShift/Resources/OriginShift-Icon-Forced.png similarity index 100% rename from .Experimental/OriginShift/Resources/OriginShift-Icon-Forced.png rename to OriginShift/Resources/OriginShift-Icon-Forced.png diff --git a/.Experimental/OriginShift/Resources/OriginShift-Icon-Inactive.png b/OriginShift/Resources/OriginShift-Icon-Inactive.png similarity index 100% rename from .Experimental/OriginShift/Resources/OriginShift-Icon-Inactive.png rename to OriginShift/Resources/OriginShift-Icon-Inactive.png diff --git a/OriginShift/format.json b/OriginShift/format.json new file mode 100644 index 0000000..39d8c48 --- /dev/null +++ b/OriginShift/format.json @@ -0,0 +1,23 @@ +{ + "_id": 211, + "name": "RelativeSync", + "modversion": "1.0.3", + "gameversion": "2024r175", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network.\n\nProvides some Experimental settings to also fix local jitter on movement parents.", + "searchtags": [ + "relative", + "sync", + "movement", + "chair" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r29/RelativeSync.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSync/", + "changelog": "- Enabled the Experimental settings to fix **local** jitter on Movement Parents by default\n- Adjusted BBCC No Interpolation fix to account for potential native fix (now respects initial value)", + "embedcolor": "#507e64" +} \ No newline at end of file diff --git a/PathCamDisabler/Properties/AssemblyInfo.cs b/PathCamDisabler/Properties/AssemblyInfo.cs index 5dedc26..75cb1f5 100644 --- a/PathCamDisabler/Properties/AssemblyInfo.cs +++ b/PathCamDisabler/Properties/AssemblyInfo.cs @@ -17,16 +17,16 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PathCamDisabler" )] -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[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: MelonColor(255, 155, 89, 182)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] namespace NAK.PathCamDisabler.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.4"; + public const string Version = "1.0.2"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/PathCamDisabler/format.json b/PathCamDisabler/format.json index 44362ba..0be5676 100644 --- a/PathCamDisabler/format.json +++ b/PathCamDisabler/format.json @@ -1,9 +1,9 @@ { "_id": 110, "name": "PathCamDisabler", - "modversion": "1.0.4", - "gameversion": "2025r180", - "loaderversion": "0.7.2", + "modversion": "1.0.2", + "gameversion": "2023r171", + "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", "description": "Adds option to disable the Path Camera Controller to free up your numkeys.\nAdditional option to disable flight binding.", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/PathCamDisabler.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r16/PathCamDisabler.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PathCamDisabler/", - "changelog": "- Fixes for 2025r180", - "embedcolor": "#f61963" + "changelog": "- Fixes for 2023r171.", + "embedcolor": "#9b59b6" } \ No newline at end of file diff --git a/PhysicsGunMod/Components/ObjectSyncBridge.cs b/PhysicsGunMod/Components/ObjectSyncBridge.cs new file mode 100644 index 0000000..fe3c563 --- /dev/null +++ b/PhysicsGunMod/Components/ObjectSyncBridge.cs @@ -0,0 +1,92 @@ +using ABI_RC.Core.Savior; +using ABI_RC.Core.Util; +using ABI.CCK.Components; +using UnityEngine; + +namespace NAK.PhysicsGunMod.Components; + +public class ObjectSyncBridge : MonoBehaviour +{ + private PhysicsGunInteractionBehavior _physicsGun; + + private void Start() + { + // find physics gun + if (!TryGetComponent(out _physicsGun)) + { + PhysicsGunMod.Logger.Msg("Failed to find physics gun!"); + Destroy(this); + return; + } + + // listen for events + _physicsGun.OnPreGrabbedObject = o => + { + bool canTakeOwnership = false; + + // + CVRObjectSync objectSync = o.GetComponentInParent(); + // if (objectSync != null + // && (objectSync.SyncType == 0 // check if physics synced or synced by us + // || objectSync.SyncedByMe)) + // canTakeOwnership = true; + // + CVRSpawnable spawnable = o.GetComponentInParent(); + // if (spawnable != null) + // { + // CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(match => match.InstanceId == spawnable.instanceId); + // if (propData != null + // && (propData.syncType == 0 // check if physics synced or synced by us + // || propData.syncedBy == MetaPort.Instance.ownerId)) + // canTakeOwnership = true; + // } + // + CVRPickupObject pickup = o.GetComponentInParent(); + // if (pickup != null + // && (pickup.grabbedBy == MetaPort.Instance.ownerId // check if already grabbed by us + // || pickup.grabbedBy == "" || !pickup.disallowTheft)) // check if not grabbed or allows theft + // canTakeOwnership = true; + // + // if (!canTakeOwnership // if we can't take ownership, don't grab, unless there is no syncing at all (local object) + // && (objectSync || spawnable || pickup )) + // return false; + + if (pickup) + { + pickup.GrabbedBy = MetaPort.Instance.ownerId; + pickup._grabStartTime = Time.time; + } + if (spawnable) spawnable.isPhysicsSynced = true; + if (objectSync) objectSync.isPhysicsSynced = true; + + return true; + }; + + _physicsGun.OnObjectReleased = o => + { + // CVRObjectSync objectSync = o.GetComponentInParent(); + // if (objectSync != null && objectSync.SyncType == 0) + // objectSync.isPhysicsSynced = false; + // + // CVRSpawnable spawnable = o.GetComponentInParent(); + // if (spawnable != null) + // { + // CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(match => match.InstanceId == spawnable.instanceId); + // if (propData != null && (propData.syncType == 0 || propData.syncedBy == MetaPort.Instance.ownerId)) + // spawnable.isPhysicsSynced = false; + // } + + // CVRPickupObject pickup = o.GetComponentInParent(); + // if (pickup != null && pickup.grabbedBy == MetaPort.Instance.ownerId) + // pickup.grabbedBy = ""; + + return false; + }; + } + + private void OnDestroy() + { + // stop listening for events + _physicsGun.OnObjectGrabbed = null; + } +} \ No newline at end of file diff --git a/PhysicsGunMod/Components/PhysicsGunInteractionBehavior.cs b/PhysicsGunMod/Components/PhysicsGunInteractionBehavior.cs new file mode 100644 index 0000000..bce3321 --- /dev/null +++ b/PhysicsGunMod/Components/PhysicsGunInteractionBehavior.cs @@ -0,0 +1,572 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.InputManagement; +using ABI.CCK.Attributes; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Scripting.APIUpdating; + +/* + * Physics Gun script from repository - https://github.com/Laumania/Unity3d-PhysicsGun + * Created by: + * Mads Laumann, https://github.com/laumania + * WarmedxMints, https://github.com/WarmedxMints + * + * Original/initial script "Gravity Gun": https://pastebin.com/w1G8m3dH + * Original author: Jake Perry, reddit.com/user/nandos13 +*/ + +namespace NAK.PhysicsGunMod.Components; + +[CCKWhitelistComponent(spawnable: true)] +public class PhysicsGunInteractionBehavior : MonoBehaviour +{ + public static PhysicsGunInteractionBehavior Instance; + + [Header("LayerMask")] [Tooltip("The layer which the gun can grab objects from")] [SerializeField] + private LayerMask _grabLayer; + + [SerializeField] private Camera _camera; + + [Header("Input Setting")] [Space(10)] public KeyCode Rotate = KeyCode.R; + public KeyCode SnapRotation = KeyCode.LeftShift; + public KeyCode SwitchAxis = KeyCode.Tab; + public KeyCode RotateZ = KeyCode.Space; + public KeyCode RotationSpeedIncrease = KeyCode.LeftControl; + public KeyCode ResetRotation = KeyCode.LeftAlt; + + /// The rigidbody we are currently holding + private Rigidbody _grabbedRigidbody; + + /// The offset vector from the object's position to hit point, in local space + private Vector3 _hitOffsetLocal; + + /// The distance we are holding the object at + private float _currentGrabDistance; + + /// The interpolation state when first grabbed + private RigidbodyInterpolation _initialInterpolationSetting; + + /// The difference between player & object rotation, updated when picked up or when rotated by the player + private Quaternion _rotationDifference; + + /// The start point for the Laser. This will typically be on the end of the gun + [SerializeField] private Transform _laserStartPoint; + + /// Tracks player input to rotate current object. Used and reset every fixedupdate call + private Vector3 _rotationInput = Vector3.zero; + + [Header("Rotation Settings")] [Tooltip("Transform of the player, that rotations should be relative to")] + public Transform PlayerTransform; + + [SerializeField] private float _rotationSenstivity = 10f; + + public float SnapRotationDegrees = 45f; + [SerializeField] private float _snappedRotationSens = 25f; + [SerializeField] private float _rotationSpeed = 10f; + + private Quaternion _desiredRotation = Quaternion.identity; + + [SerializeField] [Tooltip("Input values above this will be considered and intentional change in rotation")] + private float _rotationTollerance = 0.8f; + + private bool m_UserRotation; + + public bool UserRotation + { + get => m_UserRotation; + private set + { + if (m_UserRotation == value) + return; + + m_UserRotation = value; + OnRotation?.Invoke(value); + } + } + + private bool m_SnapRotation; + + private bool _snapRotation + { + get => m_SnapRotation; + set + { + if (m_SnapRotation == value) + return; + + m_SnapRotation = value; + OnRotationSnapped?.Invoke(value); + } + } + + private bool m_RotationAxis; + + private bool _rotationAxis + { + get => m_RotationAxis; + set + { + if (m_RotationAxis == value) + return; + + m_RotationAxis = value; + OnAxisChanged?.Invoke(value); + } + } + + private Vector3 _lockedRot; + + private Vector3 _forward; + private Vector3 _up; + private Vector3 _right; + + //ScrollWheel ObjectMovement + private Vector3 _scrollWheelInput = Vector3.zero; + + [Header("Scroll Wheel Object Movement")] [Space(5)] [SerializeField] + private float _scrollWheelSensitivity = 5f; + + [SerializeField] [Tooltip("The min distance the object can be from the player")] + private float _minObjectDistance = 2.5f; + + /// The maximum distance at which a new object can be picked up + [SerializeField] [Tooltip("The maximum distance at which a new object can be picked up")] + private float _maxGrabDistance = 50f; + + private bool _distanceChanged; + + //Vector3.Zero and Vector2.zero create a new Vector3 each time they are called so these simply save that process and a small amount of cpu runtime. + private readonly Vector3 _zeroVector3 = Vector3.zero; + private readonly Vector3 _oneVector3 = Vector3.one; + private readonly Vector3 _zeroVector2 = Vector2.zero; + + private bool _justReleased; + private bool _wasKinematic; + + [Serializable] + public class BoolEvent : UnityEvent {} + + [Serializable] + public class GrabEvent : UnityEvent {} + + [Header("Events")] [Space(10)] + public BoolEvent OnRotation; + public BoolEvent OnRotationSnapped; + public BoolEvent OnAxisChanged; + + public GrabEvent OnObjectGrabbed; + public Func OnObjectReleased; + + // pre event for OnPreGrabbedObject, return false to cancel grab + public Func OnPreGrabbedObject; + // public Action OnGrabbedObject; + + //public properties for the Axis Arrows. These are optional and can be safely removed + // public Vector3 CurrentForward => _forward; + // public Vector3 CurrentUp => _up; + // public Vector3 CurrentRight => _right; + + /// The transfor of the rigidbody we are holding + public Transform CurrentGrabbedTransform { get; private set; } + + //public properties for the Line Renderer + public Vector3 StartPoint { get; private set; } + public Vector3 MidPoint { get; private set; } + public Vector3 EndPoint { get; private set; } + + private void Start() + { + if (Instance != null + && Instance != this) + { + Destroy(this); + return; + } + Instance = this; + gameObject.AddComponent(); + + _camera = PlayerSetup.Instance.GetActiveCamera().GetComponent(); + PlayerTransform = PlayerSetup.Instance.transform; // TODO: this might be fucked in VR + } + + private void OnDestroy() + { + if (Instance == this) + Instance = null; + } + + private void Update() + { + if (!Input.GetMouseButton(0)) + { + // We are not holding the mouse button. Release the object and return before checking for a new one + if (_grabbedRigidbody != null) ReleaseObject(); + + _justReleased = false; + return; + } + + if (_grabbedRigidbody == null && !_justReleased) + { + // We are not holding an object, look for one to pick up + Ray ray = CenterRay(); + RaycastHit hit; + + //Just so These aren't included in a build +#if UNITY_EDITOR + Debug.DrawRay(ray.origin, ray.direction * _maxGrabDistance, Color.blue, 0.01f); +#endif + if (Physics.Raycast(ray, out hit, _maxGrabDistance, _grabLayer)) + // Don't pick up kinematic rigidbodies (they can't move) + if (hit.rigidbody != null /*&& !hit.rigidbody.isKinematic*/) + { + // Check if we are allowed to pick up this object + if (OnPreGrabbedObject != null + && !OnPreGrabbedObject(hit.rigidbody.gameObject)) + return; + + // Track rigidbody's initial information + _grabbedRigidbody = hit.rigidbody; + _wasKinematic = _grabbedRigidbody.isKinematic; + _grabbedRigidbody.isKinematic = false; + _grabbedRigidbody.freezeRotation = true; + _initialInterpolationSetting = _grabbedRigidbody.interpolation; + _rotationDifference = Quaternion.Inverse(PlayerTransform.rotation) * _grabbedRigidbody.rotation; + _hitOffsetLocal = hit.transform.InverseTransformVector(hit.point - hit.transform.position); + _currentGrabDistance = hit.distance; // Vector3.Distance(ray.origin, hit.point); + CurrentGrabbedTransform = _grabbedRigidbody.transform; + // Set rigidbody's interpolation for proper collision detection when being moved by the player + _grabbedRigidbody.interpolation = RigidbodyInterpolation.Interpolate; + + OnObjectGrabbed?.Invoke(_grabbedRigidbody.gameObject); + +#if UNITY_EDITOR + Debug.DrawRay(hit.point, hit.normal * 10f, Color.red, 10f); +#endif + } + } + else if (_grabbedRigidbody != null) + { + UserRotation = Input.GetKey(Rotate); + + if (Input.GetKeyDown(Rotate)) + _desiredRotation = _grabbedRigidbody.rotation; + + if (Input.GetKey(ResetRotation)) + { + _desiredRotation = Quaternion.identity; + } + + // We are already holding an object, listen for rotation input + if (Input.GetKey(Rotate)) + { + var rotateZ = Input.GetKey(RotateZ); + + var increaseSens = Input.GetKey(RotationSpeedIncrease) ? 2.5f : 1f; + + if (Input.GetKeyDown(SwitchAxis)) + { + _rotationAxis = !_rotationAxis; + + OnAxisChanged?.Invoke(_rotationAxis); + } + + //Snap Object nearest _snapRotationDegrees + if (Input.GetKeyDown(SnapRotation)) + { + _snapRotation = true; + + Vector3 newRot = _grabbedRigidbody.transform.rotation.eulerAngles; + + newRot.x = Mathf.Round(newRot.x / SnapRotationDegrees) * SnapRotationDegrees; + newRot.y = Mathf.Round(newRot.y / SnapRotationDegrees) * SnapRotationDegrees; + newRot.z = Mathf.Round(newRot.z / SnapRotationDegrees) * SnapRotationDegrees; + + Quaternion rot = Quaternion.Euler(newRot); + + _desiredRotation = rot; + //_grabbedRigidbody.MoveRotation(rot); + } + else if (Input.GetKeyUp(SnapRotation)) + { + _snapRotation = false; + } + + var x = Input.GetAxisRaw("Mouse X"); + var y = Input.GetAxisRaw("Mouse Y"); + + if (Mathf.Abs(x) > _rotationTollerance) + { + _rotationInput.x = rotateZ ? 0f : x * _rotationSenstivity * increaseSens; + _rotationInput.z = rotateZ ? x * _rotationSenstivity * increaseSens : 0f; + } + + if (Mathf.Abs(y) > _rotationTollerance) _rotationInput.y = y * _rotationSenstivity * increaseSens; + } + else + { + _snapRotation = false; + } + + var direction = Input.GetAxis("Mouse ScrollWheel"); + + //Optional Keyboard inputs + if (Input.GetKeyDown(KeyCode.T)) + direction = -0.1f; + else if (Input.GetKeyDown(KeyCode.G)) + direction = 0.1f; + + if (Mathf.Abs(direction) > 0 && CheckObjectDistance(direction)) + { + _distanceChanged = true; + _scrollWheelInput = PlayerTransform.forward * (_scrollWheelSensitivity * direction); + } + else + { + _scrollWheelInput = _zeroVector3; + } + + if (Input.GetMouseButtonDown(1)) + { + //To prevent warnings in the inpector + _grabbedRigidbody.collisionDetectionMode = !_wasKinematic + ? CollisionDetectionMode.ContinuousSpeculative + : CollisionDetectionMode.Continuous; + _grabbedRigidbody.isKinematic = _wasKinematic = !_wasKinematic; + + _justReleased = true; + ReleaseObject(); + } + } + } + + private void FixedUpdate() + { + if (_grabbedRigidbody) + { + // We are holding an object, time to rotate & move it + Ray ray = CenterRay(); + + UpdateRotationAxis(); + +#if UNITY_EDITOR + Debug.DrawRay(_grabbedTransform.position, _up * 5f , Color.green); + Debug.DrawRay(_grabbedTransform.position, _right * 5f , Color.red); + Debug.DrawRay(_grabbedTransform.position, _forward * 5f , Color.blue); +#endif + // Apply any intentional rotation input made by the player & clear tracked input + Quaternion intentionalRotation = Quaternion.AngleAxis(_rotationInput.z, _forward) * + Quaternion.AngleAxis(_rotationInput.y, _right) * + Quaternion.AngleAxis(-_rotationInput.x, _up) * _desiredRotation; + Quaternion relativeToPlayerRotation = PlayerTransform.rotation * _rotationDifference; + + if (UserRotation && _snapRotation) + { + //Add mouse movement to vector so we can measure the amount of movement + _lockedRot += _rotationInput; + + //If the mouse has moved far enough to rotate the snapped object + if (Mathf.Abs(_lockedRot.x) > _snappedRotationSens || Mathf.Abs(_lockedRot.y) > _snappedRotationSens || + Mathf.Abs(_lockedRot.z) > _snappedRotationSens) + { + for (var i = 0; i < 3; i++) + if (_lockedRot[i] > _snappedRotationSens) + _lockedRot[i] += SnapRotationDegrees; + else if (_lockedRot[i] < -_snappedRotationSens) + _lockedRot[i] += -SnapRotationDegrees; + else + _lockedRot[i] = 0; + + Quaternion q = Quaternion.AngleAxis(-_lockedRot.x, _up) * + Quaternion.AngleAxis(_lockedRot.y, _right) * + Quaternion.AngleAxis(_lockedRot.z, _forward) * _desiredRotation; + + Vector3 newRot = q.eulerAngles; + + newRot.x = Mathf.Round(newRot.x / SnapRotationDegrees) * SnapRotationDegrees; + newRot.y = Mathf.Round(newRot.y / SnapRotationDegrees) * SnapRotationDegrees; + newRot.z = Mathf.Round(newRot.z / SnapRotationDegrees) * SnapRotationDegrees; + + _desiredRotation = Quaternion.Euler(newRot); + + _lockedRot = _zeroVector2; + } + } + else + { + //Rotate the object to remain consistent with any changes in player's rotation + _desiredRotation = UserRotation ? intentionalRotation : relativeToPlayerRotation; + } + + // Remove all torque, reset rotation input & store the rotation difference for next FixedUpdate call + _grabbedRigidbody.angularVelocity = _zeroVector3; + _rotationInput = _zeroVector2; + _rotationDifference = Quaternion.Inverse(PlayerTransform.rotation) * _desiredRotation; + + // Calculate object's center position based on the offset we stored + // NOTE: We need to convert the local-space point back to world coordinates + // Get the destination point for the point on the object we grabbed + Vector3 holdPoint = ray.GetPoint(_currentGrabDistance) + _scrollWheelInput; + Vector3 centerDestination = holdPoint - CurrentGrabbedTransform.TransformVector(_hitOffsetLocal); + +#if UNITY_EDITOR + Debug.DrawLine(ray.origin, holdPoint, Color.blue, Time.fixedDeltaTime); +#endif + // Find vector from current position to destination + Vector3 toDestination = centerDestination - CurrentGrabbedTransform.position; + + // Calculate force + Vector3 force = toDestination / Time.fixedDeltaTime * 0.3f/* / _grabbedRigidbody.mass*/; + + //force += _scrollWheelInput; + // Remove any existing velocity and add force to move to final position + _grabbedRigidbody.velocity = _zeroVector3; + _grabbedRigidbody.AddForce(force, ForceMode.VelocityChange); + + //Rotate object + RotateGrabbedObject(); + + //We need to recalculte the grabbed distance as the object distance from the player has been changed + if (_distanceChanged) + { + _distanceChanged = false; + _currentGrabDistance = Vector3.Distance(ray.origin, holdPoint); + } + + //Update public properties + StartPoint = _laserStartPoint.transform.position; + MidPoint = holdPoint; + EndPoint = CurrentGrabbedTransform.TransformPoint(_hitOffsetLocal); + } + } + + private void RotateGrabbedObject() + { + if (_grabbedRigidbody == null) + return; + + // lerp to desired rotation + _grabbedRigidbody.MoveRotation(Quaternion.Lerp(_grabbedRigidbody.rotation, _desiredRotation, + Time.fixedDeltaTime * _rotationSpeed)); + } + + //Update Rotation axis based on movement + private void UpdateRotationAxis() + { + if (!_snapRotation) + { + _forward = PlayerTransform.forward; + _right = PlayerTransform.right; + _up = PlayerTransform.up; + + return; + } + + if (_rotationAxis) + { + _forward = CurrentGrabbedTransform.forward; + _right = CurrentGrabbedTransform.right; + _up = CurrentGrabbedTransform.up; + + return; + } + + NearestTranformDirection(CurrentGrabbedTransform, PlayerTransform, ref _up, ref _forward, ref _right); + } + + private void NearestTranformDirection(Transform transformToCheck, Transform referenceTransform, ref Vector3 up, + ref Vector3 forward, ref Vector3 right) + { + var directions = new List + { + transformToCheck.forward, + -transformToCheck.forward, + transformToCheck.up, + -transformToCheck.up, + transformToCheck.right, + -transformToCheck.right + }; + + //Find the up Vector + up = GetDirectionVector(directions, referenceTransform.up); + //Remove Vectors from list to prevent duplicates and the opposite vector being found in case where the player is at around a 45 degree angle to the object + directions.Remove(up); + directions.Remove(-up); + //Find the Forward Vector + forward = GetDirectionVector(directions, referenceTransform.forward); + //Remove used directions + directions.Remove(forward); + directions.Remove(-forward); + + right = GetDirectionVector(directions, referenceTransform.right); + } + + private Vector3 GetDirectionVector(List directions, Vector3 direction) + { + var maxDot = -Mathf.Infinity; + Vector3 ret = Vector3.zero; + + for (var i = 0; i < directions.Count; i++) + { + var dot = Vector3.Dot(direction, directions[i]); + + if (dot > maxDot) + { + ret = directions[i]; + maxDot = dot; + } + } + + return ret; + } + + /// Ray from center of the main camera's viewport forward + private Ray CenterRay() + { + return _camera.ViewportPointToRay(_oneVector3 * 0.5f); + } + + //Check distance is within range when moving object with the scroll wheel + private bool CheckObjectDistance(float direction) + { + Vector3 pointA = PlayerTransform.position; + Vector3 pointB = _grabbedRigidbody.position; + + var distance = Vector3.Distance(pointA, pointB); + + if (direction > 0) + return distance <= _maxGrabDistance; + + if (direction < 0) + return distance >= _minObjectDistance; + + return false; + } + + private void ReleaseObject() + { + if (OnObjectReleased != null) + OnObjectReleased(_grabbedRigidbody.gameObject); + + if (_grabbedRigidbody) + { + //Move rotation to desired rotation in case the lerp hasn't finished + //_grabbedRigidbody.MoveRotation(_desiredRotation); + // Reset the rigidbody to how it was before we grabbed it + _grabbedRigidbody.isKinematic = _wasKinematic; + _grabbedRigidbody.interpolation = _initialInterpolationSetting; + _grabbedRigidbody.freezeRotation = false; + _grabbedRigidbody = null; + } + + _scrollWheelInput = _zeroVector3; + CurrentGrabbedTransform = null; + UserRotation = false; + _snapRotation = false; + StartPoint = _zeroVector3; + MidPoint = _zeroVector3; + EndPoint = _zeroVector3; + + OnObjectGrabbed?.Invoke(null); + } +} \ No newline at end of file diff --git a/PhysicsGunMod/HarmonyPatches.cs b/PhysicsGunMod/HarmonyPatches.cs new file mode 100644 index 0000000..73a618c --- /dev/null +++ b/PhysicsGunMod/HarmonyPatches.cs @@ -0,0 +1,20 @@ +using ABI_RC.Systems.InputManagement; +using HarmonyLib; +using NAK.PhysicsGunMod.Components; +using UnityEngine; + +namespace NAK.PhysicsGunMod.HarmonyPatches; + +internal static class CVRInputManagerPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRInputManager), nameof(CVRInputManager.Update))] + private static void Postfix_CVRInputManager_Update(ref CVRInputManager __instance) + { + if (PhysicsGunInteractionBehavior.Instance == null) + return; + + if (PhysicsGunInteractionBehavior.Instance.UserRotation) + __instance.lookVector = Vector2.zero; + } +} diff --git a/PhysicsGunMod/Main.cs b/PhysicsGunMod/Main.cs new file mode 100644 index 0000000..b431ca1 --- /dev/null +++ b/PhysicsGunMod/Main.cs @@ -0,0 +1,43 @@ +using ABI_RC.Core.Util.AssetFiltering; +using MelonLoader; +using NAK.PhysicsGunMod.Components; +using NAK.PhysicsGunMod.HarmonyPatches; + +namespace NAK.PhysicsGunMod; + +public class PhysicsGunMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + // // add to prop whitelist + // //SharedFilter._spawnableWhitelist.Add(typeof(PhysicsGunInteractionBehavior)); + // + // // add to event whitelist + // SharedFilter._allowedEventComponents.Add(typeof(PhysicsGunInteractionBehavior)); + // SharedFilter._allowedEventFunctions.Add(typeof(PhysicsGunInteractionBehavior), new List + // { + // "set_enabled", + // // TODO: expose more methods like release ? + // }); + + // apply patches + ApplyPatches(typeof(CVRInputManagerPatches)); + } + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } +} \ No newline at end of file diff --git a/PhysicsGunMod/ModSettings.cs b/PhysicsGunMod/ModSettings.cs new file mode 100644 index 0000000..86e4b48 --- /dev/null +++ b/PhysicsGunMod/ModSettings.cs @@ -0,0 +1,12 @@ +using MelonLoader; + +namespace NAK.PhysicsGunMod; + +internal static class ModSettings +{ + internal const string ModName = nameof(PhysicsGunMod); + internal const string ASM_SettingsCategory = "Physics Gun Mod"; + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(ModName); +} \ No newline at end of file diff --git a/PhysicsGunMod/PhysicsGunMod.csproj b/PhysicsGunMod/PhysicsGunMod.csproj new file mode 100644 index 0000000..22588f8 --- /dev/null +++ b/PhysicsGunMod/PhysicsGunMod.csproj @@ -0,0 +1,37 @@ + + + + + netstandard2.1 + + + + + + + + + + + + + + + + + + + + + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\BTKUILib.dll + False + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\ActionMenu.dll + False + + + + \ No newline at end of file diff --git a/.Deprecated/FuckOffUICamera/Properties/AssemblyInfo.cs b/PhysicsGunMod/Properties/AssemblyInfo.cs similarity index 67% rename from .Deprecated/FuckOffUICamera/Properties/AssemblyInfo.cs rename to PhysicsGunMod/Properties/AssemblyInfo.cs index b7a9e9f..df4ffd2 100644 --- a/.Deprecated/FuckOffUICamera/Properties/AssemblyInfo.cs +++ b/PhysicsGunMod/Properties/AssemblyInfo.cs @@ -1,30 +1,30 @@ using MelonLoader; -using NAK.FuckOffUICamera.Properties; +using NAK.PhysicsGunMod.Properties; using System.Reflection; [assembly: AssemblyVersion(AssemblyInfoParams.Version)] [assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] [assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.FuckOffUICamera))] +[assembly: AssemblyTitle(nameof(NAK.PhysicsGunMod))] [assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.FuckOffUICamera))] +[assembly: AssemblyProduct(nameof(NAK.PhysicsGunMod))] [assembly: MelonInfo( - typeof(NAK.FuckOffUICamera.FuckOffUICameraMod), - nameof(NAK.FuckOffUICamera), + typeof(NAK.PhysicsGunMod.PhysicsGunMod), + nameof(NAK.PhysicsGunMod), AssemblyInfoParams.Version, AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/FuckOffUICamera" + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PhysicsGunMod" )] [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: MelonColor(255, 241, 200, 82)] +[assembly: MelonAuthorColor(255, 114, 17, 25)] [assembly: HarmonyDontPatchAll] -namespace NAK.FuckOffUICamera.Properties; +namespace NAK.PhysicsGunMod.Properties; internal static class AssemblyInfoParams { public const string Version = "1.0.0"; diff --git a/PhysicsGunMod/README.md b/PhysicsGunMod/README.md new file mode 100644 index 0000000..eca47c9 --- /dev/null +++ b/PhysicsGunMod/README.md @@ -0,0 +1,28 @@ +# AvatarScaleMod + +Proof of concept mod to add Avatar Scaling to any avatar. This is local-only, but I may toy with using Mod Network. + +Legit threw this together in three hours. ChilloutVR handles all the hard stuff already with its existing animation-clip-based Avatar Scaling. + +https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/7405cef5-fd68-4103-8c18-b3164029eab1 + +## Notes: +* Constraint scaling partially conflicts with avatars run through my [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool). +* This is local-only, at least unless I bother with Mod Network. +* The entire thing is pretty messy and I am unsure of the performance impact, especially with scaling all lights, audio, & constraints. + +## Relevant Feedback Posts: +https://feedback.abinteractive.net/p/built-in-avatar-scaling-system + +This mod is me creating the system I wanted when I wrote the above feedback post. + +--- + +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. diff --git a/PhysicsGunMod/format.json b/PhysicsGunMod/format.json new file mode 100644 index 0000000..b1de45e --- /dev/null +++ b/PhysicsGunMod/format.json @@ -0,0 +1,23 @@ +{ + "_id": 126, + "name": "BetterCalibration", + "modversion": "1.0.0", + "gameversion": "2022r173", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Mod to improve the calibration process for FBT.", + "searchtags": [ + "IK", + "FBT", + "VRIK", + "calibration", + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r3/BetterCalibration.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/BetterCalibration/", + "changelog": "", + "embedcolor": "9b59b6" +} \ No newline at end of file diff --git a/PortableCameraAdditions/Properties/AssemblyInfo.cs b/PortableCameraAdditions/Properties/AssemblyInfo.cs index 841c286..44b24ae 100644 --- a/PortableCameraAdditions/Properties/AssemblyInfo.cs +++ b/PortableCameraAdditions/Properties/AssemblyInfo.cs @@ -20,13 +20,13 @@ using System.Reflection; [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: MelonColor(255, 255, 217, 106)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] namespace NAK.PortableCameraAdditions.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.6"; + public const string Version = "1.0.5"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/PortableCameraAdditions/format.json b/PortableCameraAdditions/format.json index 957110c..b9b8f25 100644 --- a/PortableCameraAdditions/format.json +++ b/PortableCameraAdditions/format.json @@ -1,8 +1,8 @@ { "_id": 123, "name": "PortableCameraAdditions", - "modversion": "1.0.6", - "gameversion": "2025r179", + "modversion": "1.0.5", + "gameversion": "2023r173", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", @@ -18,8 +18,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/PortableCameraAdditions.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r22/PortableCameraAdditions.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PortableCameraAdditions/", - "changelog": "- Recompiled for 2025r179", + "changelog": "- Fixes for 2023r173.", "embedcolor": "#ffd96a" } \ No newline at end of file diff --git a/Portals/format.json b/Portals/format.json new file mode 100644 index 0000000..d3c7457 --- /dev/null +++ b/Portals/format.json @@ -0,0 +1 @@ + "None" diff --git a/PropLoadingHexagon/Main.cs b/PropLoadingHexagon/Main.cs index eac818f..aecb7df 100644 --- a/PropLoadingHexagon/Main.cs +++ b/PropLoadingHexagon/Main.cs @@ -50,9 +50,9 @@ public class PropLoadingHexagonMod : MelonMod ); HarmonyInstance.Patch( // delete mode on prop placeholder - typeof(ControllerRay).GetMethod(nameof(ControllerRay.HandleSpawnableClicked), + typeof(ControllerRay).GetMethod(nameof(ControllerRay.DeleteSpawnable), BindingFlags.NonPublic | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(PropLoadingHexagonMod).GetMethod(nameof(OnHandleSpawnableClicked), + prefix: new HarmonyMethod(typeof(PropLoadingHexagonMod).GetMethod(nameof(OnDeleteSpawnableCheck), BindingFlags.NonPublic | BindingFlags.Static)) ); @@ -185,7 +185,7 @@ public class PropLoadingHexagonMod : MelonMod Loading_Hex_List.Add(loadingHex); } - private static void OnHandleSpawnableClicked(ref ControllerRay __instance) + private static void OnDeleteSpawnableCheck(ref ControllerRay __instance) { if (!__instance._interactDown) return; // not interacted, no need to check diff --git a/PropLoadingHexagon/Properties/AssemblyInfo.cs b/PropLoadingHexagon/Properties/AssemblyInfo.cs index 35e50f1..a423d48 100644 --- a/PropLoadingHexagon/Properties/AssemblyInfo.cs +++ b/PropLoadingHexagon/Properties/AssemblyInfo.cs @@ -22,11 +22,12 @@ using System.Reflection; [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonColor(255, 246, 25, 99)] // red-pink [assembly: MelonAuthorColor(255, 158, 21, 32)] // red +[assembly: MelonOptionalDependencies("TheClapper")] [assembly: HarmonyDontPatchAll] namespace NAK.PropLoadingHexagon.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.0"; public const string Author = "Exterrata & NotAKidoS"; } \ No newline at end of file diff --git a/PropLoadingHexagon/format.json b/PropLoadingHexagon/format.json index 4d12613..541cd73 100644 --- a/PropLoadingHexagon/format.json +++ b/PropLoadingHexagon/format.json @@ -1,8 +1,8 @@ { "_id": 220, "name": "PropLoadingHexagon", - "modversion": "1.0.1", - "gameversion": "2025r179", + "modversion": "1.0.0", + "gameversion": "2024r175", "loaderversion": "0.6.1", "modtype": "Mod", "author": "Exterrata & NotAKidoS", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/PropLoadingHexagon.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r34/PropLoadingHexagon.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PropLoadingHexagon/", - "changelog": "- Fixes for 2025r179", + "changelog": "- Initial release", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/PropUndoButton/Main.cs b/PropUndoButton/Main.cs index c3c6337..f0c0c67 100644 --- a/PropUndoButton/Main.cs +++ b/PropUndoButton/Main.cs @@ -16,17 +16,17 @@ namespace NAK.PropUndoButton; public class PropUndoButton : MelonMod { - private static readonly MelonPreferences_Category Category = + public static readonly MelonPreferences_Category Category = MelonPreferences.CreateCategory(nameof(PropUndoButton)); - private static readonly MelonPreferences_Entry EntryEnabled = + public static readonly MelonPreferences_Entry EntryEnabled = Category.CreateEntry("Enabled", true, description: "Toggle Undo Prop Button."); - private static readonly MelonPreferences_Entry EntryUseSFX = + public static readonly MelonPreferences_Entry EntryUseSFX = Category.CreateEntry("Use SFX", true, description: "Toggle audio queues for prop spawn, undo, redo, and warning."); - private static readonly List deletedProps = []; + internal static List deletedProps = new(); // audio clip names, InterfaceAudio adds "PropUndo_" prefix private const string sfx_spawn = "PropUndo_sfx_spawn"; @@ -92,11 +92,11 @@ public class PropUndoButton : MelonMod if (!File.Exists(clipPath)) { // read the clip data from embedded resources - byte[] clipData; + byte[] clipData = null; var resourceName = "PropUndoButton.SFX." + clipName; using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { - clipData = new byte[stream!.Length]; + clipData = new byte[stream.Length]; stream.Read(clipData, 0, clipData.Length); } diff --git a/PropUndoButton/Properties/AssemblyInfo.cs b/PropUndoButton/Properties/AssemblyInfo.cs index 3849345..424a9d7 100644 --- a/PropUndoButton/Properties/AssemblyInfo.cs +++ b/PropUndoButton/Properties/AssemblyInfo.cs @@ -17,16 +17,14 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/UndoPropButton" )] -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[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.PropUndoButton.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.4"; + public const string Version = "1.0.2"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/PropUndoButton/format.json b/PropUndoButton/format.json index 721593d..9f6f07a 100644 --- a/PropUndoButton/format.json +++ b/PropUndoButton/format.json @@ -1,9 +1,9 @@ { "_id": 147, "name": "PropUndoButton", - "modversion": "1.0.4", - "gameversion": "2025r180", - "loaderversion": "0.7.2", + "modversion": "1.0.2", + "gameversion": "2024r175", + "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", "description": "**CTRL+Z** to undo latest spawned prop. **CTRL+SHIFT+Z** to redo deleted prop.\nIncludes optional SFX for prop spawn, undo, redo, warn, and deny, which can be disabled in settings.\n\nYou can replace the sfx in 'ChilloutVR\\ChilloutVR_Data\\StreamingAssets\\Cohtml\\UIResources\\GameUI\\mods\\PropUndo\\audio'.", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/PropUndoButton.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r25/PropUndoButton.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PropUndoButton/", - "changelog": "- Fixes for 2025r180", + "changelog": "- Recompiled for 2024r175", "embedcolor": "#00FFFF" } \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/ModSettings.cs b/RCCVirtualSteeringWheel/ModSettings.cs deleted file mode 100644 index f07436c..0000000 --- a/RCCVirtualSteeringWheel/ModSettings.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MelonLoader; - -namespace NAK.RCCVirtualSteeringWheel; - -internal static class ModSettings -{ - #region Constants - - private const string ModName = nameof(RCCVirtualSteeringWheel); - - #endregion Constants - - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(ModName); - - internal static readonly MelonPreferences_Entry EntryOverrideSteeringRange = - Category.CreateEntry("override_steering_range", false, - "Override Steering Range", description: "Should the steering wheel use a custom steering range instead of the vehicle's default?"); - - internal static readonly MelonPreferences_Entry EntryCustomSteeringRange = - Category.CreateEntry("custom_steering_range", 60f, - "Custom Steering Range", description: "The custom steering range in degrees when override is enabled (default: 60)"); - - internal static readonly MelonPreferences_Entry EntryInvertSteering = - Category.CreateEntry("invert_steering", false, - "Invert Steering", description: "Inverts the steering direction"); - - #endregion Melon Preferences -} \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/Patches.cs b/RCCVirtualSteeringWheel/Patches.cs deleted file mode 100644 index 1ef3064..0000000 --- a/RCCVirtualSteeringWheel/Patches.cs +++ /dev/null @@ -1,59 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Systems.InputManagement; -using ABI_RC.Systems.Movement; -using HarmonyLib; -using NAK.RCCVirtualSteeringWheel.Util; -using UnityEngine; - -namespace NAK.RCCVirtualSteeringWheel.Patches; - -internal static class RCCCarControllerV3_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(RCC_CarControllerV3), nameof(RCC_CarControllerV3.Awake))] - private static void Postfix_RCC_CarControllerV3_Awake(RCC_CarControllerV3 __instance) - { - Transform steeringWheelTransform = __instance.SteeringWheel; - if (steeringWheelTransform == null) - return; - - BoneVertexBoundsUtility.CalculateBoneWeightedBounds( - steeringWheelTransform, - 0.8f, - BoneVertexBoundsUtility.BoundsCalculationFlags.All, - result => - { - if (!result.IsValid) - return; - - if (!__instance) - return; - - SteeringWheelRoot.SetupSteeringWheel(__instance, result.LocalBounds); - }); - } -} - -internal static class CVRInputManager_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRInputManager), nameof(CVRInputManager.UpdateInput))] - private static void Postfix_CVRInputManager_UpdateInput(ref CVRInputManager __instance) - { - BetterBetterCharacterController characterController = BetterBetterCharacterController.Instance; - if (!characterController._isSitting) - return; // Must be sitting - - CVRSeat cvrSeat = characterController._lastCvrSeat; - if (!cvrSeat - || !cvrSeat.lockControls) - return; // Must be a driver seat - - RCC_CarControllerV3 carController = characterController._lastCvrSeat._carController; - if (!carController) - return; // Specific to RCC - - if (SteeringWheelRoot.TryGetWheelInput(carController, out float steeringValue)) - __instance.steering = steeringValue; - } -} \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/Properties/AssemblyInfo.cs b/RCCVirtualSteeringWheel/Properties/AssemblyInfo.cs deleted file mode 100644 index 03e9324..0000000 --- a/RCCVirtualSteeringWheel/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using MelonLoader; -using NAK.RCCVirtualSteeringWheel; -using NAK.RCCVirtualSteeringWheel.Properties; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.RCCVirtualSteeringWheel))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.RCCVirtualSteeringWheel))] - -[assembly: MelonInfo( - typeof(RCCVirtualSteeringWheelMod), - nameof(NAK.RCCVirtualSteeringWheel), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RCCVirtualSteeringWheel" -)] - -[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.RCCVirtualSteeringWheel.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.6"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.csproj b/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.csproj deleted file mode 100644 index 8920c39..0000000 --- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - net48 - GrabbableSteeringWheel - - - - ..\.ManagedLibs\BTKUILib.dll - - - ..\.ManagedLibs\TheClapper.dll - - - diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelPickup.cs b/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelPickup.cs deleted file mode 100644 index 91495dd..0000000 --- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelPickup.cs +++ /dev/null @@ -1,42 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.InteractionSystem.Base; -using ABI_RC.Core.Savior; -using UnityEngine; - -namespace NAK.RCCVirtualSteeringWheel; - -public class SteeringWheelPickup : Pickupable -{ - internal SteeringWheelRoot root; - - #region Pickupable Properties - - public override bool DisallowTheft => true; - public override float MaxGrabDistance => 0.8f; - public override float MaxPushDistance => 0f; - public override bool IsAutoHold => false; - public override bool IsObjectRotationAllowed => false; - public override bool IsObjectPushPullAllowed => false; - public override bool IsObjectUseAllowed => false; - public override bool CanPickup => IsPickupable && !IsPickedUp && MetaPort.Instance.isUsingVr; - - #endregion Pickupable Properties - - #region Pickupable Methods - - public override void OnUseDown(InteractionContext context) { } - public override void OnUseUp(InteractionContext context) { } - public override void FlingTowardsTarget(ControllerRay controllerRay) { } - - public override void OnGrab(InteractionContext context, Vector3 grabPoint) { - if (ControllerRay?.pivotPoint != null) - root.StartTrackingTransform(ControllerRay.transform); - } - - public override void OnDrop(InteractionContext context) { - if (ControllerRay?.transform != null) - root.StopTrackingTransform(ControllerRay.transform); - } - - #endregion Pickupable Methods -} \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelRoot.cs b/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelRoot.cs deleted file mode 100644 index 088752d..0000000 --- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelRoot.cs +++ /dev/null @@ -1,295 +0,0 @@ -using UnityEngine; - -namespace NAK.RCCVirtualSteeringWheel; - -public class SteeringWheelRoot : MonoBehaviour -{ - #region Static Variables - - private static readonly Dictionary ActiveWheels = new(); - - #endregion Static Variables - - #region Static Methods - - public static bool TryGetWheelInput(RCC_CarControllerV3 carController, out float steeringInput) - { - if (ActiveWheels.TryGetValue(carController, out SteeringWheelRoot wheel) && wheel._averageAngle != 0f) - { - steeringInput = wheel.GetNormalizedValue(); - return true; - } - - steeringInput = 0f; - return false; - } - - public static void SetupSteeringWheel(RCC_CarControllerV3 carController, Bounds steeringWheelBounds) - { - Transform steeringWheel = carController.SteeringWheel; - - SteeringWheelRoot wheel = steeringWheel.gameObject.AddComponent(); - wheel._carController = carController; - - Array.Resize(ref wheel._pickups, 2); - CreatePickup(out wheel._pickups[0]); - CreatePickup(out wheel._pickups[1]); - - return; - - void CreatePickup(out SteeringWheelPickup wheelPickup) - { - GameObject pickup = new() - { - transform = - { - parent = steeringWheel.transform, - localPosition = Vector3.zero, - localRotation = Quaternion.identity, - localScale = Vector3.one - } - }; - - BoxCollider collider = pickup.AddComponent(); - collider.size = steeringWheelBounds.size; - collider.center = steeringWheelBounds.center; - collider.isTrigger = true; - - wheelPickup = pickup.AddComponent(); - wheelPickup.root = wheel; - } - } - - #endregion Static Methods - - #region Public Properties - - private bool IsWheelBeingHeld => _pickups[0].IsPickedUp || _pickups[1].IsPickedUp; - private bool IsWheelInactive => !IsWheelBeingHeld && _averageAngle == 0f; - - #endregion Public Properties - - #region Private Variables - - private RCC_CarControllerV3 _carController; - private SteeringWheelPickup[] _pickups; - - private float _originalSteeringWheelAngleMultiplier; - private float _originalSteeringWheelSign; - private bool _originalCounterSteer; - private bool _originalSteerSmoothing; - - private readonly List _trackedTransforms = new(); - private readonly List _lastPositions = new(); - private readonly List _totalAngles = new(); - - private bool _isTracking; - private float _averageAngle; - private float _timeWheelReleased = -1f; - private const float RETURN_TO_CENTER_DURATION = 2f; - private const float RETURN_START_DELAY = 0.1f; - - #endregion Private Variables - - #region Unity Events - - private void Start() - { - ActiveWheels.TryAdd(_carController, this); - InitializeWheel(); - } - - private void Update() - { - if (_carController == null) return; - UpdateWheelState(); - UpdateSteeringBehavior(); - } - - private void OnDestroy() - { - ActiveWheels.Remove(_carController); - } - - #endregion Unity Events - - #region Public Methods - - internal void StartTrackingTransform(Transform trans) - { - if (trans == null) return; - - var currentAngle = _isTracking ? CalculateAverageAngle(_trackedTransforms, _totalAngles) : 0f; - - _trackedTransforms.Add(trans); - _lastPositions.Add(GetLocalPositionWithoutRotation(transform.position)); - _totalAngles.Add(currentAngle); - _isTracking = true; - } - - internal void StopTrackingTransform(Transform trans) - { - var index = _trackedTransforms.IndexOf(trans); - if (index == -1) return; - - var currentAverage = CalculateAverageAngle(_trackedTransforms, _totalAngles); - _trackedTransforms.RemoveAt(index); - _lastPositions.RemoveAt(index); - _totalAngles.RemoveAt(index); - - if (_trackedTransforms.Count <= 0) - return; - - for (var i = 0; i < _totalAngles.Count; i++) - _totalAngles[i] = currentAverage; - } - - #endregion Public Methods - - #region Private Methods - - private void InitializeWheel() - { - _originalSteeringWheelAngleMultiplier = _carController.steeringWheelAngleMultiplier; - _originalSteeringWheelSign = Mathf.Sign(_originalSteeringWheelAngleMultiplier); - _originalCounterSteer = _carController.useCounterSteering; - _originalSteerSmoothing = _carController.useSteeringSmoother; - } - - private void UpdateWheelState() - { - var isHeld = IsWheelBeingHeld; - if (!isHeld && _timeWheelReleased < 0f) - _timeWheelReleased = Time.time; - else if (isHeld) - _timeWheelReleased = -1f; - } - - private void UpdateSteeringBehavior() - { - SetSteeringAssistsState(!IsWheelInactive); - - if (IsWheelBeingHeld) - UpdateRotationTracking(); - else if (_timeWheelReleased >= 0f) - HandleWheelReturn(); - } - - private void SetSteeringAssistsState(bool shouldOverride) - { - _carController.useCounterSteering = !shouldOverride && _originalCounterSteer; - _carController.useSteeringSmoother = !shouldOverride && _originalSteerSmoothing; - _carController.steeringWheelAngleMultiplier = ModSettings.EntryOverrideSteeringRange.Value && shouldOverride - ? ModSettings.EntryCustomSteeringRange.Value * _originalSteeringWheelSign / _carController.steerAngle - : _originalSteeringWheelAngleMultiplier; - } - - private void HandleWheelReturn() - { - var timeSinceRelease = Time.time - _timeWheelReleased; - - if (timeSinceRelease < RETURN_START_DELAY) - return; - - var returnTime = timeSinceRelease - RETURN_START_DELAY; - if (returnTime < RETURN_TO_CENTER_DURATION) - { - var t = returnTime / RETURN_TO_CENTER_DURATION; - _averageAngle = Mathf.Lerp(_averageAngle, 0f, t); - - for (var i = 0; i < _totalAngles.Count; i++) - _totalAngles[i] = _averageAngle; - } - else - { - _averageAngle = 0f; - for (var i = 0; i < _totalAngles.Count; i++) - _totalAngles[i] = 0f; - } - } - - private float GetMaxSteeringRange() - { - return _carController.steerAngle * Mathf.Abs(_carController.steeringWheelAngleMultiplier); - } - - private float GetSteeringWheelSign() - { - return _originalSteeringWheelSign * (ModSettings.EntryInvertSteering.Value ? 1f : -1f); - } - - private Vector3 GetSteeringWheelLocalAxis() - { - return _carController.steeringWheelRotateAround switch - { - RCC_CarControllerV3.SteeringWheelRotateAround.XAxis => Vector3.right, - RCC_CarControllerV3.SteeringWheelRotateAround.YAxis => Vector3.up, - RCC_CarControllerV3.SteeringWheelRotateAround.ZAxis => Vector3.forward, - _ => Vector3.forward - }; - } - - private Vector3 GetLocalPositionWithoutRotation(Vector3 worldPosition) - { - Transform steeringTransform = _carController.SteeringWheel; - Quaternion localRotation = steeringTransform.localRotation; - steeringTransform.localRotation = _carController.orgSteeringWheelRot; - Vector3 localPosition = steeringTransform.InverseTransformPoint(worldPosition); - steeringTransform.localRotation = localRotation; - return localPosition; - } - - private float GetNormalizedValue() - { - return Mathf.Clamp(_averageAngle / GetMaxSteeringRange(), -1f, 1f) * GetSteeringWheelSign(); - } - - private void UpdateRotationTracking() - { - if (!_isTracking || _trackedTransforms.Count == 0) return; - - Vector3 trackingAxis = GetSteeringWheelLocalAxis(); - UpdateTransformAngles(trackingAxis); - _averageAngle = CalculateAverageAngle(_trackedTransforms, _totalAngles); - } - - private void UpdateTransformAngles(Vector3 trackingAxis) - { - for (var i = 0; i < _trackedTransforms.Count; i++) - { - if (_trackedTransforms[i] == null) continue; - - Vector3 currentPosition = GetLocalPositionWithoutRotation(_trackedTransforms[i].position); - if (currentPosition == _lastPositions[i]) continue; - - Vector3 previousVector = Vector3.ProjectOnPlane(_lastPositions[i], trackingAxis).normalized; - Vector3 currentVector = Vector3.ProjectOnPlane(currentPosition, trackingAxis).normalized; - - if (previousVector.sqrMagnitude > 0.001f && currentVector.sqrMagnitude > 0.001f) - { - var deltaAngle = Vector3.SignedAngle(previousVector, currentVector, trackingAxis); - if (Mathf.Abs(deltaAngle) < 90f) - _totalAngles[i] += deltaAngle; - } - - _lastPositions[i] = currentPosition; - } - } - - private static float CalculateAverageAngle(List transforms, List angles) - { - var sum = 0f; - var validTransforms = 0; - - for (var i = 0; i < transforms.Count; i++) - { - if (transforms[i] == null) continue; - sum += angles[i]; - validTransforms++; - } - - return validTransforms > 0 ? sum / validTransforms : 0f; - } - - #endregion Private Methods -} \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Util/BoneVertexBoundsUtility.cs b/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Util/BoneVertexBoundsUtility.cs deleted file mode 100644 index f55f241..0000000 --- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Util/BoneVertexBoundsUtility.cs +++ /dev/null @@ -1,403 +0,0 @@ -using System.Collections; -using Unity.Burst; -using Unity.Collections; -using Unity.Jobs; -using Unity.Profiling; -using UnityEngine; -using UnityEngine.Animations; - -namespace NAK.RCCVirtualSteeringWheel.Util; - -public class BoneVertexBoundsUtility : MonoBehaviour -{ - private static readonly ProfilerMarker s_calculateBoundsMarker = new("BoneVertexBounds.Calculate"); - private static readonly ProfilerMarker s_processChildRenderersMarker = new("BoneVertexBounds.ProcessChildRenderers"); - private static readonly ProfilerMarker s_processSkinnedMeshesMarker = new("BoneVertexBounds.ProcessSkinnedMeshes"); - private static readonly ProfilerMarker s_processVerticesMarker = new("BoneVertexBounds.ProcessVertices"); - private static readonly ProfilerMarker s_processConstraintsMarker = new("BoneVertexBounds.ProcessConstraints"); - private static readonly ProfilerMarker s_jobExecutionMarker = new("BoneVertexBounds.JobExecution"); - private static readonly ProfilerMarker s_meshCopyMarker = new("BoneVertexBounds.MeshCopy"); - - private static BoneVertexBoundsUtility instance; - - private static BoneVertexBoundsUtility Instance - { - get - { - if (instance != null) return instance; - GameObject go = new("BoneVertexBoundsUtility"); - instance = go.AddComponent(); - DontDestroyOnLoad(go); - return instance; - } - } - - [Flags] - public enum BoundsCalculationFlags - { - None = 0, - IncludeChildren = 1 << 0, - IncludeSkinnedMesh = 1 << 1, - IncludeConstraints = 1 << 2, - All = IncludeChildren | IncludeSkinnedMesh | IncludeConstraints - } - - public struct BoundsResult - { - public bool IsValid; - public Bounds LocalBounds; - } - - /// - /// Calculates the bounds of a transform based on: - /// - Children Renderers - /// - Skinned Mesh Weights - /// - Constrained Child Renderers & Skinned Mesh Weights (thanks Fearless) - /// - public static void CalculateBoneWeightedBounds(Transform bone, float weightThreshold, BoundsCalculationFlags flags, Action onComplete) - => Instance.StartCoroutine(Instance.CalculateBoundsCoroutine(bone, weightThreshold, flags, onComplete)); - - private IEnumerator CalculateBoundsCoroutine(Transform bone, float weightThreshold, BoundsCalculationFlags flags, Action onComplete) - { - using (s_calculateBoundsMarker.Auto()) - { - BoundsResult result = new(); - var allWeightedPoints = new List(); - bool hasValidPoints = false; - - // Child renderers - IEnumerator ProcessChildRenderersLocal(Transform targetBone, Action> onChildPoints) - { - using (s_processChildRenderersMarker.Auto()) - { - var points = new List(); - var childRenderers = targetBone.GetComponentsInChildren() - .Where(r => r is not SkinnedMeshRenderer) - .ToArray(); - - foreach (Renderer childRend in childRenderers) - { - Bounds bounds = childRend.localBounds; - var corners = new Vector3[8]; - Vector3 ext = bounds.extents; - Vector3 center = bounds.center; - - corners[0] = new Vector3(center.x - ext.x, center.y - ext.y, center.z - ext.z); - corners[1] = new Vector3(center.x + ext.x, center.y - ext.y, center.z - ext.z); - corners[2] = new Vector3(center.x - ext.x, center.y + ext.y, center.z - ext.z); - corners[3] = new Vector3(center.x + ext.x, center.y + ext.y, center.z - ext.z); - corners[4] = new Vector3(center.x - ext.x, center.y - ext.y, center.z + ext.z); - corners[5] = new Vector3(center.x + ext.x, center.y - ext.y, center.z + ext.z); - corners[6] = new Vector3(center.x - ext.x, center.y + ext.y, center.z + ext.z); - corners[7] = new Vector3(center.x + ext.x, center.y + ext.y, center.z + ext.z); - - for (int i = 0; i < 8; i++) - points.Add(targetBone.InverseTransformPoint(childRend.transform.TransformPoint(corners[i]))); - } - - onChildPoints?.Invoke(points); - } - yield break; - } - - // Skinned mesh renderers - IEnumerator ProcessSkinnedMeshRenderersLocal(Transform targetBone, float threshold, Action> onSkinnedPoints) - { - using (s_processSkinnedMeshesMarker.Auto()) - { - var points = new List(); - var siblingAndParentSkinnedMesh = targetBone.root.GetComponentsInChildren(); - var relevantMeshes = siblingAndParentSkinnedMesh.Where(smr => DoesMeshUseBone(smr, targetBone)).ToArray(); - - foreach (SkinnedMeshRenderer smr in relevantMeshes) - { - yield return StartCoroutine(ProcessSkinnedMesh(smr, targetBone, threshold, meshPoints => - { - if (meshPoints is { Length: > 0 }) - points.AddRange(meshPoints); - })); - } - - onSkinnedPoints?.Invoke(points); - } - } - - // Constraints - IEnumerator ProcessConstraintsLocal(Transform targetBone, float threshold, BoundsCalculationFlags constraintFlags, Action> onConstraintPoints) - { - using (s_processConstraintsMarker.Auto()) - { - var points = new List(); - var processedTransforms = new HashSet(); - var constrainedTransforms = new List(); - - // Find all constrained objects that reference our bone - var constraints = targetBone.root.GetComponentsInChildren(); - - foreach (IConstraint constraint in constraints) - { - for (int i = 0; i < constraint.sourceCount; i++) - { - if (constraint.GetSource(i).sourceTransform != targetBone) continue; - constrainedTransforms.Add(((Behaviour)constraint).transform); - break; - } - } - - // Process each constrained transform - foreach (Transform constrainedTransform in constrainedTransforms) - { - if (!processedTransforms.Add(constrainedTransform)) - continue; - - var localPoints = new List(); - bool hasLocalPoints = false; - - // Process child renderers if enabled - if ((constraintFlags & BoundsCalculationFlags.IncludeChildren) != 0) - { - yield return StartCoroutine(ProcessChildRenderersLocal(constrainedTransform, childPoints => - { - if (childPoints is not { Count: > 0 }) return; - localPoints.AddRange(childPoints); - hasLocalPoints = true; - })); - } - - // Process skinned mesh if enabled - if ((constraintFlags & BoundsCalculationFlags.IncludeSkinnedMesh) != 0) - { - yield return StartCoroutine(ProcessSkinnedMeshRenderersLocal(constrainedTransform, threshold, skinnedPoints => - { - if (skinnedPoints is not { Count: > 0 }) return; - localPoints.AddRange(skinnedPoints); - hasLocalPoints = true; - })); - } - - if (!hasLocalPoints) - continue; - - // Convert all points to bone space - foreach (Vector3 point in localPoints) - points.Add(targetBone.InverseTransformPoint(constrainedTransform.TransformPoint(point))); - } - - onConstraintPoints?.Invoke(points); - } - } - - // Process child renderers - if ((flags & BoundsCalculationFlags.IncludeChildren) != 0) - { - yield return StartCoroutine(ProcessChildRenderersLocal(bone, childPoints => - { - if (childPoints is not { Count: > 0 }) return; - allWeightedPoints.AddRange(childPoints); - hasValidPoints = true; - })); - } - - // Process skinned mesh renderers - if ((flags & BoundsCalculationFlags.IncludeSkinnedMesh) != 0) - { - yield return StartCoroutine(ProcessSkinnedMeshRenderersLocal(bone, weightThreshold, skinnedPoints => - { - if (skinnedPoints is not { Count: > 0 }) return; - allWeightedPoints.AddRange(skinnedPoints); - hasValidPoints = true; - })); - } - - // Process constraints - if ((flags & BoundsCalculationFlags.IncludeConstraints) != 0) - { - // Use only Children and SkinnedMesh flags for constraint processing to prevent recursion (maybe make optional)? - BoundsCalculationFlags constraintFlags = flags & ~BoundsCalculationFlags.IncludeConstraints; - yield return StartCoroutine(ProcessConstraintsLocal(bone, weightThreshold, constraintFlags, constraintPoints => - { - if (constraintPoints is not { Count: > 0 }) return; - allWeightedPoints.AddRange(constraintPoints); - hasValidPoints = true; - })); - } - - if (!hasValidPoints) - { - result.IsValid = false; - onComplete?.Invoke(result); - yield break; - } - - // Calculate final bounds in bone space - Bounds bounds = new(allWeightedPoints[0], Vector3.zero); - foreach (Vector3 point in allWeightedPoints) - bounds.Encapsulate(point); - - // Ensure minimum size - Vector3 size = bounds.size; - size = Vector3.Max(size, Vector3.one * 0.01f); - bounds.size = size; - - result.IsValid = true; - result.LocalBounds = bounds; - - onComplete?.Invoke(result); - } - } - - private static bool DoesMeshUseBone(SkinnedMeshRenderer smr, Transform bone) - => smr.bones != null && smr.bones.Contains(bone); - - private IEnumerator ProcessSkinnedMesh(SkinnedMeshRenderer smr, Transform bone, float weightThreshold, - Action onComplete) - { - Mesh mesh = smr.sharedMesh; - if (mesh == null) - { - onComplete?.Invoke(null); - yield break; - } - - // Find bone index - int boneIndex = Array.IndexOf(smr.bones, bone); - if (boneIndex == -1) - { - onComplete?.Invoke(null); - yield break; - } - - Mesh meshToUse = mesh; - GameObject tempGO = null; - - try - { - // Handle non-readable meshes (ReadItAnyway lmao) - if (!mesh.isReadable) - { - using (s_meshCopyMarker.Auto()) - { - tempGO = new GameObject("TempMeshReader"); - SkinnedMeshRenderer tempSMR = tempGO.AddComponent(); - tempSMR.sharedMesh = mesh; - meshToUse = new Mesh(); - tempSMR.BakeMesh(meshToUse); - } - } - - Mesh.MeshDataArray meshDataArray = Mesh.AcquireReadOnlyMeshData(meshToUse); - Mesh.MeshData meshData = meshDataArray[0]; - - var vertexCount = meshData.vertexCount; - var vertices = new NativeArray(vertexCount, Allocator.TempJob); - var weights = new NativeArray(vertexCount, Allocator.TempJob); - var results = new NativeArray(vertexCount, Allocator.TempJob); - - meshData.GetVertices(vertices); - weights.CopyFrom(mesh.boneWeights); - - // Debug.Log(vertices.Length); - // Debug.Log(weights.Length); - - using (s_processVerticesMarker.Auto()) - { - try - { - Transform rootBone = smr.rootBone ? smr.rootBone.transform : smr.transform; - Matrix4x4 meshToWorld = Matrix4x4.TRS(smr.transform.position, smr.transform.rotation, rootBone.lossyScale); - - // Fixes setup where mesh was in diff hierarchy & 0.001 scale, bone & root bone outside & above - meshToWorld *= Matrix4x4.TRS(Vector3.zero, Quaternion.identity, smr.transform.localScale); - - ProcessVerticesJob processJob = new() - { - Vertices = vertices, - BoneWeights = weights, - Results = results, - BoneIndex = boneIndex, - WeightThreshold = weightThreshold, - MeshToWorld = meshToWorld, - WorldToBone = bone.worldToLocalMatrix - }; - - using (s_jobExecutionMarker.Auto()) - { - int batchCount = Mathf.Max(1, vertexCount / 64); - JobHandle jobHandle = processJob.Schedule(vertexCount, batchCount); - while (!jobHandle.IsCompleted) - yield return null; - - jobHandle.Complete(); - } - - // Collect valid points - var validPoints = new List(); - for (int i = 0; i < results.Length; i++) - if (results[i].IsValid) validPoints.Add(results[i].Position); - - onComplete?.Invoke(validPoints.ToArray()); - } - finally - { - vertices.Dispose(); - weights.Dispose(); - results.Dispose(); - meshDataArray.Dispose(); - } - } - } - finally - { - // Destroy duplicated baked mesh if we created one to read mesh data - if (!mesh.isReadable && meshToUse != mesh) Destroy(meshToUse); - if (tempGO != null) Destroy(tempGO); - } - } - - private struct VertexResult - { - public Vector3 Position; - public bool IsValid; - } - - [BurstCompile] - private struct ProcessVerticesJob : IJobParallelFor - { - [ReadOnly] public NativeArray Vertices; - [ReadOnly] public NativeArray BoneWeights; - [NativeDisableParallelForRestriction] - public NativeArray Results; - [ReadOnly] public int BoneIndex; - [ReadOnly] public float WeightThreshold; - [ReadOnly] public Matrix4x4 MeshToWorld; - [ReadOnly] public Matrix4x4 WorldToBone; - - public void Execute(int i) - { - BoneWeight weight = BoneWeights[i]; - float totalWeight = 0f; - - if (weight.boneIndex0 == BoneIndex) totalWeight += weight.weight0; - if (weight.boneIndex1 == BoneIndex) totalWeight += weight.weight1; - if (weight.boneIndex2 == BoneIndex) totalWeight += weight.weight2; - if (weight.boneIndex3 == BoneIndex) totalWeight += weight.weight3; - - if (totalWeight >= WeightThreshold) - { - // Transform vertex to bone space - Vector3 worldPos = MeshToWorld.MultiplyPoint3x4(Vertices[i]); - Vector3 boneLocalPos = WorldToBone.MultiplyPoint3x4(worldPos); - - Results[i] = new VertexResult - { - Position = boneLocalPos, - IsValid = true - }; - } - else - { - Results[i] = new VertexResult { IsValid = false }; - } - } - } -} \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/README.md b/RCCVirtualSteeringWheel/README.md deleted file mode 100644 index 8d9f529..0000000 --- a/RCCVirtualSteeringWheel/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# RCCVirtualSteeringWheel - -Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. - ---- - -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.~~~~ \ No newline at end of file diff --git a/RCCVirtualSteeringWheel/format.json b/RCCVirtualSteeringWheel/format.json deleted file mode 100644 index 78825b0..0000000 --- a/RCCVirtualSteeringWheel/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 248, - "name": "RCCVirtualSteeringWheel", - "modversion": "1.0.6", - "gameversion": "2025r180", - "loaderversion": "0.7.2", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component.\n", - "searchtags": [ - "rcc", - "steering", - "vehicle", - "car" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RCCVirtualSteeringWheel.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RCCVirtualSteeringWheel/", - "changelog": "- Fixes for 2025r180", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/README.md b/README.md index ed9da33..024e796 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,30 @@ ## NotAKids ChilloutVR Mods - +## Released Mods -### Released Mods - -| Name | Description | Download | -|------|-------------|----------| -| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ASTExtension.dll) | -| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | No Download | -| [ConfigureCalibrationPose](ConfigureCalibrationPose/README.md) | Select FBT calibration pose. | No Download | -| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/CustomSpawnPoint.dll) | -| [DoubleTapJumpToExitSeat](DoubleTapJumpToExitSeat/README.md) | Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/DoubleTapJumpToExitSeat.dll) | -| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in Halfbody or Fullbody. | No Download | -| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | No Download | -| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | No Download | -| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | No Download | -| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RCCVirtualSteeringWheel.dll) | -| [RelativeSyncJitterFix](RelativeSyncJitterFix/README.md) | Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RelativeSyncJitterFix.dll) | -| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ShareBubbles.dll) | -| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | No Download | -| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | No Download | -| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ThirdPerson.dll) | -| [Tinyboard](Tinyboard/README.md) | Makes the keyboard small and smart. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/Tinyboard.dll) | -| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/YouAreMyPropNowWeAreHavingSoftTacosLater.dll) | - -### Experimental Mods - -| Name | Description | Download | -|------|-------------|----------| -| [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | No Download | -| [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Lets you customize the Steam & Discord rich presence messages & values. | No Download | -| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Adds a simple module for creating network variables & events *kinda* similar to Garry's Mod. | No Download | -| [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | No Download | -| [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | No Download | -| [ScriptingSpoofer](.Experimental/ScriptingSpoofer/README.md) | Prevents **local** scripts from accessing your Username or UserID by spoofing them with random values each session. | No Download | - - +| Mod Name | README | Download | Description | +|------------------------------|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------| +| CVRGizmos | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/CVRGizmos) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/CVRGizmos.dll) | Adds runtime gizmos to common CCK components. | +| PathCamDisabler | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/PathCamDisabler) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/PathCamDisabler.dll) | Adds option to disable the Path Camera Controller keybinds. | +| PortableCameraAdditions | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/PortableCameraAdditions) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/PortableCameraAdditions.dll) | Adds a few basic settings to the Portable Camera. | +| PropUndoButton | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/PropUndoButton) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/PropUndoButton.dll) | CTRL+Z to undo latest spawned prop. | +| ThirdPerson | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/ThirdPerson) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/ThirdPerson.dll) | Allows you to go into third person view. | +| FOVAdjustment | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/FOVAdjustment) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/FOVAdjustment.dll) | Makes CVR_DesktopCameraController default FOV configurable. | +| MuteSFX | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/MuteSFX) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/MuteSFX.dll) | Adds an audio cue for muting and unmuting. | +| ShadowCloneFallback | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/ShadowCloneFallback) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/ShadowCloneFallback.dll) | Exposes a toggle for the Fallback Shadow Clone. | +| StopClosingMyMenuOnWorldLoad | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/StopClosingMyMenuOnWorldLoad)| [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/StopClosingMyMenuOnWorldLoad.dll) | Prevents your menu from being closed when a world is loaded. | +| SwitchToDesktopOnSteamVRExit | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/SwitchToDesktopOnSteamVRExit)| [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/SwitchToDesktopOnSteamVRExit.dll) | Initiates a VR Switch to Desktop when SteamVR is exited. | +| MoreMenuOptions | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/MoreMenuOptions) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/MoreMenuOptions.dll) | Exposes some menu placement configuration options. | +| RelativeSync | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/RelativeSync) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/RelativeSync.dll) | Relative sync for Movement Parent & Chairs. | +| LazyPrune | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/LazyPrune) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/LazyPrune.dll) | Prevents loaded objects from immediately unloading. | +| ReconnectionSystemFix | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/ReconnectionSystemFix) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/ReconnectionSystemFix.dll) | Prevents recreating and reloading all remote players. | +| AASDefaultProfileFix | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/AASDefaultProfileFix) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/AASDefaultProfileFix.dll) | Fixes the Default AAS profile not being applied. | +| ScrollFlight | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/ScrollFlight) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/ScrollFlight.dll) | Scroll-wheel to adjust flight speed in Desktop. | +| PropLoadingHexagon | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/PropLoadingHexagon) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/PropLoadingHexagon.dll) | Adds a hexagon indicator to downloading props. | +| IKSimulatedRootAngleFix | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/IKSimulatedRootAngleFix) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/IKSimulatedRootAngleFix.dll) | Fixes Desktop & HalfBody root angle issues. | +| DropPropTweak | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/DropPropTweak) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/DropPropTweak.dll) | Allows you to drop props in the air. | +| VisualCloneFix | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/VisualCloneFix) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/VisualCloneFix.dll) | Fixes the Visual Clone system. | +| KeepVelocityOnExitFlight | [README](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/KeepVelocityOnExitFlight) | [Download](https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/latest/download/KeepVelocityOnExitFlight.dll) | Keeps the player's velocity when exiting flight mode. | # How To Install @@ -47,12 +36,6 @@ If preferred, you may also download the most recent version directly from the ** It is important to exercise caution when using anything outside of the ChilloutVR Modding Group releases, as it may not have undergone review and could be harmful or non-functional. -# License - -Copyright © 2024 NotAKidoS - All Rights Reserved unless otherwise specified. - -The repository history contains MIT-licensed commits. Refer to specific commits or versions for the applicable license terms. Commits up to #848 are MIT-licensed. - --- Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI. diff --git a/.Deprecated/ReconnectionSystemFix/Main.cs b/ReconnectionSystemFix/Main.cs similarity index 100% rename from .Deprecated/ReconnectionSystemFix/Main.cs rename to ReconnectionSystemFix/Main.cs diff --git a/.Deprecated/ReconnectionSystemFix/Patches.cs b/ReconnectionSystemFix/Patches.cs similarity index 100% rename from .Deprecated/ReconnectionSystemFix/Patches.cs rename to ReconnectionSystemFix/Patches.cs diff --git a/.Deprecated/ReconnectionSystemFix/Properties/AssemblyInfo.cs b/ReconnectionSystemFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/ReconnectionSystemFix/Properties/AssemblyInfo.cs rename to ReconnectionSystemFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/ReconnectionSystemFix/README.md b/ReconnectionSystemFix/README.md similarity index 100% rename from .Deprecated/ReconnectionSystemFix/README.md rename to ReconnectionSystemFix/README.md diff --git a/.Deprecated/ReconnectionSystemFix/ReconnectionSystemFix.csproj b/ReconnectionSystemFix/ReconnectionSystemFix.csproj similarity index 100% rename from .Deprecated/ReconnectionSystemFix/ReconnectionSystemFix.csproj rename to ReconnectionSystemFix/ReconnectionSystemFix.csproj diff --git a/.Deprecated/ReconnectionSystemFix/format.json b/ReconnectionSystemFix/format.json similarity index 100% rename from .Deprecated/ReconnectionSystemFix/format.json rename to ReconnectionSystemFix/format.json diff --git a/References.Items.props b/References.Items.props index 69164f3..b618151 100644 --- a/References.Items.props +++ b/References.Items.props @@ -52,10 +52,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\Bhaptics.Tact.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\BouncyCastle.Crypto.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\Boxophobic.TheVehetationEngine.Runtime.dll False @@ -100,10 +96,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\DarkRift.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\DTLS.Net.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\ECM2.dll False @@ -204,18 +196,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\PICO.Platform.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\Pico.Spatializer.dll - False - - - $(MsBuildThisFileDirectory)\.ManagedLibs\Pico.Spatializer.Example.dll - False - - - $(MsBuildThisFileDirectory)\.ManagedLibs\PICO.TobSupport.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\PWCommon3DLL.dll False @@ -312,10 +292,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\System.Runtime.Serialization.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\System.Security.Cryptography.Cng.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\System.Security.dll False @@ -376,10 +352,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\UniTask.TextMeshPro.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.2D.Common.Runtime.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.AI.Navigation.dll False @@ -424,14 +396,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.InputSystem.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.InputSystem.ForUI.dll - False - - - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.InternalAPIEngineBridge.001.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.InternalAPIEngineBridge.002.dll False @@ -620,10 +584,6 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.Hands.Samples.VisualizerSample.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.Interaction.Toolkit.dll - False - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.Management.dll False @@ -656,12 +616,16 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.OpenXR.Features.OculusQuestSupport.dll False + + $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.OpenXR.Features.PICOSupport.dll + False + $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.OpenXR.Features.RuntimeDebugger.dll False - - $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.PICO.dll + + $(MsBuildThisFileDirectory)\.ManagedLibs\Unity.XR.OpenXRPico.dll False diff --git a/RelativeSync/Main.cs b/RelativeSync/Main.cs new file mode 100644 index 0000000..83ec331 --- /dev/null +++ b/RelativeSync/Main.cs @@ -0,0 +1,48 @@ +using MelonLoader; +using NAK.RelativeSync.Networking; +using NAK.RelativeSync.Patches; + +namespace NAK.RelativeSync; + +public class RelativeSyncMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + ModNetwork.Subscribe(); + ModSettings.Initialize(); + + // Experimental sync hack + ApplyPatches(typeof(CVRSpawnablePatches)); + + // Experimental no interpolation on Better Better Character Controller + ApplyPatches(typeof(BetterBetterCharacterControllerPatches)); + + // Send relative sync update after network root data update + ApplyPatches(typeof(NetworkRootDataUpdatePatches)); + + // Add components if missing (for relative sync monitor and controller) + ApplyPatches(typeof(PlayerSetupPatches)); + ApplyPatches(typeof(PuppetMasterPatches)); + + // Add components if missing (for relative sync markers) + ApplyPatches(typeof(CVRSeatPatches)); + ApplyPatches(typeof(CVRMovementParentPatches)); + } + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } +} \ No newline at end of file diff --git a/RelativeSync/ModSettings.cs b/RelativeSync/ModSettings.cs new file mode 100644 index 0000000..91a60a7 --- /dev/null +++ b/RelativeSync/ModSettings.cs @@ -0,0 +1,48 @@ +using MelonLoader; +using NAK.RelativeSync.Networking; + +namespace NAK.RelativeSync; + +internal static class ModSettings +{ + internal const string ModName = nameof(RelativeSync); + + #region Melon Preferences + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(ModName); + + private static readonly MelonPreferences_Entry DebugLogInbound = + Category.CreateEntry("DebugLogInbound", false, + "Debug Log Inbound", description: "Log inbound network messages."); + + private static readonly MelonPreferences_Entry DebugLogOutbound = + Category.CreateEntry("DebugLogOutbound", false, + "Debug Log Outbound", description: "Log outbound network messages."); + + private static readonly MelonPreferences_Entry ExpSyncedObjectHack = + Category.CreateEntry("ExpSyncedObjectHack", true, + "Exp Spawnable Sync Fix", description: "Forces CVRSpawnable to update position in FixedUpdate. May help reduce local jitter on synced movement parents."); + + private static readonly MelonPreferences_Entry ExpNoInterpolationOnBBCC = + Category.CreateEntry("ExpNoInterpolationOnBBCC", true, + "Exp Disable Interpolation on BBCC", description: "Disable interpolation on Better Better Character Controller. May help reduce local jitter on synced movement parents."); + + #endregion Melon Preferences + + internal static void Initialize() + { + foreach (MelonPreferences_Entry setting in Category.Entries) + setting.OnEntryValueChangedUntyped.Subscribe(OnSettingsChanged); + + OnSettingsChanged(); + } + + private static void OnSettingsChanged(object oldValue = null, object newValue = null) + { + ModNetwork.Debug_NetworkInbound = DebugLogInbound.Value; + ModNetwork.Debug_NetworkOutbound = DebugLogOutbound.Value; + Patches.CVRSpawnablePatches.UseHack = ExpSyncedObjectHack.Value; + Patches.BetterBetterCharacterControllerPatches.NoInterpolation = ExpNoInterpolationOnBBCC.Value; + } +} \ No newline at end of file diff --git a/RelativeSync/Networking/ModNetwork.cs b/RelativeSync/Networking/ModNetwork.cs new file mode 100644 index 0000000..713f27c --- /dev/null +++ b/RelativeSync/Networking/ModNetwork.cs @@ -0,0 +1,151 @@ +using ABI_RC.Core.Networking; +using ABI_RC.Systems.ModNetwork; +using DarkRift; +using UnityEngine; + +namespace NAK.RelativeSync.Networking; + +public static class ModNetwork +{ + public static bool Debug_NetworkInbound = false; + public static bool Debug_NetworkOutbound = false; + + private static bool _isSubscribedToModNetwork; + + private struct MovementParentSyncData + { + public bool HasSyncedThisData; + public int MarkerHash; + public Vector3 RootPosition; + public Vector3 RootRotation; + // public Vector3 HipPosition; + // public Vector3 HipRotation; + } + + private static MovementParentSyncData _latestMovementParentSyncData; + + #region Constants + + private const string ModId = "MelonMod.NAK.RelativeSync"; + + #endregion + + #region Enums + + private enum MessageType : byte + { + MovementParentOrChair = 0 + //RelativePickup = 1, + //RelativeAttachment = 2, + } + + #endregion + + #region Mod Network Internals + + internal static void Subscribe() + { + ModNetworkManager.Subscribe(ModId, OnMessageReceived); + + _isSubscribedToModNetwork = ModNetworkManager.IsSubscribed(ModId); + if (!_isSubscribedToModNetwork) + Debug.LogError("Failed to subscribe to Mod Network!"); + } + + // Called right after NetworkRootDataUpdate.Submit() + internal static void SendRelativeSyncUpdate() + { + if (!_isSubscribedToModNetwork) + return; + + if (_latestMovementParentSyncData.HasSyncedThisData) + return; + + SendMessage(MessageType.MovementParentOrChair, _latestMovementParentSyncData.MarkerHash, + _latestMovementParentSyncData.RootPosition, _latestMovementParentSyncData.RootRotation); + + _latestMovementParentSyncData.HasSyncedThisData = true; + } + + public static void SetLatestRelativeSync( + int markerHash, + Vector3 position, Vector3 rotation) + { + // check if the data has changed + if (_latestMovementParentSyncData.MarkerHash == markerHash + && _latestMovementParentSyncData.RootPosition == position + && _latestMovementParentSyncData.RootRotation == rotation) + return; // no need to update (shocking) + + _latestMovementParentSyncData.HasSyncedThisData = false; // reset + _latestMovementParentSyncData.MarkerHash = markerHash; + _latestMovementParentSyncData.RootPosition = position; + _latestMovementParentSyncData.RootRotation = rotation; + } + + private static void SendMessage(MessageType messageType, int markerHash, Vector3 position, Vector3 rotation) + { + if (!IsConnectedToGameNetwork()) + return; + + using ModNetworkMessage modMsg = new(ModId); + modMsg.Write((byte)messageType); + modMsg.Write(markerHash); + modMsg.Write(position); + modMsg.Write(rotation); + modMsg.Send(); + + if (Debug_NetworkOutbound) + Debug.Log( + $"[Outbound] MessageType: {messageType}, MarkerHash: {markerHash}, Position: {position}, " + + $"Rotation: {rotation}"); + } + + private static void OnMessageReceived(ModNetworkMessage msg) + { + msg.Read(out byte msgTypeRaw); + + if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) + return; + + switch ((MessageType)msgTypeRaw) + { + case MessageType.MovementParentOrChair: + msg.Read(out int markerHash); + msg.Read(out Vector3 receivedPosition); + msg.Read(out Vector3 receivedRotation); + // msg.Read(out Vector3 receivedHipPosition); + // msg.Read(out Vector3 receivedHipRotation); + + OnNetworkPositionUpdateReceived(msg.Sender, markerHash, receivedPosition, receivedRotation); + + if (Debug_NetworkInbound) + Debug.Log($"[Inbound] Sender: {msg.Sender}, MarkerHash: {markerHash}, " + + $"Position: {receivedPosition}, Rotation: {receivedRotation}"); + break; + default: + Debug.LogError($"Invalid message type received from: {msg.Sender}"); + break; + } + } + + #endregion + + #region Private Methods + + private static bool IsConnectedToGameNetwork() + { + return NetworkManager.Instance != null + && NetworkManager.Instance.GameNetwork != null + && NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected; + } + + private static void OnNetworkPositionUpdateReceived( + string sender, int markerHash, + Vector3 position, Vector3 rotation) + { + RelativeSyncManager.ApplyRelativeSync(sender, markerHash, position, rotation); + } + + #endregion +} \ No newline at end of file diff --git a/RelativeSync/Patches.cs b/RelativeSync/Patches.cs new file mode 100644 index 0000000..71d4ec8 --- /dev/null +++ b/RelativeSync/Patches.cs @@ -0,0 +1,113 @@ +using ABI_RC.Core.Base; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Networking.Jobs; +using ABI_RC.Core.Player; +using ABI_RC.Systems.Movement; +using ABI.CCK.Components; +using HarmonyLib; +using NAK.RelativeSync.Components; +using NAK.RelativeSync.Networking; +using UnityEngine; + +namespace NAK.RelativeSync.Patches; + +internal static class PlayerSetupPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))] + private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance) + { + __instance.AddComponentIfMissing(); + } + +} + +internal static class PuppetMasterPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(PuppetMaster), nameof(PuppetMaster.Start))] + private static void Postfix_PuppetMaster_Start(ref PuppetMaster __instance) + { + __instance.AddComponentIfMissing(); + } +} + +internal static class CVRSeatPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRSeat), nameof(CVRSeat.Awake))] + private static void Postfix_CVRSeat_Awake(ref CVRSeat __instance) + { + __instance.AddComponentIfMissing(); + } +} + +internal static class CVRMovementParentPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRMovementParent), nameof(CVRMovementParent.Start))] + private static void Postfix_CVRMovementParent_Start(ref CVRMovementParent __instance) + { + __instance.AddComponentIfMissing(); + } +} + +internal static class NetworkRootDataUpdatePatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(NetworkRootDataUpdate), nameof(NetworkRootDataUpdate.Submit))] + private static void Postfix_NetworkRootDataUpdater_Submit() + { + ModNetwork.SendRelativeSyncUpdate(); // Send the relative sync update after the network root data update + } +} + +internal static class CVRSpawnablePatches +{ + internal static bool UseHack; + + private static bool _canUpdate; + + [HarmonyPrefix] + [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.Update))] + private static bool Prefix_CVRSpawnable_Update() + => !UseHack || _canUpdate; + + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.FixedUpdate))] + private static void Postfix_CVRSpawnable_FixedUpdate(ref CVRSpawnable __instance) + { + if (!UseHack) return; + + _canUpdate = true; + __instance.Update(); + _canUpdate = false; + } +} + +internal static class BetterBetterCharacterControllerPatches +{ + private static bool _noInterpolation; + internal static bool NoInterpolation + { + get => _noInterpolation; + set + { + _noInterpolation = value; + if (_rigidbody == null) return; + _rigidbody.interpolation = value ? RigidbodyInterpolation.None : _initialInterpolation; + } + } + + private static Rigidbody _rigidbody; + private static RigidbodyInterpolation _initialInterpolation; + + [HarmonyPostfix] + [HarmonyPatch(typeof(BetterBetterCharacterController), nameof(BetterBetterCharacterController.Start))] + private static void Postfix_BetterBetterCharacterController_Update(ref BetterBetterCharacterController __instance) + { + _rigidbody = __instance.GetComponent(); + _initialInterpolation = _rigidbody.interpolation; + NoInterpolation = _noInterpolation; // get initial value as patch runs later than settings init + } +} \ No newline at end of file diff --git a/.Deprecated/MutualMute/Properties/AssemblyInfo.cs b/RelativeSync/Properties/AssemblyInfo.cs similarity index 73% rename from .Deprecated/MutualMute/Properties/AssemblyInfo.cs rename to RelativeSync/Properties/AssemblyInfo.cs index 6db6d18..f014b61 100644 --- a/.Deprecated/MutualMute/Properties/AssemblyInfo.cs +++ b/RelativeSync/Properties/AssemblyInfo.cs @@ -1,20 +1,20 @@ -using NAK.MutualMute.Properties; +using NAK.RelativeSync.Properties; using MelonLoader; using System.Reflection; [assembly: AssemblyVersion(AssemblyInfoParams.Version)] [assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] [assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.MutualMute))] +[assembly: AssemblyTitle(nameof(NAK.RelativeSync))] [assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.MutualMute))] +[assembly: AssemblyProduct(nameof(NAK.RelativeSync))] [assembly: MelonInfo( - typeof(NAK.MutualMute.MutualMuteMod), - nameof(NAK.MutualMute), + typeof(NAK.RelativeSync.RelativeSyncMod), + nameof(NAK.RelativeSync), AssemblyInfoParams.Version, AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/MutualMute" + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSync" )] [assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] @@ -24,9 +24,9 @@ using System.Reflection; [assembly: MelonAuthorColor(255, 158, 21, 32)] // red [assembly: HarmonyDontPatchAll] -namespace NAK.MutualMute.Properties; +namespace NAK.RelativeSync.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.4"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/RelativeSync/README.md b/RelativeSync/README.md new file mode 100644 index 0000000..4ab0455 --- /dev/null +++ b/RelativeSync/README.md @@ -0,0 +1,30 @@ +# RelativeSync + +Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. + +https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/ae6c6e4b-7529-42e2-bd2c-afa050849906 + +## Mod Settings +- **Debug Network Inbound**: Log network messages received from other players. +- **Debug Network Outbound**: Log network messages sent to other players. +- **Exp Spawnable Sync Hack**: Forces CVRSpawnable to update position in FixedUpdate. This can help with local jitter while on a remote synced movement parent. +- **Exp Disable Interpolation on BBCC**: Disables interpolation on BetterBetterCharacterController. This can help with local jitter while on any movement parent. + +## Known Issues +- Movement Parents on remote users will still locally jitter. + - PuppetMaster/NetIkController applies received position updates in LateUpdate, while character controller updates in FixedUpdate. +- Movement Parents using CVRObjectSync synced by remote users will still locally jitter. + - CVRObjectSync applies received position updates in LateUpdate, while character controller updates in FixedUpdate. +- Slight interpolation issue with humanoid avatar hips while standing on a Movement Parent. + - Requires further investigation. I believe it to be because hips are not synced properly, requiring me to relative sync the hips as well. + +--- + +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. diff --git a/RelativeSyncJitterFix/RelativeSyncJitterFix.csproj b/RelativeSync/RelativeSync.csproj similarity index 100% rename from RelativeSyncJitterFix/RelativeSyncJitterFix.csproj rename to RelativeSync/RelativeSync.csproj diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncController.cs b/RelativeSync/RelativeSync/Components/RelativeSyncController.cs new file mode 100644 index 0000000..2e11301 --- /dev/null +++ b/RelativeSync/RelativeSync/Components/RelativeSyncController.cs @@ -0,0 +1,185 @@ +using ABI_RC.Core.Player; +using UnityEngine; + +namespace NAK.RelativeSync.Components; + +[DefaultExecutionOrder(9000)] // make sure this runs after NetIKController, but before Totally Wholesome LineController (9999) +public class RelativeSyncController : MonoBehaviour +{ + private const float MaxMagnitude = 750000000000f; + + private float _updateInterval = 0.05f; + private float _lastUpdate; + + private string _userId; + private PuppetMaster puppetMaster { get; set; } + private RelativeSyncMarker _relativeSyncMarker; + + private RelativeSyncData _relativeSyncData; + private RelativeSyncData _lastSyncData; + + #region Unity Events + + private void Start() + { + puppetMaster = GetComponent(); + + _userId = puppetMaster._playerDescriptor.ownerId; + RelativeSyncManager.RelativeSyncControllers.Add(_userId, this); + } + + private void OnDestroy() + { + RelativeSyncManager.RelativeSyncControllers.Remove(_userId); + } + + private void LateUpdate() + { + // if (puppetMaster._isHidden) + // return; + + if (_relativeSyncMarker == null) + return; + + if (!_relativeSyncMarker.IsComponentActive) + return; + + Animator animator = puppetMaster._animator; + if (animator == null) + return; + + Transform avatarTransform = animator.transform; + Transform hipTrans = (animator.avatar != null && animator.isHuman) + ? animator.GetBoneTransform(HumanBodyBones.Hips) : null; + + Vector3 relativeHipPos = default; + Quaternion relativeHipRot = default; + if (hipTrans != null) + { + Vector3 worldRootPos = avatarTransform.position; + Quaternion worldRootRot = avatarTransform.rotation; + + Vector3 hipPos = hipTrans.position; + Quaternion hipRot = hipTrans.rotation; + + relativeHipPos = Quaternion.Inverse(worldRootRot) * (hipPos - worldRootPos); + relativeHipRot = Quaternion.Inverse(worldRootRot) * hipRot; + } + + // TODO: handle the case where hip is not synced but is found on remote client + + float lerp = Mathf.Min((Time.time - _lastUpdate) / _updateInterval, 1f); + + ApplyRelativeRotation(avatarTransform, hipTrans, lerp); + ApplyRelativePosition(hipTrans, lerp); + + // idk if needed (both player root & avatar root are set to same world position) -_-_-_- + avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + + // fix hip syncing because it is not relative to root, it is synced in world space -_- + if (hipTrans != null) + { + hipTrans.position = transform.position + transform.rotation * relativeHipPos; + hipTrans.rotation = transform.rotation * relativeHipRot; + } + } + + private void ApplyRelativeRotation(Transform avatarTransform, Transform hipTransform, float lerp) + { + if (!_relativeSyncMarker.ApplyRelativeRotation || + !(_relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude)) + return; // not applying relative rotation or data is invalid + + Quaternion markerRotation = _relativeSyncMarker.transform.rotation; + Quaternion lastWorldRotation = markerRotation * Quaternion.Euler(_lastSyncData.LocalRootRotation); + Quaternion worldRotation = markerRotation * Quaternion.Euler(_relativeSyncData.LocalRootRotation); + + if (_relativeSyncMarker.OnlyApplyRelativeHeading) + { + Vector3 currentWorldUp = avatarTransform.up; + + Vector3 currentForward = lastWorldRotation * Vector3.forward; + Vector3 targetForward = worldRotation * Vector3.forward; + + currentForward = Vector3.ProjectOnPlane(currentForward, currentWorldUp).normalized; + targetForward = Vector3.ProjectOnPlane(targetForward, currentWorldUp).normalized; + + lastWorldRotation = Quaternion.LookRotation(currentForward, currentWorldUp); + worldRotation = Quaternion.LookRotation(targetForward, currentWorldUp); + } + + transform.rotation = Quaternion.Slerp(lastWorldRotation, worldRotation, lerp); + } + + private void ApplyRelativePosition(Transform hipTransform, float lerp) + { + if (!_relativeSyncMarker.ApplyRelativePosition || + !(_relativeSyncData.LocalRootPosition.sqrMagnitude < MaxMagnitude)) + return; // not applying relative position or data is invalid + + Transform targetTransform = _relativeSyncMarker.transform; + + Vector3 lastWorldPosition = targetTransform.TransformPoint(_lastSyncData.LocalRootPosition); + Vector3 worldPosition = targetTransform.TransformPoint(_relativeSyncData.LocalRootPosition); + transform.position = Vector3.Lerp(lastWorldPosition, worldPosition, lerp); + + // if (hipTransform == null) + // return; + // + // Vector3 lastWorldHipPosition = targetTransform.TransformPoint(_lastSyncData.LocalHipPosition); + // Vector3 worldHipPosition = targetTransform.TransformPoint(_relativeSyncData.LocalHipPosition); + // hipTransform.position = Vector3.Lerp(lastWorldHipPosition, worldHipPosition, lerp); + } + + #endregion Unity Events + + #region Public Methods + + public void SetRelativeSyncMarker(RelativeSyncMarker target) + { + if (_relativeSyncMarker == target) + return; + + _relativeSyncMarker = target; + + // calculate relative position and rotation so lerp can smooth it out (hack) + if (_relativeSyncMarker == null) + return; + + Animator avatarAnimator = puppetMaster._animator; + if (avatarAnimator == null) + return; // i dont care to bother + + RelativeSyncManager.GetRelativeAvatarPositionsFromMarker( + avatarAnimator, _relativeSyncMarker.transform, + out Vector3 relativePosition, out Vector3 relativeRotation); + + // set last sync data to current position and rotation so we don't lerp from the last marker + _lastSyncData.LocalRootPosition = relativePosition; + _lastSyncData.LocalRootRotation = relativeRotation; + _lastUpdate = Time.time; // reset update time + } + + public void SetRelativePositions(Vector3 position, Vector3 rotation) + { + // calculate update interval + float prevUpdate = _lastUpdate; + _lastUpdate = Time.time; + _updateInterval = _lastUpdate - prevUpdate; + + // cycle last sync data + _lastSyncData = _relativeSyncData; + + // set new sync data + _relativeSyncData.LocalRootPosition = position; + _relativeSyncData.LocalRootRotation = rotation; + } + + #endregion Public Methods + + private struct RelativeSyncData + { + public Vector3 LocalRootPosition; + public Vector3 LocalRootRotation; + } +} \ No newline at end of file diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs new file mode 100644 index 0000000..0669d81 --- /dev/null +++ b/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs @@ -0,0 +1,100 @@ +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI.CCK.Components; +using UnityEngine; + +namespace NAK.RelativeSync.Components; + +public class RelativeSyncMarker : MonoBehaviour +{ + public int pathHash { get; private set; } + + public bool IsComponentActive + => _component.isActiveAndEnabled; + + public bool ApplyRelativePosition = true; + public bool ApplyRelativeRotation = true; + public bool OnlyApplyRelativeHeading; + + private MonoBehaviour _component; + + private void Start() + { + string path = GetGameObjectPath(transform); + int hash = path.GetHashCode(); + + // check if it already exists (this **should** only matter in worlds) + if (RelativeSyncManager.RelativeSyncTransforms.ContainsKey(hash)) + { + RelativeSyncMod.Logger.Warning($"Duplicate RelativeSyncMarker found at path {path}"); + if (!FindAvailableHash(ref hash)) // super lazy fix idfc + { + RelativeSyncMod.Logger.Error($"Failed to find available hash for RelativeSyncMarker after 16 tries! {path}"); + return; + } + } + + pathHash = hash; + RelativeSyncManager.RelativeSyncTransforms.Add(hash, this); + + ConfigureForPotentialMovementParent(); + } + + private void OnDestroy() + { + RelativeSyncManager.RelativeSyncTransforms.Remove(pathHash); + } + + private void ConfigureForPotentialMovementParent() + { + if (!gameObject.TryGetComponent(out CVRMovementParent movementParent)) + { + _component = GetComponent(); // users cant animate enabled state so i dont think matters + return; + } + _component = movementParent; + + // TODO: a refactor may be needed to handle the orientation mode being animated + + // respect orientation mode & gravity zone + ApplyRelativeRotation = movementParent.orientationMode == CVRMovementParent.OrientationMode.RotateWithParent; + OnlyApplyRelativeHeading = movementParent.GetComponent() == null; + } + + private static string GetGameObjectPath(Transform transform) + { + // props already have a unique instance identifier at root + // worlds uhhhh, dont duplicate the same thing over and over thx + // avatars on remote/local client have diff path, we need to account for it -_- + + string path = transform.name; + while (transform.parent != null) + { + transform = transform.parent; + + // only true at root of local player object + if (transform.CompareTag("Player")) + { + path = MetaPort.Instance.ownerId + "/" + path; + break; + } // remote player object root is already player guid + + path = transform.name + "/" + path; + } + + return path; + } + + private bool FindAvailableHash(ref int hash) + { + for (int i = 0; i < 16; i++) + { + hash += 1; + if (!RelativeSyncManager.RelativeSyncTransforms.ContainsKey(hash)) return true; + } + + // failed to find a hash in 16 tries, dont care + return false; + } +} \ No newline at end of file diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs new file mode 100644 index 0000000..134234c --- /dev/null +++ b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs @@ -0,0 +1,79 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.Movement; +using NAK.RelativeSync.Networking; +using UnityEngine; + +namespace NAK.RelativeSync.Components; + +[DefaultExecutionOrder(int.MaxValue)] +public class RelativeSyncMonitor : MonoBehaviour +{ + private BetterBetterCharacterController _characterController { get; set; } + + private RelativeSyncMarker _relativeSyncMarker; + private RelativeSyncMarker _lastRelativeSyncMarker; + + private void Start() + { + _characterController = GetComponent(); + } + + private void LateUpdate() + { + if (_characterController == null) + return; + + CheckForRelativeSyncMarker(); + + if (_relativeSyncMarker == null) + { + if (_lastRelativeSyncMarker == null) + return; + + // send empty position and rotation to stop syncing + SendEmptyPositionAndRotation(); + _lastRelativeSyncMarker = null; + return; + } + + _lastRelativeSyncMarker = _relativeSyncMarker; + + Animator avatarAnimator = PlayerSetup.Instance._animator; + if (avatarAnimator == null) + return; // i dont care to bother + + RelativeSyncManager.GetRelativeAvatarPositionsFromMarker( + avatarAnimator, _relativeSyncMarker.transform, + out Vector3 relativePosition, out Vector3 relativeRotation); + + ModNetwork.SetLatestRelativeSync( + _relativeSyncMarker.pathHash, + relativePosition, relativeRotation); + } + + private void CheckForRelativeSyncMarker() + { + if (_characterController._isSitting && _characterController._lastCvrSeat) + { + RelativeSyncMarker newMarker = _characterController._lastCvrSeat.GetComponent(); + _relativeSyncMarker = newMarker; + return; + } + + if (_characterController._previousMovementParent != null) + { + RelativeSyncMarker newMarker = _characterController._previousMovementParent.GetComponent(); + _relativeSyncMarker = newMarker; + return; + } + + // none found + _relativeSyncMarker = null; + } + + private void SendEmptyPositionAndRotation() + { + ModNetwork.SetLatestRelativeSync(RelativeSyncManager.NoTarget, + Vector3.zero, Vector3.zero); + } +} \ No newline at end of file diff --git a/RelativeSync/RelativeSync/RelativeSyncManager.cs b/RelativeSync/RelativeSync/RelativeSyncManager.cs new file mode 100644 index 0000000..9a532f4 --- /dev/null +++ b/RelativeSync/RelativeSync/RelativeSyncManager.cs @@ -0,0 +1,70 @@ +using ABI_RC.Core.Base; +using ABI_RC.Core.Player; +using NAK.RelativeSync.Components; +using UnityEngine; + +namespace NAK.RelativeSync; + +public static class RelativeSyncManager +{ + public const int NoTarget = -1; + + public static readonly Dictionary RelativeSyncTransforms = new(); + public static readonly Dictionary RelativeSyncControllers = new(); + + public static void ApplyRelativeSync(string userId, int target, Vector3 position, Vector3 rotation) + { + if (!RelativeSyncControllers.TryGetValue(userId, out RelativeSyncController controller)) + { + if (CVRPlayerManager.Instance.GetPlayerPuppetMaster(userId, out PuppetMaster pm)) + { + controller = pm.AddComponentIfMissing(); + RelativeSyncMod.Logger.Msg($"Found PuppetMaster for user {userId}. This user is now eligible for relative sync."); + } + else + { + RelativeSyncControllers.Add(userId, null); // add null controller to prevent future lookups + RelativeSyncMod.Logger.Warning($"Failed to find PuppetMaster for user {userId}. This is likely because the user is blocked or has blocked you. This user will not be eligible for relative sync until next game restart."); + } + } + + if (controller == null) + return; + + // find target transform + RelativeSyncMarker syncMarker = null; + if (target != NoTarget) RelativeSyncTransforms.TryGetValue(target, out syncMarker); + + controller.SetRelativeSyncMarker(syncMarker); + controller.SetRelativePositions(position, rotation); + } + + public static void GetRelativeAvatarPositionsFromMarker( + Animator avatarAnimator, Transform markerTransform, + out Vector3 relativePosition, out Vector3 relativeRotation) + // out Vector3 relativeHipPosition, out Vector3 relativeHipRotation) + { + Transform avatarTransform = avatarAnimator.transform; + + // because our syncing is retarded, we need to sync relative from the avatar root... + Vector3 avatarRootPosition = avatarTransform.position; // PlayerSetup.Instance.GetPlayerPosition() + Quaternion avatarRootRotation = avatarTransform.rotation; // PlayerSetup.Instance.GetPlayerRotation() + + relativePosition = markerTransform.InverseTransformPoint(avatarRootPosition); + relativeRotation = (Quaternion.Inverse(markerTransform.rotation) * avatarRootRotation).eulerAngles; + + // Transform hipTrans = (avatarAnimator.avatar != null && avatarAnimator.isHuman) + // ? avatarAnimator.GetBoneTransform(HumanBodyBones.Hips) : null; + // + // if (hipTrans == null) + // { + // relativeHipPosition = Vector3.zero; + // relativeHipRotation = Vector3.zero; + // } + // else + // { + // relativeHipPosition = markerTransform.InverseTransformPoint(hipTrans.position); + // relativeHipRotation = (Quaternion.Inverse(markerTransform.rotation) * hipTrans.rotation).eulerAngles; + // } + } +} \ No newline at end of file diff --git a/RelativeSync/format.json b/RelativeSync/format.json new file mode 100644 index 0000000..7b5c456 --- /dev/null +++ b/RelativeSync/format.json @@ -0,0 +1,23 @@ +{ + "_id": 211, + "name": "RelativeSync", + "modversion": "1.0.4", + "gameversion": "2024r175", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network.\n\nProvides some Experimental settings to also fix local jitter on movement parents.", + "searchtags": [ + "relative", + "sync", + "movement", + "chair" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r34/RelativeSync.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSync/", + "changelog": "- Fixed log spam when receiving relative sync data from a blocked user (thanks Mod Network for still forwarding that data -_-)\n- Adjusted execution order to apply relative sync before Totally Wholesomes LineController\n- Adjusted Relative Sync to still apply to avatar distance-hidden users\n- Adjusted Relative Sync to not apply if the CVRSeat or Movement Parent is disabled on receiving client", + "embedcolor": "#f61963" +} \ No newline at end of file diff --git a/RelativeSyncJitterFix/Patches.cs b/RelativeSyncJitterFix/Patches.cs deleted file mode 100644 index 71a67f7..0000000 --- a/RelativeSyncJitterFix/Patches.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ABI.CCK.Components; -using HarmonyLib; - -namespace NAK.RelativeSyncJitterFix.Patches; - -internal static class CVRSpawnablePatches -{ - private static bool _canUpdate; - - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.FixedUpdate))] - private static void Postfix_CVRSpawnable_FixedUpdate(ref CVRSpawnable __instance) - { - _canUpdate = true; - __instance.Update(); - _canUpdate = false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.Update))] - private static bool Prefix_CVRSpawnable_Update() => _canUpdate; -} \ No newline at end of file diff --git a/RelativeSyncJitterFix/Properties/AssemblyInfo.cs b/RelativeSyncJitterFix/Properties/AssemblyInfo.cs deleted file mode 100644 index ff4fcd8..0000000 --- a/RelativeSyncJitterFix/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.RelativeSyncJitterFix.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.RelativeSyncJitterFix))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.RelativeSyncJitterFix))] - -[assembly: MelonInfo( - typeof(NAK.RelativeSyncJitterFix.RelativeSyncJitterFixMod), - nameof(NAK.RelativeSyncJitterFix), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSyncJitterFix" -)] - -[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.RelativeSyncJitterFix.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/RelativeSyncJitterFix/README.md b/RelativeSyncJitterFix/README.md deleted file mode 100644 index 7978ed2..0000000 --- a/RelativeSyncJitterFix/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# RelativeSyncJitterFix - -Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. -Changes when props apply their incoming sync data to be before the character controller simulation. - -## Known Issues -- Movement Parents on remote users will still locally jitter. - - PuppetMaster/NetIkController applies received position updates in LateUpdate, while character controller updates in FixedUpdate. -- Movement Parents using CVRObjectSync synced by remote users will still locally jitter. - - CVRObjectSync applies received position updates in LateUpdate, while character controller updates in FixedUpdate. - ---- - -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/#7-modding-our-games - -> 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. diff --git a/RelativeSyncJitterFix/format.json b/RelativeSyncJitterFix/format.json deleted file mode 100644 index b128e2a..0000000 --- a/RelativeSyncJitterFix/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": -1, - "name": "RelativeSyncJitterFix", - "modversion": "1.0.0", - "gameversion": "2025r180", - "loaderversion": "0.7.2", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync.\nChanges when props apply their incoming sync data to be before the character controller simulation.", - "searchtags": [ - "relative", - "sync", - "movement", - "chair" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RelativeSyncJitterFix.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSyncJitterFix/", - "changelog": "- Removed RelativeSync except for a single harmony patch", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/.Experimental/ScriptingSpoofer/Main.cs b/ScriptingSpoofer/Main.cs similarity index 85% rename from .Experimental/ScriptingSpoofer/Main.cs rename to ScriptingSpoofer/Main.cs index e0d34d8..9de6130 100644 --- a/.Experimental/ScriptingSpoofer/Main.cs +++ b/ScriptingSpoofer/Main.cs @@ -83,8 +83,9 @@ public class ScriptingSpoofer : MelonMod private static class PlayerApiPatches { [HarmonyPrefix] - [HarmonyPatch(typeof(Player), nameof(Player.Username), MethodType.Getter)] - private static bool GetSpoofedUsername(ref Player __instance, ref string __result) + [HarmonyPatch(typeof(LocalPlayerAPI), nameof(LocalPlayerAPI.Username), MethodType.Getter)] + [HarmonyPatch(typeof(PlayerAPIBase), nameof(PlayerAPIBase.Username), MethodType.Getter)] + private static bool GetSpoofedUsername(ref PlayerAPIBase __instance, ref string __result) { if (__instance.IsRemote) return true; if (!EntryEnabled.Value) return true; @@ -94,8 +95,9 @@ public class ScriptingSpoofer : MelonMod } [HarmonyPrefix] - [HarmonyPatch(typeof(Player), nameof(Player.UserID), MethodType.Getter)] - private static bool GetSpoofedUserId(ref Player __instance, ref string __result) + [HarmonyPatch(typeof(LocalPlayerAPI), nameof(LocalPlayerAPI.UserID), MethodType.Getter)] + [HarmonyPatch(typeof(PlayerAPIBase), nameof(PlayerAPIBase.UserID), MethodType.Getter)] + private static bool GetSpoofedUserId(ref PlayerAPIBase __instance, ref string __result) { if (__instance.IsRemote) return true; if (!EntryEnabled.Value) return true; diff --git a/.Experimental/ScriptingSpoofer/Properties/AssemblyInfo.cs b/ScriptingSpoofer/Properties/AssemblyInfo.cs similarity index 88% rename from .Experimental/ScriptingSpoofer/Properties/AssemblyInfo.cs rename to ScriptingSpoofer/Properties/AssemblyInfo.cs index f6ce835..94c7f77 100644 --- a/.Experimental/ScriptingSpoofer/Properties/AssemblyInfo.cs +++ b/ScriptingSpoofer/Properties/AssemblyInfo.cs @@ -20,13 +20,11 @@ using System.Reflection; [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.ScriptingSpoofer.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/.Experimental/ScriptingSpoofer/README.md b/ScriptingSpoofer/README.md similarity index 100% rename from .Experimental/ScriptingSpoofer/README.md rename to ScriptingSpoofer/README.md diff --git a/.Experimental/ScriptingSpoofer/ScriptingSpoofer.csproj b/ScriptingSpoofer/ScriptingSpoofer.csproj similarity index 100% rename from .Experimental/ScriptingSpoofer/ScriptingSpoofer.csproj rename to ScriptingSpoofer/ScriptingSpoofer.csproj diff --git a/.Experimental/ScriptingSpoofer/format.json b/ScriptingSpoofer/format.json similarity index 87% rename from .Experimental/ScriptingSpoofer/format.json rename to ScriptingSpoofer/format.json index 581884e..09662f0 100644 --- a/.Experimental/ScriptingSpoofer/format.json +++ b/ScriptingSpoofer/format.json @@ -1,7 +1,7 @@ { "_id": -1, "name": "ScriptingSpoofer", - "modversion": "1.0.1", + "modversion": "1.0.0", "gameversion": "2024r176", "loaderversion": "0.6.1", "modtype": "Mod", @@ -18,6 +18,6 @@ ], "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r25/ScriptingSpoofer.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ScriptingSpoofer/", - "changelog": "- Initial release", - "embedcolor": "#f61963" + "changelog": "- Initial Release", + "embedcolor": "#00FFFF" } \ No newline at end of file diff --git a/ScrollFlight/Main.cs b/ScrollFlight/Main.cs index 4cce7f0..e446609 100644 --- a/ScrollFlight/Main.cs +++ b/ScrollFlight/Main.cs @@ -1,7 +1,10 @@ using System.Globalization; +using System.Reflection; using ABI_RC.Core.UI; +using ABI_RC.Systems.IK.VRIKHandlers; using ABI_RC.Systems.Movement; using ABI.CCK.Components; +using HarmonyLib; using MelonLoader; using UnityEngine; @@ -20,10 +23,6 @@ public class ScrollFlightMod : MelonMod Category.CreateEntry("use_scroll_flight", true, "Use Scroll Flight", description: "Toggle Scroll Flight."); - private static readonly MelonPreferences_Entry EntryResetOnExitFlight = - Category.CreateEntry("reset_on_exit_flight", false, - "Reset On Exit Flight", description: "Reset Scroll Flight speed on exit flight."); - #endregion Melon Preferences #region Private Fields @@ -39,8 +38,6 @@ public class ScrollFlightMod : MelonMod { CVRWorld.GameRulesUpdated += OnApplyMovementSettings; // thank you kafe for using actions } - - private bool wasFlying; // stole from LucMod :3 public override void OnUpdate() @@ -48,22 +45,9 @@ public class ScrollFlightMod : MelonMod if (!EntryUseScrollFlight.Value) return; - if (BetterBetterCharacterController.Instance == null) - return; - - bool isFlying = BetterBetterCharacterController.Instance.IsFlying(); - - if (EntryResetOnExitFlight.Value - && (wasFlying && !isFlying)) - { - BetterBetterCharacterController.Instance.worldFlightSpeedMultiplier = _originalWorldFlightSpeedMultiplier; - _currentWorldFlightSpeedMultiplier = _originalWorldFlightSpeedMultiplier; - } - - wasFlying = isFlying; - - if (!isFlying - || Input.GetKey(KeyCode.Mouse2) // scroll zoom (TODO: Use CVRInputManager.zoom, but requires fixing zoom toggle mode on client) + if (BetterBetterCharacterController.Instance == null + || !BetterBetterCharacterController.Instance.IsFlying() + || Input.GetKey(KeyCode.Mouse2) // scroll zoom || Input.GetKey(KeyCode.LeftControl) // third person / better interact desktop || Cursor.lockState != CursorLockMode.Locked) // unity explorer / in menu return; @@ -74,7 +58,7 @@ public class ScrollFlightMod : MelonMod private static void AdjustFlightModifier(float adjustValue) { - _currentWorldFlightSpeedMultiplier = Mathf.Max(0f, _currentWorldFlightSpeedMultiplier + adjustValue); + _currentWorldFlightSpeedMultiplier = Math.Max(0f, _currentWorldFlightSpeedMultiplier + adjustValue); if (_currentWorldFlightSpeedMultiplier <= 0f) { diff --git a/ScrollFlight/Properties/AssemblyInfo.cs b/ScrollFlight/Properties/AssemblyInfo.cs index 22630c9..c659f5b 100644 --- a/ScrollFlight/Properties/AssemblyInfo.cs +++ b/ScrollFlight/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ScrollFlight" )] -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[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 @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.ScrollFlight.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.4"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/ScrollFlight/format.json b/ScrollFlight/format.json index 486ad34..3981ee1 100644 --- a/ScrollFlight/format.json +++ b/ScrollFlight/format.json @@ -1,9 +1,9 @@ { - "_id": 219, + "_id": -1, "name": "ScrollFlight", - "modversion": "1.0.4", - "gameversion": "2025r180", - "loaderversion": "0.7.2", + "modversion": "1.0.0", + "gameversion": "2024r175", + "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", "description": "Scroll-wheel to adjust flight speed in Desktop. Stole idea from Luc.", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ScrollFlight.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r34/ScrollFlight.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ScrollFlight/", - "changelog": "- Fixes for 2025r180", + "changelog": "- Initial release", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/.Deprecated/ShadowCloneFallback/Main.cs b/ShadowCloneFallback/Main.cs similarity index 100% rename from .Deprecated/ShadowCloneFallback/Main.cs rename to ShadowCloneFallback/Main.cs diff --git a/.Deprecated/ShadowCloneFallback/Properties/AssemblyInfo.cs b/ShadowCloneFallback/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/ShadowCloneFallback/Properties/AssemblyInfo.cs rename to ShadowCloneFallback/Properties/AssemblyInfo.cs diff --git a/.Deprecated/ShadowCloneFallback/README.md b/ShadowCloneFallback/README.md similarity index 100% rename from .Deprecated/ShadowCloneFallback/README.md rename to ShadowCloneFallback/README.md diff --git a/.Deprecated/SwitchToDesktopOnSteamVRExit/SwitchToDesktopOnSteamVRExit.csproj b/ShadowCloneFallback/ShadowCloneFallback.csproj similarity index 100% rename from .Deprecated/SwitchToDesktopOnSteamVRExit/SwitchToDesktopOnSteamVRExit.csproj rename to ShadowCloneFallback/ShadowCloneFallback.csproj diff --git a/.Deprecated/ShadowCloneFallback/format.json b/ShadowCloneFallback/format.json similarity index 100% rename from .Deprecated/ShadowCloneFallback/format.json rename to ShadowCloneFallback/format.json diff --git a/ShareBubbles/Main.cs b/ShareBubbles/Main.cs deleted file mode 100644 index 802a6be..0000000 --- a/ShareBubbles/Main.cs +++ /dev/null @@ -1,94 +0,0 @@ -using MelonLoader; -using NAK.ShareBubbles.Networking; -using UnityEngine; - -namespace NAK.ShareBubbles; - -public class ShareBubblesMod : MelonMod -{ - internal static MelonLogger.Instance Logger; - - #region Melon Mod Overrides - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - - ShareBubbleManager.Initialize(); - TempShareManager.Initialize(); - - ModNetwork.Initialize(); - ModNetwork.Subscribe(); - - //ModSettings.Initialize(); - - ApplyPatches(typeof(Patches.PlayerSetup_Patches)); - ApplyPatches(typeof(Patches.ControllerRay_Patches)); - ApplyPatches(typeof(Patches.ViewManager_Patches)); - - LoadAssetBundle(); - } - - public override void OnApplicationQuit() - { - ModNetwork.Unsubscribe(); - } - - #endregion Melon Mod Overrides - - #region Melon Mod Utilities - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #endregion Melon Mod Utilities - - #region Asset Bundle Loading - - private const string SharingBubbleAssets = "ShareBubbles.Resources.sharingbubble.assets"; - private const string SharingBubblePrefabPath = "Assets/Mods/SharingBubble/SharingBubble.prefab"; - - internal static GameObject SharingBubblePrefab; - - private void LoadAssetBundle() - { - LoggerInstance.Msg($"Loading required asset bundle..."); - using Stream resourceStream = MelonAssembly.Assembly.GetManifestResourceStream(SharingBubbleAssets); - using MemoryStream memoryStream = new(); - if (resourceStream == null) { - LoggerInstance.Error($"Failed to load {SharingBubbleAssets}!"); - return; - } - - resourceStream.CopyTo(memoryStream); - AssetBundle assetBundle = AssetBundle.LoadFromMemory(memoryStream.ToArray()); - if (assetBundle == null) { - LoggerInstance.Error($"Failed to load {SharingBubbleAssets}! Asset bundle is null!"); - return; - } - - SharingBubblePrefab = assetBundle.LoadAsset(SharingBubblePrefabPath); - if (SharingBubblePrefab == null) { - LoggerInstance.Error($"Failed to load {SharingBubblePrefab}! Prefab is null!"); - return; - } - SharingBubblePrefab.hideFlags |= HideFlags.DontUnloadUnusedAsset; - LoggerInstance.Msg($"Loaded {SharingBubblePrefab}!"); - - // load - - LoggerInstance.Msg("Asset bundle successfully loaded!"); - } - - #endregion Asset Bundle Loading -} \ No newline at end of file diff --git a/ShareBubbles/ModSettings.cs b/ShareBubbles/ModSettings.cs deleted file mode 100644 index 22f708c..0000000 --- a/ShareBubbles/ModSettings.cs +++ /dev/null @@ -1,51 +0,0 @@ -using MelonLoader; - -namespace NAK.ShareBubbles; - -// TODO: -// Setting for ShareBubbles scaling with player size -// Setting for receiving notification when a direct share is received -// Setting for ShareBubble being interactable outside of the UI buttons (Grab & Click) -// Store last Visibility, Lifetime, and Access Control settings for ShareBubble placement in hidden melon preferences - -public static class ModSettings -{ - #region Constants & Category - - internal const string ModName = nameof(ShareBubbles); // TODO idea: BTKUI player page button to remove player's ShareBubbles ? - - //internal const string SM_SettingsCategory = "Share Bubbles Mod"; - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(ModName); - - #endregion Constants & Category - - #region Debug Settings - - internal static readonly MelonPreferences_Entry Debug_NetworkInbound = - Category.CreateEntry("debug_inbound", false, display_name: "Debug Inbound", description: "Log inbound Mod Network updates."); - - internal static readonly MelonPreferences_Entry Debug_NetworkOutbound = - Category.CreateEntry("debug_outbound", false, display_name: "Debug Outbound", description: "Log outbound Mod Network updates."); - - #endregion Debug Settings - - #region Initialization - - internal static void Initialize() - { - - } - - #endregion Initialization - - #region Setting Changed Callbacks - - // private static void OnPlayerUpAlignmentThresholdChanged(float oldValue, float newValue) - // { - // Entry_PlayerUpAlignmentThreshold.Value = Mathf.Clamp(newValue, 0f, 180f); - // } - - #endregion Setting Changed Callbacks -} \ No newline at end of file diff --git a/ShareBubbles/Patches.cs b/ShareBubbles/Patches.cs deleted file mode 100644 index eaf2440..0000000 --- a/ShareBubbles/Patches.cs +++ /dev/null @@ -1,148 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using HarmonyLib; - -namespace NAK.ShareBubbles.Patches; - -internal static class PlayerSetup_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetSafeScale))] - public static void Postfix_PlayerSetup_SetSafeScale() - { - // I wish there was a callback to listen for player scale changes - ShareBubbleManager.Instance.OnPlayerScaleChanged(); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.GetCurrentPropSelectionMode))] - private static void Postfix_PlayerSetup_GetCurrentPropSelectionMode(ref PlayerSetup.PropSelectionMode __result) - { - // Stickers mod uses invalid enum value 4, so we use 5 - // https://github.com/NotAKidoS/NAK_CVR_Mods/blob/d0c8298074c4dcfc089ccb34ed8b8bd7e0b9cedf/Stickers/Patches.cs#L17 - if (ShareBubbleManager.Instance.IsPlacingBubbleMode) __result = (PlayerSetup.PropSelectionMode)5; - } -} - -internal static class ControllerRay_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.HandleSpawnableClicked))] - public static void Postfix_ControllerRay_DeleteSpawnable(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 - - ShareBubble shareBubble = __instance.hitTransform.GetComponentInParent(); - if (shareBubble == null) return; - - ShareBubbleManager.Instance.DestroyBubble(shareBubble); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.HandlePropSpawn))] - private static void Prefix_ControllerRay_HandlePropSpawn(ref ControllerRay __instance) - { - if (!ShareBubbleManager.Instance.IsPlacingBubbleMode) - return; - - if (__instance._gripDown) ShareBubbleManager.Instance.IsPlacingBubbleMode = false; - if (__instance._hitUIInternal || !__instance._interactDown) - return; - - ShareBubbleManager.Instance.PlaceSelectedBubbleFromControllerRay(__instance.rayDirectionTransform); - } -} - -internal static class ViewManager_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(ViewManager), nameof(ViewManager.RegisterShareEvents))] - public static void Postfix_ViewManager_RegisterShareEvents(ViewManager __instance) - { - __instance.cohtmlView.View.BindCall("NAKCallShareContent", OnShareContent); - __instance.cohtmlView.Listener.FinishLoad += (_) => { - __instance.cohtmlView.View._view.ExecuteScript( -""" -(function waitForContentShare(){ - if (typeof ContentShare !== 'undefined') { - ContentShare.HasShareBubbles = true; - console.log('ShareBubbles patch applied'); - } else { - setTimeout(waitForContentShare, 50); - } -})(); -"""); - }; - - return; - void OnShareContent( - string action, - string bubbleImpl, - string bubbleContent, - string shareRule, - string shareLifetime, - string shareAccess, - string contentImage, - string contentName) - { - // Action: drop, select - // BubbleImpl: Avatar, Prop, World, User - // BubbleContent: AvatarId, PropId, WorldId, UserId - // ShareRule: Public, FriendsOnly - // ShareLifetime: TwoMinutes, Session - // ShareAccess: PermanentAccess, SessionAccess, NoAccess - - ShareRule rule = shareRule switch - { - "Everyone" => ShareRule.Everyone, - "FriendsOnly" => ShareRule.FriendsOnly, - _ => ShareRule.Everyone - }; - - ShareLifetime lifetime = shareLifetime switch - { - "Session" => ShareLifetime.Session, - "TwoMinutes" => ShareLifetime.TwoMinutes, - _ => ShareLifetime.TwoMinutes - }; - - ShareAccess access = shareAccess switch - { - "Permanent" => ShareAccess.Permanent, - "Session" => ShareAccess.Session, - "None" => ShareAccess.None, - _ => ShareAccess.None - }; - - uint implTypeHash = ShareBubbleManager.GetMaskedHash(bubbleImpl); - ShareBubbleData bubbleData = new() - { - BubbleId = ShareBubbleManager.GenerateBubbleId(bubbleContent, implTypeHash), - ImplTypeHash = implTypeHash, - ContentId = bubbleContent, - Rule = rule, - Lifetime = lifetime, - Access = access, - CreatedAt = DateTime.UtcNow - }; - - switch (action) - { - case "drop": - ShareBubbleManager.Instance.DropBubbleInFront(bubbleData); - break; - case "select": - ShareBubbleManager.Instance.SelectBubbleForPlace(contentImage, contentName, bubbleData); - break; - } - - // Close menu - ViewManager.Instance.UiStateToggle(false); - } - } -} \ No newline at end of file diff --git a/ShareBubbles/Properties/AssemblyInfo.cs b/ShareBubbles/Properties/AssemblyInfo.cs deleted file mode 100644 index 13d5a60..0000000 --- a/ShareBubbles/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using System.Reflection; -using NAK.ShareBubbles.Properties; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.ShareBubbles))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.ShareBubbles))] - -[assembly: MelonInfo( - typeof(NAK.ShareBubbles.ShareBubblesMod), - nameof(NAK.ShareBubbles), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles" -)] - -[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.ShareBubbles.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.1.6"; - public const string Author = "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler"; -} \ No newline at end of file diff --git a/ShareBubbles/README.md b/ShareBubbles/README.md deleted file mode 100644 index 650f790..0000000 --- a/ShareBubbles/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Share Bubbles - -Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. - -### NOTICE - 1.0.2 -**Some features will not work as intended until ChilloutVRs API is updated.** -- Bubbles will display "Private" label regardless of publication state. -- Bubbles of private content you do not own will display "Claim" button regardless of if the content is already shared with you. -- Unshare button will be accessible when viewing Public content not owned by you. - -### How to use -Open a Content Details page and click the Share button. You can choose between placing a Share Bubble or Sharing directly with users in the same instance. When placing a Share Bubble, you will be prompted to configure the bubble. Once you're ready, you can then Drop or Select the bubble for placement. - -While viewing content details of an item shared *to you* by another player, you can also click **Unshare** to revoke access to the content. - -#### Visibility -- **Everyone** - All players with the mod installed can see the bubble. -- **Friends Only** - Only friends with the mod installed can see the bubble. - -#### Lifetime -- **2 Minutes** - The bubble will disappear after 2 minutes. -- **Session** - The bubble will only disappear once you delete it with Delete mode or leave the instance. - -#### Access Control -- **Keep Access** - Users who claim the bubble get to keep the shared content. -- **Session Access** - Users who claim the bubble only get to keep the shared content for however long they are in the instance with you. -- **No Access** - Users will not be able to claim the contents of the bubble and will only be able to view it. - -Access Control is only available for **Private Content** and serves as a way to share content in-game. - -**Note:** Session Access requires the game to be running to revoke access once you or the claimant leaves the instance. -If the game is closed unexpectedly, the claimant will keep the content until you next launch the game and connect to an online instance. - -## Credits -- Noachi - the bubble -- RaidShadowLily - the particles -- Tejler - the bell sound -- Luc - the fixing pedestal api endpoint -- Exterrata - the loading hexagon model from the hit mod PropLoadingHexagon - ---- - -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. diff --git a/ShareBubbles/Resources/sharingbubble.assets b/ShareBubbles/Resources/sharingbubble.assets deleted file mode 100644 index 339e629..0000000 Binary files a/ShareBubbles/Resources/sharingbubble.assets and /dev/null differ diff --git a/ShareBubbles/ShareBubbles.csproj b/ShareBubbles/ShareBubbles.csproj deleted file mode 100644 index 734c1ec..0000000 --- a/ShareBubbles/ShareBubbles.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - net48 - - - TRACE;TRACE;UNITY_2017_1_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2019_3_OR_NEWER;USE_BURST;USE_NEWMATHS;USE_BURST_REALLY;USE_TERRAINS - - - - - - - - - - - - - - - - - - ..\.ManagedLibs\BTKUILib.dll - - - - - - - - diff --git a/ShareBubbles/ShareBubbles/API/PedestalInfoBatchProcessor.cs b/ShareBubbles/ShareBubbles/API/PedestalInfoBatchProcessor.cs deleted file mode 100644 index 43287cc..0000000 --- a/ShareBubbles/ShareBubbles/API/PedestalInfoBatchProcessor.cs +++ /dev/null @@ -1,114 +0,0 @@ -using ABI_RC.Core.Networking.API; -using ABI_RC.Core.Networking.API.Responses; - -namespace NAK.ShareBubbles.API; - -public enum PedestalType -{ - Avatar, - Prop -} - -/// -/// Batch processor for fetching pedestal info in bulk. The pedestal endpoints are meant to be used in batch, so might -/// as well handle bubble requests in batches if in the very unlikely case that multiple requests are made at once. -/// -/// May only really matter for when a late joiner joins a room with a lot of bubbles. -/// -/// Luc: if there is a lot being dropped, you could try to batch them -/// -public static class PedestalInfoBatchProcessor -{ - private static readonly Dictionary>> _pendingRequests - = new() - { - { PedestalType.Avatar, new Dictionary>() }, - { PedestalType.Prop, new Dictionary>() } - }; - - private static readonly Dictionary _isBatchProcessing - = new() - { - { PedestalType.Avatar, false }, - { PedestalType.Prop, false } - }; - - // This breaks compile accepting this change. - // ReSharper disable once ChangeFieldTypeToSystemThreadingLock - private static readonly object _lock = new(); - private const float BATCH_DELAY = 2f; - - public static Task QueuePedestalInfoRequest(PedestalType type, string contentId) - { - var tcs = new TaskCompletionSource(); - - lock (_lock) - { - var requests = _pendingRequests[type]; - - if (!requests.TryAdd(contentId, tcs)) - return requests[contentId].Task; - - if (_isBatchProcessing[type]) - return tcs.Task; - - _isBatchProcessing[type] = true; - ProcessBatchAfterDelay(type); - } - - return tcs.Task; - } - - private static async void ProcessBatchAfterDelay(PedestalType type) - { - await Task.Delay(TimeSpan.FromSeconds(BATCH_DELAY)); - - List contentIds; - Dictionary> requestBatch; - - lock (_lock) - { - contentIds = _pendingRequests[type].Keys.ToList(); - requestBatch = new Dictionary>(_pendingRequests[type]); - _pendingRequests[type].Clear(); - _isBatchProcessing[type] = false; - //ShareBubblesMod.Logger.Msg($"Processing {type} pedestal info batch with {contentIds.Count} items"); - } - - try - { - ApiConnection.ApiOperation operation = type switch - { - PedestalType.Avatar => ApiConnection.ApiOperation.AvatarPedestal, - PedestalType.Prop => ApiConnection.ApiOperation.PropPedestal, - _ => throw new ArgumentException($"Unsupported pedestal type: {type}") - }; - - var response = await ApiConnection.MakeRequest>(operation, contentIds); - - if (response?.Data != null) - { - var responseDict = response.Data.ToDictionary(info => info.Id); - - foreach (var kvp in requestBatch) - { - if (responseDict.TryGetValue(kvp.Key, out var info)) - kvp.Value.SetResult(info); - else - kvp.Value.SetException(new Exception($"Content info not found for ID: {kvp.Key}")); - } - } - else - { - Exception exception = new($"Failed to fetch {type} info batch"); - foreach (var tcs in requestBatch.Values) - tcs.SetException(exception); - } - } - catch (Exception ex) - { - foreach (var tcs in requestBatch.Values) - tcs.SetException(ex); - } - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/DataTypes/BubblePedestalInfo.cs b/ShareBubbles/ShareBubbles/DataTypes/BubblePedestalInfo.cs deleted file mode 100644 index 87699e4..0000000 --- a/ShareBubbles/ShareBubbles/DataTypes/BubblePedestalInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace NAK.ShareBubbles; - -public class BubblePedestalInfo -{ - public string Name; - public string ImageUrl; - public string AuthorId; - public bool IsPermitted; - public bool IsPublic; -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/DataTypes/ShareBubbleData.cs b/ShareBubbles/ShareBubbles/DataTypes/ShareBubbleData.cs deleted file mode 100644 index 2650773..0000000 --- a/ShareBubbles/ShareBubbles/DataTypes/ShareBubbleData.cs +++ /dev/null @@ -1,80 +0,0 @@ -using ABI_RC.Systems.ModNetwork; - -namespace NAK.ShareBubbles; - -/// -/// Used to create a bubble. Need to define locally & over-network. -/// -public struct ShareBubbleData -{ - public uint BubbleId; // Local ID of the bubble (ContentId & ImplTypeHash), only unique per user - public uint ImplTypeHash; // Hash of the implementation type (for lookup in ShareBubbleRegistry) - - public string ContentId; // ID of the content being shared - - public ShareRule Rule; // Rule for sharing the bubble - public ShareLifetime Lifetime; // Lifetime of the bubble - public ShareAccess Access; // Access given if requesting bubble content share - public DateTime CreatedAt; // Time the bubble was created for checking lifetime - - public static void AddConverterForModNetwork() - { - ModNetworkMessage.AddConverter(Read, Write); - return; - - ShareBubbleData Read(ModNetworkMessage msg) - { - msg.Read(out uint bubbleId); - msg.Read(out uint implTypeHash); - msg.Read(out string contentId); - - // Pack rule, lifetime, and access into a single byte to save bandwidth - msg.Read(out byte packedFlags); - ShareRule rule = (ShareRule)(packedFlags & 0x0F); // First 4 bits for Rule - ShareLifetime lifetime = (ShareLifetime)((packedFlags >> 4) & 0x3); // Next 2 bits for Lifetime - ShareAccess access = (ShareAccess)((packedFlags >> 6) & 0x3); // Last 2 bits for Access - - // Read timestamp as uint (seconds since epoch) to save space compared to DateTime - msg.Read(out uint timestamp); - DateTime createdAt = DateTime.UnixEpoch.AddSeconds(timestamp); - //ShareBubblesMod.Logger.Msg($"Reading bubble - Seconds from epoch: {timestamp}"); - //ShareBubblesMod.Logger.Msg($"Converted back to time: {createdAt}"); - - // We do not support time traveling bubbles - DateTime now = DateTime.UtcNow; - if (createdAt > now) - createdAt = now; - - return new ShareBubbleData - { - BubbleId = bubbleId, - ImplTypeHash = implTypeHash, - ContentId = contentId, - Rule = rule, - Lifetime = lifetime, - Access = access, - CreatedAt = createdAt - }; - } - - void Write(ModNetworkMessage msg, ShareBubbleData data) - { - msg.Write(data.BubbleId); - msg.Write(data.ImplTypeHash); - msg.Write(data.ContentId); - - // Pack flags into a single byte - byte packedFlags = (byte)( - ((byte)data.Rule & 0x0F) | // First 4 bits for Rule - (((byte)data.Lifetime & 0x3) << 4) | // Next 2 bits for Lifetime - (((byte)data.Access & 0x3) << 6) // Last 2 bits for Access - ); - msg.Write(packedFlags); - - // Write timestamp as uint seconds since epoch - uint timestamp = (uint)(data.CreatedAt.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; - //ShareBubblesMod.Logger.Msg($"Writing bubble - Original time: {data.CreatedAt}, Converted to seconds: {timestamp}"); - msg.Write(timestamp); - } - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Enums/ShareAccess.cs b/ShareBubbles/ShareBubbles/Enums/ShareAccess.cs deleted file mode 100644 index af6b417..0000000 --- a/ShareBubbles/ShareBubbles/Enums/ShareAccess.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace NAK.ShareBubbles; - -public enum ShareAccess -{ - Permanent, - Session, - None -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Enums/ShareLifetime.cs b/ShareBubbles/ShareBubbles/Enums/ShareLifetime.cs deleted file mode 100644 index 64962aa..0000000 --- a/ShareBubbles/ShareBubbles/Enums/ShareLifetime.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NAK.ShareBubbles; - -public enum ShareLifetime -{ - Session, - TwoMinutes, -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Enums/ShareRule.cs b/ShareBubbles/ShareBubbles/Enums/ShareRule.cs deleted file mode 100644 index 192aab5..0000000 --- a/ShareBubbles/ShareBubbles/Enums/ShareRule.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NAK.ShareBubbles; - -public enum ShareRule -{ - Everyone, - FriendsOnly -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Implementation/AvatarBubbleImpl.cs b/ShareBubbles/ShareBubbles/Implementation/AvatarBubbleImpl.cs deleted file mode 100644 index 1c9e3de..0000000 --- a/ShareBubbles/ShareBubbles/Implementation/AvatarBubbleImpl.cs +++ /dev/null @@ -1,118 +0,0 @@ -using ABI_RC.Core.EventSystem; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.IO; -using ABI_RC.Core.Networking.API; -using ABI_RC.Core.Networking.API.Exceptions; -using ABI_RC.Core.Networking.API.UserWebsocket; -using NAK.ShareBubbles.API; -using ShareBubbles.ShareBubbles.Implementation; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace NAK.ShareBubbles.Impl -{ - public class AvatarBubbleImpl : IShareBubbleImpl - { - private ShareBubble bubble; - private string avatarId; - private BubblePedestalInfo details; - private Texture2D downloadedTexture; - - public bool IsPermitted => details is { IsPermitted: true }; - public string AuthorId => details?.AuthorId; - - public void Initialize(ShareBubble shareBubble) - { - bubble = shareBubble; - avatarId = shareBubble.Data.ContentId; - bubble.SetHue(0.5f); - bubble.SetEquipButtonLabel(" Wear"); - } - - public async Task FetchContentInfo() - { - var infoResponse = await PedestalInfoBatchProcessor - .QueuePedestalInfoRequest(PedestalType.Avatar, avatarId); - - details = new BubblePedestalInfo - { - Name = infoResponse.Name, - ImageUrl = infoResponse.ImageUrl, - AuthorId = infoResponse.User.Id, - IsPublic = infoResponse.IsPublished, - IsPermitted = infoResponse.Permitted, - }; - - downloadedTexture = await ImageCache.GetImageAsync(details.ImageUrl); - - // Check if bubble was destroyed before image was downloaded - if (bubble == null || bubble.gameObject == null) - { - Object.Destroy(downloadedTexture); - return; - } - - bubble.UpdateContent(new BubbleContentInfo - { - Name = details.Name, - Label = $" {(details.IsPublic ? "Public" : "Private")} Avatar", - Icon = downloadedTexture - }); - } - - public async Task HandleClaimAccept(string userId) - { - if (details == null) - return ShareClaimResult.Rejected(); - - try - { - await ShareApiHelper.ShareContentAsync( - ShareApiHelper.ShareContentType.Avatar, - avatarId, - userId); - - // Add to temp shares if session access - if (bubble.Data.Access == ShareAccess.Session) - { - TempShareManager.Instance.AddTempShare(ShareApiHelper.ShareContentType.Avatar, - avatarId, userId); - } - - return ShareClaimResult.Success(bubble.Data.Access == ShareAccess.Session); - } - catch (ContentAlreadySharedException) - { - return ShareClaimResult.AlreadyShared(); - } - catch (UserOnlyAllowsSharesFromFriendsException) - { - return ShareClaimResult.FriendsOnly(); - } - catch (Exception ex) - { - ShareBubblesMod.Logger.Error($"Error sharing avatar: {ex.Message}"); - return ShareClaimResult.Rejected(); - } - } - - public void ViewDetailsPage() - { - if (details == null) return; - ViewManager.Instance.RequestAvatarDetailsPage(avatarId); - } - - public void EquipContent() - { - if (details == null) return; - AssetManagement.Instance.LoadLocalAvatar(avatarId); - } - - public void Cleanup() - { - if (downloadedTexture == null) return; - Object.Destroy(downloadedTexture); - downloadedTexture = null; - } - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Implementation/IShareBubbleImpl.cs b/ShareBubbles/ShareBubbles/Implementation/IShareBubbleImpl.cs deleted file mode 100644 index e6ad17e..0000000 --- a/ShareBubbles/ShareBubbles/Implementation/IShareBubbleImpl.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ShareBubbles.ShareBubbles.Implementation; - -namespace NAK.ShareBubbles.Impl; - -public interface IShareBubbleImpl -{ - bool IsPermitted { get; } // Is user permitted to use the content (Public, Owned, Shared) - string AuthorId { get; } // Author ID of the content - void Initialize(ShareBubble shareBubble); - Task FetchContentInfo(); // Load the content info from the API - void ViewDetailsPage(); // Open the details page for the content - void EquipContent(); // Equip the content (Switch/Select) - Task HandleClaimAccept(string userId); - void Cleanup(); // Cleanup any resources -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Implementation/ShareClaimResult.cs b/ShareBubbles/ShareBubbles/Implementation/ShareClaimResult.cs deleted file mode 100644 index ebb309a..0000000 --- a/ShareBubbles/ShareBubbles/Implementation/ShareClaimResult.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NAK.ShareBubbles.Networking; - -namespace ShareBubbles.ShareBubbles.Implementation; - -public class ShareClaimResult -{ - public ModNetwork.ClaimResponseType ResponseType { get; } - public bool RequiresSessionTracking { get; } - - private ShareClaimResult(ModNetwork.ClaimResponseType responseType, bool requiresSessionTracking = false) - { - ResponseType = responseType; - RequiresSessionTracking = requiresSessionTracking; - } - - public static ShareClaimResult Success(bool isSessionShare = false) - => new(ModNetwork.ClaimResponseType.Accepted, isSessionShare); - - public static ShareClaimResult AlreadyShared() - => new(ModNetwork.ClaimResponseType.AlreadyShared); - - public static ShareClaimResult FriendsOnly() - => new(ModNetwork.ClaimResponseType.NotAcceptingSharesFromNonFriends); - - public static ShareClaimResult Rejected() - => new(ModNetwork.ClaimResponseType.Rejected); -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Implementation/SpawnableBubbleImpl.cs b/ShareBubbles/ShareBubbles/Implementation/SpawnableBubbleImpl.cs deleted file mode 100644 index 9de2b05..0000000 --- a/ShareBubbles/ShareBubbles/Implementation/SpawnableBubbleImpl.cs +++ /dev/null @@ -1,129 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.IO; -using ABI_RC.Core.Networking.API; -using ABI_RC.Core.Networking.API.Exceptions; -using ABI_RC.Core.Networking.API.UserWebsocket; -using ABI_RC.Core.Player; -using NAK.ShareBubbles.API; -using ShareBubbles.ShareBubbles.Implementation; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace NAK.ShareBubbles.Impl -{ - public class SpawnableBubbleImpl : IShareBubbleImpl - { - private ShareBubble bubble; - private string spawnableId; - private BubblePedestalInfo details; - private Texture2D downloadedTexture; - - public bool IsPermitted => details is { IsPermitted: true }; - public string AuthorId => details?.AuthorId; - - public void Initialize(ShareBubble shareBubble) - { - bubble = shareBubble; - spawnableId = shareBubble.Data.ContentId; - bubble.SetHue(0f); - bubble.SetEquipButtonLabel(" Select"); - } - - public async Task FetchContentInfo() - { - var infoResponse = await PedestalInfoBatchProcessor - .QueuePedestalInfoRequest(PedestalType.Prop, spawnableId); - - details = new BubblePedestalInfo - { - Name = infoResponse.Name, - ImageUrl = infoResponse.ImageUrl, - AuthorId = infoResponse.User.Id, - IsPublic = infoResponse.IsPublished, - IsPermitted = infoResponse.Permitted, - }; - - downloadedTexture = await ImageCache.GetImageAsync(details.ImageUrl); - - // Check if bubble was destroyed before image was downloaded - if (bubble == null || bubble.gameObject == null) - { - Object.Destroy(downloadedTexture); - return; - } - - bubble.UpdateContent(new BubbleContentInfo - { - Name = details.Name, - Label = $" {(details.IsPublic ? "Public" : "Private")} Prop", - Icon = downloadedTexture - }); - } - - public async Task HandleClaimAccept(string userId) - { - if (details == null) - return ShareClaimResult.Rejected(); - - try - { - await ShareApiHelper.ShareContentAsync( - ShareApiHelper.ShareContentType.Spawnable, - spawnableId, - userId); - - // Add to temp shares if session access - if (bubble.Data.Access == ShareAccess.Session) - { - TempShareManager.Instance.AddTempShare(ShareApiHelper.ShareContentType.Spawnable, - spawnableId, userId); - } - - return ShareClaimResult.Success(bubble.Data.Access == ShareAccess.Session); - } - catch (ContentAlreadySharedException) - { - return ShareClaimResult.AlreadyShared(); - } - catch (UserOnlyAllowsSharesFromFriendsException) - { - return ShareClaimResult.FriendsOnly(); - } - catch (Exception ex) - { - ShareBubblesMod.Logger.Error($"Error sharing spawnable: {ex.Message}"); - return ShareClaimResult.Rejected(); - } - } - - public void ViewDetailsPage() - { - if (details == null) return; - ViewManager.Instance.GetPropDetails(spawnableId); - } - - public void EquipContent() - { - if (details == null || !IsPermitted) return; - - try - { - PlayerSetup.Instance.SelectPropToSpawn( - spawnableId, - details.ImageUrl, - details.Name); - } - catch (Exception ex) - { - ShareBubblesMod.Logger.Error($"Error equipping spawnable: {ex.Message}"); - } - } - - public void Cleanup() - { - if (downloadedTexture == null) return; - Object.Destroy(downloadedTexture); - downloadedTexture = null; - } - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Implementation/TempShareManager.cs b/ShareBubbles/ShareBubbles/Implementation/TempShareManager.cs deleted file mode 100644 index f0425e5..0000000 --- a/ShareBubbles/ShareBubbles/Implementation/TempShareManager.cs +++ /dev/null @@ -1,230 +0,0 @@ -using ABI_RC.Core.Networking.API; -using ABI_RC.Core.Networking.API.UserWebsocket; -using ABI_RC.Core.Networking.IO.Instancing; -using ABI_RC.Core.Player; -using ABI_RC.Systems.GameEventSystem; -using Newtonsoft.Json; -using UnityEngine; - -namespace NAK.ShareBubbles; - -// Debating on whether to keep this as a feature or not -// Is janky and for avatars it causes this: -// https://feedback.abinteractive.net/p/when-avatar-is-unshared-by-owner-remotes-will-see-content-incompatible-bot - -public class TempShareManager -{ - #region Constructor - - private TempShareManager() - { - string userDataPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "UserData")); - savePath = Path.Combine(userDataPath, "sharebubbles_session_shares.json"); - } - - #endregion Constructor - - #region Singleton - - public static TempShareManager Instance { get; private set; } - - public static void Initialize() - { - if (Instance != null) return; - Instance = new TempShareManager(); - - Instance.LoadShares(); - Instance.InitializeEvents(); - } - - #endregion - - #region Constants & Fields - - private readonly string savePath; - private readonly object fileLock = new(); - - private TempShareData shareData = new(); - private readonly HashSet grantedSharesThisSession = new(); - - #endregion Constants & Fields - - #region Data Classes - - [Serializable] - private class TempShare - { - public ShareApiHelper.ShareContentType ContentType { get; set; } - public string ContentId { get; set; } - public string UserId { get; set; } - public DateTime CreatedAt { get; set; } - } - - [Serializable] - private class TempShareData - { - public List Shares { get; set; } = new(); - } - - #endregion Data Classes - - #region Public Methods - - public void AddTempShare(ShareApiHelper.ShareContentType contentType, string contentId, string userId) - { - ShareBubblesMod.Logger.Msg($"Adding temp share for {userId}..."); - - TempShare share = new() - { - ContentType = contentType, - ContentId = contentId, - UserId = userId, - CreatedAt = DateTime.UtcNow - }; - - // So we can monitor when they leave - grantedSharesThisSession.Add(userId); - - shareData.Shares.Add(share); - SaveShares(); - } - - #endregion Public Methods - - #region Event Handlers - - private void InitializeEvents() - { - CVRGameEventSystem.Instance.OnConnected.AddListener(OnConnected); - CVRGameEventSystem.Player.OnLeaveEntity.AddListener(OnPlayerLeft); - Application.quitting += OnApplicationQuit; - } - - private async void OnConnected(string _) - { - if (Instances.IsReconnecting) - return; - - if (shareData.Shares.Count == 0) - return; - - ShareBubblesMod.Logger.Msg($"Revoking {shareData.Shares.Count} shares from last session..."); - - // Attempt to revoke all shares when connecting to an online instance - // This will catch shares not revoked last session, and in prior instance - await RevokeAllShares(); - - ShareBubblesMod.Logger.Msg($"There are {shareData.Shares.Count} shares remaining."); - } - - private async void OnPlayerLeft(CVRPlayerEntity player) - { - // If they were granted shares this session, revoke them - if (grantedSharesThisSession.Contains(player.Uuid)) - await RevokeSharesForUser(player.Uuid); - } - - private void OnApplicationQuit() - { - // Attempt to revoke all shares when the game closes - RevokeAllShares().GetAwaiter().GetResult(); - } - - #endregion Event Handlers - - #region Share Management - - private async Task RevokeSharesForUser(string userId) - { - for (int i = shareData.Shares.Count - 1; i >= 0; i--) - { - TempShare share = shareData.Shares[i]; - if (share.UserId != userId) continue; - - if (!await RevokeShare(share)) - continue; - - shareData.Shares.RemoveAt(i); - SaveShares(); - } - } - - private async Task RevokeAllShares() - { - for (int i = shareData.Shares.Count - 1; i >= 0; i--) - { - if (!await RevokeShare(shareData.Shares[i])) - continue; - - shareData.Shares.RemoveAt(i); - SaveShares(); - } - } - - private async Task RevokeShare(TempShare share) - { - try - { - var response = await ShareApiHelper.ReleaseShareAsync( - share.ContentType, - share.ContentId, - share.UserId - ); - return response.IsSuccessStatusCode; - } - catch (Exception ex) - { - Debug.LogError($"Failed to revoke share: {ex.Message}"); - return false; - } - } - - #endregion Share Management - - #region File Operations - - private void LoadShares() - { - try - { - lock (fileLock) - { - if (!File.Exists(savePath)) - return; - - string json = File.ReadAllText(savePath); - shareData = JsonConvert.DeserializeObject(json) ?? new TempShareData(); - } - } - catch (Exception ex) - { - Debug.LogError($"Failed to load temp shares: {ex.Message}"); - shareData = new TempShareData(); - } - } - - private void SaveShares() - { - try - { - lock (fileLock) - { - string directory = Path.GetDirectoryName(savePath); - if (!Directory.Exists(directory)) - { - if (directory == null) throw new Exception("Failed to get directory path"); - Directory.CreateDirectory(directory); - } - - string json = JsonConvert.SerializeObject(shareData, Formatting.Indented); - File.WriteAllText(savePath, json); - } - } - catch (Exception ex) - { - Debug.LogError($"Failed to save temp shares: {ex.Message}"); - } - } - - #endregion -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Constants.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Constants.cs deleted file mode 100644 index 3450ed7..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Constants.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - #region Constants - - private const string NetworkVersion = "1.0.1"; // change each time network protocol changes - private const string ModId = $"NAK.SB:{NetworkVersion}"; // Cannot exceed 32 characters - - private const float ClaimRequestTimeout = 30f; // 30 second timeout - - #endregion Constants -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Enums.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Enums.cs deleted file mode 100644 index ac7cecd..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Enums.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - #region Enums - - // Remotes will ask owner of a bubble to share its content - // Owner can accept or deny the request, and the result will be sent back to the remote - // TODO: need rate limiting to prevent malicious users from spamming requests - - private enum MessageType : byte - { - // Lifecycle of a bubble - BubbleCreated, // bubbleId, bubbleType, position, rotation - BubbleDestroyed, // bubbleId - BubbleMoved, // bubbleId, position, rotation - - // Requesting share of a bubbles content - BubbleClaimRequest, // bubbleId - BubbleClaimResponse, // bubbleId, success - - // Requesting all active bubbles on instance join - ActiveBubblesRequest, // none - ActiveBubblesResponse, // int count, bubbleId[count], bubbleType[count], position[count], rotation[count] - - // Notification of share being sent to a user - DirectShareNotification, // userId, contentType, contentId - } - - private enum MNLogLevel : byte - { - Info, - Warning, - Error - } - - public enum ClaimResponseType : byte - { - Accepted, - Rejected, - NotAcceptingSharesFromNonFriends, - AlreadyShared, - Timeout - } - - #endregion Enums -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Helpers.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Helpers.cs deleted file mode 100644 index e65a178..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Helpers.cs +++ /dev/null @@ -1,30 +0,0 @@ -using ABI_RC.Core.Networking; -using ABI_RC.Core.Player; -using DarkRift; -using UnityEngine; - -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - #region Private Methods - - private static bool CanSendModNetworkMessage() - => _isSubscribedToModNetwork - && IsConnectedToGameNetwork() - && CVRPlayerManager.Instance.NetworkPlayers.Count > 0; // No need to send if there are no players - - private static bool IsConnectedToGameNetwork() - { - return NetworkManager.Instance != null - && NetworkManager.Instance.GameNetwork != null - && NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected; - } - - /// Checks if a Vector3 is invalid (NaN or Infinity). - private static bool IsInvalidVector3(Vector3 vector) - => float.IsNaN(vector.x) || float.IsNaN(vector.y) || float.IsNaN(vector.z) - || float.IsInfinity(vector.x) || float.IsInfinity(vector.y) || float.IsInfinity(vector.z); - - #endregion Private Methods -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Inbound.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Inbound.cs deleted file mode 100644 index cdf302c..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Inbound.cs +++ /dev/null @@ -1,209 +0,0 @@ -using ABI_RC.Core.Networking.API; -using ABI_RC.Core.Networking.IO.Social; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.ModNetwork; -using UnityEngine; - -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - #region Reset Method - - public static void Reset() - { - LoggerInbound("ModNetwork has been reset."); - } - - #endregion Reset Method - - #region Inbound Methods - - private static bool ShouldReceiveFromSender(string sender) - { - // if (_disallowedForSession.Contains(sender)) - // return false; // ignore messages from disallowed users - - if (MetaPort.Instance.blockedUserIds.Contains(sender)) - return false; // ignore messages from blocked users - - // if (ModSettings.Entry_FriendsOnly.Value && !Friends.FriendsWith(sender)) - // return false; // ignore messages from non-friends if friends only is enabled - - // if (StickerSystem.Instance.IsRestrictedInstance) // ignore messages from users when the world is restricted. This also includes older or modified version of Stickers mod. - // return false; - - return true; - } - - private static void HandleMessageReceived(ModNetworkMessage msg) - { - try - { - string sender = msg.Sender; - msg.Read(out byte msgTypeRaw); - - if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) - return; - - if (!ShouldReceiveFromSender(sender)) - return; - - LoggerInbound($"Received message from {msg.Sender}, Type: {(MessageType)msgTypeRaw}"); - - switch ((MessageType)msgTypeRaw) - { - case MessageType.BubbleCreated: - HandleBubbleCreated(msg); - break; - case MessageType.BubbleDestroyed: - HandleBubbleDestroyed(msg); - break; - case MessageType.BubbleMoved: - HandleBubbleMoved(msg); - break; - case MessageType.BubbleClaimRequest: - HandleBubbleClaimRequest(msg); - break; - case MessageType.BubbleClaimResponse: - HandleBubbleClaimResponse(msg); - break; - case MessageType.ActiveBubblesRequest: - HandleActiveBubblesRequest(msg); - break; - case MessageType.ActiveBubblesResponse: - HandleActiveBubblesResponse(msg); - break; - case MessageType.DirectShareNotification: - HandleDirectShareNotification(msg); - break; - default: - LoggerInbound($"Invalid message type received: {msgTypeRaw}"); - break; - } - } - catch (Exception e) - { - LoggerInbound($"Error handling message from {msg.Sender}: {e.Message}", MNLogLevel.Warning); - } - } - - private static void HandleBubbleCreated(ModNetworkMessage msg) - { - msg.Read(out Vector3 position); - msg.Read(out Vector3 rotation); - msg.Read(out ShareBubbleData data); - - // Check if the position or rotation is invalid - if (IsInvalidVector3(position) - || IsInvalidVector3(rotation)) - return; - - // Check if we should ignore this bubble - // Client enforced, sure, but only really matters for share claim, which is requires bubble owner to verify - if (data.Rule == ShareRule.FriendsOnly && !Friends.FriendsWith(msg.Sender)) - { - LoggerInbound($"Bubble with ID {data.BubbleId} is FriendsOnly and sender is not a friend, ignoring."); - return; - } - - ShareBubbleManager.Instance.OnRemoteBubbleCreated(msg.Sender, position, rotation, data); - - LoggerInbound($"Bubble with ID {data.BubbleId} created at {position}"); - } - - private static void HandleBubbleDestroyed(ModNetworkMessage msg) - { - msg.Read(out uint bubbleNetworkId); - - // Destroy bubble - ShareBubbleManager.Instance.OnRemoteBubbleDestroyed(msg.Sender, bubbleNetworkId); - - LoggerInbound($"Bubble with ID {bubbleNetworkId} destroyed"); - } - - - private static void HandleBubbleMoved(ModNetworkMessage msg) - { - msg.Read(out uint bubbleId); - msg.Read(out Vector3 position); - msg.Read(out Vector3 rotation); - - // Check if the position or rotation is invalid - if (IsInvalidVector3(position) - || IsInvalidVector3(rotation)) - return; - - ShareBubbleManager.Instance.OnRemoteBubbleMoved(msg.Sender, bubbleId, position, rotation); - LoggerInbound($"Bubble {bubbleId} moved to {position}"); - } - - private static void HandleBubbleClaimRequest(ModNetworkMessage msg) - { - msg.Read(out uint bubbleNetworkId); - - ShareBubbleManager.Instance.OnRemoteBubbleClaimRequest(msg.Sender, bubbleNetworkId); - - LoggerInbound($"Bubble with ID {bubbleNetworkId} claimed by {msg.Sender}"); - } - - private static void HandleBubbleClaimResponse(ModNetworkMessage msg) - { - msg.Read(out uint bubbleNetworkId); - msg.Read(out byte responseTypeRaw); - - if (!Enum.IsDefined(typeof(ClaimResponseType), responseTypeRaw)) - { - LoggerInbound($"Invalid claim response type received: {responseTypeRaw}"); - return; - } - - ClaimResponseType responseType = (ClaimResponseType)responseTypeRaw; - - if (_pendingClaimRequests.TryGetValue(bubbleNetworkId, out PendingClaimRequest request)) - request.CompletionSource.TrySetResult(responseType); - - LoggerInbound($"Bubble with ID {bubbleNetworkId} claim response: {responseType}"); - } - - private static void HandleActiveBubblesRequest(ModNetworkMessage msg) - { - LoggerInbound($"Received ActiveBubblesRequest from {msg.Sender}"); - - ShareBubbleManager.Instance.OnRemoteActiveBubbleRequest(msg.Sender); - } - - private static void HandleActiveBubblesResponse(ModNetworkMessage msg) - { - try - { - // hacky, but im tired and didnt think - ShareBubbleManager.Instance.SetRemoteBatchCreateBubbleState(msg.Sender, true); - - msg.Read(out int bubbleCount); - LoggerInbound($"Received ActiveBubblesResponse from {msg.Sender} with {bubbleCount} bubbles"); - - // Create up to MaxBubblesPerUser bubbles - for (int i = 0; i < Mathf.Min(bubbleCount, ShareBubbleManager.MaxBubblesPerUser); i++) - HandleBubbleCreated(msg); - } - catch (Exception e) - { - LoggerInbound($"Error handling ActiveBubblesResponse from {msg.Sender}: {e.Message}", MNLogLevel.Warning); - } - finally - { - ShareBubbleManager.Instance.SetRemoteBatchCreateBubbleState(msg.Sender, false); - } - } - - private static void HandleDirectShareNotification(ModNetworkMessage msg) - { - msg.Read(out ShareApiHelper.ShareContentType contentType); - msg.Read(out string contentId); - - LoggerInbound($"Received DirectShareNotification from {msg.Sender} for {contentType} {contentId}"); - } - - #endregion Inbound Methods -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Logging.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Logging.cs deleted file mode 100644 index 4f55048..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Logging.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - #region Network Logging - - private static void LoggerInbound(string message, MNLogLevel type = MNLogLevel.Info) - => _logger($"[Inbound] {message}", type, ModSettings.Debug_NetworkInbound.Value); - - private static void LoggerOutbound(string message, MNLogLevel type = MNLogLevel.Info) - => _logger($"[Outbound] {message}", type, ModSettings.Debug_NetworkOutbound.Value); - - private static void _logger(string message, MNLogLevel type = MNLogLevel.Info, bool loggerSetting = true) - { - switch (type) - { - default: - case MNLogLevel.Info when loggerSetting: - ShareBubblesMod.Logger.Msg(message); - break; - case MNLogLevel.Warning when loggerSetting: - ShareBubblesMod.Logger.Warning(message); - break; - case MNLogLevel.Error: // Error messages are always logged, regardless of setting - ShareBubblesMod.Logger.Error(message); - break; - - } - } - - #endregion Network Logging -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Main.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Main.cs deleted file mode 100644 index be0b2bb..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Main.cs +++ /dev/null @@ -1,57 +0,0 @@ -using ABI_RC.Systems.ModNetwork; - -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - - #region Mod Network Internals - - private static bool _isSubscribedToModNetwork; - - internal static void Initialize() - { - // Packs the share bubble data a bit, also makes things just nicer - ShareBubbleData.AddConverterForModNetwork(); - } - - internal static void Subscribe() - { - ModNetworkManager.Subscribe(ModId, HandleMessageReceived); - - _isSubscribedToModNetwork = ModNetworkManager.IsSubscribed(ModId); - if (!_isSubscribedToModNetwork) ShareBubblesMod.Logger.Error("Failed to subscribe to Mod Network! This should not happen."); - else ShareBubblesMod.Logger.Msg("Subscribed to Mod Network."); - } - - internal static void Unsubscribe() - { - ModNetworkManager.Unsubscribe(ModId); - - _isSubscribedToModNetwork = ModNetworkManager.IsSubscribed(ModId); - if (_isSubscribedToModNetwork) ShareBubblesMod.Logger.Error("Failed to unsubscribe from Mod Network! This should not happen."); - else ShareBubblesMod.Logger.Msg("Unsubscribed from Mod Network."); - } - - #endregion Mod Network Internals - - #region Pending Claim Requests - - private static readonly Dictionary _pendingClaimRequests = new(); - - public class PendingClaimRequest - { - public TaskCompletionSource CompletionSource { get; } - public DateTime RequestTime { get; } - public uint BubbleId { get; } - - public PendingClaimRequest(uint bubbleId) - { - CompletionSource = new TaskCompletionSource(); - RequestTime = DateTime.UtcNow; - BubbleId = bubbleId; - } - } - - #endregion -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Outbound.cs b/ShareBubbles/ShareBubbles/Networking/ModNetwork.Outbound.cs deleted file mode 100644 index 16b065c..0000000 --- a/ShareBubbles/ShareBubbles/Networking/ModNetwork.Outbound.cs +++ /dev/null @@ -1,154 +0,0 @@ -using ABI_RC.Core.Networking.API; -using ABI_RC.Systems.ModNetwork; -using UnityEngine; - -namespace NAK.ShareBubbles.Networking; - -public static partial class ModNetwork -{ - #region Outbound Methods - - public static void SendBubbleCreated(Vector3 position, Quaternion rotation, ShareBubbleData data) - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage modMsg = new(ModId); - modMsg.Write((byte)MessageType.BubbleCreated); - modMsg.Write(position); - modMsg.Write(rotation.eulerAngles); - modMsg.Write(data); - modMsg.Send(); - - LoggerOutbound($"Sending BubbleCreated message for bubble {data.BubbleId}"); - } - - public static void SendBubbleDestroyed(uint bubbleNetworkId) - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage modMsg = new(ModId); - modMsg.Write((byte)MessageType.BubbleDestroyed); - modMsg.Write(bubbleNetworkId); - modMsg.Send(); - - LoggerOutbound($"Sending BubbleDestroyed message for bubble {bubbleNetworkId}"); - } - - public static void SendBubbleMove(int bubbleId, Vector3 position, Quaternion rotation) - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage msg = new(ModId); - msg.Write((byte)MessageType.BubbleMoved); - msg.Write(bubbleId); - msg.Write(position); - msg.Write(rotation.eulerAngles); - msg.Send(); - - LoggerOutbound($"Sending BubbleMove message for bubble {bubbleId}"); - } - - public static async Task SendBubbleClaimRequestAsync(string bubbleOwnerId, uint bubbleNetworkId) - { - if (!CanSendModNetworkMessage()) - return ClaimResponseType.Rejected; - - // Create pending request - PendingClaimRequest request = new(bubbleNetworkId); - _pendingClaimRequests[bubbleNetworkId] = request; - - // Send request - using (ModNetworkMessage modMsg = new(ModId, bubbleOwnerId)) - { - modMsg.Write((byte)MessageType.BubbleClaimRequest); - modMsg.Write(bubbleNetworkId); - modMsg.Send(); - } - - LoggerOutbound($"Sending BubbleClaimRequest message for bubble {bubbleNetworkId}"); - - try - { - // Wait for response with timeout - using CancellationTokenSource cts = new(TimeSpan.FromSeconds(ClaimRequestTimeout)); - Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(ClaimRequestTimeout), cts.Token); - var responseTask = request.CompletionSource.Task; - - Task completedTask = await Task.WhenAny(responseTask, timeoutTask); - if (completedTask == timeoutTask) return ClaimResponseType.Timeout; - - return await responseTask; - } - finally - { - _pendingClaimRequests.Remove(bubbleNetworkId); - } - } - - public static void SendBubbleClaimResponse(string requesterUserId, uint bubbleNetworkId, ClaimResponseType responseType) - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage modMsg = new(ModId, requesterUserId); - modMsg.Write((byte)MessageType.BubbleClaimResponse); - modMsg.Write(bubbleNetworkId); - modMsg.Write((byte)responseType); - modMsg.Send(); - - LoggerOutbound($"Sending BubbleClaimResponse message for bubble {bubbleNetworkId}: {responseType}"); - } - - public static void SendActiveBubblesRequest() - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage modMsg = new(ModId); - modMsg.Write((byte)MessageType.ActiveBubblesRequest); - modMsg.Send(); - - LoggerOutbound("Sending ActiveBubblesRequest message"); - } - - public static void SendActiveBubblesResponse(string requesterUserId, List activeBubbles) - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage modMsg = new(ModId, requesterUserId); - modMsg.Write((byte)MessageType.ActiveBubblesResponse); - modMsg.Write(activeBubbles.Count); - - foreach (ShareBubble bubble in activeBubbles) - { - Transform parent = bubble.transform.parent; - modMsg.Write(parent.position); - modMsg.Write(parent.rotation.eulerAngles); - modMsg.Write(bubble.Data); - } - - modMsg.Send(); - - LoggerOutbound($"Sending ActiveBubblesResponse message with {activeBubbles.Count} bubbles"); - } - - public static void SendDirectShareNotification(string userId, ShareApiHelper.ShareContentType contentType, string contentId) - { - if (!CanSendModNetworkMessage()) - return; - - using ModNetworkMessage modMsg = new(ModId, userId); - modMsg.Write((byte)MessageType.DirectShareNotification); - modMsg.Write(contentType); - modMsg.Write(contentId); - modMsg.Send(); - - LoggerOutbound($"Sending DirectShareNotification message for {userId}"); - } - - #endregion Outbound Methods -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/ShareBubble.cs b/ShareBubbles/ShareBubbles/ShareBubble.cs deleted file mode 100644 index 6a0d5a1..0000000 --- a/ShareBubbles/ShareBubbles/ShareBubble.cs +++ /dev/null @@ -1,406 +0,0 @@ -using UnityEngine; -using ABI_RC.Core.Networking; -using ABI_RC.Core.Networking.IO.Social; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using NAK.ShareBubbles.Impl; -using NAK.ShareBubbles.Networking; -using NAK.ShareBubbles.UI; -using TMPro; -using System.Collections; -using ABI_RC.Core.InteractionSystem; -using ShareBubbles.ShareBubbles.Implementation; - -namespace NAK.ShareBubbles; - -public struct BubbleContentInfo -{ - public string Name { get; set; } - public string Label { get; set; } - public Texture2D Icon { get; set; } -} - -public class ShareBubble : MonoBehaviour -{ - // Shader properties - private static readonly int _MatcapHueShiftId = Shader.PropertyToID("_MatcapHueShift"); - private static readonly int _MatcapReplaceId = Shader.PropertyToID("_MatcapReplace"); - - #region Enums - - // State of fetching content info from api - private enum InfoFetchState - { - Fetching, // Fetching content info from api - Error, // Content info fetch failed - Ready // Content info fetched successfully - } - - // State of asking for content claim from owner - private enum ClaimState - { - Waiting, // No claim request sent - Requested, // Claim request sent to owner, waiting for response - Rejected, // Claim request rejected by owner - Permitted, // Claim request accepted by owner, or content is already unlocked - } - - #endregion Enums - - #region Properties - - private InfoFetchState _currentApiState; - - private InfoFetchState CurrentApiState - { - get => _currentApiState; - set - { - _currentApiState = value; - UpdateVisualState(); - } - } - - private ClaimState _currentClaimState; - - private ClaimState CurrentClaimState - { - get => _currentClaimState; - set - { - _currentClaimState = value; - UpdateClaimState(); - } - } - - public bool IsDestroyed { get; set; } - public string OwnerId; - public ShareBubbleData Data { get; private set; } - public bool IsOwnBubble => OwnerId == MetaPort.Instance.ownerId; - public bool IsPermitted => (implementation?.IsPermitted ?? false) || CurrentClaimState == ClaimState.Permitted; - - #endregion Properties - - #region Private Fields - - private IShareBubbleImpl implementation; - private DateTime? lastClaimRequest; - private const float ClaimTimeout = 30f; - private Coroutine claimStateResetCoroutine; - - private float lifetimeTotal; - - #endregion Private Fields - - #region Serialized Fields - - [Header("Visual Components")] - [SerializeField] private Renderer hexRenderer; - [SerializeField] private Renderer iconRenderer; - [SerializeField] private TextMeshPro droppedByText; - [SerializeField] private TextMeshPro contentName; - [SerializeField] private TextMeshPro contentLabel; - [SerializeField] private BubbleAnimController animController; - - [Header("State Objects")] - [SerializeField] private GameObject loadingState; - [SerializeField] private GameObject lockedState; - [SerializeField] private GameObject errorState; - [SerializeField] private GameObject contentInfo; - - [Header("Button UI")] - [SerializeField] private TextMeshProUGUI equipButtonLabel; - [SerializeField] private TextMeshProUGUI claimButtonLabel; - - #endregion Serialized Fields - - #region Public Methods - - public void SetEquipButtonLabel(string label) - { - equipButtonLabel.text = label; - } - - #endregion Public Methods - - #region Lifecycle Methods - - public async void Initialize(ShareBubbleData data, IShareBubbleImpl impl) - { - animController = GetComponent(); - - Data = data; - implementation = impl; - implementation.Initialize(this); - - CurrentApiState = InfoFetchState.Fetching; - CurrentClaimState = ClaimState.Waiting; - - string playerName = IsOwnBubble - ? AuthManager.Username - : CVRPlayerManager.Instance.TryGetPlayerName(OwnerId); - - droppedByText.text = $"Dropped by\n{playerName}"; - - if (Data.Lifetime == ShareLifetime.TwoMinutes) - lifetimeTotal = 120f; - - try - { - await implementation.FetchContentInfo(); - if (this == null || gameObject == null) return; // Bubble was destroyed during fetch - CurrentApiState = InfoFetchState.Ready; - } - catch (Exception ex) - { - ShareBubblesMod.Logger.Error($"Failed to load content info: {ex}"); - if (this == null || gameObject == null) return; // Bubble was destroyed during fetch - CurrentApiState = InfoFetchState.Error; - } - } - - private void Update() - { - if (Data.Lifetime == ShareLifetime.Session) - return; - - float lifetimeElapsed = (float)(DateTime.UtcNow - Data.CreatedAt).TotalSeconds; - float lifetimeProgress = Mathf.Clamp01(lifetimeElapsed / lifetimeTotal); - animController.SetLifetimeVisual(lifetimeProgress); - if (lifetimeProgress >= 1f) - { - //ShareBubblesMod.Logger.Msg($"Bubble expired: {Data.BubbleId}"); - Destroy(gameObject); - } - } - - private void OnDestroy() - { - implementation?.Cleanup(); - if (ShareBubbleManager.Instance != null && !IsDestroyed) - ShareBubbleManager.Instance.OnBubbleDestroyed(OwnerId, Data.BubbleId); - } - - #endregion Lifecycle Methods - - #region Visual State Management - - private void UpdateVisualState() - { - loadingState.SetActive(CurrentApiState == InfoFetchState.Fetching); - errorState.SetActive(CurrentApiState == InfoFetchState.Error); - contentInfo.SetActive(CurrentApiState == InfoFetchState.Ready); - - hexRenderer.material.SetFloat(_MatcapReplaceId, - CurrentApiState == InfoFetchState.Fetching ? 0f : 1f); - - if (CurrentApiState == InfoFetchState.Ready) - { - if (IsPermitted) CurrentClaimState = ClaimState.Permitted; - animController.ShowHubPivot(); - UpdateButtonStates(); - } - } - - private void UpdateButtonStates() - { - bool canClaim = !IsPermitted && Data.Access != ShareAccess.None && CanRequestClaim(); - bool canEquip = IsPermitted && CurrentApiState == InfoFetchState.Ready; - - claimButtonLabel.transform.parent.gameObject.SetActive(canClaim); // Only show claim button if content is locked & claimable - equipButtonLabel.transform.parent.gameObject.SetActive(canEquip); // Only show equip button if content is unlocked - - if (canClaim) UpdateClaimButtonState(); - } - - private void UpdateClaimButtonState() - { - switch (CurrentClaimState) - { - case ClaimState.Requested: - claimButtonLabel.text = "Claiming..."; - break; - case ClaimState.Rejected: - claimButtonLabel.text = "Denied :("; - StartClaimStateResetTimer(); - break; - case ClaimState.Permitted: - claimButtonLabel.text = "Claimed!"; - StartClaimStateResetTimer(); - break; - default: - claimButtonLabel.text = " Claim"; - break; - } - } - - private void StartClaimStateResetTimer() - { - if (claimStateResetCoroutine != null) StopCoroutine(claimStateResetCoroutine); - claimStateResetCoroutine = StartCoroutine(ResetClaimStateAfterDelay()); - } - - private IEnumerator ResetClaimStateAfterDelay() - { - yield return new WaitForSeconds(2f); - CurrentClaimState = ClaimState.Waiting; - UpdateClaimButtonState(); - } - - private void UpdateClaimState() - { - bool isLocked = !IsPermitted && CurrentApiState == InfoFetchState.Ready; - lockedState.SetActive(isLocked); // Only show locked state if content is locked & ready - UpdateButtonStates(); - } - - #endregion Visual State Management - - #region Content Info Updates - - public void SetHue(float hue) - { - hexRenderer.material.SetFloat(_MatcapHueShiftId, hue); - } - - public void UpdateContent(BubbleContentInfo info) - { - contentName.text = info.Name; - contentLabel.text = info.Label; - if (info.Icon != null) - iconRenderer.material.mainTexture = info.Icon; - } - - #endregion Content Info Updates - - #region Interaction Methods - - public void ViewDetailsPage() - { - if (CurrentApiState != InfoFetchState.Ready) return; - implementation?.ViewDetailsPage(); - } - - public void EquipContent() - { - // So uh, selecting a prop in will also spawn it as interact is down in same frame - // Too lazy to fix so we gonna wait a frame before actually equipping :) - StartCoroutine(ActuallyEquipContentNextFrame()); - - return; - IEnumerator ActuallyEquipContentNextFrame() - { - yield return null; // Wait a frame - - if (CurrentApiState != InfoFetchState.Ready) yield break; - - if (CanRequestClaim()) // Only possible on hold & click, as button is hidden when not permitted - { - RequestContentClaim(); - yield break; - } - - if (!IsPermitted) - yield break; - - implementation?.EquipContent(); - } - } - - public void RequestContentClaim() - { - if (!CanRequestClaim()) return; - - // Fire and forget but with error handling~ - _ = RequestContentClaimAsync().ContinueWith(task => - { - if (!task.IsFaulted) - return; - - ShareBubblesMod.Logger.Error($"Error requesting content claim: {task.Exception}"); - CurrentClaimState = ClaimState.Rejected; - }, TaskScheduler.FromCurrentSynchronizationContext()); - } - - private async Task RequestContentClaimAsync() - { - if (!CanRequestClaim()) - return ModNetwork.ClaimResponseType.Rejected; - - CurrentClaimState = ClaimState.Requested; - - try - { - ModNetwork.ClaimResponseType response = await ModNetwork.SendBubbleClaimRequestAsync(OwnerId, Data.BubbleId); - - switch (response) - { - case ModNetwork.ClaimResponseType.Accepted: - case ModNetwork.ClaimResponseType.AlreadyShared: - CurrentClaimState = ClaimState.Permitted; - break; - default: - case ModNetwork.ClaimResponseType.Rejected: - case ModNetwork.ClaimResponseType.Timeout: - CurrentClaimState = ClaimState.Rejected; - break; - case ModNetwork.ClaimResponseType.NotAcceptingSharesFromNonFriends: - CurrentClaimState = ClaimState.Rejected; - - string ownerName = CVRPlayerManager.Instance.TryGetPlayerName(OwnerId); - - ShareBubblesMod.Logger.Msg($"Claim request for {Data.BubbleId} rejected: " + - $"You are not friends with the owner ({ownerName}) and do not have the permission " + - $"enabled on the Community Hub to accept shares from non-friends."); - // Display in UI - ViewManager.Instance.TriggerAlert("Claim Request Rejected", - $"You are not friends with the owner ({ownerName}) and do not have the permission " + - "enabled on the Community Hub to accept shares from non-friends.", -1, false); - - break; - } - - return response; - } - catch - { - CurrentClaimState = ClaimState.Rejected; - return ModNetwork.ClaimResponseType.Rejected; - } - } - - private bool CanRequestClaim() - { - if (IsPermitted) return false; - if (IsOwnBubble) return false; - return OwnerId == implementation.AuthorId; - } - - #endregion Interaction Methods - - #region Mod Network Callbacks - - public void OnClaimResponseReceived(bool accepted) - { - lastClaimRequest = null; - CurrentClaimState = accepted ? ClaimState.Permitted : ClaimState.Rejected; - UpdateButtonStates(); - } - - public async void OnRemoteWantsClaim(string requesterId) - { - if (!IsOwnBubble) return; - - // Rule check bypass attempt - reject immediately - if (Data.Rule == ShareRule.FriendsOnly && !Friends.FriendsWith(requesterId)) - { - ModNetwork.SendBubbleClaimResponse(requesterId, Data.BubbleId, ModNetwork.ClaimResponseType.Rejected); - return; - } - - ShareClaimResult result = await implementation.HandleClaimAccept(requesterId); - ModNetwork.SendBubbleClaimResponse(requesterId, Data.BubbleId, result.ResponseType); - } - - #endregion Mod Network Callbacks -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/ShareBubbleManager.cs b/ShareBubbles/ShareBubbles/ShareBubbleManager.cs deleted file mode 100644 index fd45f81..0000000 --- a/ShareBubbles/ShareBubbles/ShareBubbleManager.cs +++ /dev/null @@ -1,429 +0,0 @@ -using ABI_RC.Core.IO; -using ABI_RC.Core.Networking.IO.Instancing; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Core.UI; -using ABI_RC.Systems.GameEventSystem; -using ABI_RC.Systems.Gravity; -using NAK.ShareBubbles.Impl; -using NAK.ShareBubbles.Networking; -using UnityEngine; -using UnityEngine.SceneManagement; -using Object = UnityEngine.Object; - -namespace NAK.ShareBubbles; - -public class ShareBubbleManager -{ - #region Constants - - public const int MaxBubblesPerUser = 3; - private const float BubbleCreationCooldown = 1f; - - #endregion Constants - - #region Singleton - - public static ShareBubbleManager Instance { get; private set; } - - public static void Initialize() - { - if (Instance != null) - return; - - Instance = new ShareBubbleManager(); - RegisterDefaultBubbleTypes(); - - CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(Instance.OnPlayerSetupStart); - } - - private static void RegisterDefaultBubbleTypes() - { - ShareBubbleRegistry.RegisterBubbleType(GetMaskedHash("Avatar"), () => new AvatarBubbleImpl()); - ShareBubbleRegistry.RegisterBubbleType(GetMaskedHash("Spawnable"), () => new SpawnableBubbleImpl()); - } - - public static uint GetMaskedHash(string typeName) => StringToHash(typeName) & 0xFFFF; - - public static uint GenerateBubbleId(string content, uint implTypeHash) - { - // Allows us to extract the implementation type hash from the bubble ID - return (implTypeHash << 16) | GetMaskedHash(content); - } - - private static uint StringToHash(string str) - { - unchecked - { - uint hash = 5381; - foreach (var ch in str) hash = ((hash << 5) + hash) + ch; - return hash; - } - } - - #endregion - - #region Fields - - private class PlayerBubbleData - { - // Can only ever be true when the batched bubble message is what created the player data - public bool IgnoreBubbleCreationCooldown; - public float LastBubbleCreationTime; - public readonly Dictionary BubblesByLocalId = new(); - } - - private readonly Dictionary playerBubbles = new(); - - public bool IsPlacingBubbleMode { get; set; } - private ShareBubbleData selectedBubbleData; - - #endregion Fields - - #region Game Events - - private void OnPlayerSetupStart() - { - CVRGameEventSystem.Instance.OnConnected.AddListener(OnConnected); - CVRGameEventSystem.Player.OnLeaveEntity.AddListener(OnPlayerLeft); - } - - private void OnConnected(string _) - { - if (Instances.IsReconnecting) - return; - - // Clear all bubbles on disconnect (most should have died during world unload) - foreach (PlayerBubbleData playerData in playerBubbles.Values) - { - foreach (ShareBubble bubble in playerData.BubblesByLocalId.Values.ToList()) - DestroyBubble(bubble); - } - playerBubbles.Clear(); - - // This also acts to signal to other clients we rejoined and need our bubbles cleared - ModNetwork.SendActiveBubblesRequest(); - } - - private void OnPlayerLeft(CVRPlayerEntity player) - { - if (!playerBubbles.TryGetValue(player.Uuid, out PlayerBubbleData playerData)) - return; - - foreach (ShareBubble bubble in playerData.BubblesByLocalId.Values.ToList()) - DestroyBubble(bubble); - - playerBubbles.Remove(player.Uuid); - } - - #endregion Game Events - - #region Local Operations - - public void DropBubbleInFront(ShareBubbleData data) - { - PlayerSetup localPlayer = PlayerSetup.Instance; - string localPlayerId = MetaPort.Instance.ownerId; - - if (!CanPlayerCreateBubble(localPlayerId)) - { - ShareBubblesMod.Logger.Msg("Bubble creation on cooldown!"); - return; - } - - float playSpaceScale = localPlayer.GetPlaySpaceScale(); - Vector3 playerForward = localPlayer.GetPlayerForward(); - Vector3 position = localPlayer.activeCam.transform.position + playerForward * 0.5f * playSpaceScale; - - if (Physics.Raycast(position, - localPlayer.CharacterController.GetGravityDirection(), - out RaycastHit raycastHit, 4f, localPlayer.dropPlacementMask)) - { - CreateBubbleForPlayer(localPlayerId, raycastHit.point, - Quaternion.LookRotation(playerForward, raycastHit.normal), data); - return; - } - - CreateBubbleForPlayer(localPlayerId, position, - Quaternion.LookRotation(playerForward, -localPlayer.transform.up), data); - } - - public void SelectBubbleForPlace(string contentCouiPath, string contentName, ShareBubbleData data) - { - selectedBubbleData = data; - IsPlacingBubbleMode = true; - CohtmlHud.Instance.SelectPropToSpawn( - contentCouiPath, - contentName, - "Selected content to bubble:"); - } - - public void PlaceSelectedBubbleFromControllerRay(Transform transform) - { - Vector3 position = transform.position; - Vector3 forward = transform.forward; - - // Every layer other than IgnoreRaycast, PlayerLocal, PlayerClone, PlayerNetwork, and UI Internal - const int LayerMask = ~((1 << 2) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 15)); - if (!Physics.Raycast(position, forward, out RaycastHit hit, - 10f, LayerMask, QueryTriggerInteraction.Ignore)) - return; // No hit - - if (selectedBubbleData.ImplTypeHash == 0) - return; - - Vector3 bubblePos = hit.point; - - PlayerSetup localPlayer = PlayerSetup.Instance; - Vector3 playerPos = localPlayer.GetPlayerPosition(); - - // Sample gravity at bubble position to get bubble orientation - Vector3 bubbleUp = -GravitySystem.TryGetResultingGravity(bubblePos, false).AppliedGravity.normalized; - Vector3 bubbleForward = Vector3.ProjectOnPlane((playerPos - bubblePos).normalized, bubbleUp); - - // Bubble faces player aligned with gravity - Quaternion bubbleRot = Quaternion.LookRotation(bubbleForward, bubbleUp); - - CreateBubbleForPlayer(MetaPort.Instance.ownerId, bubblePos, bubbleRot, selectedBubbleData); - } - - #endregion Local Operations - - #region Player Callbacks - - private void CreateBubbleForPlayer( - string playerId, - Vector3 position, - Quaternion rotation, - ShareBubbleData data) - { - GameObject bubbleRootObject = null; - try - { - if (!CanPlayerCreateBubble(playerId)) - return; - - if (!ShareBubbleRegistry.TryCreateImplementation(data.ImplTypeHash, out IShareBubbleImpl impl)) - { - Debug.LogError($"Failed to create bubble: Unknown bubble type hash: {data.ImplTypeHash}"); - return; - } - - // Get or create player data - if (!playerBubbles.TryGetValue(playerId, out PlayerBubbleData playerData)) - { - playerData = new PlayerBubbleData(); - playerBubbles[playerId] = playerData; - } - - // If a bubble with this ID already exists for this player, destroy it - if (playerData.BubblesByLocalId.TryGetValue(data.BubbleId, out ShareBubble existingBubble)) - { - ShareBubblesMod.Logger.Msg($"Replacing existing bubble: {data.BubbleId}"); - DestroyBubble(existingBubble); - } - // Check bubble limit only if we're not replacing an existing bubble - else if (playerData.BubblesByLocalId.Count >= MaxBubblesPerUser) - { - ShareBubble oldestBubble = playerData.BubblesByLocalId.Values - .OrderBy(b => b.Data.CreatedAt) - .First(); - ShareBubblesMod.Logger.Msg($"Bubble limit reached, destroying oldest bubble: {oldestBubble.Data.BubbleId}"); - DestroyBubble(oldestBubble); - } - - bubbleRootObject = Object.Instantiate(ShareBubblesMod.SharingBubblePrefab); - bubbleRootObject.transform.SetLocalPositionAndRotation(position, rotation); - bubbleRootObject.SetActive(true); - - // Move to Additive scene to prevent being hit by shader replacement on VR switch - SceneManager.MoveGameObjectToScene(bubbleRootObject, SceneManager.GetSceneByName(CVRObjectLoader.AdditiveContentSceneName)); - - Transform bubbleTransform = bubbleRootObject.transform.GetChild(0); - if (bubbleTransform == null) - throw new InvalidOperationException("Bubble prefab is missing expected child transform"); - - (float targetHeight, float scaleModifier) = GetBubbleHeightAndScale(); - bubbleTransform.localPosition = new Vector3(0f, targetHeight, 0f); - bubbleTransform.localScale = new Vector3(scaleModifier, scaleModifier, scaleModifier); - - ShareBubble bubble = bubbleRootObject.GetComponent(); - if (bubble == null) - throw new InvalidOperationException("Bubble prefab is missing ShareBubble component"); - - bubble.OwnerId = playerId; - bubble.Initialize(data, impl); - playerData.BubblesByLocalId[data.BubbleId] = bubble; - playerData.LastBubbleCreationTime = Time.time; - - if (playerId == MetaPort.Instance.ownerId) - ModNetwork.SendBubbleCreated(position, rotation, data); - } - catch (Exception ex) - { - Debug.LogError($"Failed to create bubble (ID: {data.BubbleId}, Owner: {playerId}): {ex}"); - - // Clean up the partially created bubble if it exists - if (bubbleRootObject != null) - { - Object.DestroyImmediate(bubbleRootObject); - - // Remove from player data if it was added - if (playerBubbles.TryGetValue(playerId, out PlayerBubbleData playerData)) - playerData.BubblesByLocalId.Remove(data.BubbleId); - } - } - } - - public void DestroyBubble(ShareBubble bubble) - { - if (bubble == null) return; - bubble.IsDestroyed = true; // Prevents ShareBubble.OnDestroy invoking OnBubbleDestroyed - Object.DestroyImmediate(bubble.gameObject); - OnBubbleDestroyed(bubble.OwnerId, bubble.Data.BubbleId); - } - - public void OnBubbleDestroyed(string ownerId, uint bubbleId) - { - if (playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData)) - playerData.BubblesByLocalId.Remove(bubbleId); - - if (ownerId == MetaPort.Instance.ownerId) ModNetwork.SendBubbleDestroyed(bubbleId); - } - - #endregion - - #region Remote Events - - public void SetRemoteBatchCreateBubbleState(string ownerId, bool batchCreateBubbleState) - { - // Get or create player data - if (!playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData)) - { - playerData = new PlayerBubbleData(); - playerBubbles[ownerId] = playerData; - - // If player data didn't exist, ignore bubble creation cooldown for the time we create active bubbles on join - playerData.IgnoreBubbleCreationCooldown = batchCreateBubbleState; - } - else - { - // If player data already exists, ignore batched bubble creation - // Probably makes a race condition here, but it's not critical - // This will prevent users from using the batched bubble creation when they've already created bubbles - playerData.IgnoreBubbleCreationCooldown = false; - } - } - - public void OnRemoteBubbleCreated(string ownerId, Vector3 position, Vector3 rotation, ShareBubbleData data) - { - CreateBubbleForPlayer(ownerId, position, Quaternion.Euler(rotation), data); - } - - public void OnRemoteBubbleDestroyed(string ownerId, uint bubbleId) - { - if (!playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData) || - !playerData.BubblesByLocalId.TryGetValue(bubbleId, out ShareBubble bubble)) - return; - - DestroyBubble(bubble); - } - - public void OnRemoteBubbleMoved(string ownerId, uint bubbleId, Vector3 position, Vector3 rotation) - { - if (!playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData) || - !playerData.BubblesByLocalId.TryGetValue(bubbleId, out ShareBubble bubble)) - return; - - // TODO: fix - bubble.transform.parent.SetPositionAndRotation(position, Quaternion.Euler(rotation)); - } - - public void OnRemoteBubbleClaimRequest(string requesterUserId, uint bubbleId) - { - // Get our bubble data if exists, respond to requester - string ownerId = MetaPort.Instance.ownerId; - if (!playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData) || - !playerData.BubblesByLocalId.TryGetValue(bubbleId, out ShareBubble bubble)) - return; - - bubble.OnRemoteWantsClaim(requesterUserId); - } - - public void OnRemoteBubbleClaimResponse(string ownerId, uint bubbleId, bool wasAccepted) - { - // Get senders bubble data if exists, receive response - if (!playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData) || - !playerData.BubblesByLocalId.TryGetValue(bubbleId, out ShareBubble bubble)) - return; - - bubble.OnClaimResponseReceived(wasAccepted); - } - - public void OnRemoteActiveBubbleRequest(string requesterUserId) - { - // Clear all bubbles for requester (as this msg is sent by them on initial join) - // This catches the case where they rejoin faster than we heard their leave event - if (playerBubbles.TryGetValue(requesterUserId, out PlayerBubbleData playerData)) - { - foreach (ShareBubble bubble in playerData.BubblesByLocalId.Values.ToList()) - DestroyBubble(bubble); - } - - var myBubbles = GetOwnActiveBubbles(); - if (myBubbles.Count == 0) - return; // No bubbles to send - - // Send all active bubbles to requester - ModNetwork.SendActiveBubblesResponse(requesterUserId, myBubbles); - } - - #endregion - - #region Utility Methods - - private bool CanPlayerCreateBubble(string playerId) - { - if (!playerBubbles.TryGetValue(playerId, out PlayerBubbleData playerData)) - return true; - - if (playerData.IgnoreBubbleCreationCooldown) - return true; // Only ignore for the time we create active bubbles on join - - return Time.time - playerData.LastBubbleCreationTime >= BubbleCreationCooldown; - } - - private static (float, float) GetBubbleHeightAndScale() - { - float targetHeight = 0.5f * PlayerSetup.Instance.GetAvatarHeight(); - float scaleModifier = PlayerSetup.Instance.GetPlaySpaceScale() * 1.8f; - return (targetHeight, scaleModifier); - } - - public void OnPlayerScaleChanged() - { - (float targetHeight, float scaleModifier) = GetBubbleHeightAndScale(); - - foreach (PlayerBubbleData playerData in playerBubbles.Values) - { - foreach (ShareBubble bubble in playerData.BubblesByLocalId.Values) - { - if (bubble == null) continue; - Transform bubbleOffset = bubble.transform.GetChild(0); - Vector3 localPos = bubbleOffset.localPosition; - bubbleOffset.localPosition = new Vector3(localPos.x, targetHeight, localPos.z); - bubbleOffset.localScale = new Vector3(scaleModifier, scaleModifier, scaleModifier); - } - } - } - - private List GetOwnActiveBubbles() - { - string ownerId = MetaPort.Instance.ownerId; - return playerBubbles.TryGetValue(ownerId, out PlayerBubbleData playerData) - ? playerData.BubblesByLocalId.Values.ToList() - : new List(); - } - - #endregion -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/ShareBubbleRegistry.cs b/ShareBubbles/ShareBubbles/ShareBubbleRegistry.cs deleted file mode 100644 index a0eb759..0000000 --- a/ShareBubbles/ShareBubbles/ShareBubbleRegistry.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAK.ShareBubbles.Impl; - -namespace NAK.ShareBubbles; - -public delegate IShareBubbleImpl BubbleImplFactory(); - -// This is all so fucked because I wanted to allow for custom bubble types, so Stickers could maybe be shared via ShareBubbles -// but it is aaaaaaaaaaaaaaaaaaaaaaa - -public static class ShareBubbleRegistry -{ - #region Type Registration - - private static readonly Dictionary registeredTypes = new(); - - public static void RegisterBubbleType(uint typeHash, BubbleImplFactory factory) - { - registeredTypes[typeHash] = factory; - } - - public static bool TryCreateImplementation(uint typeHash, out IShareBubbleImpl implementation) - { - implementation = null; - if (!registeredTypes.TryGetValue(typeHash, out BubbleImplFactory factory)) - return false; - - implementation = factory(); - return implementation != null; - } - - #endregion Type Registration -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/BubbleAnimController.cs b/ShareBubbles/ShareBubbles/UI/BubbleAnimController.cs deleted file mode 100644 index d51e880..0000000 --- a/ShareBubbles/ShareBubbles/UI/BubbleAnimController.cs +++ /dev/null @@ -1,230 +0,0 @@ -using UnityEngine; -using System.Collections; - -namespace NAK.ShareBubbles.UI -{ - public class BubbleAnimController : MonoBehaviour - { - [Header("Transform References")] - [SerializeField] private Transform centerPoint; - [SerializeField] private Transform hubPivot; - [SerializeField] private Transform base1; - [SerializeField] private Transform base2; - - [Header("Animation Settings")] - [SerializeField] private float spawnDuration = 1.2f; - [SerializeField] private float hubScaleDuration = 0.5f; - [SerializeField] private float centerHeightOffset = 0.2f; - [SerializeField] private AnimationCurve spawnBounceCurve; - [SerializeField] private AnimationCurve hubScaleCurve; - - [Header("Movement Settings")] - [SerializeField] private float positionSmoothTime = 0.1f; - [SerializeField] private float rotationSmoothTime = 0.15f; - [SerializeField] private float floatSpeed = 1f; - [SerializeField] private float floatHeight = 0.05f; - [SerializeField] private float baseRotationSpeed = 15f; - [SerializeField] private float spawnRotationSpeed = 180f; - - [Header("Inner Rotation Settings")] - [SerializeField] private float innerRotationSpeed = 0.8f; - [SerializeField] private float innerRotationRange = 10f; - [SerializeField] private AnimationCurve innerRotationGradient = AnimationCurve.Linear(0, 0.4f, 1, 1f); - - private SkinnedMeshRenderer base1Renderer; - private SkinnedMeshRenderer base2Renderer; - - private Transform[] base1Inners; - private Transform[] base2Inners; - private Vector3 centerTargetPos; - private Vector3 centerVelocity; - private float base1Rotation; - private float base2Rotation; - private float base1RotationSpeed; - private float base2RotationSpeed; - private float targetBase1RotationSpeed; - private float targetBase2RotationSpeed; - private float rotationSpeedVelocity1; - private float rotationSpeedVelocity2; - private float animationTime; - private bool isSpawning = true; - - private Quaternion[] base1InnerStartRots; - private Quaternion[] base2InnerStartRots; - - private void Start() - { - if (spawnBounceCurve.length == 0) - { - spawnBounceCurve = new AnimationCurve( - new Keyframe(0, 0, 0, 2), - new Keyframe(0.6f, 1.15f, 0, 0), - new Keyframe(0.8f, 0.95f, 0, 0), - new Keyframe(1, 1, 0, 0) - ); - } - - if (hubScaleCurve.length == 0) - { - hubScaleCurve = new AnimationCurve( - new Keyframe(0, 0, 0, 2), - new Keyframe(1, 1, 0, 0) - ); - } - - base1Inners = new Transform[3]; - base2Inners = new Transform[3]; - base1InnerStartRots = new Quaternion[3]; - base2InnerStartRots = new Quaternion[3]; - - Transform currentBase1 = base1; - Transform currentBase2 = base2; - for (int i = 0; i < 3; i++) - { - base1Inners[i] = currentBase1.GetChild(0); - base2Inners[i] = currentBase2.GetChild(0); - base1InnerStartRots[i] = base1Inners[i].localRotation; - base2InnerStartRots[i] = base2Inners[i].localRotation; - currentBase1 = base1Inners[i]; - currentBase2 = base2Inners[i]; - } - - base1RotationSpeed = spawnRotationSpeed; - base2RotationSpeed = -spawnRotationSpeed; - targetBase1RotationSpeed = spawnRotationSpeed; - targetBase2RotationSpeed = -spawnRotationSpeed; - - // hack - base1Renderer = base1.GetComponentInChildren(); - base2Renderer = base2.GetComponentInChildren(); - - ResetTransforms(); - StartCoroutine(SpawnAnimation()); - } - - private void ResetTransforms() - { - centerPoint.localScale = Vector3.zero; - centerPoint.localPosition = Vector3.zero; - hubPivot.localScale = Vector3.zero; - - base1.localScale = new Vector3(0.04f, 0.04f, 0.04f); - base1.localRotation = Quaternion.Euler(0, 180, 0); - - base2.localScale = new Vector3(0.02f, 0.02f, 0.02f); - base2.localRotation = Quaternion.Euler(0, 180, 0); - - centerTargetPos = Vector3.zero; - base1Rotation = 180f; - base2Rotation = 180f; - } - - private IEnumerator SpawnAnimation() - { - float elapsed = 0f; - - while (elapsed < spawnDuration) - { - float t = elapsed / spawnDuration; - float bounceT = spawnBounceCurve.Evaluate(t); - - // Center point and hub pivot animation with earlier start - float centerT = Mathf.Max(0, (t - 0.3f) * 1.43f); // Adjusted timing - centerPoint.localScale = Vector3.Lerp(Vector3.zero, Vector3.one, centerT); - centerTargetPos = Vector3.up * (bounceT * centerHeightOffset); - - // Base animations with inverted rotation - base1.localScale = Vector3.Lerp(new Vector3(0.04f, 0.04f, 0.04f), new Vector3(0.1f, 0.1f, 0.1f), bounceT); - base2.localScale = Vector3.Lerp(new Vector3(0.02f, 0.02f, 0.02f), new Vector3(0.06f, 0.06f, 0.06f), bounceT); - - base1Rotation += base1RotationSpeed * Time.deltaTime; - base2Rotation += base2RotationSpeed * Time.deltaTime; - - elapsed += Time.deltaTime; - yield return null; - } - - targetBase1RotationSpeed = baseRotationSpeed; - targetBase2RotationSpeed = -baseRotationSpeed; - isSpawning = false; - } - - public void ShowHubPivot() - { - StartCoroutine(ScaleHubPivot()); - } - - public void SetLifetimeVisual(float timeLeftNormalized) - { - float value = 100f - (timeLeftNormalized * 100f); - base1Renderer.SetBlendShapeWeight(0, value); - base2Renderer.SetBlendShapeWeight(0, value); - } - - private IEnumerator ScaleHubPivot() - { - float elapsed = 0f; - - while (elapsed < hubScaleDuration) - { - float t = elapsed / hubScaleDuration; - float scaleT = hubScaleCurve.Evaluate(t); - - hubPivot.localScale = Vector3.one * scaleT; - - elapsed += Time.deltaTime; - yield return null; - } - - hubPivot.localScale = Vector3.one; - } - - private void Update() - { - animationTime += Time.deltaTime; - - if (!isSpawning) - { - float floatOffset = Mathf.Sin(animationTime * floatSpeed) * floatHeight; - centerTargetPos = Vector3.up * (centerHeightOffset + floatOffset); - } - - centerPoint.localPosition = Vector3.SmoothDamp( - centerPoint.localPosition, - centerTargetPos, - ref centerVelocity, - positionSmoothTime - ); - - base1RotationSpeed = Mathf.SmoothDamp( - base1RotationSpeed, - targetBase1RotationSpeed, - ref rotationSpeedVelocity1, - rotationSmoothTime - ); - - base2RotationSpeed = Mathf.SmoothDamp( - base2RotationSpeed, - targetBase2RotationSpeed, - ref rotationSpeedVelocity2, - rotationSmoothTime - ); - - base1Rotation += base1RotationSpeed * Time.deltaTime; - base2Rotation += base2RotationSpeed * Time.deltaTime; - - base1.localRotation = Quaternion.Euler(0, base1Rotation, 0); - base2.localRotation = Quaternion.Euler(0, base2Rotation, 0); - - for (int i = 0; i < 3; i++) - { - float phase = (animationTime * innerRotationSpeed) * Mathf.Deg2Rad; - float gradientMultiplier = innerRotationGradient.Evaluate(i / 2f); - float rotationAmount = Mathf.Sin(phase) * innerRotationRange * gradientMultiplier; - - base1Inners[i].localRotation = base1InnerStartRots[i] * Quaternion.Euler(0, rotationAmount, 0); - base2Inners[i].localRotation = base2InnerStartRots[i] * Quaternion.Euler(0, -rotationAmount, 0); - } - } - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/BubbleInteract.cs b/ShareBubbles/ShareBubbles/UI/BubbleInteract.cs deleted file mode 100644 index ea5f369..0000000 --- a/ShareBubbles/ShareBubbles/UI/BubbleInteract.cs +++ /dev/null @@ -1,65 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.InteractionSystem.Base; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.InputManagement; -using UnityEngine; - -namespace NAK.ShareBubbles.UI; - -// The script 'NAK.ShareBubbles.UI.BubbleInteract' could not be instantiated! -// Must be added manually by ShareBubble creation... -public class BubbleInteract : Interactable -{ - public override bool IsInteractable - { - get - { - if (ViewManager.Instance.IsAnyMenuOpen) - return true; - - if (!MetaPort.Instance.isUsingVr - && CVRInputManager.Instance.unlockMouse) - return true; - - return false; - } - } - - public override bool IsInteractableWithinRange(Vector3 sourcePos) => true; - - public override void OnInteractDown(InteractionContext context, ControllerRay controllerRay) - { - // Not used - } - - public override void OnInteractUp(InteractionContext context, ControllerRay controllerRay) - { - if (PlayerSetup.Instance.GetCurrentPropSelectionMode() - != PlayerSetup.PropSelectionMode.None) - return; - - // Check if the player is holding a pickup on the same hand - if (controllerRay.grabbedObject != null - && controllerRay.grabbedObject.transform == transform) - { - // Use the pickup - GetComponentInParent().EquipContent(); - controllerRay.DropObject(); // Causes null ref in ControllerRay, but doesn't break anything - return; - } - - // Open the details page - GetComponentInParent().ViewDetailsPage(); - } - - public override void OnHoverEnter(InteractionContext context, ControllerRay controllerRay) - { - // Not used - } - - public override void OnHoverExit(InteractionContext context, ControllerRay controllerRay) - { - // Not used - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/FacePlayerCamDirection.cs b/ShareBubbles/ShareBubbles/UI/FacePlayerCamDirection.cs deleted file mode 100644 index e2c181c..0000000 --- a/ShareBubbles/ShareBubbles/UI/FacePlayerCamDirection.cs +++ /dev/null @@ -1,21 +0,0 @@ -using ABI_RC.Core.Player; -using UnityEngine; - -namespace NAK.ShareBubbles.UI; - -public class FacePlayerCamDirection : MonoBehaviour -{ - private const float lerpSpeed = 5.0f; - - private void LateUpdate() - { - Transform ourTransform = transform; - Vector3 playerCamPos = PlayerSetup.Instance.activeCam.transform.position; - Vector3 playerUp = PlayerSetup.Instance.transform.up; - - Vector3 direction = (playerCamPos - ourTransform.position).normalized; - - Quaternion targetRotation = Quaternion.LookRotation(direction, playerUp); - ourTransform.rotation = Quaternion.Slerp(ourTransform.rotation, targetRotation, Time.deltaTime * lerpSpeed); - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/FacePlayerDirectionAxis.cs b/ShareBubbles/ShareBubbles/UI/FacePlayerDirectionAxis.cs deleted file mode 100644 index 78acf94..0000000 --- a/ShareBubbles/ShareBubbles/UI/FacePlayerDirectionAxis.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ABI_RC.Core.Player; -using UnityEngine; - -namespace NAK.ShareBubbles.UI; - -public class FacePlayerDirectionAxis : MonoBehaviour -{ - private const float rotationSpeed = 5.0f; - - private void Update() - { - Transform ourTransform = transform; - - Vector3 ourUpDirection = ourTransform.up; - Vector3 playerDirection = PlayerSetup.Instance.GetPlayerPosition() - ourTransform.position; - - Vector3 projectedDirection = Vector3.ProjectOnPlane(playerDirection, ourUpDirection).normalized; - - Quaternion targetRotation = Quaternion.LookRotation(-projectedDirection, ourUpDirection); - ourTransform.rotation = Quaternion.Lerp(ourTransform.rotation, targetRotation, Time.deltaTime * rotationSpeed); - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/HexagonSpinner.cs b/ShareBubbles/ShareBubbles/UI/HexagonSpinner.cs deleted file mode 100644 index 64c8499..0000000 --- a/ShareBubbles/ShareBubbles/UI/HexagonSpinner.cs +++ /dev/null @@ -1,71 +0,0 @@ -using UnityEngine; - -namespace NAK.ShareBubbles.UI -{ - public class HexagonSpinner : MonoBehaviour - { - [Tooltip("Base distance from the center point to each child")] - [SerializeField] private float radius = 1f; - - [Tooltip("How much the radius pulses in/out")] - [SerializeField] private float radiusPulseAmount = 0.2f; - - [Tooltip("Speed of the animation")] - [SerializeField] private float animationSpeed = 3f; - - private Transform[] children; - private Vector3[] hexagonPoints; - - private void Start() - { - children = new Transform[6]; - for (int i = 0; i < 6; i++) - { - if (i < transform.childCount) - { - children[i] = transform.GetChild(i); - } - else - { - Debug.LogError("HexagonSpinner requires exactly 6 child objects!"); - enabled = false; - return; - } - } - - // Calculate base hexagon points (XY plane, Z-up) - hexagonPoints = new Vector3[6]; - for (int i = 0; i < 6; i++) - { - float angle = i * 60f * Mathf.Deg2Rad; - hexagonPoints[i] = new Vector3( - Mathf.Sin(angle), - Mathf.Cos(angle), - 0f - ); - } - } - - private void Update() - { - for (int i = 0; i < 6; i++) - { - float phaseOffset = (i * 60f * Mathf.Deg2Rad); - float currentPhase = (Time.time * animationSpeed) + phaseOffset; - - // Calculate radius variation - float currentRadius = radius + (Mathf.Sin(currentPhase) * radiusPulseAmount); - - // Position each child - Vector3 basePosition = hexagonPoints[i] * currentRadius; - children[i].localPosition = basePosition; - - // Calculate scale based on sine wave, but only show points on the "visible" half - // Remap sine wave from [-1,1] to [0,1] for cleaner scaling - float scaleMultiplier = Mathf.Sin(currentPhase); - scaleMultiplier = Mathf.Max(0f, scaleMultiplier); // Only positive values - children[i].localScale = Vector3.one * scaleMultiplier; - } - } - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/ReturnOnRelease.cs b/ShareBubbles/ShareBubbles/UI/ReturnOnRelease.cs deleted file mode 100644 index b6e1eda..0000000 --- a/ShareBubbles/ShareBubbles/UI/ReturnOnRelease.cs +++ /dev/null @@ -1,91 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.InteractionSystem.Base; -using UnityEngine; - -namespace NAK.ShareBubbles.UI; - -public class ReturnOnRelease : MonoBehaviour -{ - public float returnSpeed = 10.0f; - public float damping = 0.5f; - - private bool isReturning; - private Vector3 originalLocalPosition; - private Quaternion originalLocalRotation; - - private Vector3 positionVelocity = Vector3.zero; - private Vector3 angularVelocity = Vector3.zero; - - private Pickupable pickupable; - - private void Start() - { - Transform ourTransform = transform; - originalLocalPosition = ourTransform.localPosition; - originalLocalRotation = ourTransform.localRotation; - - // TODO: Instead of LateUpdate, use OnDrop to start a coroutine - pickupable = GetComponent(); - pickupable.onGrab.AddListener(OnPickupGrabbed); - pickupable.onDrop.AddListener(OnPickupRelease); - } - - public void OnPickupGrabbed(InteractionContext _) - { - isReturning = false; - } - - public void OnPickupRelease(InteractionContext _) - { - isReturning = true; - } - - private void LateUpdate() - { - if (isReturning) - { - // Smoothly damp position back to the original - Transform ourTransform = transform; - transform.localPosition = Vector3.SmoothDamp( - ourTransform.localPosition, - originalLocalPosition, - ref positionVelocity, - damping, - returnSpeed - ); - - // Smoothly damp rotation back to the original - transform.localRotation = SmoothDampQuaternion( - ourTransform.localRotation, - originalLocalRotation, - ref angularVelocity, - damping - ); - - // Stop returning when close enough - if (Vector3.Distance(transform.localPosition, originalLocalPosition) < 0.01f - && angularVelocity.magnitude < 0.01f) - { - isReturning = false; - transform.SetLocalPositionAndRotation(originalLocalPosition, originalLocalRotation); - } - } - } - - private Quaternion SmoothDampQuaternion(Quaternion current, Quaternion target, ref Vector3 velocity, float smoothTime) - { - // Decompose rotation to Euler angles for smoother interpolation - Vector3 currentEuler = current.eulerAngles; - Vector3 targetEuler = target.eulerAngles; - - // Perform SmoothDamp on each axis - Vector3 smoothedEuler = new Vector3( - Mathf.SmoothDampAngle(currentEuler.x, targetEuler.x, ref velocity.x, smoothTime), - Mathf.SmoothDampAngle(currentEuler.y, targetEuler.y, ref velocity.y, smoothTime), - Mathf.SmoothDampAngle(currentEuler.z, targetEuler.z, ref velocity.z, smoothTime) - ); - - // Convert back to Quaternion - return Quaternion.Euler(smoothedEuler); - } -} \ No newline at end of file diff --git a/ShareBubbles/ShareBubbles/UI/ShakeSoundController.cs b/ShareBubbles/ShareBubbles/UI/ShakeSoundController.cs deleted file mode 100644 index 1a0ed25..0000000 --- a/ShareBubbles/ShareBubbles/UI/ShakeSoundController.cs +++ /dev/null @@ -1,85 +0,0 @@ -using ABI_RC.Core; -using ABI_RC.Core.InteractionSystem.Base; -using UnityEngine; - -namespace NAK.ShareBubbles.UI; - -public class ShakeSoundController : MonoBehaviour -{ - [Header("Shake Detection")] - [SerializeField] private float minimumVelocityForShake = 5f; - [SerializeField] private float directionChangeThreshold = 0.8f; - [SerializeField] private float velocitySmoothing = 0.1f; - [SerializeField] private float minTimeBetweenShakes = 0.15f; - - [Header("Sound")] - [SerializeField] private AudioClip bellSound; - [SerializeField] [Range(0f, 1f)] private float minVolume = 0.8f; - [SerializeField] [Range(0f, 1f)] private float maxVolume = 1f; - [SerializeField] private float velocityToVolumeMultiplier = 0.1f; - - private AudioSource audioSource; - private Vector3 lastVelocity; - private Vector3 currentVelocity; - private Vector3 smoothedVelocity; - private float shakeTimer; - - private Pickupable _pickupable; - - private void Start() - { - audioSource = GetComponent(); - if (audioSource == null) audioSource = gameObject.AddComponent(); - audioSource.spatialize = true; - audioSource.spatialBlend = 1f; - audioSource.rolloffMode = AudioRolloffMode.Linear; - audioSource.maxDistance = 5f; - - _pickupable = GetComponent(); - - // Add source to prop mixer group so it can be muted - audioSource.outputAudioMixerGroup = RootLogic.Instance.propSfx; - - // TODO: Make jingle velocity scale with player playspace - // Make jingle velocity only sample local space, so OriginShift doesn't affect it - // Just make all this not bad... - } - - private void Update() - { - shakeTimer -= Time.deltaTime; - - if (!_pickupable.IsGrabbedByMe) - return; - - // Calculate raw velocity - currentVelocity = (transform.position - lastVelocity) / Time.deltaTime; - - // Smooth the velocity - smoothedVelocity = Vector3.Lerp(smoothedVelocity, currentVelocity, velocitySmoothing); - - // Only check for direction change if moving fast enough and cooldown has elapsed - if (smoothedVelocity.magnitude > minimumVelocityForShake && shakeTimer <= 0) - { - float directionChange = Vector3.Dot(smoothedVelocity.normalized, lastVelocity.normalized); - - if (directionChange < -directionChangeThreshold) - { - float shakePower = smoothedVelocity.magnitude * Mathf.Abs(directionChange); - PlayBellSound(shakePower); - shakeTimer = minTimeBetweenShakes; - } - } - - lastVelocity = transform.position; - } - - private void PlayBellSound(float intensity) - { - if (bellSound == null) return; - - float volume = Mathf.Clamp(intensity * velocityToVolumeMultiplier, minVolume, maxVolume); - audioSource.volume = volume; - audioSource.PlayOneShot(bellSound); - } -} \ No newline at end of file diff --git a/ShareBubbles/format.json b/ShareBubbles/format.json deleted file mode 100644 index 4c07d5d..0000000 --- a/ShareBubbles/format.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "_id": 244, - "name": "ShareBubbles", - "modversion": "1.1.6", - "gameversion": "2025r180", - "loaderversion": "0.7.2", - "modtype": "Mod", - "author": "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler, Luc", - "description": "Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network.\n### Features:\n- Can drop Share Bubbles linking to **any** Avatar or Prop page.\n - Share Bubbles can grant Permanent or Temporary shares to your **Private** Content if configured.\n### Important:\nThe claiming of content shares through Share Bubbles will likely fail if you do not allow content shares from non-friends in your ChilloutVR Account settings on the Hub!\n\n-# More information can be found on the [README](https://github.com/NotAKidoS/NAK_CVR_Mods/blob/main/ShareBubbles/README.md).", - "searchtags": [ - "share", - "bubbles", - "content", - "avatars", - "props" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ShareBubbles.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles/", - "changelog": "- Fixes for 2025r180", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/SmootherRay/Main.cs b/SmootherRay/Main.cs deleted file mode 100644 index 1d6d3f4..0000000 --- a/SmootherRay/Main.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using HarmonyLib; -using MelonLoader; - -namespace NAK.SmootherRay; - -// ChilloutVR adaptation of: -// https://github.com/kinsi55/BeatSaber_SmoothedController -// https://github.com/kinsi55/BeatSaber_SmoothedController/blob/master/LICENSE - -public class SmootherRayMod : MelonMod -{ - internal static MelonLogger.Instance Logger; - - #region Melon Preferences - - public static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(SmootherRayMod)); - - public static readonly MelonPreferences_Entry EntryEnabled = - Category.CreateEntry("Enable Smoothing", true, - description: "Enable or disable smoothing."); - - public static readonly MelonPreferences_Entry EntryMenuOnly = - Category.CreateEntry("Menu Only", false, - description: "Only use smoothing on Main Menu and Quick Menu. This will be fine for most users, but it may be desired on pickups & Unity UI elements too. When off it is best paired with WhereAmIPointing."); - - public static readonly MelonPreferences_Entry EntryPositionSmoothing = - Category.CreateEntry("Position Smoothing (3f)", 3f, - description: "How much to smooth position changes by. Use the slider to adjust the position smoothing factor. Range: 0 to 20."); - - public static readonly MelonPreferences_Entry EntryRotationSmoothing = - Category.CreateEntry("Rotation Smoothing (12f)", 12f, - description: "How much to smooth rotation changes by. Use the slider to adjust the rotation smoothing factor. Range: 0 to 20."); - - public static readonly MelonPreferences_Entry EntrySmallMovementThresholdAngle = - Category.CreateEntry("Small Angle Threshold (6f)", 6f, - description: "Angle difference to consider a 'small' movement. The less shaky your hands are, the lower you probably want to set this. This is probably the primary value you want to tweak. Use the slider to adjust the threshold angle. Range: 4 to 15."); - - public static readonly MelonPreferences_Entry EntrySmoothWhenHoldingPickup = - Category.CreateEntry("Smooth When Holding Pickup", false, - description: "Enable or disable smoothing when holding a pickup."); - - #endregion Melon Preferences - - #region Melon Events - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - ApplyPatches(typeof(PlayerSetup_Patches)); - ApplyPatches(typeof(ControllerSmoothing_Patches)); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } - - #endregion Melon Events - - #region Harmony Patches - - internal static class PlayerSetup_Patches - { - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))] - private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance) - { - __instance.vrLeftHandTracker.gameObject.AddComponent().ray = __instance.vrRayLeft; - __instance.vrRightHandTracker.gameObject.AddComponent().ray = __instance.vrRayRight; - } - } - - internal static class ControllerSmoothing_Patches - { - // SmootherRay - [HarmonyPrefix] - [HarmonyPatch(typeof(ControllerSmoothing), nameof(ControllerSmoothing.OnAppliedPoses))] - private static bool Prefix_ControllerSmoothing_OnAppliedPoses(ref ControllerSmoothing __instance) - => !EntryEnabled.Value; // SmootherRay method enforces identity local pos when disabled, so we skip it - } - - #endregion Harmony Patches -} \ No newline at end of file diff --git a/SmootherRay/Properties/AssemblyInfo.cs b/SmootherRay/Properties/AssemblyInfo.cs deleted file mode 100644 index c47196d..0000000 --- a/SmootherRay/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.SmootherRay.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.SmootherRay))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.SmootherRay))] - -[assembly: MelonInfo( - typeof(NAK.SmootherRay.SmootherRayMod), - nameof(NAK.SmootherRay), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SmootherRay" -)] - -[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.SmootherRay.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.7"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/SmootherRay/README.md b/SmootherRay/README.md deleted file mode 100644 index bf64836..0000000 --- a/SmootherRay/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# SmootherRay - -Smoothes your controller while the raycast lines are visible. -This is a CVR adaptation of a Beat Saber mod: [BeatSaber_SmoothedController](https://github.com/kinsi55/BeatSaber_SmoothedController) - -- An option is provided to only smooth when aiming at menus. -- Smoothing characteristics are completely configurable, but the defaults are basically perfect. -- Pairs well with the [WhereAmIPointing](https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing) mod. - -**Only supports OpenVR, not OpenXR.** - --# NOTE: This disables the shitty built-in Smooth Ray setting when the mod is enabled. Compare both & you'll see why. - ---- - -Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI. -https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games -~~~~ -> This mod is an independent creation and is 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. diff --git a/SmootherRay/SmootherRay.csproj b/SmootherRay/SmootherRay.csproj deleted file mode 100644 index d2a45fb..0000000 --- a/SmootherRay/SmootherRay.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - SmoothRay - - diff --git a/SmootherRay/format.json b/SmootherRay/format.json deleted file mode 100644 index 5e49dd0..0000000 --- a/SmootherRay/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 162, - "name": "SmootherRay", - "modversion": "1.0.6", - "gameversion": "2025r177", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Smoothes your controller while the raycast lines are visible.\nThis is a CVR adaptation of a Beat Saber mod: [BeatSaber_SmoothedController](https://github.com/kinsi55/BeatSaber_SmoothedController)\n\n- An option is provided to only smooth when aiming at menus.\n- Smoothing characteristics are completely configurable, but the defaults are basically perfect.\n\n**Only supports OpenVR, not OpenXR.**\n\n-# NOTE: This disables the shitty built-in Smooth Ray setting when the mod is enabled. Compare both & you'll see why.", - "searchtags": [ - "vr", - "ray", - "controller", - "smoothing" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/SmootherRay.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SmootherRay/", - "changelog": "- Fixes for 2025r179", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/Stickers/Integrations/BTKUI/BTKUILibExtensions.cs b/Stickers/Integrations/BTKUI/BTKUILibExtensions.cs new file mode 100644 index 0000000..1e16c93 --- /dev/null +++ b/Stickers/Integrations/BTKUI/BTKUILibExtensions.cs @@ -0,0 +1,103 @@ +using System.Reflection; +using BTKUILib; +using BTKUILib.UIObjects; +using BTKUILib.UIObjects.Components; +using BTKUILib.UIObjects.Objects; +using JetBrains.Annotations; +using MelonLoader; +using UnityEngine; + +namespace NAK.Stickers.Integrations; + +[PublicAPI] +public static class BTKUILibExtensions +{ + #region Icon Utils + + public static Stream GetIconStream(string iconName) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + string assemblyName = assembly.GetName().Name; + return assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}"); + } + + #endregion Icon Utils + + #region Enum Utils + + private static class EnumUtils + { + public static string[] GetPrettyEnumNames() where T : Enum + { + return Enum.GetNames(typeof(T)).Select(PrettyFormatEnumName).ToArray(); + } + + private static string PrettyFormatEnumName(string name) + { + // adds spaces before capital letters (excluding the first letter) + return System.Text.RegularExpressions.Regex.Replace(name, "(\\B[A-Z])", " $1"); + } + + public static int GetEnumIndex(T value) where T : Enum + { + return Array.IndexOf(Enum.GetValues(typeof(T)), value); + } + } + + #endregion Enum Utils + + #region Melon Preference Extensions + + public static ToggleButton AddMelonToggle(this Category category, MelonPreferences_Entry entry) + { + ToggleButton toggle = category.AddToggle(entry.DisplayName, entry.Description, entry.Value); + toggle.OnValueUpdated += b => entry.Value = b; + return toggle; + } + + public static SliderFloat AddMelonSlider(this Category category, MelonPreferences_Entry entry, float min, + float max, int decimalPlaces = 2, bool allowReset = true) + { + SliderFloat slider = category.AddSlider(entry.DisplayName, entry.Description, + Mathf.Clamp(entry.Value, min, max), min, max, decimalPlaces, entry.DefaultValue, allowReset); + slider.OnValueUpdated += f => entry.Value = f; + return slider; + } + + public static Button AddMelonStringInput(this Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) + { + Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); + button.OnPress += () => QuickMenuAPI.OpenKeyboard(entry.Value, s => entry.Value = s); + return button; + } + + public static Button AddMelonNumberInput(this Category category, MelonPreferences_Entry entry, string buttonIcon = "", ButtonStyle buttonStyle = ButtonStyle.TextOnly) + { + Button button = category.AddButton(entry.DisplayName, buttonIcon, entry.Description, buttonStyle); + button.OnPress += () => QuickMenuAPI.OpenNumberInput(entry.DisplayName, entry.Value, f => entry.Value = f); + return button; + } + + public static Category AddMelonCategory(this Page page, MelonPreferences_Entry entry, bool showHeader = true) + { + Category category = page.AddCategory(entry.DisplayName, showHeader, true, !entry.Value); + category.OnCollapse += b => entry.Value = !b; // more intuitive if pref value of true means category open + return category; + } + + public static MultiSelection CreateMelonMultiSelection(MelonPreferences_Entry entry) where TEnum : Enum + { + MultiSelection multiSelection = new( + entry.DisplayName, + EnumUtils.GetPrettyEnumNames(), + EnumUtils.GetEnumIndex(entry.Value) + ) + { + OnOptionUpdated = i => entry.Value = (TEnum)Enum.Parse(typeof(TEnum), Enum.GetNames(typeof(TEnum))[i]) + }; + + return multiSelection; + } + + #endregion Melon Preference Extensions +} \ No newline at end of file diff --git a/Stickers/Integrations/BTKUI/UIAddon.Category.DebugOptions.cs b/Stickers/Integrations/BTKUI/UIAddon.Category.DebugOptions.cs new file mode 100644 index 0000000..0a8fc32 --- /dev/null +++ b/Stickers/Integrations/BTKUI/UIAddon.Category.DebugOptions.cs @@ -0,0 +1,13 @@ +using BTKUILib.UIObjects; + +namespace NAK.Stickers.Integrations; + +public static partial class BTKUIAddon +{ + private static void Setup_DebugOptionsCategory() + { + Category debugCategory = _rootPage.AddMelonCategory(ModSettings.Hidden_Foldout_DebugCategory); + debugCategory.AddMelonToggle(ModSettings.Debug_NetworkInbound); + debugCategory.AddMelonToggle(ModSettings.Debug_NetworkOutbound); + } +} \ No newline at end of file diff --git a/Stickers/Integrations/BTKUI/UIAddon.Category.MiscOptions.cs b/Stickers/Integrations/BTKUI/UIAddon.Category.MiscOptions.cs deleted file mode 100644 index 3c6e385..0000000 --- a/Stickers/Integrations/BTKUI/UIAddon.Category.MiscOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BTKUILib; -using BTKUILib.UIObjects; -using BTKUILib.UIObjects.Components; -using NAK.Stickers.Utilities; - -namespace NAK.Stickers.Integrations; - -public static partial class BTKUIAddon -{ - private static void Setup_OtherOptionsCategory() - { - Category debugCategory = _rootPage.AddMelonCategory(ModSettings.Hidden_Foldout_MiscCategory); - debugCategory.AddMelonToggle(ModSettings.Debug_NetworkInbound); - debugCategory.AddMelonToggle(ModSettings.Debug_NetworkOutbound); - debugCategory.AddMelonToggle(ModSettings.Entry_FriendsOnly); - debugCategory.AddButton("Clear Thumbnail Cache", "Stickers-rubbish-bin", "Clear the cache of all loaded stickers.", ButtonStyle.TextWithIcon) - .OnPress += () => QuickMenuAPI.ShowConfirm("Clear Thumbnail Cache", "Are you sure you want to clear the Cohtml thumbnail cache for all stickers?", - () => - { - StickerCache.ClearCache(); - QuickMenuAPI.ShowAlertToast("Thumbnail cache cleared.", 2); - }); - } -} \ No newline at end of file diff --git a/Stickers/Integrations/BTKUI/UIAddon.Category.StickersMod.cs b/Stickers/Integrations/BTKUI/UIAddon.Category.StickersMod.cs index 0e9c0c6..03743bc 100644 --- a/Stickers/Integrations/BTKUI/UIAddon.Category.StickersMod.cs +++ b/Stickers/Integrations/BTKUI/UIAddon.Category.StickersMod.cs @@ -1,119 +1,87 @@ -using BTKUILib; -using BTKUILib.UIObjects; -using BTKUILib.UIObjects.Components; -using BTKUILib.UIObjects.Objects; - -namespace NAK.Stickers.Integrations; - -public static partial class BTKUIAddon -{ - private static Category _ourCategory; - - private static Button _placeStickersButton; - - private static readonly MultiSelection _sfxSelection = - MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_SelectedSFX); - - private static readonly MultiSelection _desktopKeybindSelection = - MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_PlaceBinding); - - private static readonly MultiSelection _tabDoubleClickSelection = - MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_TabDoubleClick); - - #region Category Setup - - private static void Setup_StickersModCategory() - { - _ourCategory = _rootPage.AddMelonCategory(ModSettings.Hidden_Foldout_SettingsCategory); - - _placeStickersButton = _ourCategory.AddButton("Place Stickers", "Stickers-magic-wand", "Place stickers via raycast.", ButtonStyle.TextWithIcon); - _placeStickersButton.OnPress += OnPlaceStickersButtonClick; - - Button clearSelfStickersButton = _ourCategory.AddButton("Clear Self", "Stickers-eraser", "Clear own stickers.", ButtonStyle.TextWithIcon); - clearSelfStickersButton.OnPress += OnClearSelfStickersButtonClick; - - Button clearAllStickersButton = _ourCategory.AddButton("Clear All", "Stickers-rubbish-bin", "Clear all stickers.", ButtonStyle.TextWithIcon); - clearAllStickersButton.OnPress += OnClearAllStickersButtonClick; - - Button openStickersFolderButton = _ourCategory.AddButton("Open Stickers Folder", "Stickers-folder", "Open UserData/Stickers folder in explorer. If above 256kb your image will automatically be downscaled for networking reasons.", ButtonStyle.TextWithIcon); - openStickersFolderButton.OnPress += OnOpenStickersFolderButtonClick; - - Button openStickerSFXButton = _ourCategory.AddButton("Sticker SFX", "Stickers-headset", "Choose the SFX used when a sticker is placed.", ButtonStyle.TextWithIcon); - openStickerSFXButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_sfxSelection); - - ToggleButton toggleDesktopKeybindButton = _ourCategory.AddToggle("Use Desktop Keybind", "Should the Desktop keybind be active.", ModSettings.Entry_UsePlaceBinding.Value); - Button openDesktopKeybindButton = _ourCategory.AddButton("Desktop Keybind", "Stickers-alphabet", "Choose the key binding to place stickers.", ButtonStyle.TextWithIcon); - openDesktopKeybindButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_desktopKeybindSelection); - toggleDesktopKeybindButton.OnValueUpdated += (b) => - { - ModSettings.Entry_UsePlaceBinding.Value = b; - openDesktopKeybindButton.Disabled = !b; - }; - - Button openTabDoubleClickButton = _ourCategory.AddButton("Tab Double Click", "Stickers-mouse", "Choose the action to perform when double clicking the Stickers tab.", ButtonStyle.TextWithIcon); - openTabDoubleClickButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_tabDoubleClickSelection); - } - - #endregion Category Setup - - #region Button Actions - - private static void OnPlaceStickersButtonClick() - { - if (!_isOurTabOpened) return; - - if (StickerSystem.Instance.IsRestrictedInstance) - { - QuickMenuAPI.ShowAlertToast("Stickers are not allowed in this world!", 2); - return; - } - - string mode = StickerSystem.Instance.IsInStickerMode ? "Exiting" : "Entering"; - QuickMenuAPI.ShowAlertToast($"{mode} sticker placement mode...", 2); - StickerSystem.Instance.IsInStickerMode = !StickerSystem.Instance.IsInStickerMode; - } - - private static void OnClearSelfStickersButtonClick() - { - if (!_isOurTabOpened) return; - QuickMenuAPI.ShowAlertToast("Clearing own stickers in world...", 2); - StickerSystem.Instance.ClearStickersSelf(); - } - - private static void OnClearAllStickersButtonClick() - { - if (!_isOurTabOpened) return; - QuickMenuAPI.ShowAlertToast("Clearing all stickers in world...", 2); - StickerSystem.Instance.ClearAllStickers(); - } - - private static void OnOpenStickersFolderButtonClick() - { - if (!_isOurTabOpened) return; - QuickMenuAPI.ShowAlertToast("Opening Stickers folder in Explorer...", 2); - StickerSystem.OpenStickersFolder(); - } - - public static void OnStickerRestrictionUpdated(bool isRestricted = false) //TODO: add Icon changing, Bono needs to expose the value first. - { - if (isRestricted) - { - _rootPage.MenuSubtitle = "Stickers... are sadly disabled in this world."; - - _placeStickersButton.Disabled = true; - _placeStickersButton.ButtonText = "Stickers Disabled"; - _placeStickersButton.ButtonTooltip = "This world is not allowing Stickers."; - _placeStickersButton.ButtonIcon = "Stickers-magic-wand-broken"; - return; - } - - _rootPage.MenuSubtitle = "Stickers! Double-click the tab to quickly toggle Sticker Mode."; - - _placeStickersButton.Disabled = false; - _placeStickersButton.ButtonText = "Place Stickers"; - _placeStickersButton.ButtonTooltip = "Place stickers via raycast."; - _placeStickersButton.ButtonIcon = "Stickers-magic-wand"; - } - - #endregion Button Actions +using BTKUILib; +using BTKUILib.UIObjects; +using BTKUILib.UIObjects.Components; +using BTKUILib.UIObjects.Objects; + +namespace NAK.Stickers.Integrations; + +public static partial class BTKUIAddon +{ + private static Category _ourCategory; + + private static readonly MultiSelection _sfxSelection = + BTKUILibExtensions.CreateMelonMultiSelection(ModSettings.Entry_SelectedSFX); + + private static readonly MultiSelection _desktopKeybindSelection = + BTKUILibExtensions.CreateMelonMultiSelection(ModSettings.Entry_PlaceBinding); + + #region Category Setup + + private static void Setup_StickersModCategory() + { + _ourCategory = _rootPage.AddMelonCategory(ModSettings.Hidden_Foldout_SettingsCategory); + + Button placeStickersButton = _ourCategory.AddButton("Place Stickers", "Stickers-magic-wand", "Place stickers via raycast.", ButtonStyle.TextWithIcon); + placeStickersButton.OnPress += OnPlaceStickersButtonClick; + + Button clearSelfStickersButton = _ourCategory.AddButton("Clear Self", "Stickers-eraser", "Clear own stickers.", ButtonStyle.TextWithIcon); + clearSelfStickersButton.OnPress += OnClearSelfStickersButtonClick; + + Button clearAllStickersButton = _ourCategory.AddButton("Clear All", "Stickers-rubbish-bin", "Clear all stickers.", ButtonStyle.TextWithIcon); + clearAllStickersButton.OnPress += OnClearAllStickersButtonClick; + + Button openStickersFolderButton = _ourCategory.AddButton("Open Stickers Folder", "Stickers-folder", "Open UserData/Stickers folder in explorer. If above 256kb your image will automatically be downscaled for networking reasons.", ButtonStyle.TextWithIcon); + openStickersFolderButton.OnPress += OnOpenStickersFolderButtonClick; + + Button openMultiSelectionButton = _ourCategory.AddButton("Sticker SFX", "Stickers-headset", "Choose the SFX used when a sticker is placed.", ButtonStyle.TextWithIcon); + openMultiSelectionButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_sfxSelection); + + _ourCategory.AddMelonToggle(ModSettings.Entry_HapticsOnPlace); + + ToggleButton toggleDesktopKeybindButton = _ourCategory.AddToggle("Use Desktop Keybind", "Should the Desktop keybind be active.", ModSettings.Entry_UsePlaceBinding.Value); + Button openDesktopKeybindButton = _ourCategory.AddButton("Desktop Keybind", "Stickers-alphabet", "Choose the key binding to place stickers.", ButtonStyle.TextWithIcon); + openDesktopKeybindButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_desktopKeybindSelection); + toggleDesktopKeybindButton.OnValueUpdated += (b) => + { + ModSettings.Entry_UsePlaceBinding.Value = b; + openDesktopKeybindButton.Disabled = !b; + }; + + //AddMelonToggle(ref _ourCategory, ModSettings.Entry_UsePlaceBinding); + } + + #endregion Category Setup + + #region Button Actions + + private static void OnPlaceStickersButtonClick() + { + if (!_isOurTabOpened) return; + string mode = StickerSystem.Instance.IsInStickerMode ? "Exiting" : "Entering"; + QuickMenuAPI.ShowAlertToast($"{mode} sticker placement mode...", 2); + StickerSystem.Instance.IsInStickerMode = !StickerSystem.Instance.IsInStickerMode; + } + + private static void OnClearSelfStickersButtonClick() + { + if (!_isOurTabOpened) return; + QuickMenuAPI.ShowAlertToast("Clearing own stickers in world...", 2); + StickerSystem.Instance.ClearStickersSelf(); + } + + private static void OnClearAllStickersButtonClick() + { + if (!_isOurTabOpened) return; + QuickMenuAPI.ShowAlertToast("Clearing all stickers in world...", 2); + StickerSystem.Instance.ClearAllStickers(); + } + + private static void OnOpenStickersFolderButtonClick() + { + if (!_isOurTabOpened) return; + QuickMenuAPI.ShowAlertToast("Opening Stickers folder in Explorer...", 2); + StickerSystem.OpenStickersFolder(); + } + + #endregion Button Actions } \ No newline at end of file diff --git a/Stickers/Integrations/BTKUI/UIAddon.Main.cs b/Stickers/Integrations/BTKUI/UIAddon.Main.cs index 327949b..8bce9c0 100644 --- a/Stickers/Integrations/BTKUI/UIAddon.Main.cs +++ b/Stickers/Integrations/BTKUI/UIAddon.Main.cs @@ -1,117 +1,92 @@ -using BTKUILib; -using BTKUILib.UIObjects; -using NAK.Stickers.Networking; -using NAK.Stickers.Utilities; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace NAK.Stickers.Integrations; - -public static partial class BTKUIAddon -{ - private static Page _rootPage; - private static string _rootPageElementID; - - private static bool _isOurTabOpened; - - public static void Initialize() - { - Setup_Icons(); - Setup_StickerModTab(); - Setup_PlayerOptionsPage(); - } - - #region Setup - - private static void Setup_Icons() - { - Assembly assembly = Assembly.GetExecutingAssembly(); - string assemblyName = assembly.GetName().Name; - - // All icons used - https://www.flaticon.com/authors/gohsantosadrive - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-alphabet", GetIconStream("Gohsantosadrive_Icons.Stickers-alphabet.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-eraser", GetIconStream("Gohsantosadrive_Icons.Stickers-eraser.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-folder", GetIconStream("Gohsantosadrive_Icons.Stickers-folder.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-headset", GetIconStream("Gohsantosadrive_Icons.Stickers-headset.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magnifying-glass", GetIconStream("Gohsantosadrive_Icons.Stickers-magnifying-glass.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand", GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand-broken", GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand-broken.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-mouse", GetIconStream("Gohsantosadrive_Icons.Stickers-mouse.png")); - //QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-pencil", GetIconStream("Gohsantosadrive_Icons.Stickers-pencil.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle", GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle.png")); - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle-disabled", GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle-disabled.png")); // disabled Sticker Puzzle - QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-rubbish-bin", GetIconStream("Gohsantosadrive_Icons.Stickers-rubbish-bin.png")); - - return; - Stream GetIconStream(string iconName) => assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}"); - } - - private static void Setup_StickerModTab() - { - _rootPage = new Page(ModSettings.ModName, ModSettings.SM_SettingsCategory, true, "Stickers-Puzzle") // sticker icon will be left blank as it is updated on world join, AFTER Icon value is exposed.. - { - MenuTitle = ModSettings.SM_SettingsCategory, - MenuSubtitle = "", // left this blank as it is defined when the world loads - }; - - _rootPageElementID = _rootPage.ElementID; - - QuickMenuAPI.OnTabChange += OnTabChange; - ModNetwork.OnTextureOutboundStateChanged += (isSending) => - { - if (_isOurTabOpened && isSending) QuickMenuAPI.ShowAlertToast("Sending Sticker over Mod Network...", 2); - //_rootPage.Disabled = isSending; // TODO: fix being able to select stickers while sending - }; - - StickerSystem.OnStickerLoaded += (slotIndex, imageRelativePath) => - { - if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast($"Sticker loaded: {imageRelativePath}", 2); - _stickerSelectionButtons[slotIndex].ButtonIcon = StickerCache.GetBtkUiIconName(imageRelativePath); - }; - - StickerSystem.OnStickerLoadFailed += (slotIndex, error) => - { - if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast(error, 3); - }; - - Setup_StickersModCategory(); - Setup_StickerSelectionCategory(); - Setup_OtherOptionsCategory(); - } - - #endregion Setup - - #region Double-Click Place Sticker - - private static DateTime lastTime = DateTime.Now; - - private static void OnTabChange(string newTab, string previousTab) - { - _isOurTabOpened = newTab == _rootPageElementID; - if (!_isOurTabOpened) return; - - TimeSpan timeDifference = DateTime.Now - lastTime; - if (timeDifference.TotalSeconds <= 0.5) - { - switch (ModSettings.Entry_TabDoubleClick.Value) - { - default: - case TabDoubleClick.ToggleStickerMode: - OnPlaceStickersButtonClick(); - break; - case TabDoubleClick.ClearAllStickers: - OnClearAllStickersButtonClick(); - break; - case TabDoubleClick.ClearSelfStickers: - OnClearSelfStickersButtonClick(); - break; - case TabDoubleClick.None: - break; - } - return; - } - lastTime = DateTime.Now; - } - - #endregion Double-Click Place Sticker +using BTKUILib; +using BTKUILib.UIObjects; +using NAK.Stickers.Networking; +using NAK.Stickers.Utilities; + +namespace NAK.Stickers.Integrations; + +public static partial class BTKUIAddon +{ + private static Page _rootPage; + private static string _rootPageElementID; + + private static bool _isOurTabOpened; + + public static void Initialize() + { + Setup_Icons(); + Setup_StickerModTab(); + Setup_PlayerOptionsPage(); + } + + #region Initialization + + private static void Setup_Icons() + { + // All icons used - https://www.flaticon.com/authors/gohsantosadrive + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-alphabet", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-alphabet.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-eraser", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-eraser.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-folder", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-folder.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-headset", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-headset.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magnifying-glass", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-magnifying-glass.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-pencil", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-pencil.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle.png")); + QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-rubbish-bin", BTKUILibExtensions.GetIconStream("Gohsantosadrive_Icons.Stickers-rubbish-bin.png")); + } + + private static void Setup_StickerModTab() + { + _rootPage = new Page(ModSettings.ModName, ModSettings.SM_SettingsCategory, true, "Stickers-puzzle") + { + MenuTitle = ModSettings.SM_SettingsCategory, + MenuSubtitle = "Stickers! Double-click the tab to quickly toggle Sticker Mode.", + }; + + _rootPageElementID = _rootPage.ElementID; + + QuickMenuAPI.OnTabChange += OnTabChange; + ModNetwork.OnTextureOutboundStateChanged += (isSending) => + { + if (_isOurTabOpened && isSending) QuickMenuAPI.ShowAlertToast("Sending Sticker over Mod Network...", 2); + //_rootPage.Disabled = isSending; // TODO: fix being able to select stickers while sending + }; + + StickerSystem.OnStickerLoaded += (slotIndex, imageRelativePath) => + { + if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast($"Sticker loaded: {imageRelativePath}", 2); + _stickerSelectionButtons[slotIndex].ButtonIcon = StickerCache.GetBtkUiIconName(imageRelativePath); + }; + + StickerSystem.OnStickerLoadFailed += (slotIndex, error) => + { + if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast(error, 3); + }; + + Setup_StickersModCategory(); + Setup_StickerSelectionCategory(); + Setup_DebugOptionsCategory(); + } + + #endregion + + #region Double-Click Place Sticker + + private static DateTime lastTime = DateTime.Now; + + private static void OnTabChange(string newTab, string previousTab) + { + _isOurTabOpened = newTab == _rootPageElementID; + if (!_isOurTabOpened) return; + + TimeSpan timeDifference = DateTime.Now - lastTime; + if (timeDifference.TotalSeconds <= 0.5) + { + StickerSystem.Instance.IsInStickerMode = !StickerSystem.Instance.IsInStickerMode; + return; + } + lastTime = DateTime.Now; + } + + #endregion Double-Click Place Sticker } \ No newline at end of file diff --git a/Stickers/Integrations/BTKUI/UIAddon.Page.PlayerOptions.cs b/Stickers/Integrations/BTKUI/UIAddon.Page.PlayerOptions.cs index efc7a8e..f44fc40 100644 --- a/Stickers/Integrations/BTKUI/UIAddon.Page.PlayerOptions.cs +++ b/Stickers/Integrations/BTKUI/UIAddon.Page.PlayerOptions.cs @@ -15,8 +15,8 @@ public static partial class BTKUIAddon { Category category = QuickMenuAPI.PlayerSelectPage.AddCategory(ModSettings.SM_SettingsCategory, ModSettings.ModName); - Button identifyButton = category.AddButton("Identify Stickers", "Stickers-magnifying-glass", "Identify this players stickers by making them flash."); - identifyButton.OnPress += OnPressIdentifyPlayerStickersButton; + //Button identifyButton = category.AddButton("Identify Stickers", "Stickers-magnifying-glass", "Identify this players stickers by making them flash."); + //identifyButton.OnPress += OnPressIdentifyPlayerStickersButton; Button clearStickersButton = category.AddButton("Clear Stickers", "Stickers-eraser", "Clear this players stickers."); clearStickersButton.OnPress += OnPressClearSelectedPlayerStickersButton; @@ -30,11 +30,11 @@ public static partial class BTKUIAddon #region Callbacks - private static void OnPressIdentifyPlayerStickersButton() - { - if (string.IsNullOrEmpty(QuickMenuAPI.SelectedPlayerID)) return; - StickerSystem.Instance.OnStickerIdentifyReceived(QuickMenuAPI.SelectedPlayerID); - } + // private static void OnPressIdentifyPlayerStickersButton() + // { + // if (string.IsNullOrEmpty(QuickMenuAPI.SelectedPlayerID)) return; + // StickerSystem.Instance.OnStickerIdentifyReceived(QuickMenuAPI.SelectedPlayerID); + // } private static void OnPressClearSelectedPlayerStickersButton() { diff --git a/Stickers/Integrations/BTKUI/UIAddon.Page.StickerSelect.cs b/Stickers/Integrations/BTKUI/UIAddon.Page.StickerSelect.cs index c36c8cb..1874ccb 100644 --- a/Stickers/Integrations/BTKUI/UIAddon.Page.StickerSelect.cs +++ b/Stickers/Integrations/BTKUI/UIAddon.Page.StickerSelect.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using BTKUILib; +using BTKUILib; using BTKUILib.UIObjects; using BTKUILib.UIObjects.Components; using MTJobSystem; @@ -18,9 +17,7 @@ public static partial class BTKUIAddon private static Category _fileCategory; private static Category _folderCategory; - private static TextBlock _noFilesTextBlock; - private const int MAX_BUTTONS = 512; // cohtml literally will start to explode private static Button[] _fileButtons = new Button[80]; // 100 files, will resize if needed private static Button[] _folderButtons = new Button[20]; // 20 folders, will resize if needed @@ -50,12 +47,10 @@ public static partial class BTKUIAddon // Create page _ourDirectoryBrowserPage = Page.GetOrCreatePage(ModSettings.ModName, "Directory Browser"); QuickMenuAPI.AddRootPage(_ourDirectoryBrowserPage); - + // Setup categories _folderCategory = _ourDirectoryBrowserPage.AddCategory("Subdirectories"); _fileCategory = _ourDirectoryBrowserPage.AddCategory("Images"); - _noFilesTextBlock = _fileCategory.AddTextBlock("No images found in this directory. You can add your own images and subfolders to the `UserData/Stickers/` folder."); - _noFilesTextBlock.Hidden = true; SetupFolderButtons(); SetupFileButtons(); @@ -91,7 +86,6 @@ public static partial class BTKUIAddon string absolutePath = Path.Combine(_curDirectoryInfo.FullName, button.ButtonTooltip[5..]); string relativePath = Path.GetRelativePath(_initialDirectory, absolutePath); StickerSystem.Instance.LoadImage(relativePath, _curSelectedSticker); - _rootPage.OpenPage(true); // close the directory browser to artificially limit loading speed }; _fileButtons[i] = button; } @@ -166,54 +160,47 @@ public static partial class BTKUIAddon return; } _stickerSelectionButtonDoubleClickTime = Time.time; - - // quick menu notification - QuickMenuAPI.ShowAlertToast($"Selected sticker slot {index + 1}", 1); } private static void OpenStickerSelectionForSlot(int index) { if (IsPopulatingPage) return; _curSelectedSticker = index; - _initialDirectory = StickerSystem.GetStickersFolderPath(); // creates folder if needed (lazy fix) _curDirectoryInfo = new DirectoryInfo(_initialDirectory); _ourDirectoryBrowserPage.OpenPage(false, true); } private static void PopulateMenuItems() { - //StickerMod.Logger.Msg("Populating menu items."); + // StickerMod.Logger.Msg("Populating menu items."); try { - Thread.CurrentThread.IsBackground = false; // working around bug in MTJobManager - var directories = _curDirectoryInfo.GetDirectories(); var files = _curDirectoryInfo.GetFiles(); MTJobManager.RunOnMainThread("PopulateMenuItems", () => { // resize the arrays to the max amount of buttons - int foldersCount = Mathf.Min(directories.Length, MAX_BUTTONS); - if (foldersCount > _folderButtons.Length) + int difference = directories.Length - _folderButtons.Length; + if (difference > 0) { - int folderEndIdx = _folderButtons.Length; - Array.Resize(ref _folderButtons, foldersCount); - SetupFolderButtons(folderEndIdx); + Array.Resize(ref _folderButtons, directories.Length); + SetupFolderButtons(difference); + StickerMod.Logger.Msg($"Resized folder buttons to {directories.Length}"); } - int filesCount = Mathf.Min(files.Length, MAX_BUTTONS); - if (filesCount > _fileButtons.Length) + difference = files.Length - _fileButtons.Length; + if (difference > 0) { - int fileEndIdx = _fileButtons.Length; - Array.Resize(ref _fileButtons, filesCount); - SetupFileButtons(fileEndIdx); + Array.Resize(ref _fileButtons, files.Length); + SetupFileButtons(difference); + StickerMod.Logger.Msg($"Resized file buttons to {files.Length}"); } - _folderCategory.Hidden = foldersCount == 0; - _folderCategory.CategoryName = $"Subdirectories ({foldersCount})"; - //_fileCategory.Hidden = filesCount == 0; - _fileCategory.CategoryName = $"Images ({filesCount})"; - _noFilesTextBlock.Hidden = filesCount > 0; + _folderCategory.Hidden = directories.Length == 0; + _folderCategory.CategoryName = $"Subdirectories ({directories.Length})"; + _fileCategory.Hidden = files.Length == 0; + _fileCategory.CategoryName = $"Images ({files.Length})"; }); PopulateFolders(directories); @@ -226,7 +213,6 @@ public static partial class BTKUIAddon finally { IsPopulatingPage = false; - Thread.CurrentThread.IsBackground = true; // working around bug in MTJobManager } } @@ -234,15 +220,13 @@ public static partial class BTKUIAddon { for (int i = 0; i < _folderButtons.Length; i++) { + Button button = _folderButtons[i]; if (i >= directories.Count) break; - - Button button = _folderButtons[i]; - //if (button == null) continue; // Array resized, excess buttons are generating - + button.ButtonText = directories[i].Name; button.ButtonTooltip = $"Open {directories[i].Name}"; - MTJobManager.RunAsyncOnMainThread("PopulateMenuItems", () => button.Hidden = false); + MTJobManager.RunOnMainThread("PopulateMenuItems", () => button.Hidden = false); if (i <= 16) Thread.Sleep(10); // For the pop-in effect } @@ -252,9 +236,10 @@ public static partial class BTKUIAddon { for (int i = 0; i < _fileButtons.Length; i++) { + Button button = _fileButtons[i]; if (i >= files.Count) break; - + FileInfo fileInfo = files[i]; if (!SUPPORTED_IMAGE_EXTENSIONS.Contains(fileInfo.Extension.ToLower())) @@ -263,9 +248,6 @@ public static partial class BTKUIAddon string relativePath = Path.GetRelativePath(_initialDirectory, fileInfo.FullName); string relativePathWithoutExtension = relativePath[..^fileInfo.Extension.Length]; - Button button = _fileButtons[i]; - //if (button == null) continue; // Array resized, excess buttons are generating - button.ButtonTooltip = $"Load {fileInfo.Name}"; // Do not change "Load " prefix, we extract file name if (StickerCache.IsThumbnailAvailable(relativePathWithoutExtension)) @@ -278,7 +260,7 @@ public static partial class BTKUIAddon StickerCache.EnqueueThumbnailGeneration(fileInfo, button); } - MTJobManager.RunAsyncOnMainThread("PopulateMenuItems", () => button.Hidden = false); + MTJobManager.RunOnMainThread("PopulateMenuItems", () => button.Hidden = false); if (i <= 16) Thread.Sleep(10); // For the pop-in effect } diff --git a/Stickers/Main.cs b/Stickers/Main.cs index 1234f10..310e198 100644 --- a/Stickers/Main.cs +++ b/Stickers/Main.cs @@ -1,8 +1,4 @@ -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 ABI_RC.Core.Player; using MelonLoader; using NAK.Stickers.Integrations; using NAK.Stickers.Networking; @@ -24,10 +20,9 @@ public class StickerMod : MelonMod ModSettings.Initialize(); StickerSystem.Initialize(); - ApplyPatches(typeof(Patches.PlayerSetup_Patches)); - ApplyPatches(typeof(Patches.ControllerRay_Patches)); - ApplyPatches(typeof(Patches.ShaderFilterHelper_Patches)); - ApplyPatches(typeof(Patches.CVRTools_Patches)); + ApplyPatches(typeof(Patches.PlayerSetupPatches)); + ApplyPatches(typeof(Patches.ControllerRayPatches)); + ApplyPatches(typeof(Patches.ShaderFilterHelperPatches)); LoadAssetBundle(); @@ -36,28 +31,11 @@ public class StickerMod : MelonMod public override void OnUpdate() { - if (StickerSystem.Instance == null) - return; - - if (StickerSystem.Instance.IsInStickerMode) - { - if (Input.mouseScrollDelta.y != 0f - && Cursor.lockState == CursorLockMode.Locked // prevent scrolling while in menus - && !CVRInputManager.Instance.zoom) // prevent scrolling while using scroll zoom - StickerSystem.Instance.SelectedStickerSlot += (int)Input.mouseScrollDelta.y; - - StickerSystem.Instance.UpdateStickerPreview(); // flashy flash - } - if (!ModSettings.Entry_UsePlaceBinding.Value) return; if (!Input.GetKeyDown((KeyCode)ModSettings.Entry_PlaceBinding.Value)) return; - - if (CVRInputManager.Instance.textInputFocused - || ViewManager.Instance.textInputFocused) // BRUH - return; // prevent placing stickers while typing StickerSystem.Instance.PlaceStickerFromControllerRay(PlayerSetup.Instance.activeCam.transform); } diff --git a/Stickers/ModSettings.cs b/Stickers/ModSettings.cs index 0ea6971..8c15b5c 100644 --- a/Stickers/ModSettings.cs +++ b/Stickers/ModSettings.cs @@ -11,7 +11,7 @@ public static class ModSettings internal const string SM_SettingsCategory = "Stickers Mod"; private const string SM_SelectionCategory = "Sticker Selection"; - private const string MISC_SettingsCategory = "Miscellaneous Options"; + private const string DEBUG_SettingsCategory = "Debug Options"; internal const int MaxStickerSlots = 4; @@ -27,14 +27,17 @@ public static class ModSettings internal static readonly MelonPreferences_Entry Hidden_Foldout_SelectionCategory = Category.CreateEntry("hidden_foldout_selection", true, is_hidden: true, display_name: SM_SelectionCategory, description: "Foldout state for Sticker selection."); - - internal static readonly MelonPreferences_Entry Hidden_Foldout_MiscCategory = - Category.CreateEntry("hidden_foldout_miscellaneous", false, is_hidden: true, display_name: MISC_SettingsCategory, description: "Foldout state for Miscellaneous settings."); + + internal static readonly MelonPreferences_Entry Hidden_Foldout_DebugCategory = + Category.CreateEntry("hidden_foldout_debug", false, is_hidden: true, display_name: DEBUG_SettingsCategory, description: "Foldout state for Debug settings."); #endregion Hidden Foldout Entries #region Stickers Mod Settings + internal static readonly MelonPreferences_Entry Entry_HapticsOnPlace = + Category.CreateEntry("haptics_on_place", true, "Haptics On Place", "Enable haptic feedback when placing stickers."); + internal static readonly MelonPreferences_Entry Entry_PlayerUpAlignmentThreshold = Category.CreateEntry("player_up_alignment_threshold", 20f, "Player Up Alignment Threshold", "The threshold the controller roll can be within to align perfectly with the player up vector. Set to 0f to always align to controller up."); @@ -47,17 +50,11 @@ public static class ModSettings internal static readonly MelonPreferences_Entry Entry_PlaceBinding = Category.CreateEntry("place_binding", KeyBind.G, "Sticker Bind", "The key binding to place stickers."); - internal static readonly MelonPreferences_Entry Entry_TabDoubleClick = - Category.CreateEntry("tab_double_click", TabDoubleClick.ToggleStickerMode, "Tab Double Click", "The action to perform when double clicking the Stickers tab."); - internal static readonly MelonPreferences_Entry Hidden_SelectedStickerNames = - Category.CreateEntry("selected_sticker_name", new[] { "", "", "", "" }, + Category.CreateEntry("selected_sticker_name", Array.Empty(), display_name: "Selected Sticker Name", description: "The name of the sticker selected for stickering.", is_hidden: true); - - internal static readonly MelonPreferences_Entry Entry_FriendsOnly = - Category.CreateEntry("friends_only", false, "Friends Only", "Only allow friends to use stickers."); #endregion Stickers Mod Settings @@ -78,11 +75,6 @@ public static class ModSettings // ensure sticker slots are initialized to the correct size string[] selectedStickerNames = Hidden_SelectedStickerNames.Value; if (selectedStickerNames.Length != MaxStickerSlots) Array.Resize(ref selectedStickerNames, MaxStickerSlots); - - // ensure theres no null entries so toml shuts the fuck up - for (int i = 0; i < selectedStickerNames.Length; i++) - selectedStickerNames[i] ??= ""; - Hidden_SelectedStickerNames.Value = selectedStickerNames; foreach (var selectedSticker in selectedStickerNames) diff --git a/Stickers/Patches.cs b/Stickers/Patches.cs index 0bb5921..717ebb2 100644 --- a/Stickers/Patches.cs +++ b/Stickers/Patches.cs @@ -1,5 +1,4 @@ -using ABI_RC.Core; -using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.IO; using ABI_RC.Core.Player; using ABI_RC.Core.Savior; @@ -8,7 +7,7 @@ using UnityEngine; namespace NAK.Stickers.Patches; -internal static class PlayerSetup_Patches +internal static class PlayerSetupPatches { [HarmonyPostfix] [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.GetCurrentPropSelectionMode))] @@ -18,7 +17,7 @@ internal static class PlayerSetup_Patches } } -internal static class ControllerRay_Patches +internal static class ControllerRayPatches { [HarmonyPrefix] [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.HandlePropSpawn))] @@ -27,17 +26,15 @@ internal static class ControllerRay_Patches if (!StickerSystem.Instance.IsInStickerMode) return; - StickerSystem.Instance.PlaceStickerFromControllerRay(__instance.rayDirectionTransform, __instance.hand, true); // preview - if (__instance._gripDown) StickerSystem.Instance.IsInStickerMode = false; - if (__instance._hitUIInternal || !__instance._interactDown) + if (__instance._hitUIInternal || !__instance._interactDown) return; StickerSystem.Instance.PlaceStickerFromControllerRay(__instance.rayDirectionTransform, __instance.hand); } } -internal static class ShaderFilterHelper_Patches +internal static class ShaderFilterHelperPatches { [HarmonyPrefix] [HarmonyPatch(typeof(ShaderFilterHelper), nameof(ShaderFilterHelper.SetupFilter))] @@ -49,15 +46,4 @@ internal static class ShaderFilterHelper_Patches StickerMod.Logger.Warning("ExperimentalShaderLimitEnabled found to be true. Disabling setting to prevent crashes when spawning stickers!"); MetaPort.Instance.settings.SetSettingsBool("ExperimentalShaderLimitEnabled", false); } -} - -internal static class CVRTools_Patches -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(CVRTools), nameof(CVRTools.ReplaceShaders), typeof(Material), typeof(string))] - private static bool Prefix_CVRTools_ReplaceShaders(Material material, string fallbackShaderName = "") - { - if (material == null || material.shader == null) return true; - return material.shader != StickerMod.DecalSimpleShader; // prevent replacing decals with fallback shader - } } \ No newline at end of file diff --git a/Stickers/Properties/AssemblyInfo.cs b/Stickers/Properties/AssemblyInfo.cs index db9f15b..994904f 100644 --- a/Stickers/Properties/AssemblyInfo.cs +++ b/Stickers/Properties/AssemblyInfo.cs @@ -1,32 +1,32 @@ -using NAK.Stickers.Properties; -using MelonLoader; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.Stickers))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.Stickers))] - -[assembly: MelonInfo( - typeof(NAK.Stickers.StickerMod), - nameof(NAK.Stickers), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Stickers" -)] - -[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.Stickers.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.9"; - public const string Author = "NotAKidoS, SketchFoxsky"; +using NAK.Stickers.Properties; +using MelonLoader; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.Stickers))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.Stickers))] + +[assembly: MelonInfo( + typeof(NAK.Stickers.StickerMod), + nameof(NAK.Stickers), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Stickers" +)] + +[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.Stickers.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.1"; + public const string Author = "Exterrata & NotAKidoS"; } \ No newline at end of file diff --git a/Stickers/README.md b/Stickers/README.md index ae0f4ae..9eab558 100644 --- a/Stickers/README.md +++ b/Stickers/README.md @@ -5,10 +5,6 @@ Stickers! Allows you to place small images on any surface. Requires both users t ### How to use Any image placed in the `UserData/Stickers/` folder will be available to choose from within the BTKUI tab. Once you’ve selected an image, enter Sticker Mode or use the Desktop Binding to start placing the sticker. Remote clients running the mod will automatically request the image data if they do not have it stored locally. -### Please read the tooltips -- You have 4 "Sticker Slots". -- You double-click or hold a "Sticker Slot" to select an image from the `UserData/Stickers/` folder. - ### Limitations - Only PNG, JPG, & JPEG images are supported. - While it would be cool to send gifs, I don't want to abuse Mod Network that much lol. @@ -18,15 +14,6 @@ Any image placed in the `UserData/Stickers/` folder will be available to choose - Image dimensions should be a power of 2 (e.g. 512x512, 1024x1024). - If the image exceeds the size limit or is not a power of 2 the mod will automatically resize it. - The automatic resizing may result in loss of quality (or may just fail), so it is recommended to resize the image yourself before placing it in the `UserData/Stickers/` folder. -- Only 512 max images per folder (otherwise Cohtml gets very upset). -- Requires the experimental Shader Safety Settings to be disabled as it will cause crashes when decals attempt to generate on GPU. - - The mod will automatically disable this setting when it is enabled on startup. - -### Restrictions -- Full Restriction. - - To disable Stickers for the whole world, name an empty GameObject "**[DisableStickers]**". -- Partial Restriction. - - To keep stickers enabled but not allowing it on certain objects, add the "**[NoSticker]**" tag to the GameObject name. ## Attributions - All icons used are by [Gohsantosadrive]() on Flaticon. diff --git a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-magic-wand-broken.png b/Stickers/Resources/Gohsantosadrive_Icons/Stickers-magic-wand-broken.png deleted file mode 100644 index a8e222c..0000000 Binary files a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-magic-wand-broken.png and /dev/null differ diff --git a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-mouse.png b/Stickers/Resources/Gohsantosadrive_Icons/Stickers-mouse.png deleted file mode 100644 index 1315c49..0000000 Binary files a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-mouse.png and /dev/null differ diff --git a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-pencil.png b/Stickers/Resources/Gohsantosadrive_Icons/Stickers-pencil.png new file mode 100644 index 0000000..8dc6558 Binary files /dev/null and b/Stickers/Resources/Gohsantosadrive_Icons/Stickers-pencil.png differ diff --git a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-puzzle-disabled.png b/Stickers/Resources/Gohsantosadrive_Icons/Stickers-puzzle-disabled.png deleted file mode 100644 index 600038b..0000000 Binary files a/Stickers/Resources/Gohsantosadrive_Icons/Stickers-puzzle-disabled.png and /dev/null differ diff --git a/Stickers/Stickers.csproj b/Stickers/Stickers.csproj index e4f12ab..19f88ea 100644 --- a/Stickers/Stickers.csproj +++ b/Stickers/Stickers.csproj @@ -4,7 +4,7 @@ net48 - TRACE;TRACE;UNITY_2017_1_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2019_3_OR_NEWER;USE_BURST;USE_NEWMATHS;USE_BURST_REALLY;USEDFORSTICKERMOD; + TRACE;TRACE;UNITY_2017_1_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2019_3_OR_NEWER;USE_BURST;USE_NEWMATHS;USE_BURST_REALLY; @@ -22,6 +22,7 @@ + @@ -30,12 +31,6 @@ - - - - - - diff --git a/Stickers/Stickers/Enums/StickerTabDoubleClick.cs b/Stickers/Stickers/Enums/StickerTabDoubleClick.cs deleted file mode 100644 index ad24b85..0000000 --- a/Stickers/Stickers/Enums/StickerTabDoubleClick.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace NAK.Stickers; - -internal enum TabDoubleClick -{ - ToggleStickerMode, - ClearAllStickers, - ClearSelfStickers, - None -} \ No newline at end of file diff --git a/Stickers/Stickers/Networking/ModNetwork.Constants.cs b/Stickers/Stickers/Networking/ModNetwork.Constants.cs index 5cc7a16..2d58a94 100644 --- a/Stickers/Stickers/Networking/ModNetwork.Constants.cs +++ b/Stickers/Stickers/Networking/ModNetwork.Constants.cs @@ -1,15 +1,16 @@ -namespace NAK.Stickers.Networking; +using NAK.Stickers.Properties; + +namespace NAK.Stickers.Networking; public static partial class ModNetwork { #region Constants internal const int MaxTextureSize = 1024 * 256; // 256KB - - private const string NetworkVersion = "1.0.3"; // change each time network protocol changes - private const string ModId = $"MelonMod.NAK.Stickers_v{NetworkVersion}"; + + private const string ModId = $"MelonMod.NAK.Stickers_v{AssemblyInfoParams.Version}"; private const int ChunkSize = 1024; // roughly 1KB per ModNetworkMessage private const int MaxChunkCount = MaxTextureSize / ChunkSize; - + #endregion Constants } \ No newline at end of file diff --git a/Stickers/Stickers/Networking/ModNetwork.Inbound.cs b/Stickers/Stickers/Networking/ModNetwork.Inbound.cs index 57b355c..fbada5b 100644 --- a/Stickers/Stickers/Networking/ModNetwork.Inbound.cs +++ b/Stickers/Stickers/Networking/ModNetwork.Inbound.cs @@ -1,258 +1,222 @@ -using ABI_RC.Core.Networking.IO.Social; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.ModNetwork; -using NAK.Stickers.Utilities; -using UnityEngine; - -namespace NAK.Stickers.Networking; - -public static partial class ModNetwork -{ - #region Inbound Buffers - - private static readonly Dictionary _textureChunkBuffers = new(); - private static readonly Dictionary _receivedChunkCounts = new(); - private static readonly Dictionary _expectedChunkCounts = new(); - private static readonly Dictionary _textureMetadata = new(); - - #endregion Inbound Buffers - - #region Reset Method - - public static void Reset() - { - _textureChunkBuffers.Clear(); - _receivedChunkCounts.Clear(); - _expectedChunkCounts.Clear(); - _textureMetadata.Clear(); - - LoggerInbound("ModNetwork inbound buffers and metadata have been reset."); - } - - #endregion Reset Method - - #region Inbound Methods - - private static bool ShouldReceiveFromSender(string sender) - { - if (_disallowedForSession.Contains(sender)) - return false; // ignore messages from disallowed users - - if (MetaPort.Instance.blockedUserIds.Contains(sender)) - return false; // ignore messages from blocked users - - if (ModSettings.Entry_FriendsOnly.Value && !Friends.FriendsWith(sender)) - return false; // ignore messages from non-friends if friends only is enabled - - if (StickerSystem.Instance.IsRestrictedInstance) // ignore messages from users when the world is restricted. This also includes older or modified version of Stickers mod. - return false; - - if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(sender, out PuppetMaster _)) - return false; // ignore messages from players that don't exist - - return true; - } - - private static void HandleMessageReceived(ModNetworkMessage msg) - { - try - { - string sender = msg.Sender; - msg.Read(out byte msgTypeRaw); - - if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) - return; - - if (!ShouldReceiveFromSender(sender)) - return; - - LoggerInbound($"Received message from {msg.Sender}, Type: {(MessageType)msgTypeRaw}"); - - switch ((MessageType)msgTypeRaw) - { - case MessageType.PlaceSticker: - HandlePlaceSticker(msg); - break; - case MessageType.ClearSticker: - HandleClearSticker(msg); - break; - case MessageType.ClearAllStickers: - HandleClearAllStickers(msg); - break; - case MessageType.StartTexture: - HandleStartTexture(msg); - break; - case MessageType.SendTexture: - HandleSendTexture(msg); - break; - case MessageType.EndTexture: - HandleEndTexture(msg); - break; - case MessageType.RequestTexture: - HandleRequestTexture(msg); - break; - default: - LoggerInbound($"Invalid message type received: {msgTypeRaw}"); - break; - } - } - catch (Exception e) - { - LoggerInbound($"Error handling message from {msg.Sender}: {e.Message}", true); - } - } - - private static void HandlePlaceSticker(ModNetworkMessage msg) - { - msg.Read(out int stickerSlot); - msg.Read(out Guid textureHash); - msg.Read(out Vector3 position); - msg.Read(out Vector3 forward); - msg.Read(out Vector3 up); - - if (!StickerSystem.Instance.HasTextureHash(msg.Sender, textureHash)) - { - SendRequestTexture(stickerSlot, textureHash); - StickerSystem.Instance.ClearStickersForPlayer(msg.Sender, stickerSlot); // Ensure no exploit - } - - StickerSystem.Instance.OnStickerPlaceReceived(msg.Sender, stickerSlot, position, forward, up); - } - - private static void HandleClearSticker(ModNetworkMessage msg) - { - msg.Read(out int stickerSlot); - StickerSystem.Instance.OnStickerClearReceived(msg.Sender, stickerSlot); - } - - private static void HandleClearAllStickers(ModNetworkMessage msg) - { - StickerSystem.Instance.OnStickerClearAllReceived(msg.Sender); - } - - private static void HandleStartTexture(ModNetworkMessage msg) - { - string sender = msg.Sender; - msg.Read(out int stickerSlot); - msg.Read(out Guid textureHash); - msg.Read(out int chunkCount); - msg.Read(out int width); - msg.Read(out int height); - - if (_textureChunkBuffers.ContainsKey(sender)) - { - LoggerInbound($"Received StartTexture message from {sender} while still receiving texture data!"); - return; - } - - if (StickerSystem.Instance.HasTextureHash(sender, textureHash)) - { - LoggerInbound($"Received StartTexture message from {sender} with existing texture hash {textureHash}, skipping texture data."); - return; - } - - if (chunkCount > MaxChunkCount) - { - LoggerInbound($"Received StartTexture message from {sender} with too many chunks: {chunkCount}", true); - return; - } - - _textureMetadata[sender] = (stickerSlot, textureHash, width, height); - _textureChunkBuffers[sender] = new byte[Mathf.Clamp(chunkCount * ChunkSize, 0, MaxTextureSize)]; - _expectedChunkCounts[sender] = Mathf.Clamp(chunkCount, 0, MaxChunkCount); - _receivedChunkCounts[sender] = 0; - - LoggerInbound($"Received StartTexture message from {sender}: Slot: {stickerSlot}, Hash: {textureHash}, Chunks: {chunkCount}, Resolution: {width}x{height}"); - } - - private static void HandleSendTexture(ModNetworkMessage msg) - { - string sender = msg.Sender; - msg.Read(out int chunkIdx); - msg.Read(out byte[] chunkData); - - if (!_textureChunkBuffers.TryGetValue(sender, out var buffer)) - return; - - int startIndex = chunkIdx * ChunkSize; - Array.Copy(chunkData, 0, buffer, startIndex, chunkData.Length); - - _receivedChunkCounts[sender]++; - if (_receivedChunkCounts[sender] < _expectedChunkCounts[sender]) - return; - - (int stickerSlot, Guid Hash, int Width, int Height) metadata = _textureMetadata[sender]; - - // All chunks received, reassemble texture - _textureChunkBuffers.Remove(sender); - _receivedChunkCounts.Remove(sender); - _expectedChunkCounts.Remove(sender); - _textureMetadata.Remove(sender); - - // Validate image - if (!ImageUtility.IsValidImage(buffer)) - { - LoggerInbound($"[Inbound] Received texture data is not a valid image from {sender}!", true); - return; - } - - // Validate data TODO: fix hash??????? - (Guid imageHash, int width, int height) = ImageUtility.ExtractImageInfo(buffer); - if (metadata.Width != width - || metadata.Height != height) - { - LoggerInbound($"Received texture data does not match metadata! Expected: {metadata.Hash} ({metadata.Width}x{metadata.Height}), received: {imageHash} ({width}x{height})", true); - return; - } - - Texture2D texture = new(1,1); - texture.LoadImage(buffer); - texture.Compress(true); - - StickerSystem.Instance.OnPlayerStickerTextureReceived(sender, metadata.Hash, texture, metadata.stickerSlot); - - LoggerInbound($"All chunks received and texture reassembled from {sender}. " + - $"Texture size: {metadata.Width}x{metadata.Height}"); - } - - private static void HandleEndTexture(ModNetworkMessage msg) - { - string sender = msg.Sender; - if (!_textureChunkBuffers.ContainsKey(sender)) - return; - - LoggerInbound($"Received EndTexture message without all chunks received from {sender}! Only {_receivedChunkCounts[sender]} out of {_expectedChunkCounts[sender]} received."); - - _textureChunkBuffers.Remove(sender); - _receivedChunkCounts.Remove(sender); - _expectedChunkCounts.Remove(sender); - _textureMetadata.Remove(sender); - } - - private static void HandleRequestTexture(ModNetworkMessage msg) - { - string sender = msg.Sender; - msg.Read(out int stickerSlot); - msg.Read(out Guid textureHash); - - if (!_isSubscribedToModNetwork || IsSendingTexture) - return; - - if (stickerSlot < 0 || stickerSlot >= _textureStorage.Length) - { - LoggerInbound($"Received RequestTexture message from {sender} with invalid slot {stickerSlot}!"); - return; - } - - if (_textureStorage[stickerSlot].textureHash != textureHash) - { - LoggerInbound($"Received RequestTexture message from {sender} with invalid texture hash {textureHash} for slot {stickerSlot}!"); - return; - } - - SendTexture(stickerSlot); - } - - #endregion Inbound Methods +using ABI_RC.Core.Savior; +using ABI_RC.Systems.ModNetwork; +using NAK.Stickers.Utilities; +using UnityEngine; + +namespace NAK.Stickers.Networking; + +public static partial class ModNetwork +{ + #region Inbound Buffers + + private static readonly Dictionary _textureChunkBuffers = new(); + private static readonly Dictionary _receivedChunkCounts = new(); + private static readonly Dictionary _expectedChunkCounts = new(); + private static readonly Dictionary _textureMetadata = new(); + + #endregion Inbound Buffers + + #region Inbound Methods + + private static void HandleMessageReceived(ModNetworkMessage msg) + { + string sender = msg.Sender; + msg.Read(out byte msgTypeRaw); + + if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) + return; + + if (_disallowedForSession.Contains(sender)) + return; // ignore messages from disallowed users + + if (MetaPort.Instance.blockedUserIds.Contains(sender)) + return; // ignore messages from blocked users + + LoggerInbound($"Received message from {msg.Sender}, Type: {(MessageType)msgTypeRaw}"); + + switch ((MessageType)msgTypeRaw) + { + case MessageType.PlaceSticker: + HandlePlaceSticker(msg); + break; + + case MessageType.ClearSticker: + HandleClearSticker(msg); + break; + + case MessageType.ClearAllStickers: + HandleClearAllStickers(msg); + break; + + case MessageType.StartTexture: + HandleStartTexture(msg); + break; + + case MessageType.SendTexture: + HandleSendTexture(msg); + break; + + case MessageType.EndTexture: + HandleEndTexture(msg); + break; + + case MessageType.RequestTexture: + HandleRequestTexture(msg); + break; + + default: + LoggerInbound($"Invalid message type received: {msgTypeRaw}"); + break; + } + } + + private static void HandlePlaceSticker(ModNetworkMessage msg) + { + msg.Read(out int stickerSlot); + msg.Read(out Guid textureHash); + msg.Read(out Vector3 position); + msg.Read(out Vector3 forward); + msg.Read(out Vector3 up); + + if (!StickerSystem.Instance.HasTextureHash(msg.Sender, textureHash)) + SendRequestTexture(stickerSlot, textureHash); + + StickerSystem.Instance.OnStickerPlaceReceived(msg.Sender, stickerSlot, position, forward, up); + } + + private static void HandleClearSticker(ModNetworkMessage msg) + { + msg.Read(out int stickerSlot); + StickerSystem.Instance.OnStickerClearReceived(msg.Sender, stickerSlot); + } + + private static void HandleClearAllStickers(ModNetworkMessage msg) + { + StickerSystem.Instance.OnStickerClearAllReceived(msg.Sender); + } + + private static void HandleStartTexture(ModNetworkMessage msg) + { + string sender = msg.Sender; + msg.Read(out int stickerSlot); + msg.Read(out Guid textureHash); + msg.Read(out int chunkCount); + msg.Read(out int width); + msg.Read(out int height); + + if (_textureChunkBuffers.ContainsKey(sender)) + { + LoggerInbound($"Received StartTexture message from {sender} while still receiving texture data!"); + return; + } + + if (StickerSystem.Instance.HasTextureHash(sender, textureHash)) + { + LoggerInbound($"Received StartTexture message from {sender} with existing texture hash {textureHash}, skipping texture data."); + return; + } + + if (chunkCount > MaxChunkCount) + { + LoggerInbound($"Received StartTexture message from {sender} with too many chunks: {chunkCount}", true); + return; + } + + _textureChunkBuffers[sender] = new byte[Mathf.Clamp(chunkCount * ChunkSize, 0, MaxTextureSize)]; + _receivedChunkCounts[sender] = 0; + _expectedChunkCounts[sender] = chunkCount; + _textureMetadata[sender] = (stickerSlot, textureHash, width, height); + + LoggerInbound($"Received StartTexture message from {sender}: Slot: {stickerSlot}, Hash: {textureHash}, Chunks: {chunkCount}, Resolution: {width}x{height}"); + } + + private static void HandleSendTexture(ModNetworkMessage msg) + { + string sender = msg.Sender; + msg.Read(out int chunkIdx); + msg.Read(out byte[] chunkData); + + if (!_textureChunkBuffers.TryGetValue(sender, out var buffer)) + return; + + int startIndex = chunkIdx * ChunkSize; + Array.Copy(chunkData, 0, buffer, startIndex, chunkData.Length); + + _receivedChunkCounts[sender]++; + if (_receivedChunkCounts[sender] < _expectedChunkCounts[sender]) + return; + + (int stickerSlot, Guid Hash, int Width, int Height) metadata = _textureMetadata[sender]; + + // All chunks received, reassemble texture + _textureChunkBuffers.Remove(sender); + _receivedChunkCounts.Remove(sender); + _expectedChunkCounts.Remove(sender); + _textureMetadata.Remove(sender); + + // Validate image + if (!ImageUtility.IsValidImage(buffer)) + { + LoggerInbound($"[Inbound] Received texture data is not a valid image from {sender}!", true); + return; + } + + // Validate data TODO: fix hash??????? + (Guid imageHash, int width, int height) = ImageUtility.ExtractImageInfo(buffer); + if (metadata.Width != width + || metadata.Height != height) + { + LoggerInbound($"Received texture data does not match metadata! Expected: {metadata.Hash} ({metadata.Width}x{metadata.Height}), received: {imageHash} ({width}x{height})", true); + return; + } + + Texture2D texture = new(1,1); + texture.LoadImage(buffer); + texture.Compress(true); + + StickerSystem.Instance.OnPlayerStickerTextureReceived(sender, metadata.Hash, texture, metadata.stickerSlot); + + LoggerInbound($"All chunks received and texture reassembled from {sender}. " + + $"Texture size: {metadata.Width}x{metadata.Height}"); + } + + private static void HandleEndTexture(ModNetworkMessage msg) + { + string sender = msg.Sender; + if (!_textureChunkBuffers.ContainsKey(sender)) + return; + + LoggerInbound($"Received EndTexture message without all chunks received from {sender}! Only {_receivedChunkCounts[sender]} out of {_expectedChunkCounts[sender]} received."); + + _textureChunkBuffers.Remove(sender); + _receivedChunkCounts.Remove(sender); + _expectedChunkCounts.Remove(sender); + _textureMetadata.Remove(sender); + } + + private static void HandleRequestTexture(ModNetworkMessage msg) + { + string sender = msg.Sender; + msg.Read(out int stickerSlot); + msg.Read(out Guid textureHash); + + if (!_isSubscribedToModNetwork || IsSendingTexture) + return; + + if (stickerSlot < 0 || stickerSlot >= _textureStorage.Length) + { + LoggerInbound($"Received RequestTexture message from {sender} with invalid slot {stickerSlot}!"); + return; + } + + if (_textureStorage[stickerSlot].textureHash != textureHash) + { + LoggerInbound($"Received RequestTexture message from {sender} with invalid texture hash {textureHash} for slot {stickerSlot}!"); + return; + } + + SendTexture(stickerSlot); + } + + #endregion Inbound Methods } \ No newline at end of file diff --git a/Stickers/Stickers/Networking/ModNetwork.Main.cs b/Stickers/Stickers/Networking/ModNetwork.Main.cs index d2cdeea..64f21b9 100644 --- a/Stickers/Stickers/Networking/ModNetwork.Main.cs +++ b/Stickers/Stickers/Networking/ModNetwork.Main.cs @@ -6,25 +6,6 @@ public static partial class ModNetwork { #region Mod Network Internals - // private static bool _isEnabled = true; - // - // public static bool IsEnabled - // { - // get => _isEnabled; - // set - // { - // if (_isEnabled == value) - // return; - // - // _isEnabled = value; - // - // if (_isEnabled) Subscribe(); - // else Unsubscribe(); - // - // Reset(); // reset buffers and metadata - // } - // } - public static bool IsSendingTexture { get; private set; } private static bool _isSubscribedToModNetwork; @@ -34,16 +15,6 @@ public static partial class ModNetwork _isSubscribedToModNetwork = ModNetworkManager.IsSubscribed(ModId); if (!_isSubscribedToModNetwork) StickerMod.Logger.Error("Failed to subscribe to Mod Network! This should not happen."); - else StickerMod.Logger.Msg("Subscribed to Mod Network."); - } - - private static void Unsubscribe() - { - ModNetworkManager.Unsubscribe(ModId); - - _isSubscribedToModNetwork = ModNetworkManager.IsSubscribed(ModId); - if (_isSubscribedToModNetwork) StickerMod.Logger.Error("Failed to unsubscribe from Mod Network! This should not happen."); - else StickerMod.Logger.Msg("Unsubscribed from Mod Network."); } #endregion Mod Network Internals diff --git a/Stickers/Stickers/Networking/ModNetwork.Outbound.cs b/Stickers/Stickers/Networking/ModNetwork.Outbound.cs index 241f566..fd16aa5 100644 --- a/Stickers/Stickers/Networking/ModNetwork.Outbound.cs +++ b/Stickers/Stickers/Networking/ModNetwork.Outbound.cs @@ -38,9 +38,6 @@ public static partial class ModNetwork public static void SendPlaceSticker(int stickerSlot, Vector3 position, Vector3 forward, Vector3 up) { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -58,9 +55,6 @@ public static partial class ModNetwork public static void SendClearSticker(int stickerSlot) { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -74,9 +68,6 @@ public static partial class ModNetwork public static void SendClearAllStickers() { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -87,11 +78,8 @@ public static partial class ModNetwork LoggerOutbound("ClearAllStickers"); } - private static void SendStartTexture(int stickerSlot, Guid textureHash, int chunkCount, int width, int height) + public static void SendStartTexture(int stickerSlot, Guid textureHash, int chunkCount, int width, int height) { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -109,9 +97,6 @@ public static partial class ModNetwork public static void SendTextureChunk(int chunkIdx, byte[] chunkData) { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -126,9 +111,6 @@ public static partial class ModNetwork public static void SendEndTexture() { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -141,9 +123,6 @@ public static partial class ModNetwork public static void SendRequestTexture(int stickerSlot, Guid textureHash) { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork()) return; @@ -158,9 +137,6 @@ public static partial class ModNetwork public static void SendTexture(int stickerSlot) { - if (!_isSubscribedToModNetwork) - return; - if (!IsConnectedToGameNetwork() || IsSendingTexture) return; @@ -170,8 +146,6 @@ public static partial class ModNetwork { try { - Thread.CurrentThread.IsBackground = false; // working around bug in MTJobManager - var textureData = _textureStorage[stickerSlot].textureData; var textureHash = _textureStorage[stickerSlot].textureHash; var width = _textureStorage[stickerSlot].width; @@ -207,7 +181,6 @@ public static partial class ModNetwork { IsSendingTexture = false; InvokeTextureOutboundStateChanged(false); - Thread.CurrentThread.IsBackground = true; // working around bug in MTJobManager } }); } diff --git a/Stickers/Stickers/StickerData.cs b/Stickers/Stickers/StickerData.cs index 2ca96bd..670c7d8 100644 --- a/Stickers/Stickers/StickerData.cs +++ b/Stickers/Stickers/StickerData.cs @@ -1,5 +1,4 @@ using ABI_RC.Core; -using ABI_RC.Core.IO; using UnityEngine; using Object = UnityEngine.Object; @@ -8,24 +7,26 @@ namespace NAK.Stickers public class StickerData { private const float DECAL_SIZE = 0.25f; - - public readonly string PlayerId; + + private static readonly int s_EmissionStrengthID = Shader.PropertyToID("_EmissionStrength"); + + public float DeathTime; // when a remote player leaves, we need to kill their stickers public float LastPlacedTime; - public float DeathTime = -1f; - public float IdentifyTime = -1f; - private Vector3 _lastPlacedPosition = Vector3.zero; + public readonly bool IsLocal; + private readonly DecalType _decal; private readonly DecalSpawner[] _decalSpawners; private readonly Guid[] _textureHashes; private readonly Material[] _materials; private readonly AudioSource _audioSource; - public StickerData(string playerId, int decalSpawnersCount) + public StickerData(bool isLocal, int decalSpawnersCount) { - PlayerId = playerId; + IsLocal = isLocal; + _decal = ScriptableObject.CreateInstance(); _decalSpawners = new DecalSpawner[decalSpawnersCount]; _materials = new Material[decalSpawnersCount]; _textureHashes = new Guid[decalSpawnersCount]; @@ -33,14 +34,14 @@ namespace NAK.Stickers for (int i = 0; i < decalSpawnersCount; i++) { _materials[i] = new Material(StickerMod.DecalSimpleShader); - DecalSpawner.InitData decalSettings = new() + _decal.decalSettings = new DecalSpawner.InitData { material = _materials[i], useShaderReplacement = false, inheritMaterialProperties = false, inheritMaterialPropertyBlock = false, }; - _decalSpawners[i] = DecalManager.GetSpawner(decalSettings, 4096, 1024); + _decalSpawners[i] = DecalManager.GetSpawner(_decal.decalSettings, 4096, 1024); } _audioSource = new GameObject("StickerAudioSource").AddComponent(); @@ -52,33 +53,19 @@ namespace NAK.Stickers _audioSource.maxDistance = 5f; _audioSource.minDistance = 1f; _audioSource.outputAudioMixerGroup = RootLogic.Instance.propSfx; // props are close enough to stickers - - // this is a hack so judge and fuck off lol - if (PlayerId == StickerSystem.PlayerLocalId) - { - Object.DontDestroyOnLoad(_audioSource.gameObject); // keep audio source through world transitions - - _previewMaterial = new Material(StickerMod.DecalSimpleShader); - _previewDecalSpawner = DecalManager.GetSpawner(new DecalSpawner.InitData - { - material = _previewMaterial, // default material - useShaderReplacement = false, - inheritMaterialProperties = false, - inheritMaterialPropertyBlock = false, - }, 4096, 1024); - } + if (isLocal) Object.DontDestroyOnLoad(_audioSource.gameObject); // keep audio source through world transitions } - // public Guid GetTextureHash(int spawnerIndex = 0) - // { - // if (spawnerIndex < 0 || spawnerIndex >= _decalSpawners.Length) - // { - // StickerMod.Logger.Warning("Invalid spawner index!"); - // return Guid.Empty; - // } - // - // return _textureHashes[spawnerIndex]; - // } + public Guid GetTextureHash(int spawnerIndex = 0) + { + if (spawnerIndex < 0 || spawnerIndex >= _decalSpawners.Length) + { + StickerMod.Logger.Warning("Invalid spawner index!"); + return Guid.Empty; + } + + return _textureHashes[spawnerIndex]; + } public bool CheckHasTextureHash(Guid textureHash) { @@ -107,15 +94,13 @@ namespace NAK.Stickers ? FilterMode.Bilinear // smear it cause its fat : FilterMode.Point; // my minecraft skin looked shit + if (IsLocal) StickerMod.Logger.Msg($"Set texture filter mode to: {texture.filterMode}"); + Material material = _materials[spawnerIndex]; - // destroy the previous texture to avoid memory leaks + // Destroy the previous texture to avoid memory leaks if (material.mainTexture != null) Object.Destroy(material.mainTexture); material.mainTexture = texture; - - // update the preview as well i guess cause lame - if (_previewMaterial != null && _previewSpawnerIndex != spawnerIndex) - _previewMaterial.mainTexture = texture; } public void Place(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0) @@ -126,23 +111,16 @@ namespace NAK.Stickers return; } - // for performance it is best to let them batch, but we also want movable objects to always - // be decaled instead of floating in-scene (so avatars/props are always considered movable) - Transform rootObject = null; - GameObject hitGO = hit.transform.gameObject; - if (hitGO.scene.buildIndex == 4 // additive (dynamic) content - || hitGO.GetComponentInParent() != null // potentially movable - || hitGO.GetComponentInParent() != null) // movable - rootObject = hitGO.transform; - + if (hit.rigidbody != null) rootObject = hit.rigidbody.transform; + _lastPlacedPosition = hit.point; LastPlacedTime = Time.time; // Add decal to the specified spawner _decalSpawners[spawnerIndex].AddDecal( _lastPlacedPosition, Quaternion.LookRotation(forwardDirection, upDirection), - hitGO, + hit.collider.gameObject, DECAL_SIZE, DECAL_SIZE, 1f, 1f, 0f, rootObject); } @@ -176,21 +154,13 @@ namespace NAK.Stickers _decalSpawners[i].Release(); _decalSpawners[i].staticGroups.Clear(); _decalSpawners[i].movableGroups.Clear(); - + // Clean up textures and materials - if (_materials[i] == null) continue; if (_materials[i].mainTexture != null) Object.Destroy(_materials[i].mainTexture); Object.Destroy(_materials[i]); } - - if (_audioSource != null) Object.Destroy(_audioSource.gameObject); - if (_previewDecalSpawner != null) // local player only - { - _previewDecalSpawner.Release(); - _previewDecalSpawner.staticGroups.Clear(); - _previewDecalSpawner.movableGroups.Clear(); - Object.Destroy(_previewMaterial); - } + + Object.Destroy(_decal); } public void PlayAudio() @@ -218,73 +188,27 @@ namespace NAK.Stickers { foreach (Material material in _materials) { - if (material == null) continue; Color color = material.color; color.a = alpha; material.color = color; } } + + #region shitty identify - #region Sticker Preview + public void Identify() + { + Color color = Color.HSVToRGB(Time.time % 1f, 1f, 1f); + foreach (Material material in _materials) + material.color = color; // cycle rainbow + } - private const float FLASH_FREQUENCY = 2f; - private readonly DecalSpawner _previewDecalSpawner; - private readonly Material _previewMaterial; - private int _previewSpawnerIndex = -1; - private float _flashTime; + public void ResetIdentify() + { + foreach (Material material in _materials) + material.color = Color.white; + } - public void PlacePreview(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0) - { - if (spawnerIndex < 0 || spawnerIndex >= _decalSpawners.Length) - { - StickerMod.Logger.Warning("Invalid spawner index for preview!"); - return; - } - - if (_previewDecalSpawner == null) - return; // uh fuck - - // place at hit pos - Transform rootObject = null; - GameObject hitGO = hit.transform.gameObject; - if (hitGO.scene.buildIndex == 4 // additive (dynamic) content - || hitGO.GetComponentInParent() != null // potentially movable - || hitGO.GetComponentInParent() != null) // movable - rootObject = hitGO.transform; - - _previewDecalSpawner.AddDecal( - hit.point, Quaternion.LookRotation(forwardDirection, upDirection), - hitGO, - DECAL_SIZE, DECAL_SIZE, 1f, 1f, 0f, rootObject); - } - - public void UpdatePreview(int spawnerIndex) - { - if (_previewSpawnerIndex != spawnerIndex) - { - _previewSpawnerIndex = spawnerIndex; // update the preview image - _previewMaterial.mainTexture = _materials[spawnerIndex].mainTexture; - } - - _flashTime += Time.deltaTime; - float baseAlpha = (Mathf.Sin(_flashTime * 2f * Mathf.PI / FLASH_FREQUENCY) + 1f) / 2f; - float alpha = Mathf.Lerp(0.5f, 0.8f, baseAlpha); // 50% to 80% alpha - - Color color = _previewMaterial.color; - color.a = alpha; - _previewMaterial.color = color; - } - - public void ClearPreview() - { - if (_previewDecalSpawner == null) - return; - - _previewDecalSpawner.Release(); - _previewDecalSpawner.staticGroups.Clear(); - _previewDecalSpawner.movableGroups.Clear(); - } - - #endregion Sticker Preview + #endregion shitty identify } } \ No newline at end of file diff --git a/Stickers/Stickers/StickerSystem.ImageLoading.cs b/Stickers/Stickers/StickerSystem.ImageLoading.cs index def203a..4f5fce4 100644 --- a/Stickers/Stickers/StickerSystem.ImageLoading.cs +++ b/Stickers/Stickers/StickerSystem.ImageLoading.cs @@ -50,7 +50,6 @@ public partial class StickerSystem { try { - Thread.CurrentThread.IsBackground = false; // working around bug in MTJobManager if (!TryLoadImage(imageName, slotIndex, out string errorMessage)) throw new Exception(errorMessage); } @@ -62,7 +61,6 @@ public partial class StickerSystem finally { _isLoadingImage[slotIndex] = false; - Thread.CurrentThread.IsBackground = true; // working around bug in MTJobManager } }); } @@ -149,11 +147,6 @@ public partial class StickerSystem if (!Directory.Exists(s_StickersFolderPath)) Directory.CreateDirectory(s_StickersFolderPath); return s_StickersFolderPath; } - - public static void EnsureStickersFolderExists() - { - if (!Directory.Exists(s_StickersFolderPath)) Directory.CreateDirectory(s_StickersFolderPath); - } #endregion Image Loading } \ No newline at end of file diff --git a/Stickers/Stickers/StickerSystem.Main.cs b/Stickers/Stickers/StickerSystem.Main.cs index 9cd8fcf..e609fed 100644 --- a/Stickers/Stickers/StickerSystem.Main.cs +++ b/Stickers/Stickers/StickerSystem.Main.cs @@ -1,143 +1,71 @@ -using ABI_RC.Core.IO; -using ABI_RC.Core.Networking.IO.Instancing; -using ABI_RC.Core.UI; -using ABI_RC.Systems.GameEventSystem; -using JetBrains.Annotations; -using NAK.Stickers.Networking; -using NAK.Stickers.Utilities; -using System.EnterpriseServices; -using UnityEngine; -using MelonLoader; -using UnityEngine.ProBuilder.MeshOperations; -using NAK.Stickers.Integrations; - -namespace NAK.Stickers; - -public partial class StickerSystem -{ - #region Singleton - - public static StickerSystem Instance { get; private set; } - - public static void Initialize() - { - if (Instance != null) - return; - - Instance = new StickerSystem(); - - // configure decalery - DecalManager.SetPreferredMode(DecalUtils.Mode.GPU, false, 0); - - // ensure cache folder exists - EnsureStickersFolderExists(); - - // listen for game events - CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(Instance.OnPlayerSetupStart); - -} - - #endregion Singleton - - #region Callback Registration - - private void OnPlayerSetupStart() - { - // TODO: this can be spammed by world author toggling CVRWorld.enabled state - CVRGameEventSystem.World.OnLoad.AddListener(_ => OnWorldLoad()); - CVRGameEventSystem.World.OnUnload.AddListener(_ => OnWorldUnload()); - CVRGameEventSystem.Instance.OnConnected.AddListener((_) => { if (!Instances.IsReconnecting) OnInitialConnection(); }); - - CVRGameEventSystem.Player.OnJoinEntity.AddListener(Instance.OnPlayerJoined); - CVRGameEventSystem.Player.OnLeaveEntity.AddListener(Instance.OnPlayerLeft); - SchedulerSystem.AddJob(Instance.OnUpdate, 10f, -1); - LoadAllImagesAtStartup(); - } - - #endregion Callback Registration - - #region Game Events - - private void OnInitialConnection() - { - ClearStickersSelf(); // clear stickers on remotes just in case we rejoined - ModNetwork.Reset(); // reset network buffers and metadata - } - - private void OnWorldLoad() - { - IsRestrictedInstance = GameObject.Find("[DisableStickers]") != null; - if (IsRestrictedInstance) StickerMod.Logger.Msg("Stickers are restricted by the world author."); - BTKUIAddon.OnStickerRestrictionUpdated(IsRestrictedInstance); - } - - private void OnWorldUnload() - { - IsRestrictedInstance = false; - CleanupAllButSelf(); - } - - #endregion Game Events - - #region Data - - // private bool _isEnabled = true; - // - // public bool IsEnabled - // { - // get => _isEnabled; - // set - // { - // if (_isEnabled == value) - // return; - // - // _isEnabled = value; - // if (!_isEnabled) ClearAllStickers(); - // ModNetwork.IsEnabled = _isEnabled; - // } - // } - - public bool IsRestrictedInstance { get; internal set; } - - private string SelectedStickerName => ModSettings.Hidden_SelectedStickerNames.Value[_selectedStickerSlot]; - - private const float StickerKillTime = 30f; - private const float StickerCooldown = 0.2f; - private readonly Dictionary _playerStickers = new(); - internal const string PlayerLocalId = "_PLAYERLOCAL"; - - private int _selectedStickerSlot; - public int SelectedStickerSlot - { - get => _selectedStickerSlot; - set - { - _selectedStickerSlot = value < 0 ? ModSettings.MaxStickerSlots - 1 : value % ModSettings.MaxStickerSlots; - IsInStickerMode = IsInStickerMode; // refresh sticker mode - } - } - - private bool _isInStickerMode; - public bool IsInStickerMode - { - get => _isInStickerMode; - set - { - _isInStickerMode = value && !IsRestrictedInstance; // ensure cannot enter when restricted - if (_isInStickerMode) - { - CohtmlHud.Instance.SelectPropToSpawn( - StickerCache.GetCohtmlResourcesPath(SelectedStickerName), - Path.GetFileNameWithoutExtension(SelectedStickerName), - "Sticker selected for stickering:"); - } - else - { - CohtmlHud.Instance.ClearPropToSpawn(); - ClearStickerPreview(); - } - } - } - - #endregion Data +using ABI_RC.Core.UI; +using ABI_RC.Systems.GameEventSystem; +using NAK.Stickers.Utilities; +using UnityEngine; + +namespace NAK.Stickers; + +public partial class StickerSystem +{ + #region Singleton + + public static StickerSystem Instance { get; private set; } + + public static void Initialize() + { + if (Instance != null) + return; + + Instance = new StickerSystem(); + + // configure decalery + DecalManager.SetPreferredMode(DecalUtils.Mode.GPU, false, 0); + + // ensure cache folder exists + if (!Directory.Exists(s_StickersFolderPath)) Directory.CreateDirectory(s_StickersFolderPath); + + // listen for game events + CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(Instance.OnPlayerSetupStart); + } + + #endregion Singleton + + #region Data + + private int _selectedStickerSlot; + public int SelectedStickerSlot + { + get => _selectedStickerSlot; + set + { + _selectedStickerSlot = Mathf.Clamp(value, 0, ModSettings.MaxStickerSlots - 1); + IsInStickerMode = IsInStickerMode; // refresh sticker mode + } + } + + private bool _isInStickerMode; + public bool IsInStickerMode + { + get => _isInStickerMode; + set + { + _isInStickerMode = value; + if (_isInStickerMode) CohtmlHud.Instance.SelectPropToSpawn( + StickerCache.GetCohtmlResourcesPath(SelectedStickerName), + Path.GetFileNameWithoutExtension(SelectedStickerName), + "Sticker selected for stickering:"); + else CohtmlHud.Instance.ClearPropToSpawn(); + } + } + + private string SelectedStickerName => ModSettings.Hidden_SelectedStickerNames.Value[_selectedStickerSlot]; + + private const float StickerKillTime = 30f; + private const float StickerCooldown = 0.2f; + private readonly Dictionary _playerStickers = new(); + private const string PlayerLocalId = "_PLAYERLOCAL"; + + private readonly List _deadStickerPool = new(); // for cleanup on player leave + + #endregion Data } \ No newline at end of file diff --git a/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs b/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs index f2b5dd3..f361e0a 100644 --- a/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs +++ b/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs @@ -1,4 +1,7 @@ -using ABI_RC.Core.Player; +using ABI_RC.Core.IO; +using ABI_RC.Core.Networking.IO.Instancing; +using ABI_RC.Core.Player; +using ABI_RC.Systems.GameEventSystem; using UnityEngine; namespace NAK.Stickers; @@ -7,6 +10,17 @@ public partial class StickerSystem { #region Player Callbacks + private void OnPlayerSetupStart() + { + CVRGameEventSystem.World.OnUnload.AddListener(_ => Instance.CleanupAllButSelf()); + CVRGameEventSystem.Instance.OnConnected.AddListener((_) => { if (!Instances.IsReconnecting) Instance.ClearStickersSelf(); }); + + CVRGameEventSystem.Player.OnJoinEntity.AddListener(Instance.OnPlayerJoined); + CVRGameEventSystem.Player.OnLeaveEntity.AddListener(Instance.OnPlayerLeft); + SchedulerSystem.AddJob(Instance.OnOccasionalUpdate, 10f, 1f); + LoadAllImagesAtStartup(); + } + private void OnPlayerJoined(CVRPlayerEntity playerEntity) { if (!_playerStickers.TryGetValue(playerEntity.Uuid, out StickerData stickerData)) @@ -14,6 +28,7 @@ public partial class StickerSystem stickerData.DeathTime = -1f; stickerData.SetAlpha(1f); + _deadStickerPool.Remove(stickerData); } private void OnPlayerLeft(CVRPlayerEntity playerEntity) @@ -23,46 +38,42 @@ public partial class StickerSystem stickerData.DeathTime = Time.time + StickerKillTime; stickerData.SetAlpha(1f); + _deadStickerPool.Add(stickerData); } - private void OnUpdate() + private void OnOccasionalUpdate() { - float currentTime = Time.time; - for (int i = 0; i < _playerStickers.Values.Count; i++) - { - StickerData stickerData = _playerStickers.Values.ElementAt(i); - - if (stickerData.DeathTime > 0f) - { - if (currentTime < stickerData.DeathTime) - { - stickerData.SetAlpha(Mathf.Lerp(0f, 1f, (stickerData.DeathTime - currentTime) / StickerKillTime)); - continue; - } + if (_deadStickerPool.Count == 0) + return; - stickerData.Cleanup(); - _playerStickers.Remove(stickerData.PlayerId); + for (var i = _deadStickerPool.Count - 1; i >= 0; i--) + { + float currentTime = Time.time; + StickerData stickerData = _deadStickerPool[i]; + if (stickerData == null) + { + _deadStickerPool.RemoveAt(i); continue; } + + if (stickerData.DeathTime < 0f) + continue; - if (stickerData.IdentifyTime > 0) + if (currentTime < stickerData.DeathTime) { - if (currentTime < stickerData.IdentifyTime) - { - // blink alpha 3 times but not completely off - stickerData.SetAlpha(Mathf.Lerp(0.2f, 1f, Mathf.PingPong((stickerData.IdentifyTime - currentTime) * 2f, 1f))); - continue; - } - - stickerData.SetAlpha(1f); - stickerData.IdentifyTime = -1; + stickerData.SetAlpha(Mathf.Lerp(0f, 1f, (stickerData.DeathTime - currentTime) / StickerKillTime)); + continue; } + + _playerStickers.Remove(_playerStickers.First(x => x.Value == stickerData).Key); + _deadStickerPool.RemoveAt(i); + stickerData.Cleanup(); } } #endregion Player Callbacks - #region Sticker Callbacks + #region Player Callbacks public void OnStickerPlaceReceived(string playerId, int stickerSlot, Vector3 position, Vector3 forward, Vector3 up) => AttemptPlaceSticker(playerId, position, forward, up, alignWithNormal: true, stickerSlot); @@ -78,8 +89,10 @@ public partial class StickerSystem if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData)) return; - stickerData.IdentifyTime = Time.time + 3f; + // todo: make prettier (idk shaders) + SchedulerSystem.AddJob(() => stickerData.Identify(), 0f, 0.1f, 30); + SchedulerSystem.AddJob(() => stickerData.ResetIdentify(), 4f, 1f, 1); } - #endregion Sticker Callbacks + #endregion Player Callbacks } diff --git a/Stickers/Stickers/StickerSystem.StickerLifecycle.cs b/Stickers/Stickers/StickerSystem.StickerLifecycle.cs index 5f350dd..a24ebd8 100644 --- a/Stickers/Stickers/StickerSystem.StickerLifecycle.cs +++ b/Stickers/Stickers/StickerSystem.StickerLifecycle.cs @@ -1,195 +1,160 @@ -using ABI_RC.Core; -using ABI_RC.Core.Player; -using ABI_RC.Systems.InputManagement; -using NAK.Stickers.Networking; -using UnityEngine; - -namespace NAK.Stickers; - -public partial class StickerSystem -{ - #region Sticker Lifecycle - - private StickerData GetOrCreateStickerData(string playerId) - { - if (_playerStickers.TryGetValue(playerId, out StickerData stickerData)) - return stickerData; - - stickerData = new StickerData(playerId, ModSettings.MaxStickerSlots); - _playerStickers[playerId] = stickerData; - return stickerData; - } - - public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left, bool isPreview = false) - { - Vector3 controllerForward = transform.forward; - Vector3 controllerUp = transform.up; - Vector3 playerUp = PlayerSetup.Instance.transform.up; - - // extracting angle of controller ray on forward axis - Vector3 projectedControllerUp = Vector3.ProjectOnPlane(controllerUp, controllerForward).normalized; - Vector3 projectedPlayerUp = Vector3.ProjectOnPlane(playerUp, controllerForward).normalized; - float angle = Vector3.Angle(projectedControllerUp, projectedPlayerUp); - - float angleThreshold = ModSettings.Entry_PlayerUpAlignmentThreshold.Value; - Vector3 targetUp = (angleThreshold != 0f && angle <= angleThreshold) - // leave 0.01% of the controller up vector to prevent issues with alignment on floor & ceiling in Desktop - ? Vector3.Slerp(controllerUp, playerUp, 0.99f) - : controllerUp; - - if (isPreview) - { - PlaceStickerPreview(transform.position, controllerForward, targetUp); - return; - } - - if (!PlaceStickerSelf(transform.position, transform.forward, targetUp)) - return; - - CVRInputManager.Instance.Vibrate(0f, 0.1f, 10f, 0.1f, hand); - } - - private bool PlaceStickerSelf(Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true) - { - if (!AttemptPlaceSticker(PlayerLocalId, position, forward, up, alignWithNormal, SelectedStickerSlot)) - return false; // failed - - // placed, now network - ModNetwork.SendPlaceSticker(SelectedStickerSlot, position, forward, up); - return true; - } - - private bool AttemptPlaceSticker(string playerId, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0, bool isPreview = false) - { - // if the world contained a gameobject with the [DisableStickers] name and restricted the instance disable stickers! - if (IsRestrictedInstance) - return false; - - StickerData stickerData = GetOrCreateStickerData(playerId); - if (Time.time - stickerData.LastPlacedTime < StickerCooldown) - return false; - - // Every layer other than IgnoreRaycast, PlayerLocal, PlayerClone, PlayerNetwork, and UI Internal - const int LayerMask = ~((1 << 2) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 15)); - if (!Physics.Raycast(position, forward, out RaycastHit hit, - 10f, LayerMask, QueryTriggerInteraction.Ignore)) - return false; - - // if gameobject name starts with [NoSticker] then don't place sticker - if (hit.transform.gameObject.name.StartsWith("[NoSticker]")) - return false; - - if (isPreview) - { - stickerData.PlacePreview(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot); - return true; - } - - stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot); - stickerData.PlayAudio(); - return true; - } - - public void ClearStickersSelf() - { - ClearStickersForPlayer(PlayerLocalId); - ModNetwork.SendClearAllStickers(); - } - - public void ClearStickerSelf(int stickerSlot) - { - ClearStickersForPlayer(PlayerLocalId, stickerSlot); - ModNetwork.SendClearSticker(stickerSlot); - } - - private void ClearStickersForPlayer(string playerId) - { - if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData)) - return; - - stickerData.Clear(); - } - - public void ClearStickersForPlayer(string playerId, int stickerSlot) - { - if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData)) - return; - - stickerData.Clear(stickerSlot); - } - - private void SetTextureSelf(byte[] imageBytes, int stickerSlot = 0) - { - Texture2D texture = new(1, 1); // placeholder - texture.LoadImage(imageBytes); - texture.Compress(true); // noachi said to do - - ClearStickerSelf(stickerSlot); // clear placed stickers in-scene so we can't replace an entire wall at once - OnPlayerStickerTextureReceived(PlayerLocalId, Guid.Empty, texture, stickerSlot); - ModNetwork.SetTexture(stickerSlot, imageBytes); - } - - public void ClearAllStickers() - { - foreach (StickerData stickerData in _playerStickers.Values) - stickerData.Clear(); - - ModNetwork.SendClearAllStickers(); - } - - public void OnPlayerStickerTextureReceived(string playerId, Guid textureHash, Texture2D texture, int stickerSlot = 0) - { - StickerData stickerData = GetOrCreateStickerData(playerId); - stickerData.SetTexture(textureHash, texture, stickerSlot); - } - - public bool HasTextureHash(string playerId, Guid textureHash) - { - StickerData stickerData = GetOrCreateStickerData(playerId); - return stickerData.CheckHasTextureHash(textureHash); - } - - public void CleanupAll() - { - foreach ((_, StickerData data) in _playerStickers) - data.Cleanup(); - - _playerStickers.Clear(); - } - - public void CleanupAllButSelf() - { - StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId); - - foreach ((_, StickerData data) in _playerStickers) - { - if (data == localStickerData) data.Clear(); - else data.Cleanup(); - } - - _playerStickers.Clear(); - _playerStickers[PlayerLocalId] = localStickerData; - } - - public void PlaceStickerPreview(Vector3 position, Vector3 forward, Vector3 up) - { - AttemptPlaceSticker(PlayerLocalId, position, forward, up, true, SelectedStickerSlot, true); - } - - public void UpdateStickerPreview() - { - if (!IsInStickerMode) return; - - StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId); - localStickerData.ClearPreview(); // clear prior frames sticker preview - localStickerData.UpdatePreview(SelectedStickerSlot); - } - - public void ClearStickerPreview() - { - StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId); - localStickerData.ClearPreview(); - } - - #endregion Sticker Lifecycle -} +using ABI_RC.Core; +using ABI_RC.Core.Player; +using ABI_RC.Systems.InputManagement; +using NAK.Stickers.Networking; +using UnityEngine; + +namespace NAK.Stickers; + +public partial class StickerSystem +{ + #region Sticker Lifecycle + + private StickerData GetOrCreateStickerData(string playerId) + { + if (_playerStickers.TryGetValue(playerId, out StickerData stickerData)) + return stickerData; + + stickerData = new StickerData(playerId == PlayerLocalId, ModSettings.MaxStickerSlots); + _playerStickers[playerId] = stickerData; + return stickerData; + } + + public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left) + { + Vector3 controllerForward = transform.forward; + Vector3 controllerUp = transform.up; + Vector3 playerUp = PlayerSetup.Instance.transform.up; + + // extracting angle of controller ray on forward axis + Vector3 projectedControllerUp = Vector3.ProjectOnPlane(controllerUp, controllerForward).normalized; + Vector3 projectedPlayerUp = Vector3.ProjectOnPlane(playerUp, controllerForward).normalized; + float angle = Vector3.Angle(projectedControllerUp, projectedPlayerUp); + + float angleThreshold = ModSettings.Entry_PlayerUpAlignmentThreshold.Value; + Vector3 targetUp = (angleThreshold != 0f && angle <= angleThreshold) + // leave 0.01% of the controller up vector to prevent issues with alignment on floor & ceiling in Desktop + ? Vector3.Slerp(controllerUp, playerUp, 0.99f) + : controllerUp; + + if (!PlaceStickerSelf(transform.position, transform.forward, targetUp)) + return; + + // do haptic if not lame + if (!ModSettings.Entry_HapticsOnPlace.Value) return; + CVRInputManager.Instance.Vibrate(0f, 0.1f, 10f, 0.1f, hand); + } + + private bool PlaceStickerSelf(Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true) + { + if (!AttemptPlaceSticker(PlayerLocalId, position, forward, up, alignWithNormal, SelectedStickerSlot)) + return false; // failed + + // placed, now network + ModNetwork.SendPlaceSticker(SelectedStickerSlot, position, forward, up); + return true; + } + + private bool AttemptPlaceSticker(string playerId, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0) + { + StickerData stickerData = GetOrCreateStickerData(playerId); + if (Time.time - stickerData.LastPlacedTime < StickerCooldown) + return false; + + // Every layer other than IgnoreRaycast, PlayerLocal, PlayerClone, PlayerNetwork, and UI Internal + const int LayerMask = ~((1 << 2) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 15)); + if (!Physics.Raycast(position, forward, out RaycastHit hit, + 10f, LayerMask, QueryTriggerInteraction.Ignore)) + return false; + + stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot); + stickerData.PlayAudio(); + return true; + } + + public void ClearStickersSelf() + { + ClearStickersForPlayer(PlayerLocalId); + ModNetwork.SendClearAllStickers(); + } + + private void ClearStickersForPlayer(string playerId) + { + if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData)) + return; + + stickerData.Clear(); + } + + private void ClearStickersForPlayer(string playerId, int stickerSlot) + { + if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData)) + return; + + stickerData.Clear(stickerSlot); + } + + private void SetTextureSelf(byte[] imageBytes, int stickerSlot = 0) + { + Texture2D texture = new(1, 1); // placeholder + texture.LoadImage(imageBytes); + texture.Compress(true); // noachi said to do + + OnPlayerStickerTextureReceived(PlayerLocalId, Guid.Empty, texture, stickerSlot); + ModNetwork.SetTexture(stickerSlot, imageBytes); + } + + public void ClearAllStickers() + { + foreach (StickerData stickerData in _playerStickers.Values) + stickerData.Clear(); + + ModNetwork.SendClearAllStickers(); + } + + public void OnPlayerStickerTextureReceived(string playerId, Guid textureHash, Texture2D texture, int stickerSlot = 0) + { + StickerData stickerData = GetOrCreateStickerData(playerId); + stickerData.SetTexture(textureHash, texture, stickerSlot); + } + + public bool HasTextureHash(string playerId, Guid textureHash) + { + StickerData stickerData = GetOrCreateStickerData(playerId); + return stickerData.CheckHasTextureHash(textureHash); + } + + public void CleanupAll() + { + foreach ((_, StickerData data) in _playerStickers) + data.Cleanup(); + + _playerStickers.Clear(); + } + + public void CleanupAllButSelf() + { + StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId); + + foreach ((_, StickerData data) in _playerStickers) + { + if (data.IsLocal) data.Clear(); + else data.Cleanup(); + } + + _playerStickers.Clear(); + _playerStickers[PlayerLocalId] = localStickerData; + } + + public void SelectStickerSlot(int stickerSlot) + { + SelectedStickerSlot = Mathf.Clamp(stickerSlot, 0, ModSettings.MaxStickerSlots - 1); + } + + public int GetCurrentStickerSlot() + { + return SelectedStickerSlot; + } + + #endregion Sticker Lifecycle +} diff --git a/Stickers/Stickers/Utilities/StickerCache.cs b/Stickers/Stickers/Utilities/StickerCache.cs index 3d4c2a2..d01b59e 100644 --- a/Stickers/Stickers/Utilities/StickerCache.cs +++ b/Stickers/Stickers/Utilities/StickerCache.cs @@ -60,19 +60,14 @@ public static class StickerCache { lock (_filesBeingProcessed) { - if (!_filesBeingProcessed.Add(fileInfo.FullName)) return; + if (!_filesBeingProcessed.Add(fileInfo.FullName)) + return; + _filesToGenerateThumbnails.Enqueue((fileInfo, button)); - StartGeneratingThumbnailsIfNeeded(); + MTJobManager.RunOnMainThread("StartGeneratingThumbnailsIfNeeded", StartGeneratingThumbnailsIfNeeded); } } - public static void ClearCache() - { - if (!Directory.Exists(ThumbnailPath)) return; - Directory.Delete(ThumbnailPath, true); - StickerMod.Logger.Msg("Cleared thumbnail cache."); - } - #endregion Public Methods #region Private Methods @@ -88,8 +83,6 @@ public static class StickerCache { try { - Thread.CurrentThread.IsBackground = false; // working around bug in MTJobManager - int generatedThumbnails = 0; while (BTKUIAddon.IsPopulatingPage || _filesToGenerateThumbnails.Count > 0) @@ -120,7 +113,6 @@ public static class StickerCache finally { IsGeneratingThumbnails = false; - Thread.CurrentThread.IsBackground = true; // working around bug in MTJobManager } }); } @@ -148,7 +140,7 @@ public static class StickerCache imageStream.Dispose(); return false; } - + PrepareIconFromMemoryStream(ModSettings.ModName, relativePathWithoutExtension, imageStream); imageStream.Dispose(); return true; @@ -162,10 +154,9 @@ public static class StickerCache } else { - //iconPath = UIUtils.GetCleanString(iconPath); + iconPath = UIUtils.GetCleanString(iconPath); iconPath = Path.Combine(ThumbnailPath, iconPath); File.WriteAllBytes(iconPath + ".png", destination.ToArray()); - //StickerMod.Logger.Msg("Prepared icon: " + iconPath); } } diff --git a/Stickers/format.json b/Stickers/format.json index 2d2cab5..fc4871f 100644 --- a/Stickers/format.json +++ b/Stickers/format.json @@ -1,11 +1,11 @@ { - "_id": 232, + "_id": -1, "name": "Stickers", - "modversion": "1.0.9", - "gameversion": "2025r179", + "modversion": "1.0.0", + "gameversion": "2024r175", "loaderversion": "0.6.1", "modtype": "Mod", - "author": "NotAKidoS, SketchFoxsky", + "author": "NotAKidoS", "description": "Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network.\n\nLimitations:\n- Image should be under 256KB in size.\n- Image dimensions should be a power of 2 (e.g. 512x512, 1024x1024).\n - If the image exceeds the size limit or is not a power of 2 the mod will automatically resize it.\n - The automatic resizing may result in loss of quality (or may just fail), so it is recommended to resize the image yourself before placing it in the `UserData/Stickers/` folder.\n\n-# More information can be found on the [README](https://github.com/NotAKidoS/NAK_CVR_Mods/blob/main/Stickers/README.md).", "searchtags": [ "stickers", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/Stickers.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r38/Stickers.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Stickers/", - "changelog": "- Fixes for 2025r179\n- Fixed placing stickers when Cohtml text input fields were focused\n- Fixed scrolling cycling selected sticker slot despite not being in placement mode", + "changelog": "- Initial release", "embedcolor": "#f61963" } \ No newline at end of file diff --git a/.Deprecated/StopClosingMyMenuOnWorldLoad/Main.cs b/StopClosingMyMenuOnWorldLoad/Main.cs similarity index 100% rename from .Deprecated/StopClosingMyMenuOnWorldLoad/Main.cs rename to StopClosingMyMenuOnWorldLoad/Main.cs diff --git a/.Deprecated/StopClosingMyMenuOnWorldLoad/Properties/AssemblyInfo.cs b/StopClosingMyMenuOnWorldLoad/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/StopClosingMyMenuOnWorldLoad/Properties/AssemblyInfo.cs rename to StopClosingMyMenuOnWorldLoad/Properties/AssemblyInfo.cs diff --git a/.Deprecated/StopClosingMyMenuOnWorldLoad/README.md b/StopClosingMyMenuOnWorldLoad/README.md similarity index 100% rename from .Deprecated/StopClosingMyMenuOnWorldLoad/README.md rename to StopClosingMyMenuOnWorldLoad/README.md diff --git a/.Deprecated/StopClosingMyMenuOnWorldLoad/StopClosingMyMenuOnWorldLoad.csproj b/StopClosingMyMenuOnWorldLoad/StopClosingMyMenuOnWorldLoad.csproj similarity index 100% rename from .Deprecated/StopClosingMyMenuOnWorldLoad/StopClosingMyMenuOnWorldLoad.csproj rename to StopClosingMyMenuOnWorldLoad/StopClosingMyMenuOnWorldLoad.csproj diff --git a/.Deprecated/StopClosingMyMenuOnWorldLoad/format.json b/StopClosingMyMenuOnWorldLoad/format.json similarity index 100% rename from .Deprecated/StopClosingMyMenuOnWorldLoad/format.json rename to StopClosingMyMenuOnWorldLoad/format.json diff --git a/.Deprecated/SwitchToDesktopOnSteamVRExit/Main.cs b/SwitchToDesktopOnSteamVRExit/Main.cs similarity index 100% rename from .Deprecated/SwitchToDesktopOnSteamVRExit/Main.cs rename to SwitchToDesktopOnSteamVRExit/Main.cs diff --git a/.Deprecated/SwitchToDesktopOnSteamVRExit/Properties/AssemblyInfo.cs b/SwitchToDesktopOnSteamVRExit/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/SwitchToDesktopOnSteamVRExit/Properties/AssemblyInfo.cs rename to SwitchToDesktopOnSteamVRExit/Properties/AssemblyInfo.cs diff --git a/.Deprecated/SwitchToDesktopOnSteamVRExit/README.md b/SwitchToDesktopOnSteamVRExit/README.md similarity index 100% rename from .Deprecated/SwitchToDesktopOnSteamVRExit/README.md rename to SwitchToDesktopOnSteamVRExit/README.md diff --git a/SwitchToDesktopOnSteamVRExit/SwitchToDesktopOnSteamVRExit.csproj b/SwitchToDesktopOnSteamVRExit/SwitchToDesktopOnSteamVRExit.csproj new file mode 100644 index 0000000..66a50a8 --- /dev/null +++ b/SwitchToDesktopOnSteamVRExit/SwitchToDesktopOnSteamVRExit.csproj @@ -0,0 +1,2 @@ + + diff --git a/.Deprecated/SwitchToDesktopOnSteamVRExit/format.json b/SwitchToDesktopOnSteamVRExit/format.json similarity index 100% rename from .Deprecated/SwitchToDesktopOnSteamVRExit/format.json rename to SwitchToDesktopOnSteamVRExit/format.json diff --git a/ThirdPerson/CameraLogic.cs b/ThirdPerson/CameraLogic.cs index 8f26e68..24d10c5 100644 --- a/ThirdPerson/CameraLogic.cs +++ b/ThirdPerson/CameraLogic.cs @@ -1,6 +1,7 @@ using ABI_RC.Core.Player; using ABI_RC.Core.Savior; using ABI_RC.Core.Util.Object_Behaviour; +using System.Collections; using ABI_RC.Core; using ABI.CCK.Components; using UnityEngine; @@ -12,6 +13,7 @@ internal static class CameraLogic private static float _dist; private static float _scale = 1f; private static Camera _thirdPersonCam; + private static Camera _uiCam; private static Camera _desktopCam; private static int _storedCamMask; private static CameraFovClone _cameraFovClone; @@ -37,20 +39,24 @@ internal static class CameraLogic if (_state) _storedCamMask = _desktopCam.cullingMask; _desktopCam.cullingMask = _state ? 0 : _storedCamMask; + _uiCam.cullingMask = _state ? _uiCam.cullingMask & ~(1 << CVRLayers.PlayerClone) : _uiCam.cullingMask | (1 << CVRLayers.PlayerClone); _thirdPersonCam.gameObject.SetActive(_state); } } - internal static void SetupCamera() + internal static IEnumerator SetupCamera() { + yield return new WaitUntil(() => PlayerSetup.Instance); + _thirdPersonCam = new GameObject("ThirdPersonCameraObj", typeof(Camera)).GetComponent(); _cameraFovClone = _thirdPersonCam.gameObject.AddComponent(); - _desktopCam = PlayerSetup.Instance.desktopCam; + _desktopCam = PlayerSetup.Instance.desktopCamera.GetComponent(); _cameraFovClone.targetCamera = _desktopCam; _thirdPersonCam.transform.SetParent(_desktopCam.transform); + _uiCam = _desktopCam.transform.Find("_UICamera").GetComponent(); RelocateCam(CameraLocation.Default); @@ -61,15 +67,12 @@ internal static class CameraLogic internal static void CopyPlayerCamValues() { - Camera activePlayerCam = PlayerSetup.Instance.activeCam; - if (!_thirdPersonCam || !activePlayerCam) + Camera activePlayerCam = PlayerSetup.Instance.GetActiveCamera().GetComponent(); + if (_thirdPersonCam == null || activePlayerCam == null) return; ThirdPerson.Logger.Msg("Copying active camera settings & components."); - CVRTools.CopyToDestCam(activePlayerCam, _thirdPersonCam); - - // Remove PlayerClone - // _thirdPersonCam.cullingMask &= ~(1 << CVRLayers.PlayerClone); + CVRTools.CopyToDestCam(activePlayerCam, _thirdPersonCam, true); if (!CheckIsRestricted()) return; @@ -111,9 +114,9 @@ internal static class CameraLogic private static void ResetDist() => _dist = 0; internal static void ScrollDist(float sign) { _dist += sign * 0.25f; RelocateCam(CurrentLocation); } - internal static void AdjustScale(float avatarScaleRelation) { _scale = avatarScaleRelation; RelocateCam(CurrentLocation); } + internal static void AdjustScale(float height) { _scale = height; RelocateCam(CurrentLocation); } internal static void CheckVRMode() { if (MetaPort.Instance.isUsingVr) State = false; } private static bool CheckIsRestricted() - => CVRWorld.Instance && !CVRWorld.Instance.enableZoom; + => CVRWorld.Instance != null && !CVRWorld.Instance.enableZoom; } \ No newline at end of file diff --git a/ThirdPerson/Patches.cs b/ThirdPerson/Patches.cs index 02a60db..9267d2e 100644 --- a/ThirdPerson/Patches.cs +++ b/ThirdPerson/Patches.cs @@ -4,6 +4,7 @@ using MelonLoader; using System.Reflection; using static NAK.ThirdPerson.CameraLogic; using ABI_RC.Core; +using ABI_RC.Core.Player.TransformHider; namespace NAK.ThirdPerson; @@ -27,11 +28,16 @@ internal static class Patches typeof(CVRTools).GetMethod(nameof(CVRTools.ConfigureHudAffinity), BindingFlags.Public | BindingFlags.Static), postfix: typeof(Patches).GetMethod(nameof(OnConfigureHudAffinity), BindingFlags.NonPublic | BindingFlags.Static).ToNewHarmonyMethod() ); + harmony.Patch( + typeof(TransformHiderManager).GetMethod(nameof(TransformHiderManager.CheckPlayerCamWithinRange), BindingFlags.NonPublic | BindingFlags.Static), + prefix: typeof(Patches).GetMethod(nameof(OnCheckPlayerCamWithinRange), BindingFlags.NonPublic | BindingFlags.Static).ToNewHarmonyMethod() + ); } //Copy camera settings & postprocessing components private static void OnPostWorldStart() => CopyPlayerCamValues(); //Adjust camera distance with height as modifier - private static void OnScaleAdjusted(ref float ____avatarScaleRelation) => AdjustScale(____avatarScaleRelation); + private static void OnScaleAdjusted(float height) => AdjustScale(height); private static void OnConfigureHudAffinity() => CheckVRMode(); + private static bool OnCheckPlayerCamWithinRange() => !State; // don't hide head if in third person } \ No newline at end of file diff --git a/ThirdPerson/Properties/AssemblyInfo.cs b/ThirdPerson/Properties/AssemblyInfo.cs index 85d8bfa..ab34afe 100644 --- a/ThirdPerson/Properties/AssemblyInfo.cs +++ b/ThirdPerson/Properties/AssemblyInfo.cs @@ -17,16 +17,16 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ThirdPerson" )] -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] -[assembly: MelonColor(255, 246, 25, 97)] // do not change color, originally chosen by Davi -[assembly: MelonAuthorColor(255, 158, 21, 32)] // do not change color, originally chosen by Davi +[assembly: MelonColor(255, 246, 25, 97)] +[assembly: MelonAuthorColor(255, 158, 21, 32)] [assembly: HarmonyDontPatchAll] namespace NAK.ThirdPerson.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.1.3"; + public const string Version = "1.0.9"; public const string Author = "Davi & NotAKidoS"; } \ No newline at end of file diff --git a/ThirdPerson/ThirdPerson.cs b/ThirdPerson/ThirdPerson.cs index 5147e39..21691c4 100644 --- a/ThirdPerson/ThirdPerson.cs +++ b/ThirdPerson/ThirdPerson.cs @@ -1,5 +1,4 @@ -using ABI_RC.Systems.GameEventSystem; -using MelonLoader; +using MelonLoader; using UnityEngine; using static NAK.ThirdPerson.CameraLogic; @@ -14,7 +13,7 @@ public class ThirdPerson : MelonMod Logger = LoggerInstance; Patches.Apply(HarmonyInstance); - CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(SetupCamera); + MelonCoroutines.Start(SetupCamera()); } public override void OnUpdate() diff --git a/ThirdPerson/format.json b/ThirdPerson/format.json index ff4c491..52c0017 100644 --- a/ThirdPerson/format.json +++ b/ThirdPerson/format.json @@ -2,9 +2,9 @@ { "_id": 16, "name": "ThirdPerson", - "modversion": "1.1.3", - "gameversion": "2025r180", - "loaderversion": "0.7.2", + "modversion": "1.0.9", + "gameversion": "2024r175", + "loaderversion": "0.6.1", "modtype": "Mod", "author": "Davi & NotAKidoS", "description": "Allows you to go into third person view by pressing Ctrl + T to toggle and Ctrl + Y to cycle modes.", @@ -14,9 +14,9 @@ "third person" ], "requirements": [], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ThirdPerson.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r26/ThirdPerson.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ThirdPerson", - "changelog": "- Fixes for 2025r180", + "changelog": "- Fixed an issue where VR Switching without using ThirdPerson prior would incorrectly set the Desktop camera culling mask to 0\n- Fixed an NRE when checking CVRWorld zoom rule when no CVRWorld instance was found\n- Prevented head hiding from persisting into third person while Avatar Overrender Ui is enabled", "embedcolor": "#F61961" } ] \ No newline at end of file diff --git a/Tinyboard/Main.cs b/Tinyboard/Main.cs deleted file mode 100644 index 540d150..0000000 --- a/Tinyboard/Main.cs +++ /dev/null @@ -1,346 +0,0 @@ -using System.Reflection; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Savior; -using ABI_RC.Core.UI; -using ABI_RC.Core.UI.UIRework.Managers; -using ABI_RC.Systems.VRModeSwitch; -using ABI_RC.VideoPlayer.Scripts; -using HarmonyLib; -using MelonLoader; -using TMPro; -using UnityEngine; -using UnityEngine.UI; - -namespace NAK.Tinyboard; - -public class TinyboardMod : MelonMod -{ - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(Tinyboard)); - - private static readonly MelonPreferences_Entry EntrySmartAlignToMenu = - Category.CreateEntry( - identifier: "smart_align_to_menu", - true, - display_name: "Smart Align To Menu", - description: "Should the keyboard align to the menu it was opened from? (Main Menu, World-Anchored Quick Menu)"); - - private static readonly MelonPreferences_Entry EntryEnforceTitle = - Category.CreateEntry( - identifier: "enforce_title", - true, - display_name: "Enforce Title", - description: "Should the keyboard enforce a title when opened from an input field or main menu?"); - - private static readonly MelonPreferences_Entry EntryResizeKeyboard = - Category.CreateEntry( - identifier: "resize_keyboard", - true, - display_name: "Resize Keyboard", - description: "Should the keyboard be resized to match XSOverlays width?"); - - private static readonly MelonPreferences_Entry EntryUseModifiers = - Category.CreateEntry( - identifier: "use_scale_distance_modifiers", - true, - display_name: "Use Scale/Distance/Offset Modifiers", - description: "Should the scale/distance/offset modifiers be used?"); - - private static readonly MelonPreferences_Entry EntryDesktopScaleModifier = - Category.CreateEntry( - identifier: "desktop_scale_modifier", - 0.75f, - display_name: "Desktop Scale Modifier", - description: "Scale modifier for desktop mode."); - - private static readonly MelonPreferences_Entry EntryDesktopDistance = - Category.CreateEntry( - identifier: "desktop_distance_modifier", - 0f, - display_name: "Desktop Distance Modifier", - description: "Distance modifier for desktop mode."); - - private static readonly MelonPreferences_Entry EntryDesktopVerticalAdjustment = - Category.CreateEntry( - identifier: "desktop_vertical_adjustment", - 0.1f, - display_name: "Desktop Vertical Adjustment", - description: "Vertical adjustment for desktop mode."); - - private static readonly MelonPreferences_Entry EntryVRScaleModifier = - Category.CreateEntry( - identifier: "vr_scale_modifier", - 0.85f, - display_name: "VR Scale Modifier", - description: "Scale modifier for VR mode."); - - private static readonly MelonPreferences_Entry EntryVRDistance = - Category.CreateEntry( - identifier: "vr_distance_modifier", - 0.2f, - display_name: "VR Distance Modifier", - description: "Distance modifier for VR mode."); - - private static readonly MelonPreferences_Entry EntryVRVerticalAdjustment = - Category.CreateEntry( - identifier: "vr_vertical_adjustment", - 0f, - display_name: "VR Vertical Adjustment", - description: "Vertical adjustment for VR mode."); - - #endregion Melon Preferences - - private static Transform _tinyBoardOffset; - private static void ApplyTinyBoardOffsetsForVRMode() - { - if (!EntryUseModifiers.Value) - { - _tinyBoardOffset.localScale = Vector3.one; - _tinyBoardOffset.localPosition = Vector3.zero; - return; - } - float distanceModifier; - float scaleModifier; - float verticalAdjustment; - if (MetaPort.Instance.isUsingVr) - { - scaleModifier = EntryVRScaleModifier.Value; - distanceModifier = EntryVRDistance.Value; - verticalAdjustment = EntryVRVerticalAdjustment.Value; - } - else - { - scaleModifier = EntryDesktopScaleModifier.Value; - distanceModifier = EntryDesktopDistance.Value; - verticalAdjustment = EntryDesktopVerticalAdjustment.Value; - } - _tinyBoardOffset.localScale = Vector3.one * scaleModifier; - _tinyBoardOffset.localPosition = new Vector3(0f, verticalAdjustment, distanceModifier); - } - - private static void ApplyTinyBoardWidthResize() - { - KeyboardManager km = KeyboardManager.Instance; - CohtmlControlledView cohtmlView = km.cohtmlView; - Transform keyboardTransform = cohtmlView.transform; - - int targetWidthPixels = EntryResizeKeyboard.Value ? 1330 : 1520; - float targetScaleX = EntryResizeKeyboard.Value ? 1.4f : 1.6f; - - cohtmlView.Width = targetWidthPixels; - Vector3 currentScale = keyboardTransform.localScale; - currentScale.x = targetScaleX; - keyboardTransform.localScale = currentScale; - } - - public override void OnInitializeMelon() - { - // add our shim transform to scale the menu down by 0.75 - HarmonyInstance.Patch( - typeof(CVRKeyboardPositionHelper).GetMethod(nameof(CVRKeyboardPositionHelper.Awake), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnCVRKeyboardPositionHelperAwake), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - // reposition the keyboard when it is opened to match the menu position if it is opened from a menu - HarmonyInstance.Patch( - typeof(MenuPositionHelperBase).GetMethod(nameof(MenuPositionHelperBase.OnMenuOpen), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnMenuPositionHelperBaseOnMenuOpen), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - // enforces a title for the keyboard in cases it did not already have one - HarmonyInstance.Patch( - typeof(KeyboardManager).GetMethod(nameof(KeyboardManager.ShowKeyboard), - BindingFlags.Public | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnKeyboardManagerShowKeyboard), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - // resize keyboard to match XSOverlays width - HarmonyInstance.Patch( - typeof(KeyboardManager).GetMethod(nameof(KeyboardManager.Start), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnKeyboardManagerStart), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - // update offsets when switching VR modes - VRModeSwitchEvents.OnPostVRModeSwitch.AddListener((_) => ApplyTinyBoardOffsetsForVRMode()); - - // listen for setting changes - EntryUseModifiers.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); - EntryDesktopScaleModifier.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); - EntryVRScaleModifier.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); - EntryDesktopDistance.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); - EntryVRDistance.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); - EntryResizeKeyboard.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardWidthResize()); - } - - private static void OnCVRKeyboardPositionHelperAwake(CVRKeyboardPositionHelper __instance) - { - _tinyBoardOffset = new GameObject("NAKTinyBoard").transform; - - Transform offsetTransform = __instance.transform.GetChild(0); - _tinyBoardOffset.SetParent(offsetTransform, false); - - ApplyTinyBoardOffsetsForVRMode(); - - Transform menuTransform = __instance.menuTransform; - menuTransform.SetParent(_tinyBoardOffset, false); - } - - private static void OnMenuPositionHelperBaseOnMenuOpen(MenuPositionHelperBase __instance) - { - if (!EntrySmartAlignToMenu.Value) return; - if (__instance is not CVRKeyboardPositionHelper { IsMenuOpen: true }) return; - - // Check if the open source was an open menu - KeyboardManager.OpenSource? openSource = KeyboardManager.Instance._keyboardOpenSource; - - MenuPositionHelperBase menuPositionHelper; - switch (openSource) - { - case KeyboardManager.OpenSource.MainMenu: - menuPositionHelper = CVRMainMenuPositionHelper.Instance; - break; - case KeyboardManager.OpenSource.QuickMenu: - menuPositionHelper = CVRQuickMenuPositionHelper.Instance; - if (!menuPositionHelper.IsUsingWorldAnchoredMenu) return; // hand anchored quick menu, don't touch - break; - default: return; - } - - // get modifiers - float rootScaleModifier = __instance.transform.lossyScale.x; - float keyboardDistanceModifier = __instance.MenuDistanceModifier; - float menuDistanceModifier = menuPositionHelper.MenuDistanceModifier; - - // get difference between modifiers - float distanceModifier = keyboardDistanceModifier - menuDistanceModifier; - - // place keyboard at menu position + difference in modifiers - Transform menuOffsetTransform = menuPositionHelper._offsetTransform; - Quaternion keyboardRotation = menuOffsetTransform.rotation; - Vector3 keyboardPosition = menuOffsetTransform.position + - menuOffsetTransform.forward * (rootScaleModifier * distanceModifier); - - // place keyboard as if it was opened with player camera in same place as menu was - __instance._offsetTransform.SetPositionAndRotation(keyboardPosition, keyboardRotation); - } - - private static void OnKeyboardManagerStart() => ApplyTinyBoardWidthResize(); - - /* - public void ShowKeyboard( - string currentText, - Action callback, - string placeholder = null, - string successText = "Success", - int maxCharacterCount = 0, - bool hidden = false, - bool multiLine = false, - string title = null, - OpenSource openSource = OpenSource.Other) - */ - - // using mix of index and args params because otherwise explodes with invalid IL ? - private static void OnKeyboardManagerShowKeyboard(ref string __7, ref string __2, object[] __args) - { - if (!EntryEnforceTitle.Value) return; - - // ReSharper disable thrice InlineTemporaryVariable - ref string title = ref __7; - ref string placeholder = ref __2; - if (!string.IsNullOrWhiteSpace(title)) return; - - Action callback = __args[1] as Action; - KeyboardManager.OpenSource? openSource = __args[8] as KeyboardManager.OpenSource?; - - if (callback?.Target != null) - { - var target = callback.Target; - switch (openSource) - { - case KeyboardManager.OpenSource.CVRInputFieldKeyboardHandler: - TrySetPlaceholderFromKeyboardHandler(target, ref title, ref placeholder); - break; - case KeyboardManager.OpenSource.MainMenu: - title = TryExtractTitleFromMainMenu(target); - break; - } - } - - if (!string.IsNullOrWhiteSpace(placeholder)) - { - // fallback to placeholder if no title found - if (string.IsNullOrWhiteSpace(title)) title = placeholder; - - // clear placeholder if it is longer than 10 characters - if (placeholder.Length > 10) placeholder = string.Empty; - } - } - - private static void TrySetPlaceholderFromKeyboardHandler(object target, ref string title, ref string placeholder) - { - Type type = target.GetType(); - - TMP_InputField tmpInput = type.GetField("input", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) as TMP_InputField; - if (tmpInput != null) - { - if (tmpInput.GetComponentInParent()) title = "VideoPlayer URL or Search"; - if (tmpInput.placeholder is TMP_Text ph) - { - placeholder = ph.text; - return; - } - placeholder = PrettyString(tmpInput.gameObject.name); - return; - } - - InputField legacyInput = type.GetField("inputField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) as InputField; - if (legacyInput != null) - { - if (legacyInput.placeholder is Text ph) - { - placeholder = ph.text; - return; - } - placeholder = PrettyString(legacyInput.gameObject.name); - return; - } - } - - private static string TryExtractTitleFromMainMenu(object target) - { - Type type = target.GetType(); - string targetId = type.GetField("targetId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) as string; - return string.IsNullOrWhiteSpace(targetId) ? null : PrettyString(targetId); - } - - private static string PrettyString(string str) - { - int len = str.Length; - Span buffer = stackalloc char[len * 2]; - int pos = 0; - bool newWord = true; - for (int i = 0; i < len; i++) - { - char c = str[i]; - if (c is '_' or '-') - { - buffer[pos++] = ' '; - newWord = true; - continue; - } - if (char.IsUpper(c) && i > 0 && !newWord) buffer[pos++] = ' '; - buffer[pos++] = newWord ? char.ToUpperInvariant(c) : c; - newWord = false; - } - return new string(buffer[..pos]); - } -} \ No newline at end of file diff --git a/Tinyboard/Properties/AssemblyInfo.cs b/Tinyboard/Properties/AssemblyInfo.cs deleted file mode 100644 index b3048a5..0000000 --- a/Tinyboard/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.Tinyboard.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.Tinyboard))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.Tinyboard))] - -[assembly: MelonInfo( - typeof(NAK.Tinyboard.TinyboardMod), - nameof(NAK.Tinyboard), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Tinyboard" -)] - -[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.Tinyboard.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/Tinyboard/README.md b/Tinyboard/README.md deleted file mode 100644 index 7ff0922..0000000 --- a/Tinyboard/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# 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. diff --git a/Tinyboard/Tinyboard.csproj b/Tinyboard/Tinyboard.csproj deleted file mode 100644 index 5a8badc..0000000 --- a/Tinyboard/Tinyboard.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - YouAreMineNow - - diff --git a/Tinyboard/format.json b/Tinyboard/format.json deleted file mode 100644 index da65a56..0000000 --- a/Tinyboard/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_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" -} \ No newline at end of file diff --git a/.Deprecated/VisualCloneFix/Main.cs b/VisualCloneFix/Main.cs similarity index 100% rename from .Deprecated/VisualCloneFix/Main.cs rename to VisualCloneFix/Main.cs diff --git a/.Deprecated/VisualCloneFix/Patches.cs b/VisualCloneFix/Patches.cs similarity index 98% rename from .Deprecated/VisualCloneFix/Patches.cs rename to VisualCloneFix/Patches.cs index d1fb84b..2fc9a14 100644 --- a/.Deprecated/VisualCloneFix/Patches.cs +++ b/VisualCloneFix/Patches.cs @@ -16,7 +16,9 @@ public static class Patches [HarmonyPatch(typeof(TransformHiderUtils), nameof(TransformHiderUtils.SetupAvatar))] private static bool OnSetupAvatar(GameObject avatar) { - if (!VisualCloneFixMod.EntryUseVisualClone.Value) return true; + if (!VisualCloneFixMod.EntryUseVisualClone.Value) + return true; + LocalCloneHelper.SetupAvatar(avatar); return false; } diff --git a/.Deprecated/VisualCloneFix/Properties/AssemblyInfo.cs b/VisualCloneFix/Properties/AssemblyInfo.cs similarity index 100% rename from .Deprecated/VisualCloneFix/Properties/AssemblyInfo.cs rename to VisualCloneFix/Properties/AssemblyInfo.cs diff --git a/.Deprecated/AvatarCloneTest/README.md b/VisualCloneFix/README.md similarity index 100% rename from .Deprecated/AvatarCloneTest/README.md rename to VisualCloneFix/README.md diff --git a/.Deprecated/VisualCloneFix/VisualCloneFix.csproj b/VisualCloneFix/VisualCloneFix.csproj similarity index 100% rename from .Deprecated/VisualCloneFix/VisualCloneFix.csproj rename to VisualCloneFix/VisualCloneFix.csproj diff --git a/.Deprecated/AvatarCloneTest/format.json b/VisualCloneFix/format.json similarity index 100% rename from .Deprecated/AvatarCloneTest/format.json rename to VisualCloneFix/format.json diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs b/YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs deleted file mode 100644 index ee47977..0000000 --- a/YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs +++ /dev/null @@ -1,575 +0,0 @@ -using System.Collections; -using System.Reflection; -using ABI_RC.Core.EventSystem; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.IO; -using ABI_RC.Core.Networking; -using ABI_RC.Core.Networking.API.Responses; -using ABI_RC.Core.Player; -using ABI_RC.Core.Util; -using ABI_RC.Core.Util.Encryption; -using ABI_RC.Systems.GameEventSystem; -using ABI_RC.Systems.Movement; -using ABI.CCK.Components; -using DarkRift; -using HarmonyLib; -using MelonLoader; -using UnityEngine; -using UnityEngine.SceneManagement; -using Object = UnityEngine.Object; -using Random = UnityEngine.Random; - -namespace NAK.YouAreMyPropNowWeAreHavingSoftTacosLater; - -public class YouAreMyPropNowWeAreHavingSoftTacosLaterMod : MelonMod -{ - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(YouAreMyPropNowWeAreHavingSoftTacosLater)); - - private static readonly MelonPreferences_Entry EntryTrackPickups = - Category.CreateEntry("track_pickups", true, display_name: "Track Pickups", description: "Should pickups be tracked?"); - - private static readonly MelonPreferences_Entry EntryTrackAttachments = - Category.CreateEntry("track_attachments", true, display_name: "Track Attachments", description: "Should attachments be tracked?"); - - private static readonly MelonPreferences_Entry EntryTrackSeats = - Category.CreateEntry("track_seats", true, display_name: "Track Seats", description: "Should seats be tracked?"); - - private static readonly MelonPreferences_Entry EntryOnlySpawnedByMe = - Category.CreateEntry("only_spawned_by_me", true, display_name: "Only Spawned By Me", description: "Should only props spawned by me be tracked?"); - - #endregion Melon Preferences - - #region Melon Events - - public override void OnInitializeMelon() - { - #region CVRPickupObject Patches - - HarmonyInstance.Patch( - typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.OnGrab), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRPickupObjectOnGrab), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.OnDrop), BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRPickupObjectOnDrop), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRPickupObject Patches - - #region CVRAttachment Patches - - HarmonyInstance.Patch( // Cannot compile when using nameof - typeof(CVRAttachment).GetMethod(nameof(CVRAttachment.DoAttachmentSetup), - BindingFlags.NonPublic | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRAttachmentAttachInternal), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(CVRAttachment).GetMethod(nameof(CVRAttachment.DeAttach)), - prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRAttachmentDeAttach), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRAttachment Patches - - #region CVRSeat Patches - - HarmonyInstance.Patch( - typeof(CVRSeat).GetMethod(nameof(CVRSeat.SitDown), - BindingFlags.Public | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRSeatSitDown), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(CVRSeat).GetMethod(nameof(CVRSeat.ExitSeat), - BindingFlags.Public | BindingFlags.Instance), - postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRSeatExitSeat), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRSeat Patches - - #region CVRSpawnable Patches - - HarmonyInstance.Patch( - typeof(CVRSpawnable).GetMethod(nameof(CVRSpawnable.OnDestroy), - BindingFlags.Public | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnSpawnableOnDestroy), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRSpawnable Patches - - #region CVRSyncHelper Patches - - HarmonyInstance.Patch( // Replaces method, original needlessly ToArray??? - typeof(CVRSyncHelper).GetMethod(nameof(CVRSyncHelper.ClearProps), - BindingFlags.Public | BindingFlags.Static), - prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRSyncHelperClearProps), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRSyncHelper Patches - - #region CVRDownloadManager Patches - - HarmonyInstance.Patch( - typeof(CVRDownloadManager).GetMethod(nameof(CVRDownloadManager.QueueTask), - BindingFlags.Public | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRDownloadManagerQueueTask), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion CVRDownloadManager Patches - - #region BetterBetterCharacterController Patches - - HarmonyInstance.Patch( - typeof(BetterBetterCharacterController).GetMethod(nameof(BetterBetterCharacterController.SetSitting), - BindingFlags.Public | BindingFlags.Instance), - prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnBetterBetterCharacterControllerSetSitting), - BindingFlags.NonPublic | BindingFlags.Static)) - ); - - #endregion BetterBetterCharacterController Patches - - #region CVRWorld Game Events - - CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoad); - CVRGameEventSystem.World.OnUnload.AddListener(OnWorldUnload); - - #endregion CVRWorld Game Events - - #region Instances Game Events - - CVRGameEventSystem.Instance.OnConnected.AddListener(OnInstanceConnected); - - #endregion Instances Game Events - } - - #endregion Melon Events - - #region Harmony Patches - - [Flags] private enum HeldPropState { None = 0, Pickup = 1, Attachment = 2, Seat = 3 } - - private static readonly Dictionary _heldPropStates = new(); - - private static void AddHeldPropState(CVRSyncHelper.PropData propData, HeldPropState state) - { - if (!_heldPropStates.TryAdd(propData, state)) _heldPropStates[propData] |= state; - } - - private static void RemoveHeldPropState(CVRSyncHelper.PropData propData, HeldPropState state) - { - if (!_heldPropStates.TryGetValue(propData, out HeldPropState currentState)) return; - currentState &= ~state; - if (currentState == HeldPropState.None) _heldPropStates.Remove(propData); - else _heldPropStates[propData] = currentState; - } - - private static GameObject _persistantPropsContainer; - private static GameObject GetOrCreatePropsContainer() - { - if (_persistantPropsContainer) return _persistantPropsContainer; - _persistantPropsContainer = new("YouAreMyPropNowWeAreHavingSoftTacosLater"); - _persistantPropsContainer.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); - _persistantPropsContainer.transform.localScale = Vector3.one; - Object.DontDestroyOnLoad(_persistantPropsContainer); - return _persistantPropsContainer; - } - - private static readonly Dictionary _keyToPropData = new(); - private static readonly Stack _spawnablePositionStack = new(); - private static bool _ignoreNextSeatExit; - private static float _heightOffset; - - private static void OnCVRPickupObjectOnGrab(CVRPickupObject __instance) - { - if (!EntryTrackPickups.Value) return; - if (!TryGetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; - AddHeldPropState(propData, HeldPropState.Pickup); - } - - private static void OnCVRPickupObjectOnDrop(CVRPickupObject __instance) - { - if (!TryGetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; - RemoveHeldPropState(propData, HeldPropState.Pickup); - } - - private static void OnCVRAttachmentAttachInternal(CVRAttachment __instance) - { - if (!EntryTrackAttachments.Value) return; - if (!TryGetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; - AddHeldPropState(propData, HeldPropState.Attachment); - } - - private static void OnCVRAttachmentDeAttach(CVRAttachment __instance) - { - if (!__instance._isAttached) return; // Can invoke DeAttach without being attached - if (!TryGetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; - RemoveHeldPropState(propData, HeldPropState.Attachment); - } - - private static void OnCVRSeatSitDown(CVRSeat __instance) - { - if (!EntryTrackSeats.Value) return; - if (!TryGetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; - AddHeldPropState(propData, HeldPropState.Seat); - } - - private static void OnCVRSeatExitSeat(CVRSeat __instance) - { - if (!TryGetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; - RemoveHeldPropState(propData, HeldPropState.Seat); - } - - private static void OnSpawnableOnDestroy(CVRSpawnable __instance) - { - if (!TryGetPropData(__instance, out CVRSyncHelper.PropData propData)) return; - _heldPropStates.Remove(propData); - } - - // ReSharper disable UnusedParameter.Local - private static bool OnCVRDownloadManagerQueueTask(AssetManagement.UgcMetadata metadata, DownloadTask.ObjectType type, string assetUrl, string fileId, string toAttach, CVRLoadingAvatarController loadingAvatarController = null, bool joinOnComplete = false, bool isHomeRequested = false, CompatibilityVersions compatibilityVersion = CompatibilityVersions.NotSpi, CVREncryptionRouter.EncryptionAlgorithm encryptionAlgorithm = CVREncryptionRouter.EncryptionAlgorithm.Gen1, string spawnerId = null) - { - if (type != DownloadTask.ObjectType.Prop) return true; // Only care about props - - // toAttach is our instanceId, lets find the propData - if (!TryGetPropDataById(toAttach, out CVRSyncHelper.PropData newPropData)) return true; - - // Check if this is a prop we requested to spawn - Vector3 identity = GetIdentityKeyFromPropData(newPropData); - if (!_keyToPropData.Remove(identity, out CVRSyncHelper.PropData originalPropData)) return true; - - // Remove original prop data from held, cache states - HeldPropState heldState = HeldPropState.None; - if (_heldPropStates.ContainsKey(originalPropData)) - { - heldState = _heldPropStates[originalPropData]; - _heldPropStates.Remove(originalPropData); - } - - // If original prop data is null spawn a new prop i guess :( - if (!originalPropData.Spawnable) return true; - - // Add the new prop data to our held props in place of the old one - // if (!_heldPropData.Contains(newPropData)) _heldPropData.Add(newPropData); - _heldPropStates.TryAdd(newPropData, heldState); - - // Apply new prop data to the spawnable - newPropData.Spawnable = originalPropData.Spawnable; - newPropData.Wrapper = originalPropData.Wrapper; - newPropData.Wrapper.BroadcastMessage("OnHavingSoftTacosNow", SendMessageOptions.DontRequireReceiver); // support with RelativeSync - newPropData.Wrapper.name = $"p+{newPropData.ObjectId}~{newPropData.InstanceId}"; - - // Copy sync values - Array.Copy(newPropData.CustomFloats, originalPropData.CustomFloats, newPropData.CustomFloatsAmount); - - CVRSyncHelper.ApplyPropValuesSpawn(newPropData); - - // Place the prop in the additive content scene - PlacePropInAdditiveContentScene(newPropData.Spawnable); - - // Clear old data so Recycle() doesn't delete our prop - originalPropData.Spawnable = null; - originalPropData.Wrapper = null; - originalPropData.Recycle(); - - return false; - } - - private static bool OnCVRSyncHelperClearProps() // Prevent deleting of our held props on scene load - { - for (var index = CVRSyncHelper.Props.Count - 1; index >= 0; index--) - { - CVRSyncHelper.PropData prop = CVRSyncHelper.Props[index]; - if (prop.Spawnable && _heldPropStates.ContainsKey(prop)) - continue; // Do not recycle props that are valid & held - - DeleteOrRecycleProp(prop); - } - - CVRSyncHelper.MySpawnedPropInstanceIds.Clear(); - return false; - } - - private static bool OnBetterBetterCharacterControllerSetSitting(bool isSitting, CVRSeat cvrSeat = null, bool callExitSeat = true) - { - if (!_ignoreNextSeatExit) return true; - _ignoreNextSeatExit = false; - if (!BetterBetterCharacterController.Instance._lastCvrSeat) return true; // run original - return false; // dont run if there is a chair & we skipped it - } - - #endregion Harmony Patches - - #region Game Events - - private object _worldLoadTimer; - - private void OnWorldLoad(string _) - { - CVRWorld worldInstance = CVRWorld.Instance; - if (worldInstance && !worldInstance.allowSpawnables) - { - foreach (CVRSyncHelper.PropData prop in _heldPropStates.Keys) DeleteOrRecycleProp(prop); // Delete all props we kept - return; - } - - for (var index = _heldPropStates.Count - 1; index >= 0; index--) - { - CVRSyncHelper.PropData prop = _heldPropStates.ElementAt(index).Key; - if (!prop.Spawnable) - { - DeleteOrRecycleProp(prop); - return; - } - - // apply positions - int stackCount = _spawnablePositionStack.Count; - for (int i = stackCount - 1; i >= 0; i--) _spawnablePositionStack.Pop().ReapplyOffsets(); - } - - // Start a timer, and anything that did not load within 3 seconds will be destroyed - if (_worldLoadTimer != null) - { - MelonCoroutines.Stop(_worldLoadTimer); - _worldLoadTimer = null; - } - _worldLoadTimer = MelonCoroutines.Start(DestroyPersistantPropContainerInFive()); - _ignoreNextSeatExit = true; // just in case we are in a car / vehicle - } - - private IEnumerator DestroyPersistantPropContainerInFive() - { - yield return new WaitForSeconds(3f); - _worldLoadTimer = null; - Object.Destroy(_persistantPropsContainer); - _persistantPropsContainer = null; - _keyToPropData.Clear(); // no more chances - } - - private static void OnWorldUnload(string _) - { - // Prevent deleting of our held props on scene destruction - foreach (CVRSyncHelper.PropData prop in _heldPropStates.Keys) - { - if (!prop.Spawnable) continue; - PlacePropInPersistantPropsContainer(prop.Spawnable); - _spawnablePositionStack.Push(new SpawnablePositionContainer(prop.Spawnable)); - } - - // Likely in a vehicle - _heightOffset = BetterBetterCharacterController.Instance._lastCvrSeat != null - ? GetHeightOffsetFromPlayer() - : 0f; - } - - private static void OnInstanceConnected(string _) - { - // Request the server to respawn our props by GUID, and add a secret key to the propData to identify it - - foreach (CVRSyncHelper.PropData prop in _heldPropStates.Keys) - { - if (!prop.Spawnable) continue; - - // Generate a new identity key for the prop (this is used to identify the prop when we respawn it) - Vector3 identityKey = new(Random.Range(0, 1000), Random.Range(0, 1000), Random.Range(0, 1000)); - _keyToPropData.Add(identityKey, prop); - - SpawnPropFromGuid(prop.ObjectId, - new Vector3(prop.PositionX, prop.PositionY, prop.PositionZ), - new Vector3(prop.RotationX, prop.RotationY, prop.RotationZ), - identityKey); - } - } - - #endregion Game Events - - #region Util - - private static bool TryGetPropData(CVRSpawnable spawnable, out CVRSyncHelper.PropData propData) - { - if (!spawnable) - { - propData = null; - return false; - } - if (EntryOnlySpawnedByMe.Value && !spawnable.IsMine()) - { - propData = null; - return false; - } - foreach (CVRSyncHelper.PropData data in CVRSyncHelper.Props) - { - if (data.InstanceId != spawnable.instanceId) continue; - propData = data; - return true; - } - propData = null; - return false; - } - - private static bool TryGetPropDataById(string instanceId, out CVRSyncHelper.PropData propData) - { - foreach (CVRSyncHelper.PropData data in CVRSyncHelper.Props) - { - if (data.InstanceId != instanceId) continue; - propData = data; - return true; - } - propData = null; - return false; - } - - private static void PlacePropInAdditiveContentScene(CVRSpawnable spawnable) - { - spawnable.transform.parent.SetParent(null); // Unparent from the prop container - SceneManager.MoveGameObjectToScene(spawnable.transform.parent.gameObject, - SceneManager.GetSceneByName(CVRObjectLoader.AdditiveContentSceneName)); - } - - private static void PlacePropInPersistantPropsContainer(CVRSpawnable spawnable) - { - spawnable.transform.parent.SetParent(GetOrCreatePropsContainer().transform); - } - - private static void DeleteOrRecycleProp(CVRSyncHelper.PropData prop) - { - if (!prop.Spawnable) prop.Recycle(); - else prop.Spawnable.Delete(); - _heldPropStates.Remove(prop); - } - - private static void SpawnPropFromGuid(string propGuid, Vector3 position, Vector3 rotation, Vector3 identityKey) - { - using DarkRiftWriter darkRiftWriter = DarkRiftWriter.Create(); - darkRiftWriter.Write(propGuid); - darkRiftWriter.Write(position.x); - darkRiftWriter.Write(position.y); - darkRiftWriter.Write(position.z); - darkRiftWriter.Write(rotation.x); - darkRiftWriter.Write(rotation.y); - darkRiftWriter.Write(rotation.z); - darkRiftWriter.Write(identityKey.x); // for scale, but unused by CVR - darkRiftWriter.Write(identityKey.y); // we will use this to identify our prop - darkRiftWriter.Write(identityKey.z); // and recycle existing instance if it exists - darkRiftWriter.Write(0f); // if not zero, prop spawn will be rejected by gs - using Message message = Message.Create(10050, darkRiftWriter); - NetworkManager.Instance.GameNetwork.SendMessage(message, SendMode.Reliable); - } - - private static Vector3 GetIdentityKeyFromPropData(CVRSyncHelper.PropData propData) - => new(propData.ScaleX, propData.ScaleY, propData.ScaleZ); - - private const int WORLD_RAYCAST_LAYER_MASK = - (1 << 0) | // Default - (1 << 16) | (1 << 17) | (1 << 18) | (1 << 19) | - (1 << 20) | (1 << 21) | (1 << 22) | (1 << 23) | - (1 << 24) | (1 << 25) | (1 << 26) | (1 << 27) | - (1 << 28) | (1 << 29) | (1 << 30) | (1 << 31); - - private static float GetHeightOffsetFromPlayer() - { - Vector3 playerPos = PlayerSetup.Instance.GetPlayerPosition(); - Ray ray = new(playerPos, Vector3.down); - - // ReSharper disable once Unity.PreferNonAllocApi - RaycastHit[] hits = Physics.RaycastAll(ray, 1000f, WORLD_RAYCAST_LAYER_MASK, QueryTriggerInteraction.Ignore); - Scene baseScene = SceneManager.GetActiveScene(); - - float closestDist = float.MaxValue; - Vector3 closestPoint = Vector3.zero; - bool foundValidHit = false; - - foreach (RaycastHit hit in hits) - { - if (hit.collider.gameObject.scene != baseScene) continue; // Ignore objects not in the world scene - if (!(hit.distance < closestDist)) continue; - closestDist = hit.distance; - closestPoint = hit.point; - foundValidHit = true; - } - - if (!foundValidHit) return 0f; // TODO: idk if i should do this - float offset = playerPos.y - closestPoint.y; - return Mathf.Clamp(offset, 0f, 20f); - } - - #endregion Util - - #region Helper Classes - - private readonly struct SpawnablePositionContainer - { - private readonly CVRSpawnable _spawnable; - private readonly Vector3[] _posOffsets; - private readonly Quaternion[] _rotOffsets; - - public SpawnablePositionContainer(CVRSpawnable spawnable) - { - _spawnable = spawnable; - - int syncedTransforms = 1 + _spawnable.subSyncs.Count; // root + subSyncs - - _posOffsets = new Vector3[syncedTransforms]; - _rotOffsets = new Quaternion[syncedTransforms]; - - Transform playerTransform = PlayerSetup.Instance.transform; - - // Save root offset relative to player - Transform _spawnableTransform = _spawnable.transform; - _posOffsets[0] = playerTransform.InverseTransformPoint(_spawnableTransform.position); - _rotOffsets[0] = Quaternion.Inverse(playerTransform.rotation) * _spawnableTransform.rotation; - - // Save subSync offsets relative to player - for (int i = 0; i < _spawnable.subSyncs.Count; i++) - { - Transform subSyncTransform = _spawnable.subSyncs[i].transform; - if (subSyncTransform == null) continue; - _posOffsets[i + 1] = playerTransform.InverseTransformPoint(subSyncTransform.position); - _rotOffsets[i + 1] = Quaternion.Inverse(playerTransform.rotation) * subSyncTransform.rotation; - } - } - - public void ReapplyOffsets() - { - Transform playerTransform = PlayerSetup.Instance.transform; - - // Reapply to root - Vector3 rootWorldPos = playerTransform.TransformPoint(_posOffsets[0]); - rootWorldPos.y += _heightOffset; - _spawnable.transform.position = rootWorldPos; - _spawnable.transform.rotation = playerTransform.rotation * _rotOffsets[0]; - - // Reapply to subSyncs - for (int i = 0; i < _spawnable.subSyncs.Count; i++) - { - Transform subSyncTransform = _spawnable.subSyncs[i].transform; - if (!subSyncTransform) continue; - - Vector3 subWorldPos = playerTransform.TransformPoint(_posOffsets[i + 1]); - subWorldPos.y += _heightOffset; - subSyncTransform.position = subWorldPos; - subSyncTransform.rotation = playerTransform.rotation * _rotOffsets[i + 1]; - } - - // hack - _spawnable.needsUpdate = true; - _spawnable.UpdateSubSyncValues(); - _spawnable.sendUpdate(); - } - } - - #endregion Helper Classes -} \ No newline at end of file diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs b/YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs deleted file mode 100644 index fa1864b..0000000 --- a/YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -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.YouAreMyPropNowWeAreHavingSoftTacosLaterMod), - nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater" -)] - -[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.YouAreMyPropNowWeAreHavingSoftTacosLater.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/README.md b/YouAreMyPropNowWeAreHavingSoftTacosLater/README.md deleted file mode 100644 index b092e40..0000000 --- a/YouAreMyPropNowWeAreHavingSoftTacosLater/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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. diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj b/YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj deleted file mode 100644 index 5a8badc..0000000 --- a/YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - YouAreMineNow - - diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/format.json b/YouAreMyPropNowWeAreHavingSoftTacosLater/format.json deleted file mode 100644 index 8e68782..0000000 --- a/YouAreMyPropNowWeAreHavingSoftTacosLater/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_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" -} \ No newline at end of file diff --git a/copy_and_nstrip_dll.ps1 b/copy_and_nstrip_dll.ps1 index 0f2c7f3..1f4dfe3 100644 --- a/copy_and_nstrip_dll.ps1 +++ b/copy_and_nstrip_dll.ps1 @@ -10,7 +10,7 @@ $cvrDefaultPath = "C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR" # $cvrDefaultPath = "E:\temp\CVR_Experimental" # Array with the dlls to strip -$dllsToStrip = @('Assembly-CSharp.dll','Assembly-CSharp-firstpass.dll','AVProVideo.Runtime.dll', 'Unity.TextMeshPro.dll', 'MagicaCloth.dll', 'MagicaClothV2.dll', 'ECM2.dll', 'TheClapper.dll', 'MTJobSystem.dll') +$dllsToStrip = @('Assembly-CSharp.dll','Assembly-CSharp-firstpass.dll','AVProVideo.Runtime.dll', 'Unity.TextMeshPro.dll', 'MagicaCloth.dll', 'MagicaClothV2.dll', 'ECM2.dll', 'TheClapper.dll') # Array with the mods to grab $modNames = @("BTKUILib", "BTKSAImmersiveHud", "PortableMirrorMod", "VRBinding", "TheClapper") @@ -35,10 +35,6 @@ else { else { Write-Host "[ERROR] ChilloutVR.exe not found in CVRPATH or the default Steam location." Write-Host " Please define the Environment Variable CVRPATH pointing to the ChilloutVR folder!" - Write-Host "" - Write-Host "Press any key to exit..." - $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() return } } @@ -143,7 +139,7 @@ if ($missingMods.Count -gt 0) { Write-Host "" Write-Host "Copied all libraries!" Write-Host "" -Write-Host "Press any key to strip the DLLs using NStrip..." +Write-Host "Press any key to strip the Dlls using NStrip" $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL $HOST.UI.RawUI.Flushinputbuffer() @@ -160,10 +156,6 @@ else { # Display an error message if NStrip.exe could not be found Write-Host "Could not find NStrip.exe in the current directory nor in the PATH." -ForegroundColor Red Write-Host "Visit https://github.com/bbepis/NStrip/releases/latest to grab a copy." -ForegroundColor Red - Write-Host "" - Write-Host "Press any key to exit..." - $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() return } } @@ -177,6 +169,6 @@ foreach($dllFile in $dllsToStrip) { Write-Host "" Write-Host "Copied all libraries and stripped the DLLs!" Write-Host "" -Write-Host "Press any key to exit..." +Write-Host "Press any key to exit" $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL $HOST.UI.RawUI.Flushinputbuffer()