mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
[AvatarScaleMod] i forgotr
This commit is contained in:
parent
2861957e3d
commit
92bbd72338
16 changed files with 544 additions and 305 deletions
|
@ -20,7 +20,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\nak_menu.css" />
|
||||
<None Remove="resources\Icon_AvatarHeightConfig.png" />
|
||||
<EmbeddedResource Include="resources\ASM_Icon_AvatarHeightConfig.png" />
|
||||
<None Remove="resources\ASM_Icon_AvatarHeightCopy.png" />
|
||||
<EmbeddedResource Include="resources\ASM_Icon_AvatarHeightCopy.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -28,9 +31,10 @@
|
|||
<HintPath>$(MsBuildThisFileDirectory)\..\.ManagedLibs\BTKUILib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ActionMenu">
|
||||
<HintPath>$(MsBuildThisFileDirectory)\..\.ManagedLibs\ActionMenu.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
</Project>
|
|
@ -1,5 +1,7 @@
|
|||
using ABI_RC.Core.IO;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Player.AvatarTracking;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.GameEventSystem;
|
||||
using NAK.AvatarScaleMod.Components;
|
||||
using NAK.AvatarScaleMod.Networking;
|
||||
|
@ -25,7 +27,7 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
set
|
||||
{
|
||||
if (value != _settingUniversalScaling && value == false)
|
||||
SetHeight(-1f);
|
||||
ResetHeight();
|
||||
|
||||
_settingUniversalScaling = value;
|
||||
}
|
||||
|
@ -54,13 +56,11 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
_settingUniversalScaling = ModSettings.EntryUseUniversalScaling.Value;
|
||||
|
||||
CVRGameEventSystem.Instance.OnConnected.AddListener(OnInstanceConnected);
|
||||
//SchedulerSystem.AddJob(new SchedulerSystem.Job(ForceHeightUpdate), 0f, 10f, -1);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CVRGameEventSystem.Instance.OnConnected.RemoveListener(OnInstanceConnected);
|
||||
//SchedulerSystem.RemoveJob(new SchedulerSystem.Job(ForceHeightUpdate));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -121,18 +121,25 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
|
||||
public void ResetHeight()
|
||||
{
|
||||
if (!_settingUniversalScaling)
|
||||
if (_localAvatarScaler == null)
|
||||
return;
|
||||
|
||||
if (!_localAvatarScaler.IsHeightAdjustedFromInitial())
|
||||
return;
|
||||
|
||||
CohtmlHud.Instance.ViewDropTextImmediate("(Local) AvatarScaleMod", "Avatar Scale Reset!",
|
||||
"Universal Scaling is now disabled.");
|
||||
|
||||
if (_localAvatarScaler != null)
|
||||
_localAvatarScaler.ResetHeight();
|
||||
ModNetwork.SendNetworkHeight(-1f);
|
||||
SetHeight(-1f);
|
||||
}
|
||||
|
||||
public float GetHeight()
|
||||
{
|
||||
if (_localAvatarScaler == null)
|
||||
return -1f;
|
||||
return PlayerAvatarPoint.defaultAvatarHeight;
|
||||
|
||||
if (!_localAvatarScaler.IsHeightAdjustedFromInitial())
|
||||
return PlayerSetup.Instance.GetAvatarHeight();
|
||||
|
||||
return _localAvatarScaler.GetHeight();
|
||||
}
|
||||
|
@ -161,10 +168,7 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
|
||||
public bool IsHeightAdjustedFromInitial()
|
||||
{
|
||||
if (_localAvatarScaler == null)
|
||||
return false;
|
||||
|
||||
return _localAvatarScaler.IsHeightAdjustedFromInitial();
|
||||
return _localAvatarScaler != null && _localAvatarScaler.IsHeightAdjustedFromInitial();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -174,9 +178,14 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
public float GetNetworkHeight(string playerId)
|
||||
{
|
||||
if (_networkedScalers.TryGetValue(playerId, out NetworkScaler scaler))
|
||||
return scaler.GetHeight();
|
||||
if (scaler.IsHeightAdjustedFromInitial()) return scaler.GetHeight();
|
||||
|
||||
//doesn't have mod, get from player avatar directly
|
||||
//doesn't have mod or has no custom height, get from player avatar directly
|
||||
CVRPlayerEntity playerEntity = CVRPlayerManager.Instance.NetworkPlayers.Find((players) => players.Uuid == playerId);
|
||||
if (playerEntity != null && playerEntity.PuppetMaster != null)
|
||||
return playerEntity.PuppetMaster.GetAvatarHeight();
|
||||
|
||||
// player is invalid???
|
||||
return -1f;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,8 @@ public class BaseScaler : MonoBehaviour
|
|||
|
||||
public void SetTargetHeight(float height)
|
||||
{
|
||||
if (Math.Abs(height - _targetHeight) < float.Epsilon)
|
||||
if (_isHeightAdjustedFromInitial
|
||||
&& Math.Abs(height - _targetHeight) < float.Epsilon)
|
||||
return;
|
||||
|
||||
if (height < float.Epsilon)
|
||||
|
@ -70,7 +71,7 @@ public class BaseScaler : MonoBehaviour
|
|||
ResetHeight();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!_isHeightAdjustedFromInitial)
|
||||
_legacyAnimationScale = Vector3.zero;
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI.CCK.Components;
|
||||
using NAK.AvatarScaleMod.AvatarScaling;
|
||||
using UnityEngine;
|
||||
|
||||
|
@ -16,7 +18,7 @@ public class LocalScaler : BaseScaler
|
|||
_isAvatarInstantiated = false;
|
||||
_isHeightAdjustedFromInitial = false;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
@ -25,7 +27,7 @@ public class LocalScaler : BaseScaler
|
|||
{
|
||||
if (avatarObject == null)
|
||||
return;
|
||||
|
||||
|
||||
base.OnAvatarInstantiated(avatarObject, initialHeight, initialScale);
|
||||
await FindComponentsOfTypeAsync(scalableComponentTypes);
|
||||
|
||||
|
@ -83,6 +85,7 @@ public class LocalScaler : BaseScaler
|
|||
_legacyAnimationScale = _avatarTransform.localScale;
|
||||
|
||||
AvatarScaleMod.Logger.Msg("AnimationClip-based avatar scaling detected. Disabling Universal Scaling.");
|
||||
CohtmlHud.Instance.ViewDropTextImmediate("(Local) AvatarScaleMod", "Avatar Scale Changed!", "Universal Scaling is now disabled in favor of built-in avatar scaling.");
|
||||
AvatarScaleManager.Instance.ResetHeight(); // disable mod, user used a scale slider
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,35 +3,41 @@ using UnityEngine;
|
|||
|
||||
namespace NAK.AvatarScaleMod.InputHandling;
|
||||
|
||||
public static class DebugKeybinds
|
||||
internal static class DebugKeybinds
|
||||
{
|
||||
public static void DoDebugInput()
|
||||
private const float Step = 0.1f;
|
||||
|
||||
internal 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}");
|
||||
AdjustHeight(Step);
|
||||
}
|
||||
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}");
|
||||
AdjustHeight(-Step);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Backspace))
|
||||
{
|
||||
AvatarScaleManager.Instance.ResetHeight();
|
||||
|
||||
AvatarScaleMod.Logger.Msg($"Resetting height.");
|
||||
ResetHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private static void AdjustHeight(float adjustment)
|
||||
{
|
||||
float currentHeight = AvatarScaleManager.Instance.GetHeight() + adjustment;
|
||||
currentHeight = Mathf.Max(0f, currentHeight);
|
||||
AvatarScaleManager.Instance.SetHeight(currentHeight);
|
||||
|
||||
AvatarScaleMod.Logger.Msg($"[Debug] Setting height: {currentHeight}");
|
||||
}
|
||||
|
||||
private static void ResetHeight()
|
||||
{
|
||||
AvatarScaleManager.Instance.ResetHeight();
|
||||
AvatarScaleMod.Logger.Msg("[Debug] Resetting height.");
|
||||
}
|
||||
}
|
|
@ -32,7 +32,8 @@ public static class ScaleReconizer
|
|||
secondGesture = CVRGestureStep.Gesture.Fist,
|
||||
startDistance = 1f,
|
||||
endDistance = 0.25f,
|
||||
direction = CVRGestureStep.GestureDirection.MovingIn
|
||||
direction = CVRGestureStep.GestureDirection.MovingIn,
|
||||
needsToBeInView = true,
|
||||
});
|
||||
gesture.onStart.AddListener(OnScaleStart);
|
||||
gesture.onStay.AddListener(OnScaleStay);
|
||||
|
@ -50,7 +51,8 @@ public static class ScaleReconizer
|
|||
secondGesture = CVRGestureStep.Gesture.Fist,
|
||||
startDistance = 0.25f,
|
||||
endDistance = 1f,
|
||||
direction = CVRGestureStep.GestureDirection.MovingOut
|
||||
direction = CVRGestureStep.GestureDirection.MovingOut,
|
||||
needsToBeInView = true,
|
||||
});
|
||||
gesture.onStart.AddListener(OnScaleStart);
|
||||
gesture.onStay.AddListener(OnScaleStay);
|
||||
|
|
|
@ -1,71 +1,137 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using ABI_RC.Core.IO;
|
||||
using BTKUILib;
|
||||
using BTKUILib.UIObjects;
|
||||
using BTKUILib.UIObjects.Components;
|
||||
using MelonLoader;
|
||||
using NAK.AvatarScaleMod.Integrations.BTKUI;
|
||||
using NAK.AvatarScaleMod.AvatarScaling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarScaleMod.Integrations;
|
||||
|
||||
public static class BTKUIAddon
|
||||
namespace NAK.AvatarScaleMod.Integrations
|
||||
{
|
||||
#region Initialization
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Initialize()
|
||||
public static class BtkUiAddon
|
||||
{
|
||||
Page as_RootPage = new(ModSettings.SettingsCategory, ModSettings.SettingsCategory, true, "")
|
||||
{
|
||||
MenuTitle = ModSettings.SettingsCategory,
|
||||
MenuSubtitle = "Avatar Scale Mod Settings"
|
||||
};
|
||||
private static string _selectedPlayer;
|
||||
|
||||
QuickMenuAPI.OnMenuRegenerate += (_) =>
|
||||
{
|
||||
SchedulerSystem.AddJob((InjectMenu), 1f, 1f, 1);
|
||||
};
|
||||
}
|
||||
#region Initialization
|
||||
|
||||
// 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))
|
||||
public static void Initialize()
|
||||
{
|
||||
return File.ReadAllText(path);
|
||||
PrepareIcons();
|
||||
SetupRootPage();
|
||||
SetupPlayerSelectPage();
|
||||
RegisterEventHandlers();
|
||||
}
|
||||
|
||||
MelonLogger.Warning($"File not found: {path}");
|
||||
return string.Empty;
|
||||
private static void PrepareIcons()
|
||||
{
|
||||
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 SetupRootPage()
|
||||
{
|
||||
// we only need the page, as we inject our own elements into it aa
|
||||
Page rootPage = new Page(ModSettings.ModName, ModSettings.SettingsCategory, true, "ASM_Icon_AvatarHeightConfig")
|
||||
{
|
||||
MenuTitle = ModSettings.SettingsCategory,
|
||||
MenuSubtitle = "Universal Scaling Settings"
|
||||
};
|
||||
}
|
||||
|
||||
private static void SetupPlayerSelectPage()
|
||||
{
|
||||
// what other things would be worth adding here?
|
||||
Category category = QuickMenuAPI.PlayerSelectPage.AddCategory(ModSettings.SettingsCategory, ModSettings.ModName);
|
||||
Button button = category.AddButton("Copy Height", "ASM_Icon_AvatarHeightCopy", "Copy selected players Eye Height.");
|
||||
button.OnPress += OnCopyPlayerHeight;
|
||||
}
|
||||
|
||||
private static void RegisterEventHandlers()
|
||||
{
|
||||
QuickMenuAPI.OnPlayerSelected += (_, id) => _selectedPlayer = id;
|
||||
QuickMenuAPI.OnMenuRegenerate += _ => ScheduleMenuInjection();
|
||||
QuickMenuAPI.OnTabChange += OnTabChange;
|
||||
}
|
||||
|
||||
private static void ScheduleMenuInjection()
|
||||
{
|
||||
CVR_MenuManager.Instance.quickMenu.View.BindCall("asm-AvatarHeightUpdated", new Action<float>(OnAvatarHeightUpdated));
|
||||
SchedulerSystem.AddJob(InjectMenu, 1f, 1f, 1);
|
||||
}
|
||||
|
||||
private static void InjectMenu()
|
||||
{
|
||||
AvatarScaleMod.Logger.Msg("Injecting into our BTKUI AvatarScaleMod page!");
|
||||
string menuJsPath = Path.Combine(Application.streamingAssetsPath, "Cohtml", "UIResources", "AvatarScaleMod", "menu.js");
|
||||
string menuJsContent = File.Exists(menuJsPath) ? File.ReadAllText(menuJsPath) : string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(menuJsContent))
|
||||
{
|
||||
AvatarScaleMod.Logger.Msg("Injecting embedded menu.js included with mod!");
|
||||
CVR_MenuManager.Instance.quickMenu.View._view.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
}
|
||||
else
|
||||
{
|
||||
AvatarScaleMod.Logger.Msg($"Injecting development menu.js found in: {menuJsPath}");
|
||||
CVR_MenuManager.Instance.quickMenu.View._view.ExecuteScript(menuJsContent);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static void OnCopyPlayerHeight()
|
||||
{
|
||||
float networkHeight = AvatarScaleManager.Instance.GetNetworkHeight(_selectedPlayer);
|
||||
if (networkHeight < 0) return;
|
||||
AvatarScaleManager.Instance.SetHeight(networkHeight);
|
||||
}
|
||||
|
||||
private static void OnAvatarHeightUpdated(float height)
|
||||
{
|
||||
AvatarScaleManager.Instance.SetHeight(height);
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
|
||||
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)
|
||||
{
|
||||
AvatarScaleManager.Instance.ResetHeight();
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastTime = DateTime.Now;
|
||||
}
|
||||
|
||||
private static Stream GetIconStream(string iconName)
|
||||
{
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
string assemblyName = assembly.GetName().Name;
|
||||
return assembly.GetManifestResourceStream($"{assemblyName}.resources.{iconName}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Melon Pref Helpers
|
||||
|
||||
internal static void AddMelonToggle(ref Category category, MelonPreferences_Entry<bool> entry)
|
||||
{
|
||||
category.AddToggle(entry.DisplayName, entry.Description, entry.Value).OnValueUpdated += b => entry.Value = b;
|
||||
}
|
||||
|
||||
internal static void AddMelonSlider(ref Page page, MelonPreferences_Entry<float> 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
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Melon Pref Helpers
|
||||
|
||||
internal static void AddMelonToggle(ref Category category, MelonPreferences_Entry<bool> entry)
|
||||
{
|
||||
category.AddToggle(entry.DisplayName, entry.Description, entry.Value).OnValueUpdated += b => entry.Value = b;
|
||||
}
|
||||
|
||||
internal static void AddMelonSlider(ref Page page, MelonPreferences_Entry<float> 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
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -11,15 +11,15 @@ public class AvatarScaleMod : MelonMod
|
|||
public override void OnInitializeMelon()
|
||||
{
|
||||
Logger = LoggerInstance;
|
||||
|
||||
ModNetwork.Subscribe();
|
||||
ModSettings.Initialize();
|
||||
|
||||
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.PuppetMasterPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.GesturePlaneTestPatches));
|
||||
|
||||
InitializeIntegration("BTKUILib", Integrations.BTKUIAddon.Initialize);
|
||||
|
||||
ModNetwork.Subscribe();
|
||||
ModSettings.Initialize();
|
||||
InitializeIntegration("BTKUILib", Integrations.BtkUiAddon.Initialize);
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
|
|
|
@ -9,10 +9,11 @@ namespace NAK.AvatarScaleMod;
|
|||
internal static class ModSettings
|
||||
{
|
||||
// Constants
|
||||
internal const string SettingsCategory = nameof(AvatarScaleMod);
|
||||
internal const string ModName = nameof(AvatarScaleMod);
|
||||
internal const string SettingsCategory = "Avatar Scale Mod";
|
||||
|
||||
public static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(SettingsCategory);
|
||||
MelonPreferences.CreateCategory(ModName);
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryUseUniversalScaling =
|
||||
Category.CreateEntry("use_universal_scaling", true, display_name: "Use Universal Scaling", description: "Enable or disable universal scaling.");
|
||||
|
|
|
@ -14,6 +14,8 @@ public static class ModNetwork
|
|||
{
|
||||
public static bool Debug_NetworkInbound = false;
|
||||
public static bool Debug_NetworkOutbound = false;
|
||||
|
||||
private static bool _isSubscribedToModNetwork;
|
||||
|
||||
#region Constants
|
||||
|
||||
|
@ -60,11 +62,15 @@ public static class ModNetwork
|
|||
|
||||
internal static void Subscribe()
|
||||
{
|
||||
_isSubscribedToModNetwork = true;
|
||||
ModNetworkManager.Subscribe(ModId, OnMessageReceived);
|
||||
}
|
||||
|
||||
internal static void Update()
|
||||
{
|
||||
if (!_isSubscribedToModNetwork)
|
||||
return;
|
||||
|
||||
ProcessOutboundQueue();
|
||||
ProcessInboundQueue();
|
||||
}
|
||||
|
@ -74,6 +80,9 @@ public static class ModNetwork
|
|||
if (!IsConnectedToGameNetwork())
|
||||
return;
|
||||
|
||||
if (!Enum.IsDefined(typeof(MessageType), messageType))
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrEmpty(playerId))
|
||||
{
|
||||
// to specific user
|
||||
|
@ -129,8 +138,7 @@ public static class ModNetwork
|
|||
public static void RequestHeightSync()
|
||||
{
|
||||
var myCurrentHeight = AvatarScaleManager.Instance.GetHeightForNetwork();
|
||||
if (myCurrentHeight > 0)
|
||||
OutboundQueue["global"] = new QueuedMessage { Type = MessageType.RequestHeight, Height = myCurrentHeight };
|
||||
OutboundQueue["global"] = new QueuedMessage { Type = MessageType.RequestHeight, Height = myCurrentHeight };
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -199,13 +207,12 @@ public static class ModNetwork
|
|||
case MessageType.RequestHeight:
|
||||
{
|
||||
var myNetworkHeight = AvatarScaleManager.Instance.GetHeightForNetwork();
|
||||
if (myNetworkHeight > 0)
|
||||
OutboundQueue[message.Sender] = new QueuedMessage
|
||||
{
|
||||
Type = MessageType.SyncHeight,
|
||||
Height = myNetworkHeight,
|
||||
TargetPlayer = message.Sender
|
||||
};
|
||||
OutboundQueue[message.Sender] = new QueuedMessage
|
||||
{
|
||||
Type = MessageType.SyncHeight,
|
||||
Height = myNetworkHeight,
|
||||
TargetPlayer = message.Sender
|
||||
};
|
||||
|
||||
AvatarScaleManager.Instance.OnNetworkHeightUpdateReceived(message.Sender, message.Height);
|
||||
break;
|
||||
|
|
|
@ -20,6 +20,7 @@ using System.Reflection;
|
|||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
[assembly: MelonOptionalDependencies("Action Menu")]
|
||||
[assembly: MelonColor(255, 241, 200, 82)]
|
||||
[assembly: MelonAuthorColor(255, 114, 17, 25)]
|
||||
[assembly: HarmonyDontPatchAll]
|
||||
|
|
BIN
AvatarScale/resources/ASM_Icon_AvatarHeightConfig.png
Normal file
BIN
AvatarScale/resources/ASM_Icon_AvatarHeightConfig.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
AvatarScale/resources/ASM_Icon_AvatarHeightCopy.png
Normal file
BIN
AvatarScale/resources/ASM_Icon_AvatarHeightCopy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -1,42 +1,47 @@
|
|||
(function() {
|
||||
|
||||
/* -------------------------------
|
||||
* CSS Embedding
|
||||
* ------------------------------- */
|
||||
{
|
||||
function injectCSS() {
|
||||
const embeddedCSS = `
|
||||
.slider-container {
|
||||
width: 80%;
|
||||
padding-left: 2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar-scale-slider-container {
|
||||
// ass stands for Avatar Scale Slider
|
||||
|
||||
/* Container styles */
|
||||
.ass-flex-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ass Slider styles */
|
||||
.ass-slider-container {
|
||||
width: 100%;
|
||||
height: 3em;
|
||||
}
|
||||
.ass-slider-base {
|
||||
height: 3.25em;
|
||||
position: relative;
|
||||
background: #555;
|
||||
border-radius: 1.5em;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar-scale-track-inner {
|
||||
.ass-slider-inner {
|
||||
height: 100%;
|
||||
background: #a9a9a9;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.avatar-scale-snap-point {
|
||||
.ass-snap-point {
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
background: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.slider-display-value {
|
||||
.ass-slider-value {
|
||||
font-size: 2em;
|
||||
position: relative;
|
||||
left: 0.5em;
|
||||
|
@ -44,16 +49,79 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.lock-icon {
|
||||
/* Category (label) styles */
|
||||
.ass-category-label {
|
||||
font-size: 2.2em;
|
||||
position: relative;
|
||||
left: 0.5em;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
/* Circle Button styles */
|
||||
.ass-circle-button {
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
border-radius: 50%;
|
||||
background-color: #555;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 1.75em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.ass-circle-button:hover {
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
/* Custom Toggle styles */
|
||||
.ass-custom-toggle {
|
||||
width: 5em;
|
||||
height: 2.5em;
|
||||
background-color: #555;
|
||||
border-radius: 1.25em;
|
||||
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;
|
||||
transform: translateY(-65%);
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.ass-toggle-circle {
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
background-color: #a9a9a9;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: left 0.3s;
|
||||
}
|
||||
|
||||
.ass-custom-toggle.active .ass-toggle-circle {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
/* Label styles */
|
||||
.ass-label {
|
||||
font-size: 2em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.ass-toggle-setting {
|
||||
position: relative;
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const styleElement = document.createElement('style');
|
||||
|
@ -63,113 +131,284 @@
|
|||
}
|
||||
|
||||
/* -------------------------------
|
||||
* Content Injection
|
||||
* Main Content Element
|
||||
* ------------------------------- */
|
||||
{
|
||||
const contentBlock = document.createElement('div');
|
||||
contentBlock.innerHTML = `
|
||||
<div class="settings-subcategory">
|
||||
<div class="subcategory-name">Avatar Motion Tweaker</div>
|
||||
<div class="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class="row-wrapper">
|
||||
<div class="option-caption">Crouch limit: </div>
|
||||
<div class="option-input">
|
||||
<div id="CrouchLimit" class="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slider-container">
|
||||
<div class="slider-display-value">
|
||||
<span class="slider-value">0m</span>
|
||||
</div>
|
||||
<div class="avatar-scale-slider-container">
|
||||
<div class="avatar-scale-track-inner"></div>
|
||||
</div>
|
||||
<div class="lock-icon"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const targetElement = document.getElementById('btkUI-AvatarScaleMod-MainPage');
|
||||
if(targetElement) {
|
||||
targetElement.appendChild(contentBlock);
|
||||
} else {
|
||||
console.warn('Target element "btkUI-AvatarScaleMod-MainPage" not found!');
|
||||
class MainContent {
|
||||
constructor(targetId) {
|
||||
this.element = document.createElement('div');
|
||||
this.element.id = "AvatarScaleModContainer";
|
||||
const targetElement = document.getElementById(targetId);
|
||||
if (targetElement) {
|
||||
targetElement.appendChild(this.element);
|
||||
} else {
|
||||
console.warn(`Target element "${targetId}" not found!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------
|
||||
* Event Handlers & Utility Functions
|
||||
* Generic Element Class
|
||||
* ------------------------------- */
|
||||
class Element {
|
||||
constructor(tagName, parentElement) {
|
||||
this.element = document.createElement(tagName);
|
||||
parentElement.appendChild(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------
|
||||
* Container Object
|
||||
* ------------------------------- */
|
||||
{
|
||||
const sliderContainer = document.querySelector('.avatar-scale-slider-container');
|
||||
const trackInner = document.querySelector('.avatar-scale-track-inner');
|
||||
const valueDisplay = document.querySelector('.slider-value');
|
||||
class Container extends Element {
|
||||
constructor(parentElement, {
|
||||
width = '100%',
|
||||
padding = '0em',
|
||||
paddingTop = null,
|
||||
paddingRight = null,
|
||||
paddingBottom = null,
|
||||
paddingLeft = null,
|
||||
margin = '0em',
|
||||
marginTop = null,
|
||||
marginRight = null,
|
||||
marginBottom = null,
|
||||
marginLeft = null
|
||||
} = {}) {
|
||||
super('div', parentElement);
|
||||
this.element.className = "ass-container";
|
||||
this.element.style.width = width;
|
||||
this.element.style.padding = padding;
|
||||
this.element.style.margin = margin;
|
||||
|
||||
const SNAP_TOLERANCE = 0.02;
|
||||
let snapPoints = [];
|
||||
// padding values
|
||||
if (paddingTop) this.element.style.paddingTop = paddingTop;
|
||||
if (paddingRight) this.element.style.paddingRight = paddingRight;
|
||||
if (paddingBottom) this.element.style.paddingBottom = paddingBottom;
|
||||
if (paddingLeft) this.element.style.paddingLeft = paddingLeft;
|
||||
|
||||
let isDragging = false;
|
||||
// margin values
|
||||
if (marginTop) this.element.style.marginTop = marginTop;
|
||||
if (marginRight) this.element.style.marginRight = marginRight;
|
||||
if (marginBottom) this.element.style.marginBottom = marginBottom;
|
||||
if (marginLeft) this.element.style.marginLeft = marginLeft;
|
||||
|
||||
sliderContainer.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
updateTrackWidth(e.clientX);
|
||||
});
|
||||
this.flexContainer = new Element('div', this.element);
|
||||
this.flexContainer.element.className = "ass-flex-container";
|
||||
}
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
updateTrackWidth(e.clientX);
|
||||
});
|
||||
appendElementToFlex(element) {
|
||||
this.flexContainer.element.appendChild(element);
|
||||
}
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
appendElement(element) {
|
||||
this.element.appendChild(element);
|
||||
}
|
||||
}
|
||||
|
||||
function updateTrackWidth(clientX) {
|
||||
const rect = sliderContainer.getBoundingClientRect();
|
||||
/* -------------------------------
|
||||
* Category (label) Class
|
||||
* ------------------------------- */
|
||||
class Category extends Element {
|
||||
constructor(parentElement, text) {
|
||||
super('span', parentElement);
|
||||
this.element.className = "ass-category-label";
|
||||
this.element.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Get padding values from the slider container
|
||||
const paddingLeft = parseFloat(getComputedStyle(sliderContainer).paddingLeft);
|
||||
const paddingRight = parseFloat(getComputedStyle(sliderContainer).paddingRight);
|
||||
/* -------------------------------
|
||||
* Circle Button Class
|
||||
* ------------------------------- */
|
||||
class CircleButton extends Element {
|
||||
constructor(parentElement, text) {
|
||||
super('button', parentElement);
|
||||
this.element.className = "ass-circle-button";
|
||||
this.element.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the effective width and position based on padding
|
||||
/* -------------------------------
|
||||
* Custom Toggle Class
|
||||
* ------------------------------- */
|
||||
class CustomToggle extends Element {
|
||||
constructor(parentElement) {
|
||||
super('div', parentElement);
|
||||
this.element.className = "ass-custom-toggle";
|
||||
|
||||
this.toggleCircle = new Element('div', this.element);
|
||||
this.toggleCircle.element.className = "ass-toggle-circle";
|
||||
this.state = false;
|
||||
|
||||
this.element.addEventListener('click', () => {
|
||||
this.toggle();
|
||||
});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.state = !this.state;
|
||||
if (this.state) {
|
||||
this.toggleCircle.element.style.left = '50%';
|
||||
} else {
|
||||
this.toggleCircle.element.style.left = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------
|
||||
* Label Class
|
||||
* ------------------------------- */
|
||||
class Label extends Element {
|
||||
constructor(parentElement, text) {
|
||||
super('span', parentElement);
|
||||
this.element.className = "ass-label";
|
||||
this.element.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------
|
||||
* ToggleSetting Class
|
||||
* ------------------------------- */
|
||||
class ToggleSetting extends Element {
|
||||
constructor(parentElement, labelText) {
|
||||
super('div', parentElement);
|
||||
this.element.className = "ass-toggle-setting";
|
||||
|
||||
const label = new Label(this.element, labelText);
|
||||
this.toggle = new CustomToggle(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------
|
||||
* Slider Object
|
||||
* ------------------------------- */
|
||||
class Slider extends Element {
|
||||
constructor(parentElement, min = 0.1, max = 5, initialValue = 1.8) {
|
||||
super('div', parentElement);
|
||||
this.element.className = "ass-slider-container";
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
|
||||
// Value display
|
||||
this.valueDisplay = new Element('span', this.element);
|
||||
this.valueDisplay.element.className = "ass-slider-value";
|
||||
|
||||
// Slider content
|
||||
this.sliderBase = new Element('div', this.element);
|
||||
this.sliderBase.element.className = "ass-slider-base";
|
||||
|
||||
this.trackInner = new Element('div', this.sliderBase.element);
|
||||
this.trackInner.element.className = "ass-slider-inner";
|
||||
|
||||
this.addEventListeners();
|
||||
|
||||
this.setInitialValue(initialValue);
|
||||
}
|
||||
|
||||
setInitialValue(value) {
|
||||
const percentage = (value - this.min) / (this.max - this.min);
|
||||
this.trackInner.element.style.width = `${percentage * 100}%`;
|
||||
this.valueDisplay.element.textContent = value.toFixed(2) + "m";
|
||||
this.addSnapPoint(percentage);
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.snapPoints = [];
|
||||
let isDragging = false;
|
||||
|
||||
this.element.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
this.updateTrackWidth(e.clientX);
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (!isDragging) return;
|
||||
this.updateTrackWidth(e.clientX);
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
}
|
||||
|
||||
updateTrackWidth(clientX) {
|
||||
const rect = this.element.getBoundingClientRect();
|
||||
const paddingLeft = parseFloat(getComputedStyle(this.element).paddingLeft);
|
||||
const paddingRight = parseFloat(getComputedStyle(this.element).paddingRight);
|
||||
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) => {
|
||||
let percentage = x / effectiveWidth;
|
||||
const closestSnap = this.snapPoints.reduce((closest, snap) => {
|
||||
return Math.abs(closest - percentage) < Math.abs(snap - percentage) ? closest : snap;
|
||||
}, 1);
|
||||
|
||||
const SNAP_TOLERANCE = 0.01;
|
||||
if (Math.abs(closestSnap - percentage) <= SNAP_TOLERANCE) {
|
||||
x = closestSnap * effectiveWidth;
|
||||
percentage = closestSnap;
|
||||
}
|
||||
|
||||
trackInner.style.width = `${x}px`;
|
||||
valueDisplay.textContent = (x / effectiveWidth * 100).toFixed(2) + "m";
|
||||
this.trackInner.element.style.width = `${x}px`;
|
||||
|
||||
const value = this.min + (this.max - this.min) * percentage;
|
||||
this.valueDisplay.element.textContent = value.toFixed(2) + "m";
|
||||
|
||||
engine.call("asm-AvatarHeightUpdated", value);
|
||||
}
|
||||
|
||||
function addSnapPoint(percentage) {
|
||||
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);
|
||||
const snap = new Element('div', this.sliderBase.element);
|
||||
snap.element.className = 'ass-snap-point';
|
||||
snap.element.style.left = `${percentage * 100}%`;
|
||||
this.snapPoints.push(percentage);
|
||||
}
|
||||
|
||||
// To evenly space out snap points:
|
||||
function addEvenSnapPoints(count) {
|
||||
addEvenSnapPoints(count) {
|
||||
for (let i = 1; i <= count; i++) {
|
||||
addSnapPoint(i / (count + 1));
|
||||
this.addSnapPoint(i / (count + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
addEvenSnapPoints(5); // Adds 5 evenly spaced snap points
|
||||
}
|
||||
|
||||
// Initialization
|
||||
injectCSS();
|
||||
const mainContent = new MainContent('btkUI-AvatarScaleMod-MainPage');
|
||||
if (mainContent.element) {
|
||||
|
||||
const mainContainer = new Container(mainContent.element, {
|
||||
width: '75%',
|
||||
marginTop: '2em',
|
||||
marginLeft: '2em',
|
||||
marginBottom: '1em'
|
||||
});
|
||||
|
||||
const slider = new Slider(mainContainer.flexContainer.element, 0.1, 3);
|
||||
const buttonContainer = new Container(mainContainer.flexContainer.element, {
|
||||
width: '20%',
|
||||
marginTop: '2.5em',
|
||||
marginLeft: '1em',
|
||||
});
|
||||
const circleButton1 = new CircleButton(buttonContainer.flexContainer.element, "+");
|
||||
const circleButton2 = new CircleButton(buttonContainer.flexContainer.element, "-");
|
||||
|
||||
const settingsContainer = new Container(mainContent.element, {
|
||||
width: '100%',
|
||||
marginTop: '1em',
|
||||
marginLeft: '1em',
|
||||
});
|
||||
const categoryLabel = new Category(settingsContainer.element, "Universal Scaling Settings:");
|
||||
|
||||
const settingsContainerInner = new Container(mainContent.element, {
|
||||
width: '90%',
|
||||
marginTop: '1em',
|
||||
marginLeft: '3em',
|
||||
});
|
||||
|
||||
const toggleSetting = new ToggleSetting(settingsContainerInner.element, "Universal Scaling (Mod Network)");
|
||||
const toggleSetting2 = new ToggleSetting(settingsContainerInner.element, "Recognize Scale Gesture");
|
||||
const toggleSetting3 = new ToggleSetting(settingsContainerInner.element, "Scale Components");
|
||||
}
|
||||
|
||||
})();
|
|
@ -1,13 +0,0 @@
|
|||
.slider-container {
|
||||
margin: 10px 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.slider-label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.test-slider {
|
||||
width: 100%;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue