From 6e556a02d440f3ec3730e266bcb30ef24d5eadb6 Mon Sep 17 00:00:00 2001 From: MapleWheels Date: Thu, 26 Feb 2026 19:16:50 -0500 Subject: [PATCH] I'm cooked, will finish this later. --- .../LuaCs/Configuration/ISettingControl.cs | 2 +- .../LuaCs/Configuration/SettingControl.cs | 146 ++++++++++++++++++ .../ClientSource/LuaCs/GUIUtil.cs | 123 +++++++++++++++ .../Services/ModsControlsSettingsMenu.cs | 17 ++ .../Services/ModsGameplaySettingsMenu.cs | 17 ++ .../LuaCs/Services/ModsSettingsMenu.cs | 35 +++++ .../LuaCs/Services/SettingsMenuSystem.cs | 99 +++++++----- .../LuaCsForBarotrauma/Texts/English.xml | 9 ++ .../LuaCsForBarotrauma/Texts/Portuguese.xml | 3 + 9 files changed, 409 insertions(+), 42 deletions(-) create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/SettingControl.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/GUIUtil.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsControlsSettingsMenu.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsGameplaySettingsMenu.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsSettingsMenu.cs create mode 100644 Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/English.xml create mode 100644 Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/Portuguese.xml diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/ISettingControl.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/ISettingControl.cs index a560e4fc4..c0202b2e8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/ISettingControl.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/ISettingControl.cs @@ -4,8 +4,8 @@ namespace Barotrauma.LuaCs.Data; public interface ISettingControl : ISettingBase { - event Action OnDown; KeyOrMouse Value { get; } bool TrySetValue(KeyOrMouse value); bool IsDown(); + bool IsHit(); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/SettingControl.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/SettingControl.cs new file mode 100644 index 000000000..b0812f425 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/SettingControl.cs @@ -0,0 +1,146 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; +using Barotrauma.LuaCs.Data; +using Microsoft.Xna.Framework.Input; +using OneOf; + +namespace Barotrauma.LuaCs.Configuration; + +public class SettingControl : ISettingControl +{ + public string InternalName { get; } + public ContentPackage OwnerPackage { get; } + public bool Equals(ISettingBase other) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public IConfigDisplayInfo GetDisplayInfo() + { + throw new NotImplementedException(); + } + + public Type GetValueType() => typeof(KeyOrMouse); + public string GetStringValue() => Value.ToString(); + + public string GetDefaultStringValue() => new KeyOrMouse(Keys.NumLock).ToString(); + + public bool TrySetValue(OneOf value) + { + var newVal = value.Match( + (string v) => GetKeyOrMouse(v), + (XElement e) => e.GetAttributeKeyOrMouse("Value", null)); + + if (newVal is null) + { + return false; + } + + Value = newVal; + OnValueChanged?.Invoke(this); + return true; + + KeyOrMouse GetKeyOrMouse(string strValue) + { + strValue ??= string.Empty; + if (Enum.TryParse(strValue, true, out Microsoft.Xna.Framework.Input.Keys key)) + { + return key; + } + else if (Enum.TryParse(strValue, out MouseButton mouseButton)) + { + return mouseButton; + } + else if (int.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out int mouseButtonInt) && + Enum.GetValues().Contains((MouseButton)mouseButtonInt)) + { + return (MouseButton)mouseButtonInt; + } + else if (string.Equals(strValue, "LeftMouse", StringComparison.OrdinalIgnoreCase)) + { + return !PlayerInput.MouseButtonsSwapped() ? MouseButton.PrimaryMouse : MouseButton.SecondaryMouse; + } + else if (string.Equals(strValue, "RightMouse", StringComparison.OrdinalIgnoreCase)) + { + return !PlayerInput.MouseButtonsSwapped() ? MouseButton.SecondaryMouse : MouseButton.PrimaryMouse; + } + + return null; + } + + } + + public event Action OnValueChanged; + public OneOf GetSerializableValue() + { + return Value.ToString(); + } + + public KeyOrMouse Value { get; private set; } = new KeyOrMouse(Keys.NumLock); + + public bool TrySetValue(KeyOrMouse value) + { + Value = value; + OnValueChanged?.Invoke(this); + return true; + } + + public bool IsDown() + { + if (this.Value is null) + return false; + switch (this.Value.MouseButton) + { + case MouseButton.None: + return Barotrauma.PlayerInput.KeyDown(this.Value.Key); + case MouseButton.PrimaryMouse: + return Barotrauma.PlayerInput.PrimaryMouseButtonHeld(); + case MouseButton.SecondaryMouse: + return Barotrauma.PlayerInput.SecondaryMouseButtonHeld(); + case MouseButton.MiddleMouse: + return Barotrauma.PlayerInput.MidButtonHeld(); + case MouseButton.MouseButton4: + return Barotrauma.PlayerInput.Mouse4ButtonHeld(); + case MouseButton.MouseButton5: + return Barotrauma.PlayerInput.Mouse5ButtonHeld(); + case MouseButton.MouseWheelUp: + return Barotrauma.PlayerInput.MouseWheelUpClicked(); + case MouseButton.MouseWheelDown: + return Barotrauma.PlayerInput.MouseWheelDownClicked(); + } + return false; + } + + public bool IsHit() + { + if (this.Value is null) + return false; + switch (this.Value.MouseButton) + { + case MouseButton.None: + return Barotrauma.PlayerInput.KeyHit(this.Value.Key); + case MouseButton.PrimaryMouse: + return Barotrauma.PlayerInput.PrimaryMouseButtonClicked(); + case MouseButton.SecondaryMouse: + return Barotrauma.PlayerInput.SecondaryMouseButtonClicked(); + case MouseButton.MiddleMouse: + return Barotrauma.PlayerInput.MidButtonClicked(); + case MouseButton.MouseButton4: + return Barotrauma.PlayerInput.Mouse4ButtonClicked(); + case MouseButton.MouseButton5: + return Barotrauma.PlayerInput.Mouse5ButtonClicked(); + case MouseButton.MouseWheelUp: + return Barotrauma.PlayerInput.MouseWheelUpClicked(); + case MouseButton.MouseWheelDown: + return Barotrauma.PlayerInput.MouseWheelDownClicked(); + } + return false; + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/GUIUtil.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/GUIUtil.cs new file mode 100644 index 000000000..f18897c08 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/GUIUtil.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using Barotrauma.Extensions; +using Microsoft.Xna.Framework; +#nullable enable + +namespace Barotrauma.LuaCs; + +/// +/// A collection of helper GUI functions. Mostly ripped from "Barotrauma/ClientSource/Settings/SettingsMenu.cs" +/// +public static class GUIUtil +{ + public static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUIFrame parent, bool split = false) + { + GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true); + GUILayoutGroup left = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false); + var centerFrame = new GUIFrame(new RectTransform((0.025f, 1.0f), layout.RectTransform), style: null); + if (split) + { + new GUICustomComponent(new RectTransform(Vector2.One, centerFrame.RectTransform), + onDraw: (sb, c) => + { + sb.DrawLine((c.Rect.Center.X, c.Rect.Top), + (c.Rect.Center.X, c.Rect.Bottom), + GUIStyle.TextColorDim, + 2f); + }); + } + GUILayoutGroup right = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false); + return (left, right); + } + + public static GUILayoutGroup CreateCenterLayout(GUIFrame parent) + => new GUILayoutGroup(new RectTransform((0.5f, 1.0f), parent.RectTransform, Anchor.TopCenter, Pivot.TopCenter)) { ChildAnchor = Anchor.TopCenter }; + + public static RectTransform NewItemRectT(GUILayoutGroup parent, Vector2 adjustRatio) + => new RectTransform((1.0f * adjustRatio.X, 0.06f * adjustRatio.Y), parent.RectTransform, Anchor.CenterLeft); + + public static void Spacer(GUILayoutGroup parent, Vector2 adjustRatio) + => new GUIFrame(new RectTransform((1.0f * adjustRatio.X, 0.03f * adjustRatio.Y), parent.RectTransform, Anchor.CenterLeft), style: null); + + public static void ClearChildElements(GUIComponent component, bool clearSelfFromParent = false) + { + component.GetAllChildren().ForEachMod(c => + { + c.Visible = false; + component.RemoveChild(c); + }); + if (clearSelfFromParent && component.Parent is not null) + component.Parent.RemoveChild(component); + } + + public static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font, Vector2 adjustRatio) + => new GUITextBlock(NewItemRectT(parent, adjustRatio), str, font: font); + + public static GUIDropDown DropdownEnum(GUILayoutGroup parent, Func textFunc, Func? tooltipFunc, T currentValue, + Action setter, Vector2 adjustRatio) where T : Enum + => Dropdown(parent, textFunc, tooltipFunc, (T[])Enum.GetValues(typeof(T)), currentValue, setter, adjustRatio); + + public static GUIDropDown Dropdown(GUILayoutGroup parent, Func textFunc, Func? tooltipFunc, IReadOnlyList values, T currentValue, Action setter, Vector2 adjustRatio) + { + var dropdown = new GUIDropDown(NewItemRectT(parent, adjustRatio)); + values.ForEach(v => dropdown.AddItem(text: textFunc(v), userData: v, toolTip: tooltipFunc?.Invoke(v) ?? null)); + int childIndex = values.IndexOf(currentValue); + dropdown.Select(childIndex); + dropdown.ListBox.ForceLayoutRecalculation(); + dropdown.ListBox.ScrollToElement(dropdown.ListBox.Content.GetChild(childIndex)); + dropdown.OnSelected = (dd, obj) => + { + setter((T)obj); + return true; + }; + return dropdown; + } + + public static (GUIScrollBar, GUITextBlock) Slider(GUILayoutGroup parent, Vector2 range, int steps, Func labelFunc, float currentValue, Action setter, LocalizedString? tooltip, Vector2 adjustRatio) + { + var layout = new GUILayoutGroup(NewItemRectT(parent, adjustRatio), isHorizontal: true); + var slider = new GUIScrollBar(new RectTransform((0.72f, 1.0f), layout.RectTransform), style: "GUISlider") + { + Range = range, + BarScrollValue = currentValue, + Step = 1.0f / (float)(steps - 1), + BarSize = 1.0f / steps + }; + if (tooltip != null) + { + slider.ToolTip = tooltip; + } + var label = new GUITextBlock(new RectTransform((0.28f, 1.0f), layout.RectTransform), + labelFunc(currentValue), wrap: false, textAlignment: Alignment.Center); + slider.OnMoved = (sb, val) => + { + label.Text = labelFunc(sb.BarScrollValue); + setter(sb.BarScrollValue); + return true; + }; + return (slider, label); + } + + public static GUITickBox Tickbox(GUILayoutGroup parent, LocalizedString label, LocalizedString tooltip, + bool currentValue, Action setter, Vector2 adjustRatio) + { + var tickbox = new GUITickBox(NewItemRectT(parent, adjustRatio), label) + { + Selected = currentValue, + ToolTip = tooltip, + OnSelected = (tb) => + { + setter(tb.Selected); + return true; + } + }; + return tickbox; + } + + public static string Percentage(float v) => ToolBox.GetFormattedPercentage(v); + + public static int Round(float v) => (int)MathF.Round(v); +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsControlsSettingsMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsControlsSettingsMenu.cs new file mode 100644 index 000000000..8b8806b7e --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsControlsSettingsMenu.cs @@ -0,0 +1,17 @@ +namespace Barotrauma.LuaCs; + +internal sealed class ModsControlsSettingsMenu : ModsSettingsMenu +{ + public ModsControlsSettingsMenu(GUIFrame contentFrame, + IPackageManagementService packageManagementService, + IConfigService configService, + SettingsMenu settingsMenuInstance) : base(contentFrame, packageManagementService, configService, settingsMenuInstance) + { + + } + + protected override void DisposeInternal() + { + // TODO: Finish this later. + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsGameplaySettingsMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsGameplaySettingsMenu.cs new file mode 100644 index 000000000..e54f384dd --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsGameplaySettingsMenu.cs @@ -0,0 +1,17 @@ +namespace Barotrauma.LuaCs; + +internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenu +{ + public ModsGameplaySettingsMenu(GUIFrame contentFrame, + IPackageManagementService packageManagementService, + IConfigService configService, + SettingsMenu settingsMenuInstance) : base(contentFrame, packageManagementService, configService, settingsMenuInstance) + { + + } + + protected override void DisposeInternal() + { + // TODO: Finish this later. + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsSettingsMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsSettingsMenu.cs new file mode 100644 index 000000000..b228d1da5 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/ModsSettingsMenu.cs @@ -0,0 +1,35 @@ +using System; +using Barotrauma.Extensions; +using Microsoft.Xna.Framework; + +namespace Barotrauma.LuaCs; + +internal abstract class ModsSettingsMenu : IDisposable +{ + public GUIFrame ContentFrame { get; private set; } + protected IPackageManagementService PackageManagementService { get; private set; } + protected IConfigService ConfigService { get; private set; } + protected SettingsMenu SettingsMenuInstance { get; private set; } + + protected ModsSettingsMenu(GUIFrame contentFrame, + IPackageManagementService packageManagementService, + IConfigService configService, SettingsMenu settingsMenuInstance) + { + ContentFrame = contentFrame; + PackageManagementService = packageManagementService; + ConfigService = configService; + SettingsMenuInstance = settingsMenuInstance; + } + + protected abstract void DisposeInternal(); + + public void Dispose() + { + DisposeInternal(); + ContentFrame?.Parent.RemoveChild(ContentFrame); + SettingsMenuInstance = null; + ContentFrame = null; + PackageManagementService = null; + ConfigService = null; + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/SettingsMenuSystem.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/SettingsMenuSystem.cs index e3cdf5f1e..bd798e7b8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/SettingsMenuSystem.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/SettingsMenuSystem.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Barotrauma.Extensions; using HarmonyLib; using Microsoft.Xna.Framework; @@ -7,65 +8,81 @@ namespace Barotrauma.LuaCs; public class SettingsMenuSystem : ISettingsMenuSystem { - private GUIFrame _menuFrame; - private GUIButton _menuOpenButton; - private readonly Harmony _harmony; - private static SettingsMenuSystem _systemInstance; - public SettingsMenuSystem() + private ModsControlsSettingsMenu _controlsMenuInstance; + private ModsGameplaySettingsMenu _gameplayMenuInstance; + + private readonly Harmony _harmony; + private readonly IPackageManagementService _packageManagementService; + private readonly IConfigService _configService; + private static SettingsMenuSystem SystemInstance; + + public SettingsMenuSystem(IPackageManagementService packageManagementService, IConfigService configService) { - _systemInstance = this; + _packageManagementService = packageManagementService; + _configService = configService; + SystemInstance = this; _harmony = Harmony.CreateAndPatchAll(typeof(SettingsMenuSystem)); } [HarmonyPatch(typeof(SettingsMenu), "CreateModsTab"), HarmonyPostfix] private static void SettingsMenu_CreateModsTab_Post(SettingsMenu __instance) { - _systemInstance.CreateSettingsMenu(__instance); + SystemInstance.CreateSettingsMenu(__instance); } private void CreateSettingsMenu(SettingsMenu __instance) { - var tabIndex = (SettingsMenu.Tab)Enum.GetValues().Length; - var contentFrame = CreateNewContentFrame(tabIndex); - contentFrame.RectTransform.RelativeSize = Vector2.One; - + DisposeMenuFrames(); + var tabCount = Enum.GetValues().Length; + var tabGameplayIndex = (SettingsMenu.Tab)tabCount; + var tabControlsIndex = (SettingsMenu.Tab)tabCount+1; - GUIFrame CreateNewContentFrame(SettingsMenu.Tab tab) + var gameplayContentFrame = CreateNewContentTab(tabGameplayIndex, __instance, + "SettingsMenuTab.Mods", "LuaCsForBarotrauma.SettingsMenu.ModGameplayButton"); + var controlsContentFrame = CreateNewContentTab(tabControlsIndex, __instance, + "SettingsMenuTab.Mods", "LuaCsForBarotrauma.SettingsMenu.ModControlsButton"); + + _gameplayMenuInstance = new ModsGameplaySettingsMenu(gameplayContentFrame, _packageManagementService, _configService, __instance); + _controlsMenuInstance = new ModsControlsSettingsMenu(controlsContentFrame, _packageManagementService, _configService, __instance); + + + } + + private GUIFrame CreateNewContentTab(SettingsMenu.Tab tab, SettingsMenu settingsMenuInstance, string settingsMenuTabName, string settingMenuHoverTextIdent) + { + if (settingsMenuInstance.tabContents.TryGetValue(tab, out (GUIButton Button, GUIFrame Content) tabContent)) { - if (__instance.tabContents.TryGetValue(tab, out (GUIButton Button, GUIFrame Content) tabContent)) - { - return tabContent.Content; - } - - var contentFr = new GUIFrame(new RectTransform(Vector2.One * 0.95f, __instance.contentFrame.RectTransform, Anchor.Center, Pivot.Center), style: null); - - var button = new GUIButton(new RectTransform(Vector2.One, __instance.tabber.RectTransform, Anchor.TopLeft, Pivot.TopLeft, scaleBasis: ScaleBasis.Smallest), "", style: $"SettingsMenuTab.Mods") - { - ToolTip = TextManager.Get($"LuaCsForBarotrauma.SettingsMenu.ModSettingsButton"), - OnClicked = (b, _) => - { - __instance.SelectTab(tab); - return false; - } - }; - button.RectTransform.MaxSize = RectTransform.MaxPoint; - button.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint); - - __instance.tabContents.Add(tab, (button, contentFr)); - - return contentFr; + return tabContent.Content; } + + var contentFr = new GUIFrame(new RectTransform(Vector2.One * 0.95f, settingsMenuInstance.contentFrame.RectTransform, Anchor.Center, Pivot.Center), style: null); + + var button = new GUIButton(new RectTransform(Vector2.One, settingsMenuInstance.tabber.RectTransform, + Anchor.TopLeft, Pivot.TopLeft, scaleBasis: ScaleBasis.Smallest), "", style: $"SettingsMenuTab.Mods") + { + ToolTip = TextManager.Get($"LuaCsForBarotrauma.SettingsMenu.ModSettingsButton"), + OnClicked = (b, _) => + { + settingsMenuInstance.SelectTab(tab); + return false; + } + }; + button.RectTransform.MaxSize = RectTransform.MaxPoint; + button.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint); + + settingsMenuInstance.tabContents.Add(tab, (button, contentFr)); + + return contentFr; } - private void DisposeMenuFrame() + private void DisposeMenuFrames() { - if (_menuFrame is not null) - { - _menuFrame.Parent.RemoveChild(_menuFrame); - _menuFrame = null; - } + _controlsMenuInstance?.Dispose(); + _gameplayMenuInstance?.Dispose(); + _controlsMenuInstance = null; + _gameplayMenuInstance = null; } #region DISPOSAL @@ -76,7 +93,7 @@ public class SettingsMenuSystem : ISettingsMenuSystem { return; } - DisposeMenuFrame(); + DisposeMenuFrames(); GC.SuppressFinalize(this); } private int _isDisposed = 0; diff --git a/Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/English.xml b/Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/English.xml new file mode 100644 index 000000000..982c44b0b --- /dev/null +++ b/Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/English.xml @@ -0,0 +1,9 @@ + + + Mod Controls Settings + Mod Gameplay Settings + Suppress GUI Popup on Error + Are C# Mods Allowed + Hide Local OS Account Name In Logs + Where to Save Local Data + diff --git a/Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/Portuguese.xml b/Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/Portuguese.xml new file mode 100644 index 000000000..4b149c002 --- /dev/null +++ b/Barotrauma/BarotraumaShared/LocalMods/LuaCsForBarotrauma/Texts/Portuguese.xml @@ -0,0 +1,3 @@ + + +