From bec47c6a26fa254ce2c90edb1f3cf019df7b56cf Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:59:06 -0500 Subject: [PATCH] [IKFixes] Removed illegal characters from melonpref identifiers. --- AvatarScale/Input/DebugKeybinds.cs | 37 ++++ .../ScaleReconizer.cs | 4 +- AvatarScale/Integrations/BTKUI/BTKUIAddon.cs | 71 +++++++ .../Integrations/BTKUI/Page_AvatarScale.cs | 87 +++++++++ AvatarScale/Scripts.cs | 27 +++ AvatarScale/resources/menu.js | 175 ++++++++++++++++++ AvatarScale/resources/nak_menu.css | 13 ++ IKFixes/Main.cs | 16 +- IKFixes/Properties/AssemblyInfo.cs | 2 +- IKFixes/format.json | 8 +- 10 files changed, 426 insertions(+), 14 deletions(-) create mode 100644 AvatarScale/Input/DebugKeybinds.cs rename AvatarScale/{GestureReconizer => Input}/ScaleReconizer.cs (96%) create mode 100644 AvatarScale/Integrations/BTKUI/BTKUIAddon.cs create mode 100644 AvatarScale/Integrations/BTKUI/Page_AvatarScale.cs create mode 100644 AvatarScale/Scripts.cs create mode 100644 AvatarScale/resources/menu.js create mode 100644 AvatarScale/resources/nak_menu.css diff --git a/AvatarScale/Input/DebugKeybinds.cs b/AvatarScale/Input/DebugKeybinds.cs new file mode 100644 index 0000000..9756930 --- /dev/null +++ b/AvatarScale/Input/DebugKeybinds.cs @@ -0,0 +1,37 @@ +using NAK.AvatarScaleMod.AvatarScaling; +using UnityEngine; + +namespace NAK.AvatarScaleMod.InputHandling; + +public static class DebugKeybinds +{ + public static void DoDebugInput() + { + if (AvatarScaleManager.Instance == null) + return; + + float currentHeight; + const float step = 0.1f; + + if (Input.GetKeyDown(KeyCode.Equals) || Input.GetKeyDown(KeyCode.KeypadPlus)) + { + currentHeight = AvatarScaleManager.Instance.GetHeight() + step; + AvatarScaleManager.Instance.SetHeight(currentHeight); + + AvatarScaleMod.Logger.Msg($"Setting height: {currentHeight}"); + } + else if (Input.GetKeyDown(KeyCode.Minus) || Input.GetKeyDown(KeyCode.KeypadMinus)) + { + currentHeight = AvatarScaleManager.Instance.GetHeight() - step; + AvatarScaleManager.Instance.SetHeight(currentHeight); + + AvatarScaleMod.Logger.Msg($"Setting height: {currentHeight}"); + } + else if (Input.GetKeyDown(KeyCode.Backspace)) + { + AvatarScaleManager.Instance.ResetHeight(); + + AvatarScaleMod.Logger.Msg($"Resetting height."); + } + } +} \ No newline at end of file diff --git a/AvatarScale/GestureReconizer/ScaleReconizer.cs b/AvatarScale/Input/ScaleReconizer.cs similarity index 96% rename from AvatarScale/GestureReconizer/ScaleReconizer.cs rename to AvatarScale/Input/ScaleReconizer.cs index 20c22b4..444659f 100644 --- a/AvatarScale/GestureReconizer/ScaleReconizer.cs +++ b/AvatarScale/Input/ScaleReconizer.cs @@ -64,7 +64,7 @@ public static class ScaleReconizer return; // Store initial modifier so we can get difference later - _initialModifier = modifier; + _initialModifier = Mathf.Max(modifier, 0.01f); // no zero _initialTargetHeight = AvatarScaleManager.Instance.GetHeight(); } @@ -73,6 +73,8 @@ public static class ScaleReconizer if (!Enabled) return; + modifier = Mathf.Max(modifier, 0.01f); // no zero + // Allow user to release triggers to reset "world grip" if (RequireTriggers && !AreBothTriggersDown()) { diff --git a/AvatarScale/Integrations/BTKUI/BTKUIAddon.cs b/AvatarScale/Integrations/BTKUI/BTKUIAddon.cs new file mode 100644 index 0000000..c2a7ae5 --- /dev/null +++ b/AvatarScale/Integrations/BTKUI/BTKUIAddon.cs @@ -0,0 +1,71 @@ +using System.Runtime.CompilerServices; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.IO; +using BTKUILib; +using BTKUILib.UIObjects; +using MelonLoader; +using NAK.AvatarScaleMod.Integrations.BTKUI; +using UnityEngine; + +namespace NAK.AvatarScaleMod.Integrations; + +public static class BTKUIAddon +{ + #region Initialization + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Initialize() + { + Page as_RootPage = new(ModSettings.SettingsCategory, ModSettings.SettingsCategory, true, "") + { + MenuTitle = ModSettings.SettingsCategory, + MenuSubtitle = "Avatar Scale Mod Settings" + }; + + QuickMenuAPI.OnMenuRegenerate += (_) => + { + SchedulerSystem.AddJob((InjectMenu), 1f, 1f, 1); + }; + } + + // private static void InjectMenu() + // { + // MelonLogger.Msg("Injecting into QM!"); + // CVR_MenuManager.Instance.quickMenu.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js")); + // } + + private static void InjectMenu() + { + MelonLogger.Msg("Injecting into QM!"); + string menuJsPath = Path.Combine(Application.streamingAssetsPath, "Cohtml", "UIResources", "AvatarScaleMod", "menu.js"); + string menuJsContent = ReadJSFile(menuJsPath); + CVR_MenuManager.Instance.quickMenu.View._view.ExecuteScript(menuJsContent); + } + + private static string ReadJSFile(string path) + { + if(File.Exists(path)) + { + return File.ReadAllText(path); + } + + MelonLogger.Warning($"File not found: {path}"); + return string.Empty; + } + + #endregion + + #region Melon Pref Helpers + + internal static void AddMelonToggle(ref Category category, MelonPreferences_Entry entry) + { + category.AddToggle(entry.DisplayName, entry.Description, entry.Value).OnValueUpdated += b => entry.Value = b; + } + + internal static void AddMelonSlider(ref Page page, MelonPreferences_Entry entry, float min, float max, int decimalPlaces = 2) + { + page.AddSlider(entry.DisplayName, entry.Description, entry.Value, min, max, decimalPlaces).OnValueUpdated += f => entry.Value = f; + } + + #endregion +} \ No newline at end of file diff --git a/AvatarScale/Integrations/BTKUI/Page_AvatarScale.cs b/AvatarScale/Integrations/BTKUI/Page_AvatarScale.cs new file mode 100644 index 0000000..7e2750f --- /dev/null +++ b/AvatarScale/Integrations/BTKUI/Page_AvatarScale.cs @@ -0,0 +1,87 @@ +using ABI_RC.Systems.Camera; +using BTKUILib; +using BTKUILib.UIObjects; +using MelonLoader; +using UnityEngine; + +namespace NAK.AvatarScaleMod.Integrations.BTKUI; + +public class PortableCameraCategory +{ + private static string categoryName = "Portable Camera"; + + internal static void AddCategory(Page parent) + { + QuickMenuAPI.OnTabChange += OnTabChange; + + // Create category and add elements to it + var category = parent.AddCategory(categoryName); + category.AddButton("Take Photo", "TakePhoto-Icon", "Quickly take a photo. This respects set timers & other related settings.").OnPress += TakePhoto; + category.AddButton("Cycle Delay", "CycleDelay-Icon", "Quickly cycle photo timers. Off, 3s, 5s, 10s.").OnPress += CycleCaptureDelay; + category.AddButton("Open Folder", "OpenFolder-Icon", "Quickly open the root of the ChilloutVR screenshots folder in Windows Explorer.").OnPress += OpenScreenshotsFolder; + + // Clone of the default camera settings page + var settingsPage = category.AddPage("Settings", "Settings-Icon", "Sub page of settings to configure the portable camera.", parent.MenuTitle); + settingsPage.AddCategory("Main Settings"); + settingsPage.AddSlider("Field of View", "Field of View of portable camera.", 40f, 10f, 120f); + settingsPage.AddSlider("Focal Length", "Focal Length of portable camera.", 50f, 24f, 200f); + settingsPage.AddSlider("Aperture", "Aperture of portable camera.", 1.8f, 1.2f, 8f); + } + + private static bool PortableCameraReady() + { + bool active = (bool)(PortableCamera.Instance?.IsActive()); + if (!active) CVRCamController.Instance?.Toggle(); + return active; + } + + private static void TakePhoto() + { + MelonLogger.Msg("Took photo!"); + if (PortableCameraReady()) + PortableCamera.Instance?.MakePhoto(); + } + + private static void CycleCaptureDelay() + { + if (PortableCameraReady()) + { + PortableCamera.Instance?.ChangeCameraCaptureDelay(); + QuickMenuAPI.ShowAlertToast("Delay set to " + PortableCamera.Instance.timerText.text, 1); + } + } + + //this was mistake, but now feature cause fuck it + private static void PauseCamera() + { + MelonLogger.Msg("Paused camera!"); + GameObject camera = PortableCamera.Instance.gameObject; + PortableCamera.Instance.gameObject.SetActive(!camera.activeSelf); + } + + private static void OpenScreenshotsFolder() + { + MelonLogger.Msg("Opened screenshots folder!"); + string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "ChilloutVR"); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + Application.OpenURL("file:///" + path); + } + + private static DateTime lastTime = DateTime.Now; + private static void OnTabChange(string newTab, string previousTab) + { + if (newTab == "btkUI-AvatarScaleMod-MainPage") + { + TimeSpan timeDifference = DateTime.Now - lastTime; + if (timeDifference.TotalSeconds <= 0.5) + { + // The new page and previous page are equal and were opened within 0.5 seconds of each other + CVRCamController.Instance?.Toggle(); + } + } + lastTime = DateTime.Now; + } +} \ No newline at end of file diff --git a/AvatarScale/Scripts.cs b/AvatarScale/Scripts.cs new file mode 100644 index 0000000..5980b65 --- /dev/null +++ b/AvatarScale/Scripts.cs @@ -0,0 +1,27 @@ +using System; +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 + { + public static string GetEmbeddedScript(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception) { } + + return l_result; + } + } +} \ No newline at end of file diff --git a/AvatarScale/resources/menu.js b/AvatarScale/resources/menu.js new file mode 100644 index 0000000..2bd9ff7 --- /dev/null +++ b/AvatarScale/resources/menu.js @@ -0,0 +1,175 @@ +(function() { + /* ------------------------------- + * CSS Embedding + * ------------------------------- */ + { + const embeddedCSS = ` + .slider-container { + width: 80%; + padding-left: 2em; + position: relative; + } + + .avatar-scale-slider-container { + width: 100%; + height: 3em; + position: relative; + background: #555; + border-radius: 1.5em; + cursor: pointer; + overflow: hidden; + } + + .avatar-scale-track-inner { + height: 100%; + background: #a9a9a9; + position: absolute; + top: 0; + left: 0; + } + + .avatar-scale-snap-point { + height: 100%; + width: 2px; + background: white; + position: absolute; + top: 0; + } + + .slider-display-value { + font-size: 2em; + position: relative; + left: 0.5em; + color: #fff; + white-space: nowrap; + } + + .lock-icon { + position: absolute; + top: 50%; + right: 1em; + transform: translateY(-50%); + width: 1.5em; + height: 1.5em; + background: url('path_to_lock_icon.png') no-repeat center; + background-size: contain; + } + `; + + const styleElement = document.createElement('style'); + styleElement.type = 'text/css'; + styleElement.innerHTML = embeddedCSS; + document.head.appendChild(styleElement); + } + + /* ------------------------------- + * Content Injection + * ------------------------------- */ + { + const contentBlock = document.createElement('div'); + contentBlock.innerHTML = ` +
+
Avatar Motion Tweaker
+
+
+ +
+
Crouch limit:
+
+
+
+
+ +
+
+ 0m +
+
+
+
+
+
+ `; + + const targetElement = document.getElementById('btkUI-AvatarScaleMod-MainPage'); + if(targetElement) { + targetElement.appendChild(contentBlock); + } else { + console.warn('Target element "btkUI-AvatarScaleMod-MainPage" not found!'); + } + } + + /* ------------------------------- + * Event Handlers & Utility Functions + * ------------------------------- */ + { + const sliderContainer = document.querySelector('.avatar-scale-slider-container'); + const trackInner = document.querySelector('.avatar-scale-track-inner'); + const valueDisplay = document.querySelector('.slider-value'); + + const SNAP_TOLERANCE = 0.02; + let snapPoints = []; + + let isDragging = false; + + sliderContainer.addEventListener('mousedown', (e) => { + isDragging = true; + updateTrackWidth(e.clientX); + }); + + window.addEventListener('mousemove', (e) => { + if (!isDragging) return; + updateTrackWidth(e.clientX); + }); + + window.addEventListener('mouseup', () => { + isDragging = false; + }); + + function updateTrackWidth(clientX) { + const rect = sliderContainer.getBoundingClientRect(); + + // Get padding values from the slider container + const paddingLeft = parseFloat(getComputedStyle(sliderContainer).paddingLeft); + const paddingRight = parseFloat(getComputedStyle(sliderContainer).paddingRight); + + // Calculate the effective width and position based on padding + const effectiveWidth = rect.width - paddingLeft - paddingRight; + let x = clientX - rect.left - paddingLeft; + + // Ensure the position is within the bounds of the effective width + x = Math.min(Math.max(0, x), effectiveWidth); + + const percentage = x / effectiveWidth; + const closestSnap = snapPoints.reduce((closest, snap) => { + return Math.abs(closest - percentage) < Math.abs(snap - percentage) ? closest : snap; + }, 1); + + if (Math.abs(closestSnap - percentage) <= SNAP_TOLERANCE) { + x = closestSnap * effectiveWidth; + } + + trackInner.style.width = `${x}px`; + valueDisplay.textContent = (x / effectiveWidth * 100).toFixed(2) + "m"; + } + + function addSnapPoint(percentage) { + if (percentage < 0 || percentage > 1) return; + const snap = document.createElement('div'); + snap.className = 'avatar-scale-snap-point'; + snap.style.left = `${percentage * 100}%`; + sliderContainer.appendChild(snap); + snapPoints.push(percentage); + } + + // To evenly space out snap points: + function addEvenSnapPoints(count) { + for (let i = 1; i <= count; i++) { + addSnapPoint(i / (count + 1)); + } + } + + // Example usage: + addEvenSnapPoints(5); // Adds 5 evenly spaced snap points + } +})(); \ No newline at end of file diff --git a/AvatarScale/resources/nak_menu.css b/AvatarScale/resources/nak_menu.css new file mode 100644 index 0000000..d732c3d --- /dev/null +++ b/AvatarScale/resources/nak_menu.css @@ -0,0 +1,13 @@ +.slider-container { + margin: 10px 0; + padding: 5px; +} + +.slider-label { + display: block; + margin-bottom: 5px; +} + +.test-slider { + width: 100%; +} diff --git a/IKFixes/Main.cs b/IKFixes/Main.cs index ac2a1df..ceccfed 100644 --- a/IKFixes/Main.cs +++ b/IKFixes/Main.cs @@ -10,26 +10,26 @@ public class IKFixes : MelonMod MelonPreferences.CreateCategory(SettingsCategory); public static readonly MelonPreferences_Entry EntryUseFakeRootAngle = - Category.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, display_name: "Use Fake Root Angle", description: "Emulates maxRootAngle. This fixes feet pointing in direction of head when looking around."); public static readonly MelonPreferences_Entry EntryFakeRootAngleLimit = - Category.CreateEntry("Fake Root Angle Limit (25f)", 25f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating."); + Category.CreateEntry("fake_root_angle_limit", 25f, display_name: "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)", 0.2f, description: "Neck stiffness."); + Category.CreateEntry("neck_stiffness", 0.2f, display_name: "Neck Stiffness (0.2f)", description: "Neck stiffness."); public static readonly MelonPreferences_Entry EntryBodyRotStiffness = - Category.CreateEntry("Body Rot Stiffness (0.1f)", 0.1f, description: "Body rotation stiffness."); + Category.CreateEntry("body_rot_stiffness", 0.1f, display_name: "Body Rot Stiffness (0.1f)", description: "Body rotation stiffness."); public static readonly MelonPreferences_Entry EntryRotateChestByHands = - Category.CreateEntry("Rot Chest By Hands (1f)", 1f, description: "Rotate chest by hands."); + Category.CreateEntry("rot_chest_by_hands", 1f, display_name: "Rot Chest By Hands (1f)", description: "Rotate chest by hands."); public static readonly MelonPreferences_Entry EntryBendToTargetWeight = - Category.CreateEntry("Leg Bend To Target (1f)", 1f, description: "Leg bend to target weight"); + Category.CreateEntry("leg_bend_to_target", 1f, display_name: "Leg Bend To Target (1f)", description: "Leg bend to target weight"); public static readonly MelonPreferences_Entry EntryAssignRemainingTrackers = - Category.CreateEntry("Assign Remaining Trackers (true)", true, description: "Should the game calibrate any additional trackers as secondary trackers for already-tracked points?"); - + Category.CreateEntry("assign_remaining_trackers", true, display_name: "Assign Remaining Trackers (true)", description: "Should the game calibrate any additional trackers as secondary trackers for already-tracked points?"); + public override void OnInitializeMelon() { ApplyPatches(typeof(HarmonyPatches.BodySystemPatches)); diff --git a/IKFixes/Properties/AssemblyInfo.cs b/IKFixes/Properties/AssemblyInfo.cs index ca5b645..6d8d270 100644 --- a/IKFixes/Properties/AssemblyInfo.cs +++ b/IKFixes/Properties/AssemblyInfo.cs @@ -25,6 +25,6 @@ using System.Reflection; namespace NAK.IKFixes.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.7"; + public const string Version = "1.0.8"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/IKFixes/format.json b/IKFixes/format.json index 7ff1caf..fb824c0 100644 --- a/IKFixes/format.json +++ b/IKFixes/format.json @@ -1,8 +1,8 @@ { "_id": 142, "name": "IKFixes", - "modversion": "1.0.7", - "gameversion": "2023r171", + "modversion": "1.0.8", + "gameversion": "2023r172", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", @@ -17,8 +17,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r20/IKFixes.dll", + "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r21/IKFixes.dll", "sourcelink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/IKFixes/", - "changelog": "- Updates for 2023r171\n- Removed unneeded settings (IKPose & NetIKPass).\n- Fixed halfbody fake root angle option.\n- Added Reset Settings UIExpansionKit button.", + "changelog": "- Updates for 2023r172\n- Removed unneeded settings (IKPose & NetIKPass).\n- Fixed halfbody fake root angle option.\n- Added Reset Settings UIExpansionKit button.\n- Fixed issue with MelonPref identifiers containing illegal characters.", "embedcolor": "f46e49" } \ No newline at end of file