|
|
|
|
@@ -1,6 +1,7 @@
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UIElements;
|
|
|
|
|
using UnityEngine.Audio;
|
|
|
|
|
using UnityEngine.InputSystem;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System;
|
|
|
|
|
@@ -68,9 +69,54 @@ namespace Hallucinate.UI
|
|
|
|
|
|
|
|
|
|
_masterVol = PlayerPrefs.GetFloat("MasterVolume", 80f);
|
|
|
|
|
|
|
|
|
|
// Enforce Video Settings on start
|
|
|
|
|
ApplyVideoSettings();
|
|
|
|
|
|
|
|
|
|
SwitchTab("GENERAL");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyVideoSettings()
|
|
|
|
|
{
|
|
|
|
|
// Frame Limiter
|
|
|
|
|
int frameLimitIdx = PlayerPrefs.GetInt("FrameLimiter", 2);
|
|
|
|
|
ApplyFrameLimit(frameLimitIdx);
|
|
|
|
|
|
|
|
|
|
// FPS Counter
|
|
|
|
|
_fpsVisible = PlayerPrefs.GetInt("ShowFPS", 0) == 1;
|
|
|
|
|
PerformanceOverlay.SetVisible(_fpsVisible);
|
|
|
|
|
|
|
|
|
|
// Background Dim
|
|
|
|
|
float dim = PlayerPrefs.GetFloat("BackgroundDim", 50f);
|
|
|
|
|
ApplyBackgroundDim(dim);
|
|
|
|
|
|
|
|
|
|
// Fullscreen
|
|
|
|
|
bool isFull = PlayerPrefs.GetInt("Fullscreen", Screen.fullScreen ? 1 : 0) == 1;
|
|
|
|
|
Screen.fullScreen = isFull;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyFrameLimit(int index)
|
|
|
|
|
{
|
|
|
|
|
switch (index)
|
|
|
|
|
{
|
|
|
|
|
case 0: QualitySettings.vSyncCount = 1; Application.targetFrameRate = -1; break;
|
|
|
|
|
case 1: QualitySettings.vSyncCount = 0; Application.targetFrameRate = 60; break;
|
|
|
|
|
case 2: QualitySettings.vSyncCount = 0; Application.targetFrameRate = 144; break;
|
|
|
|
|
case 3: QualitySettings.vSyncCount = 0; Application.targetFrameRate = 999; break;
|
|
|
|
|
}
|
|
|
|
|
PlayerPrefs.SetInt("FrameLimiter", index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyBackgroundDim(float value)
|
|
|
|
|
{
|
|
|
|
|
PlayerPrefs.SetFloat("BackgroundDim", value);
|
|
|
|
|
// Search for dim overlay in root
|
|
|
|
|
var dimOverlay = uiManager.Root.Q<VisualElement>("BackgroundDimOverlay");
|
|
|
|
|
if (dimOverlay != null)
|
|
|
|
|
{
|
|
|
|
|
dimOverlay.style.backgroundColor = new Color(0, 0, 0, value / 100f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetupHierarchicalVolumeOverlay()
|
|
|
|
|
{
|
|
|
|
|
_volumeContainer = new VisualElement();
|
|
|
|
|
@@ -161,8 +207,7 @@ namespace Hallucinate.UI
|
|
|
|
|
// Debug Log to see if event is even reaching here
|
|
|
|
|
Debug.Log($"[SettingsController] Mouse Wheel Detected. Settings Open: {uiManager.IsSettingsOpen}");
|
|
|
|
|
|
|
|
|
|
// Do not control volume if we are in MainMenu (unless Settings is explicitly open)
|
|
|
|
|
// Fix: Check if MainMenu is actually visible (Flex), not just exists in hierarchy
|
|
|
|
|
// 1. Do not control volume if we are in MainMenu (unless Settings is explicitly open)
|
|
|
|
|
var mainMenuRoot = uiManager.Root.Q<VisualElement>("MainMenuRoot");
|
|
|
|
|
bool isMainMenuVisible = mainMenuRoot != null && mainMenuRoot.style.display == DisplayStyle.Flex;
|
|
|
|
|
|
|
|
|
|
@@ -172,10 +217,23 @@ namespace Hallucinate.UI
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Osu style volume control
|
|
|
|
|
// 2. Detect target and context
|
|
|
|
|
VisualElement target = evt.target as VisualElement;
|
|
|
|
|
bool isDirectUIInteraction = _hoveredSubVolume != null || (_hoveredSlider != null && _activeTab == "SOUND");
|
|
|
|
|
|
|
|
|
|
// 3. If NOT direct interaction, check for ScrollView to prevent accidental volume changes while scrolling lists
|
|
|
|
|
if (!isDirectUIInteraction && target != null)
|
|
|
|
|
{
|
|
|
|
|
if (target is ScrollView || target.GetFirstAncestorOfType<ScrollView>() != null)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log("[SettingsController] Ignoring volume scroll: Hovering a ScrollView.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Osu style volume control
|
|
|
|
|
_overlayActiveCount++;
|
|
|
|
|
ShowVolumeOverlay();
|
|
|
|
|
// ... rest of method unchanged
|
|
|
|
|
|
|
|
|
|
if (_hoveredSubVolume != null)
|
|
|
|
|
{
|
|
|
|
|
@@ -350,36 +408,50 @@ namespace Hallucinate.UI
|
|
|
|
|
private void RenderVideoTab()
|
|
|
|
|
{
|
|
|
|
|
_content.Add(CreateSection("RENDERER"));
|
|
|
|
|
var frameLimit = new DropdownField("Frame Limiter", new List<string> { "VSync", "Power Saving", "Optimal", "Unlimited" }, 2);
|
|
|
|
|
|
|
|
|
|
int currentFrameLimit = PlayerPrefs.GetInt("FrameLimiter", 2);
|
|
|
|
|
var frameLimit = new DropdownField("Frame Limiter", new List<string> { "VSync", "Power Saving", "Optimal", "Unlimited" }, currentFrameLimit);
|
|
|
|
|
frameLimit.RegisterValueChangedCallback(evt => {
|
|
|
|
|
switch (evt.newValue) {
|
|
|
|
|
case "VSync": QualitySettings.vSyncCount = 1; Application.targetFrameRate = -1; break;
|
|
|
|
|
case "Power Saving": QualitySettings.vSyncCount = 0; Application.targetFrameRate = 60; break;
|
|
|
|
|
case "Optimal": QualitySettings.vSyncCount = 0; Application.targetFrameRate = 144; break;
|
|
|
|
|
case "Unlimited": QualitySettings.vSyncCount = 0; Application.targetFrameRate = 999; break;
|
|
|
|
|
}
|
|
|
|
|
ApplyFrameLimit(frameLimit.index);
|
|
|
|
|
});
|
|
|
|
|
_content.Add(frameLimit);
|
|
|
|
|
|
|
|
|
|
var fpsToggle = new Toggle("Show FPS Counter") { value = _fpsVisible };
|
|
|
|
|
fpsToggle.RegisterValueChangedCallback(evt => { _fpsVisible = evt.newValue; PerformanceOverlay.SetVisible(_fpsVisible); });
|
|
|
|
|
fpsToggle.RegisterValueChangedCallback(evt => {
|
|
|
|
|
_fpsVisible = evt.newValue;
|
|
|
|
|
PlayerPrefs.SetInt("ShowFPS", _fpsVisible ? 1 : 0);
|
|
|
|
|
PerformanceOverlay.SetVisible(_fpsVisible);
|
|
|
|
|
});
|
|
|
|
|
_content.Add(fpsToggle);
|
|
|
|
|
|
|
|
|
|
_content.Add(CreateSection("LAYOUT"));
|
|
|
|
|
Resolution native = Screen.currentResolution;
|
|
|
|
|
var resList = Screen.resolutions.Select(r => $"{r.width}x{r.height}").Distinct().Select(s => s == $"{native.width}x{native.height}" ? s + " (native)" : s).ToList();
|
|
|
|
|
var resDropdown = new DropdownField("Resolution", resList, resList.FindIndex(s => s.Contains("native")));
|
|
|
|
|
|
|
|
|
|
// Find current res in list
|
|
|
|
|
string currentResStr = $"{Screen.width}x{Screen.height}";
|
|
|
|
|
int currentResIdx = resList.FindIndex(s => s.StartsWith(currentResStr));
|
|
|
|
|
if (currentResIdx == -1) currentResIdx = resList.FindIndex(s => s.Contains("native"));
|
|
|
|
|
|
|
|
|
|
var resDropdown = new DropdownField("Resolution", resList, currentResIdx);
|
|
|
|
|
resDropdown.RegisterValueChangedCallback(evt => {
|
|
|
|
|
string[] parts = evt.newValue.Split(' ')[0].Split('x');
|
|
|
|
|
Screen.SetResolution(int.Parse(parts[0]), int.Parse(parts[1]), Screen.fullScreen);
|
|
|
|
|
int w = int.Parse(parts[0]);
|
|
|
|
|
int h = int.Parse(parts[1]);
|
|
|
|
|
Screen.SetResolution(w, h, Screen.fullScreen);
|
|
|
|
|
PlayerPrefs.SetInt("ScreenWidth", w);
|
|
|
|
|
PlayerPrefs.SetInt("ScreenHeight", h);
|
|
|
|
|
});
|
|
|
|
|
_content.Add(resDropdown);
|
|
|
|
|
|
|
|
|
|
var fullToggle = new Toggle("Fullscreen Mode") { value = Screen.fullScreen };
|
|
|
|
|
fullToggle.RegisterValueChangedCallback(evt => Screen.fullScreen = evt.newValue);
|
|
|
|
|
fullToggle.RegisterValueChangedCallback(evt => {
|
|
|
|
|
Screen.fullScreen = evt.newValue;
|
|
|
|
|
PlayerPrefs.SetInt("Fullscreen", evt.newValue ? 1 : 0);
|
|
|
|
|
});
|
|
|
|
|
_content.Add(fullToggle);
|
|
|
|
|
|
|
|
|
|
_content.Add(CreateSliderWithInput("Background Dim", 0, 100, PlayerPrefs.GetFloat("BackgroundDim", 50), val => PlayerPrefs.SetFloat("BackgroundDim", val)));
|
|
|
|
|
_content.Add(CreateSliderWithInput("Background Dim", 0, 100, PlayerPrefs.GetFloat("BackgroundDim", 50), val => ApplyBackgroundDim(val)));
|
|
|
|
|
_content.Add(CreateSliderWithInput("UI Scale", 0.5f, 2.0f, PlayerPrefs.GetFloat("UIScale", 1.0f), val => uiManager.SetUIScale(val)));
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
@@ -424,9 +496,123 @@ namespace Hallucinate.UI
|
|
|
|
|
private void RenderControlTab()
|
|
|
|
|
{
|
|
|
|
|
_content.Add(CreateSection("KEY BINDINGS"));
|
|
|
|
|
var pendingLabel = new Label("Controls Implementation Pending context.");
|
|
|
|
|
pendingLabel.AddToClassList("text-body");
|
|
|
|
|
_content.Add(pendingLabel);
|
|
|
|
|
|
|
|
|
|
if (uiManager.InputReader == null || uiManager.InputReader.InputActions == null)
|
|
|
|
|
{
|
|
|
|
|
var errorLabel = new Label("Input Actions not found.");
|
|
|
|
|
errorLabel.AddToClassList("text-body");
|
|
|
|
|
_content.Add(errorLabel);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var map in uiManager.InputReader.InputActions.actionMaps)
|
|
|
|
|
{
|
|
|
|
|
// Add a sub-header for each map
|
|
|
|
|
var mapHeader = new Label(map.name.ToUpper());
|
|
|
|
|
mapHeader.style.fontSize = 14;
|
|
|
|
|
mapHeader.style.unityFontStyleAndWeight = FontStyle.Bold;
|
|
|
|
|
mapHeader.style.color = Color.cyan;
|
|
|
|
|
mapHeader.style.marginTop = 15;
|
|
|
|
|
mapHeader.style.marginBottom = 5;
|
|
|
|
|
_content.Add(mapHeader);
|
|
|
|
|
|
|
|
|
|
foreach (var action in map.actions)
|
|
|
|
|
{
|
|
|
|
|
// Skip internal/complex actions that shouldn't be rebound manually
|
|
|
|
|
if (action.name == "Look" || action.name == "Scroll" ||
|
|
|
|
|
action.name == "Navigate" || action.name == "Point" || action.name == "Click") continue;
|
|
|
|
|
|
|
|
|
|
if (action.bindings.Any(b => b.isComposite))
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < action.bindings.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (action.bindings[i].isPartOfComposite)
|
|
|
|
|
{
|
|
|
|
|
if (action.bindings[i].groups.Contains("Keyboard&Mouse"))
|
|
|
|
|
{
|
|
|
|
|
string label = $"{action.name} {action.bindings[i].name}".ToUpper();
|
|
|
|
|
_content.Add(CreateRebindRow(action, i, label));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int bindingIndex = action.bindings.ToList().FindIndex(b => b.groups.Contains("Keyboard&Mouse"));
|
|
|
|
|
if (bindingIndex != -1)
|
|
|
|
|
{
|
|
|
|
|
_content.Add(CreateRebindRow(action, bindingIndex, action.name.ToUpper()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var resetBtn = new Button { text = "RESET ALL TO DEFAULT" };
|
|
|
|
|
resetBtn.AddToClassList("button-spring");
|
|
|
|
|
resetBtn.style.marginTop = 30;
|
|
|
|
|
resetBtn.style.alignSelf = Align.Center;
|
|
|
|
|
resetBtn.clicked += () => {
|
|
|
|
|
uiManager.InputReader.ResetBindings();
|
|
|
|
|
SwitchTab("CONTROL");
|
|
|
|
|
};
|
|
|
|
|
_content.Add(resetBtn);
|
|
|
|
|
}
|
|
|
|
|
private VisualElement CreateRebindRow(UnityEngine.InputSystem.InputAction action, int bindingIndex, string labelText)
|
|
|
|
|
{
|
|
|
|
|
var row = new VisualElement();
|
|
|
|
|
row.AddToClassList("rebind-row");
|
|
|
|
|
row.style.flexDirection = FlexDirection.Row;
|
|
|
|
|
row.style.justifyContent = Justify.SpaceBetween;
|
|
|
|
|
row.style.alignItems = Align.Center;
|
|
|
|
|
row.style.paddingTop = 10;
|
|
|
|
|
row.style.paddingBottom = 10;
|
|
|
|
|
row.style.borderBottomWidth = 1;
|
|
|
|
|
row.style.borderBottomColor = new Color(1, 1, 1, 0.1f);
|
|
|
|
|
|
|
|
|
|
var label = new Label(labelText);
|
|
|
|
|
label.AddToClassList("rebind-label");
|
|
|
|
|
label.style.color = Color.white;
|
|
|
|
|
row.Add(label);
|
|
|
|
|
|
|
|
|
|
var bindingDisplay = action.GetBindingDisplayString(bindingIndex);
|
|
|
|
|
var btn = new Button { text = bindingDisplay.ToUpper() };
|
|
|
|
|
btn.AddToClassList("rebind-button");
|
|
|
|
|
btn.style.width = 150;
|
|
|
|
|
|
|
|
|
|
btn.clicked += () => StartRebinding(action, bindingIndex, btn);
|
|
|
|
|
|
|
|
|
|
row.Add(btn);
|
|
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void StartRebinding(UnityEngine.InputSystem.InputAction action, int bindingIndex, Button btn)
|
|
|
|
|
{
|
|
|
|
|
btn.text = "> <"; // Minecraft style "waiting"
|
|
|
|
|
btn.style.color = Color.yellow;
|
|
|
|
|
|
|
|
|
|
// Disable input while rebinding to avoid side effects
|
|
|
|
|
action.actionMap.Disable();
|
|
|
|
|
|
|
|
|
|
var rebindOperation = action.PerformInteractiveRebinding(bindingIndex)
|
|
|
|
|
.WithControlsExcluding("<Mouse>/position")
|
|
|
|
|
.WithControlsExcluding("<Mouse>/delta")
|
|
|
|
|
.WithControlsExcluding("<Keyboard>/escape") // Keep ESC for cancel if needed
|
|
|
|
|
.OnMatchWaitForAnother(0.1f)
|
|
|
|
|
.OnComplete(operation => {
|
|
|
|
|
btn.text = action.GetBindingDisplayString(bindingIndex).ToUpper();
|
|
|
|
|
btn.style.color = Color.white;
|
|
|
|
|
operation.Dispose();
|
|
|
|
|
action.actionMap.Enable();
|
|
|
|
|
uiManager.InputReader.SaveBindings();
|
|
|
|
|
})
|
|
|
|
|
.OnCancel(operation => {
|
|
|
|
|
btn.text = action.GetBindingDisplayString(bindingIndex).ToUpper();
|
|
|
|
|
btn.style.color = Color.white;
|
|
|
|
|
operation.Dispose();
|
|
|
|
|
action.actionMap.Enable();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
rebindOperation.Start();
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|