diff --git a/.gitignore b/.gitignore index 426d76d..b72d213 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +# Nstrip & ManagedLibs stuff +NStrip.exe +_ManagedLibs/*.dll + # User-specific files *.rsuser *.suo diff --git a/IKFixes/HarmonyPatches.cs b/IKFixes/HarmonyPatches.cs index 3059a58..1ebc6eb 100644 --- a/IKFixes/HarmonyPatches.cs +++ b/IKFixes/HarmonyPatches.cs @@ -87,12 +87,12 @@ internal static class BodySystemPatches SetPelvisWeight(solver.spine, 0f); } - if (IKFixesMod.EntryUseFakeRootAngle.Value && !BodySystem.isCalibratedAsFullBody) + if (IKFixes.EntryUseFakeRootAngle.Value && !BodySystem.isCalibratedAsFullBody) { // Emulate maxRootAngle because CVR doesn't have the player controller set up ideally for VRIK. // This is a small small fix, but makes it so the feet dont point in the direction of the head // when turning. It also means turning with joystick & turning IRL make feet behave the same and follow behind. - float weightedAngleLimit = IKFixesMod.EntryFakeRootAngleLimit.Value * solver.locomotion.weight; + float weightedAngleLimit = IKFixes.EntryFakeRootAngleLimit.Value * solver.locomotion.weight; float pivotAngle = MovementSystem.Instance.rotationPivot.eulerAngles.y; float deltaAngleRoot = Mathf.DeltaAngle(pivotAngle, _ikSimulatedRootAngle); float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot); @@ -111,6 +111,11 @@ internal static class BodySystemPatches // VR default is 25 degrees, but maybe during emotes needs 180 degrees..? solver.spine.maxRootAngle = BodySystem.isCalibratedAsFullBody ? 0f : 25f; } + + // custom IK settings + solver.spine.neckStiffness = IKFixes.EntryNeckStiffness.Value; + solver.spine.bodyRotStiffness = IKFixes.EntryBodyRotStiffness.Value; + solver.spine.rotateChestByHands = IKFixes.EntryRotateChestByHands.Value; } int num = 0; diff --git a/IKFixes/Main.cs b/IKFixes/Main.cs index 59ab31b..3a443f0 100644 --- a/IKFixes/Main.cs +++ b/IKFixes/Main.cs @@ -2,16 +2,25 @@ namespace NAK.Melons.IKFixes; -public class IKFixesMod : MelonMod +public class IKFixes : MelonMod { - public const string SettingsCategory = "IKFixes"; - public static readonly MelonPreferences_Category CategoryIKFixes = MelonPreferences.CreateCategory(SettingsCategory); + public const string SettingsCategory = nameof(IKFixes); + public static readonly MelonPreferences_Category Category = MelonPreferences.CreateCategory(SettingsCategory); public static readonly MelonPreferences_Entry EntryUseFakeRootAngle = - CategoryIKFixes.CreateEntry("Use Fake Root Angle", true, description: "Emulates maxRootAngle. This fixes feet pointing in direction of head when looking around."); + Category.CreateEntry("Use Fake Root Angle", true, description: "Emulates maxRootAngle. This fixes feet pointing in direction of head when looking around."); public static readonly MelonPreferences_Entry EntryFakeRootAngleLimit = - CategoryIKFixes.CreateEntry("Fake Root Angle Limit", 25f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating."); + Category.CreateEntry("Fake Root Angle Limit", 25f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating."); + + public static readonly MelonPreferences_Entry EntryNeckStiffness = + Category.CreateEntry("Neck Stiffness", 0.2f, description: "Neck stiffness."); + + public static readonly MelonPreferences_Entry EntryBodyRotStiffness = + Category.CreateEntry("Body Rot Stiffness", 0.1f, description: "Body rotation stiffness."); + + public static readonly MelonPreferences_Entry EntryRotateChestByHands = + Category.CreateEntry("Rot Chest By Hands", 1f, description: "Rotate chest by hands."); public override void OnInitializeMelon() { diff --git a/IKFixes/Properties/AssemblyInfo.cs b/IKFixes/Properties/AssemblyInfo.cs index 47cc61b..a3e9278 100644 --- a/IKFixes/Properties/AssemblyInfo.cs +++ b/IKFixes/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Reflection; [assembly: AssemblyProduct(nameof(NAK.Melons.IKFixes))] [assembly: MelonInfo( - typeof(NAK.Melons.IKFixes.IKFixesMod), + typeof(NAK.Melons.IKFixes.IKFixes), nameof(NAK.Melons.IKFixes), AssemblyInfoParams.Version, AssemblyInfoParams.Author, diff --git a/NAK.CustomComponents/Components/NAKPointerTracker.cs b/NAK.CustomComponents/Components/NAKPointerTracker.cs new file mode 100644 index 0000000..257fd7d --- /dev/null +++ b/NAK.CustomComponents/Components/NAKPointerTracker.cs @@ -0,0 +1,109 @@ +using ABI.CCK.Components; +using UnityEngine; + +namespace NAK.CCK.CustomComponents; + +public class NAKPointerTracker : MonoBehaviour +{ + // Configuration + public Transform referenceTransform; + public string pointerType = ""; + public float radius = 0.1f; + public Vector3 offset = Vector3.zero; + + // Animator module + public Animator animator; + public string parameterName; + + // Internal stuff + float initialAngle; + CVRPointer trackedPointer; + + void Start() + { + // Create collider + Collider collider = base.gameObject.GetComponent(); + if (collider == null) + { + SphereCollider sphereCollider = base.gameObject.AddComponent(); + sphereCollider.isTrigger = true; + Vector3 lossyScale = base.transform.lossyScale; + sphereCollider.radius = radius / Mathf.Max(Mathf.Max(lossyScale.x, lossyScale.y), lossyScale.z); + sphereCollider.center = offset; + } + + // Create rigidbody (required for triggers) + Rigidbody rigidbody = base.gameObject.GetComponent(); + if (rigidbody == null) + { + rigidbody = base.gameObject.AddComponent(); + rigidbody.useGravity = false; + rigidbody.isKinematic = true; + } + + // Initial setup + if (referenceTransform == null) referenceTransform = transform; + Vector3 direction = (transform.TransformPoint(offset) - referenceTransform.position); + Vector3 projectedDirection = Vector3.ProjectOnPlane(direction, referenceTransform.up); + initialAngle = Vector3.SignedAngle(referenceTransform.forward, projectedDirection, referenceTransform.up); + } + + void OnDrawGizmosSelected() + { + if (base.isActiveAndEnabled) + { + Gizmos.color = Color.red; + Gizmos.matrix = Matrix4x4.TRS(base.transform.position, base.transform.rotation, Vector3.one); + Gizmos.DrawWireSphere(offset, radius); + } + } + + void OnTriggerEnter(Collider other) + { + if (trackedPointer != null) return; + + // generic pointer or specific pointer + CVRPointer pointer = other.gameObject.GetComponent(); + if (pointer != null && (String.IsNullOrEmpty(pointerType) || pointer.type == pointerType)) + { + trackedPointer = pointer; + } + } + + void Update() + { + if (trackedPointer == null) return; + + // Check if tracked pointer was disabled + if (!trackedPointer.isActiveAndEnabled) + { + ReleasePointer(); + return; + } + + TrackPointer(); + } + + void TrackPointer() + { + if (animator != null) + { + float angle = GetAngleFromPosition(trackedPointer.transform.position, initialAngle); + animator.SetFloat(parameterName + "_Angle", angle / 360); + } + } + + void ReleasePointer() + { + trackedPointer = null; + } + + float GetAngleFromPosition(Vector3 trackedPos, float offset = 0) + { + Vector3 direction = (trackedPos - referenceTransform.position); + Vector3 projectedDirection = Vector3.ProjectOnPlane(direction, referenceTransform.up); + float angle = Vector3.SignedAngle(referenceTransform.forward, projectedDirection, referenceTransform.up) - offset; + if (angle < 0) angle += 360f; + return angle; + } +} diff --git a/NAK.CustomComponents/CustomComponents.csproj b/NAK.CustomComponents/CustomComponents.csproj new file mode 100644 index 0000000..4fb4393 --- /dev/null +++ b/NAK.CustomComponents/CustomComponents.csproj @@ -0,0 +1,53 @@ + + + + + net472 + enable + latest + false + True + + + + + + + + + + + ..\_ManagedLibs\0Harmony.dll + + + ..\_ManagedLibs\Assembly-CSharp.dll + + + ..\_ManagedLibs\Assembly-CSharp-firstpass.dll + + + ..\_ManagedLibs\DarkRift.dll + + + ..\_ManagedLibs\MelonLoader.dll + + + ..\_ManagedLibs\UnityEngine.AnimationModule.dll + + + ..\_ManagedLibs\UnityEngine.CoreModule.dll + + + ..\_ManagedLibs\UnityEngine.InputLegacyModule.dll + + + ..\_ManagedLibs\UnityEngine.PhysicsModule.dll + + + + + + + + + diff --git a/NAK.CustomComponents/CustomComponents.sln b/NAK.CustomComponents/CustomComponents.sln new file mode 100644 index 0000000..9b418c1 --- /dev/null +++ b/NAK.CustomComponents/CustomComponents.sln @@ -0,0 +1,25 @@ + +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}") = "CustomComponents", "CustomComponents.csproj", "{D83D5845-288A-4783-993D-09364AA48593}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D83D5845-288A-4783-993D-09364AA48593}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D83D5845-288A-4783-993D-09364AA48593}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D83D5845-288A-4783-993D-09364AA48593}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D83D5845-288A-4783-993D-09364AA48593}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A05CB0F0-E92F-4634-AA89-357AA256AD29} + EndGlobalSection +EndGlobal diff --git a/NAK.CustomComponents/Main.cs b/NAK.CustomComponents/Main.cs new file mode 100644 index 0000000..dfe1697 --- /dev/null +++ b/NAK.CustomComponents/Main.cs @@ -0,0 +1,15 @@ +using ABI_RC.Core.Util.AssetFiltering; +using MelonLoader; +using NAK.CCK.CustomComponents; + +namespace NAK.Melons.CustomComponents; + +public class CustomComponents : MelonMod +{ + public override void OnInitializeMelon() + { + // Add our CCK component to the prop whitelist + var propWhitelist = SharedFilter._spawnableWhitelist; + propWhitelist.Add(typeof(NAKPointerTracker)); + } +} \ No newline at end of file diff --git a/NAK.CustomComponents/Properties/AssemblyInfo.cs b/NAK.CustomComponents/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..69d96bb --- /dev/null +++ b/NAK.CustomComponents/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using MelonLoader; +using NAK.Melons.CustomComponents.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.Melons.CustomComponents))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.Melons.CustomComponents))] + +[assembly: MelonInfo( + typeof(NAK.Melons.CustomComponents.CustomComponents), + nameof(NAK.Melons.CustomComponents), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidOnSteam/UndoPropButton" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: HarmonyDontPatchAll] + +namespace NAK.Melons.CustomComponents.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/NAK.CustomComponents/format.json b/NAK.CustomComponents/format.json new file mode 100644 index 0000000..e9cc453 --- /dev/null +++ b/NAK.CustomComponents/format.json @@ -0,0 +1,23 @@ +{ + "_id": 147, + "name": "PropUndoButton", + "modversion": "1.0.1", + "gameversion": "2022r170", + "loaderversion": "0.5.7", + "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'.", + "searchtags": [ + "prop", + "undo", + "bind", + "button" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidOnSteam/PropUndoButton/releases/download/v1.0.1/PropUndoButton.dll", + "sourcelink": "https://github.com/NotAKidOnSteam/PropUndoButton/", + "changelog": "- Initial Release\n- Added redo button.\n- Mitigated issue of props getting stuck locally if deleting them before they fully spawn.\n- Lowered SFX volume to match existing UI sounds.", + "embedcolor": "#00FFFF" +} \ No newline at end of file diff --git a/_ManagedLibs/.keep b/_ManagedLibs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/nstrip_copy_stuff.ps1 b/nstrip_copy_stuff.ps1 new file mode 100644 index 0000000..9290c3c --- /dev/null +++ b/nstrip_copy_stuff.ps1 @@ -0,0 +1,132 @@ +# CVR and Melon Loader Dependencies +$0HarmonydllPath = "\MelonLoader\0Harmony.dll" +$melonLoaderdllPath = "\MelonLoader\MelonLoader.dll" +$cvrManagedDataPath = "\ChilloutVR_Data\Managed" + +$cvrPath = $env:CVRPATH +$cvrExecutable = "ChilloutVR.exe" +$cvrDefaultPath = "C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR" + +if ($cvrPath -and (Test-Path "$cvrPath\$cvrExecutable")) { + # Found ChilloutVR.exe in the existing CVRPATH + Write-Host "" + Write-Host "Found the ChilloutVR folder on: $cvrPath" +} +else { + # Check if ChilloutVR.exe exists in default Steam location + if (Test-Path "$cvrDefaultPath\$cvrExecutable") { + # Set CVRPATH environment variable to default Steam location + Write-Host "Found the ChilloutVR on the default steam location, setting the CVRPATH Env Var at User Level!" + [Environment]::SetEnvironmentVariable("CVRPATH", $cvrDefaultPath, "User") + $env:CVRPATH = $cvrDefaultPath + $cvrPath = $env:CVRPATH + } + 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!" + return + } +} + +$scriptDir = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition +$managedLibsFolder = $scriptDir + "\_ManagedLibs" + +Write-Host "" +Write-Host "Copying the DLLs from the CVR, MelonLoader, and Mods folder to the ManagedLibs" + + +Copy-Item $cvrPath$0HarmonydllPath -Destination $managedLibsFolder +Copy-Item $cvrPath$melonLoaderdllPath -Destination $managedLibsFolder +Copy-Item $cvrPath$cvrManagedDataPath"\*" -Destination $managedLibsFolder + + +# Third Party Dependencies +$melonModsPath="\Mods\" +$modNames = @("BTKUILib") +$missingMods = New-Object System.Collections.Generic.List[string] + + +foreach ($modName in $modNames) { + $modDll = $modName + ".dll" + $modPath = $cvrPath + $melonModsPath + $modDll + $managedLibsModPath = "$managedLibsFolder\$modDll" + + # Attempt to grab from the mods folder + if (Test-Path $modPath -PathType Leaf) { + Write-Host " Copying $modDll from $melonModsPath to \_ManagedLibs!" + Copy-Item $modPath -Destination $managedLibsFolder + } + # Check if they already exist in the ManagedLibs + elseif (Test-Path $managedLibsModPath -PathType Leaf) { + Write-Host " Ignoring $modDll since already exists in \_ManagedLibs!" + } + # If we fail, lets add to the missing mods list + else { + $missingMods.Add($modName) + } +} + +if ($missingMods.Count -gt 0) { + # If we have missing mods, let's fetch them from the latest CVR Modding Group API + Write-Host "" + Write-Host "Failed to find $($missingMods.Count) mods. We're going to search in CVR MG verified mods" -ForegroundColor Red + Write-Host "You can download them and move to $managedLibsFolder" + + $cvrModdingApiUrl = "https://api.cvrmg.com/v1/mods" + $cvrModdingDownloadUrl = "https://api.cvrmg.com/v1/mods/download/" + $latestModsResponse = Invoke-RestMethod $cvrModdingApiUrl -UseBasicParsing + + foreach ($modName in $missingMods) { + $mod = $latestModsResponse | Where-Object { $_.name -eq $modName } + if ($mod) { + $modDownloadUrl = $cvrModdingDownloadUrl + $($mod._id) + # It seems power shell doesn't like to download .dll from https://api.cvrmg.com (messes with some anti-virus) + # Invoke-WebRequest -Uri $modDownloadUrl -OutFile $managedLibsFolder -UseBasicParsing + # Write-Host " $modName was downloaded successfully to $managedLibsFolder!" + Write-Host " $modName Download Url: $modDownloadUrl" + } else { + Write-Host " $modName was not found in the CVR Modding Group verified mods!" + } + } +} + + +Write-Host "" +Write-Host "Copied all libraries!" +Write-Host "" +Write-Host "Press any key to strip the Dlls using NStrip" +$HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL +$HOST.UI.RawUI.Flushinputbuffer() + +Write-Host "NStrip Convert all private/protected stuff to public. Requires true>" + +# Create an array to hold the file names to strip +$dllsToStrip = @('Assembly-CSharp.dll','Assembly-CSharp-firstpass.dll','AVProVideo.Runtime.dll') + +# Check if NStrip.exe exists in the current directory +if(Test-Path -Path ".\NStrip.exe") { + $nStripPath = ".\NStrip.exe" +} +else { + # Try to locate NStrip.exe in the PATH + $nStripPath = Get-Command -Name NStrip.exe -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source + if($nStripPath -eq $null) { + # 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 + return + } +} + +# Loop through each DLL file to strip and call NStrip.exe +foreach($dllFile in $dllsToStrip) { + $dllPath = Join-Path -Path $managedLibsFolder -ChildPath $dllFile + & $nStripPath -p -n $dllPath $dllPath +} + +Write-Host "" +Write-Host "Copied all libraries and stripped the DLLs!" +Write-Host "" +Write-Host "Press any key to exit" +$HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL +$HOST.UI.RawUI.Flushinputbuffer()