Add pages to client options

This commit is contained in:
D1GQ committed 2026-03-04 19:05:24 -06:00
1 parent e644537355
commit 55391e7d4e
2 files changed
+113 -116

No files matched your search

+100 -103
View File
@@ -2,7 +2,6 @@
using BetterAmongUs.Helpers; using BetterAmongUs.Helpers;
using BetterAmongUs.Patches.Client; using BetterAmongUs.Patches.Client;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object;
namespace BetterAmongUs.Modules; namespace BetterAmongUs.Modules;
@@ -24,24 +23,23 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Gets the list of all created client option items. /// Gets the list of all created client option items.
/// </summary> /// </summary>
internal static readonly List<ClientOptionItem> ClientOptions = []; internal static readonly Dictionary<int, List<ClientOptionItem>> ClientOptions = [];
/// <summary> /// <summary>
/// Creates a toggle option with configuration binding. /// Creates a toggle option with configuration binding.
/// </summary> /// </summary>
/// <param name="name">The display name of the option.</param> public static ClientOptionItem CreateToggle(string name, ConfigEntry<bool> config, int page, OptionsMenuBehaviour optionsMenuBehaviour, Action? onToggle = null, Func<bool>? toggleCheck = null)
/// <param name="config">The configuration entry to bind to.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <param name="onToggle">Optional action to execute when the toggle is changed.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
/// <returns>A new ClientOptionItem instance.</returns>
public static ClientOptionItem CreateToggle(string name, ConfigEntry<bool> config, OptionsMenuBehaviour optionsMenuBehaviour, Action? onToggle = null, Func<bool>? toggleCheck = null)
{ {
var toggleButton = CreateToggleButton(name, config.Value, optionsMenuBehaviour); var toggleButton = CreateToggleButton(name, optionsMenuBehaviour, GetOrCreatePage(page, optionsMenuBehaviour).transform);
toggleButton.transform.localPosition = CalculateButtonPosition(page);
var item = new ClientOptionItem(name, config, toggleButton); var item = new ClientOptionItem(name, config, toggleButton);
item.SetupToggleButton(onToggle, toggleCheck); item.SetupToggleButton(onToggle, toggleCheck);
ClientOptions.Add(item); if (!ClientOptions.TryGetValue(page, out var options))
{
options = ClientOptions[page] = [];
}
options.Add(item);
return item; return item;
} }
@@ -49,38 +47,18 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Creates a button option without toggle state. /// Creates a button option without toggle state.
/// </summary> /// </summary>
/// <param name="name">The display name of the button.</param> public static ClientOptionItem CreateButton(string name, int page, OptionsMenuBehaviour optionsMenuBehaviour, Action onClick, Func<bool>? clickCheck = null)
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <param name="onClick">The action to execute when the button is clicked.</param>
/// <param name="clickCheck">Optional function to check if the button can be clicked.</param>
/// <returns>A new ClientOptionItem instance.</returns>
public static ClientOptionItem CreateButton(string name, OptionsMenuBehaviour optionsMenuBehaviour, Action onClick, Func<bool>? clickCheck = null)
{ {
var toggleButton = CreateToggleButton(name, false, optionsMenuBehaviour); var toggleButton = CreateToggleButton(name, optionsMenuBehaviour, GetOrCreatePage(page, optionsMenuBehaviour).transform);
toggleButton.transform.localPosition = CalculateButtonPosition(page);
var item = new ClientOptionItem(name, null, toggleButton); var item = new ClientOptionItem(name, null, toggleButton);
item.SetupButton(onClick, clickCheck); item.SetupButton(onClick, clickCheck);
ClientOptions.Add(item); if (!ClientOptions.TryGetValue(page, out var options))
{
return item; options = ClientOptions[page] = [];
} }
options.Add(item);
/// <summary>
/// Creates a toggle option without configuration binding (manual state management).
/// </summary>
/// <param name="name">The display name of the option.</param>
/// <param name="initialState">The initial state of the toggle.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <param name="onToggle">The action to execute when the toggle is changed, receiving the new state.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
/// <returns>A new ClientOptionItem instance.</returns>
public static ClientOptionItem CreateManualToggle(string name, bool initialState, OptionsMenuBehaviour optionsMenuBehaviour, Action<bool> onToggle, Func<bool>? toggleCheck = null)
{
var toggleButton = CreateToggleButton(name, initialState, optionsMenuBehaviour);
var item = new ClientOptionItem(name, null, toggleButton);
item.SetupManualToggle(initialState, onToggle, toggleCheck);
ClientOptions.Add(item);
return item; return item;
} }
@@ -88,10 +66,7 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Initializes a new instance of the ClientOptionItem class. /// Initializes a new instance of the ClientOptionItem class.
/// </summary> /// </summary>
/// <param name="name">The name of the option.</param> internal ClientOptionItem(string name, ConfigEntry<bool>? config, ToggleButtonBehaviour toggleButton)
/// <param name="config">The configuration entry, or null for non-config options.</param>
/// <param name="toggleButton">The toggle button behavior instance.</param>
private ClientOptionItem(string name, ConfigEntry<bool>? config, ToggleButtonBehaviour toggleButton)
{ {
Config = config; Config = config;
ToggleButton = toggleButton; ToggleButton = toggleButton;
@@ -101,21 +76,10 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Creates a toggle button GameObject for the options menu. /// Creates a toggle button GameObject for the options menu.
/// </summary> /// </summary>
/// <param name="name">The name of the toggle button.</param> private static ToggleButtonBehaviour CreateToggleButton(string name, OptionsMenuBehaviour optionsMenuBehaviour, Transform parent)
/// <param name="initialState">The initial state of the toggle.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <returns>A new ToggleButtonBehaviour instance.</returns>
private static ToggleButtonBehaviour CreateToggleButton(string name, bool initialState, OptionsMenuBehaviour optionsMenuBehaviour)
{ {
if (OptionsMenuBehaviourPatch.BetterOptionsTab?.Content == null)
{
throw new InvalidOperationException("BetterOptionsTab is not initialized");
}
var mouseMoveToggle = optionsMenuBehaviour.DisableMouseMovement; var mouseMoveToggle = optionsMenuBehaviour.DisableMouseMovement;
var toggleButton = Object.Instantiate(mouseMoveToggle, OptionsMenuBehaviourPatch.BetterOptionsTab.Content.transform); var toggleButton = UnityEngine.Object.Instantiate(mouseMoveToggle, parent);
toggleButton.transform.localPosition = CalculateButtonPosition();
toggleButton.name = name; toggleButton.name = name;
toggleButton.Text.text = name; toggleButton.Text.text = name;
@@ -125,12 +89,15 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Calculates the position for a new button based on the current number of options. /// Calculates the position for a new button based on the current number of options.
/// </summary> /// </summary>
/// <returns>A Vector3 representing the button position.</returns> private static Vector3 CalculateButtonPosition(int page)
private static Vector3 CalculateButtonPosition()
{ {
if (!ClientOptions.TryGetValue(page, out var options))
{
options = ClientOptions[page] = [];
}
return new Vector3( return new Vector3(
ClientOptions.Count % 2 == 0 ? -1.3f : 1.3f, options.Count % 2 == 0 ? -1.3f : 1.3f,
1.8f - 0.5f * (ClientOptions.Count / 2), 1.8f - 0.5f * (options.Count / 2),
-6f -6f
); );
} }
@@ -138,9 +105,7 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Sets up a configuration-bound toggle button with click handler. /// Sets up a configuration-bound toggle button with click handler.
/// </summary> /// </summary>
/// <param name="onToggle">Optional action to execute when toggled.</param> internal void SetupToggleButton(Action? onToggle, Func<bool>? toggleCheck)
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
private void SetupToggleButton(Action? onToggle, Func<bool>? toggleCheck)
{ {
var passiveButton = ToggleButton.GetComponent<PassiveButton>(); var passiveButton = ToggleButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new(); passiveButton.OnClick = new();
@@ -163,9 +128,7 @@ internal sealed class ClientOptionItem
/// <summary> /// <summary>
/// Sets up a button (non-toggle) with click handler. /// Sets up a button (non-toggle) with click handler.
/// </summary> /// </summary>
/// <param name="onClick">The action to execute when clicked.</param> internal void SetupButton(Action onClick, Func<bool>? clickCheck)
/// <param name="clickCheck">Optional function to check if the button can be clicked.</param>
private void SetupButton(Action onClick, Func<bool>? clickCheck)
{ {
var passiveButton = ToggleButton.GetComponent<PassiveButton>(); var passiveButton = ToggleButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new(); passiveButton.OnClick = new();
@@ -182,31 +145,6 @@ internal sealed class ClientOptionItem
}); });
} }
/// <summary>
/// Sets up a manually managed toggle button with state tracking.
/// </summary>
/// <param name="initialState">The initial state of the toggle.</param>
/// <param name="onToggle">The action to execute when toggled, receiving the new state.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
private void SetupManualToggle(bool initialState, Action<bool> onToggle, Func<bool>? toggleCheck)
{
var passiveButton = ToggleButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
bool currentState = initialState;
passiveButton.OnClick.AddListener(() =>
{
if (toggleCheck?.Invoke() == false) return;
currentState = !currentState;
UpdateManualToggle(currentState);
onToggle?.Invoke(currentState);
});
UpdateManualToggle(initialState);
}
/// <summary> /// <summary>
/// Updates the visual state of a config-bound toggle button. /// Updates the visual state of a config-bound toggle button.
/// </summary> /// </summary>
@@ -217,21 +155,9 @@ internal sealed class ClientOptionItem
UpdateToggleVisuals(Config.Value); UpdateToggleVisuals(Config.Value);
} }
/// <summary>
/// Updates the visual state of a manually managed toggle button.
/// </summary>
/// <param name="isEnabled">The current enabled state of the toggle.</param>
internal void UpdateManualToggle(bool isEnabled)
{
if (ToggleButton == null) return;
UpdateToggleVisuals(isEnabled);
}
/// <summary> /// <summary>
/// Updates the visual appearance of a toggle button based on its state. /// Updates the visual appearance of a toggle button based on its state.
/// </summary> /// </summary>
/// <param name="isEnabled">Whether the toggle is enabled.</param>
private void UpdateToggleVisuals(bool isEnabled) private void UpdateToggleVisuals(bool isEnabled)
{ {
var color = isEnabled ? var color = isEnabled ?
@@ -247,4 +173,75 @@ internal sealed class ClientOptionItem
ToggleButton.Text.color = textColor; ToggleButton.Text.color = textColor;
ToggleButton.Text.text = $"{ToggleButton.name}: {(isEnabled ? "On" : "Off")}"; ToggleButton.Text.text = $"{ToggleButton.name}: {(isEnabled ? "On" : "Off")}";
} }
/// <summary>
/// Retrieves an existing page GameObject or creates a new one with navigation buttons.
/// </summary>
private static GameObject? GetOrCreatePage(int page, OptionsMenuBehaviour optionsMenuBehaviour, bool doNotCreate = false)
{
string name = "Page " + page;
var currentPage = OptionsMenuBehaviourPatch.BetterOptionsTab.Content.transform.Find(name)?.gameObject;
if (currentPage == null)
{
if (doNotCreate)
{
return null;
}
currentPage = new GameObject(name);
currentPage.SetActive(page == 1);
currentPage.transform.SetParent(OptionsMenuBehaviourPatch.BetterOptionsTab.Content.transform);
currentPage.transform.localPosition = Vector3.zero;
currentPage.transform.localScale = Vector3.one;
int previous = page - 1;
if (previous > 0)
{
var previousPage = GetOrCreatePage(previous, optionsMenuBehaviour, true);
if (previousPage != null)
{
CreatePreviousButton(currentPage, previousPage, optionsMenuBehaviour);
CreateNextButton(previousPage, currentPage, optionsMenuBehaviour);
}
}
}
return currentPage;
}
/// <summary>
/// Creates a "Next" navigation button that switches to the specified next page.
/// </summary>
private static void CreateNextButton(GameObject page, GameObject nextPage, OptionsMenuBehaviour optionsMenuBehaviour)
{
var button = CreateToggleButton("Next >", optionsMenuBehaviour, page.transform);
button.transform.localPosition = new Vector3(2f, -2.5f, 0f);
button.transform.Find("Background")?.localScale = new Vector3(0.5f, 1f, 1f);
button.transform.Find("ButtonHighlight")?.localScale = new Vector3(0.5f, 0.95f, 1f);
var passiveButton = button.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
passiveButton.OnClick.AddListener(() =>
{
page.SetActive(false);
nextPage.SetActive(true);
});
}
/// <summary>
/// Creates a "Previous" navigation button that switches to the specified previous page.
/// </summary>
private static void CreatePreviousButton(GameObject page, GameObject previousPage, OptionsMenuBehaviour optionsMenuBehaviour)
{
var button = CreateToggleButton("< Prev", optionsMenuBehaviour, page.transform);
button.transform.localPosition = new Vector3(-2f, -2.5f, 0f);
button.transform.Find("Background")?.localScale = new Vector3(0.5f, 1f, 1f);
button.transform.Find("ButtonHighlight")?.localScale = new Vector3(0.5f, 0.95f, 1f);
var passiveButton = button.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
passiveButton.OnClick.AddListener(() =>
{
page.SetActive(false);
previousPage.SetActive(true);
});
}
} }
+13 -13
View File
@@ -1,4 +1,4 @@
using BepInEx; using BepInEx;
using BetterAmongUs.Data; using BetterAmongUs.Data;
using BetterAmongUs.Helpers; using BetterAmongUs.Helpers;
using BetterAmongUs.Managers; using BetterAmongUs.Managers;
@@ -39,19 +39,19 @@ internal static class OptionsMenuBehaviourPatch
ClientOptionItem.ClientOptions.Clear(); ClientOptionItem.ClientOptions.Clear();
// Toggle options with config binding // Toggle options with config binding
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.AntiCheat"), BAUPlugin.AntiCheat, __instance); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.AntiCheat"), BAUPlugin.AntiCheat, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.SendBetterRpc"), BAUPlugin.SendBetterRpc, __instance, SendBetterRpcAction); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.SendBetterRpc"), BAUPlugin.SendBetterRpc, 1, __instance, SendBetterRpcAction);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.BetterNotifications"), BAUPlugin.BetterNotifications, __instance, ClearNotifications); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.BetterNotifications"), BAUPlugin.BetterNotifications, 1, __instance, ClearNotifications);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ForceOwnLanguage"), BAUPlugin.ForceOwnLanguage, __instance); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ForceOwnLanguage"), BAUPlugin.ForceOwnLanguage, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatDarkMode"), BAUPlugin.ChatDarkMode, __instance, ChatPatch.SetChatTheme); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatDarkMode"), BAUPlugin.ChatDarkMode, 1, __instance, ChatPatch.SetChatTheme);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatInGame"), BAUPlugin.ChatInGameplay, __instance); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatInGame"), BAUPlugin.ChatInGameplay, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyInfo"), BAUPlugin.LobbyPlayerInfo, __instance); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyInfo"), BAUPlugin.LobbyPlayerInfo, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyTheme"), BAUPlugin.DisableLobbyTheme, __instance, ToggleLobbyTheme); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyTheme"), BAUPlugin.DisableLobbyTheme, 1, __instance, ToggleLobbyTheme);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.UnlockFPS"), BAUPlugin.UnlockFPS, __instance, UpdateFrameRate); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.UnlockFPS"), BAUPlugin.UnlockFPS, 1, __instance, UpdateFrameRate);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ShowFPS"), BAUPlugin.ShowFPS, __instance); ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ShowFPS"), BAUPlugin.ShowFPS, 1, __instance);
// Button options (no toggle) // Button options (no toggle)
ClientOptionItem.CreateButton(Translator.GetString("BetterOption.SaveData"), __instance, OpenSaveData, () => ClientOptionItem.CreateButton(Translator.GetString("BetterOption.SaveData"), 1, __instance, OpenSaveData, () =>
{ {
// Only allow opening save data in lobby/main menu, not during gameplay // Only allow opening save data in lobby/main menu, not during gameplay
bool cannotOpen = GameState.IsInGame && !GameState.IsLobby; bool cannotOpen = GameState.IsInGame && !GameState.IsLobby;
@@ -62,7 +62,7 @@ internal static class OptionsMenuBehaviourPatch
return !cannotOpen; return !cannotOpen;
}); });
ClientOptionItem.CreateButton(Translator.GetString("BetterOption.ToVanilla"), __instance, SwitchToVanilla, () => ClientOptionItem.CreateButton(Translator.GetString("BetterOption.ToVanilla"), 1, __instance, SwitchToVanilla, () =>
{ {
// Prevent switching to vanilla while in a game // Prevent switching to vanilla while in a game
bool cannotSwitch = GameState.IsInGame; bool cannotSwitch = GameState.IsInGame;