From 080b3c4bd8c03ee9b7351f57131ca5b32392e42e Mon Sep 17 00:00:00 2001 From: Robbie Lodico Date: Sun, 26 Jan 2025 16:01:28 +0100 Subject: [PATCH] feat: expanded zoom for increased map size Zoom was limited to effective 32px, 64px, 128px before, but this was a limitation due to the maps not being big enough at small resolutions, but if map size is factored in, smaller tile sizes can have an expanded zoom range e.g. 16px tiles with a 64x52 map size can have zoom 1x while 16px with 32x26 maps have to have at least 2x --- .../Config/MapOptions.cs | 96 +++++++++++++++++-- Intersect.Client.Core/Core/Graphics.cs | 4 +- Intersect.Client.Core/Core/Input.cs | 8 +- .../Interface/Shared/SettingsWindow.cs | 64 +++++++++---- Intersect.Client.Core/Localization/Strings.cs | 4 + .../Gwen/Control/LabeledHorizontalSlider.cs | 26 +++++ .../Gwen/Skin/TexturedBase.cs | 2 +- 7 files changed, 172 insertions(+), 32 deletions(-) diff --git a/Framework/Intersect.Framework.Core/Config/MapOptions.cs b/Framework/Intersect.Framework.Core/Config/MapOptions.cs index 18749198ac..fdcb8f72cc 100644 --- a/Framework/Intersect.Framework.Core/Config/MapOptions.cs +++ b/Framework/Intersect.Framework.Core/Config/MapOptions.cs @@ -12,10 +12,32 @@ namespace Intersect.Config; [RequiresRestart] public partial class MapOptions { + #region Constants + public const int DefaultMapWidth = 32; public const int DefaultMapHeight = 26; + #endregion Constants + + #region Backing Fields + private bool _enableDiagonalMovement = true; + private int _mapWidth = DefaultMapWidth; + private int _mapHeight = DefaultMapHeight; + private int _tileWidth = 32; + private int _tileHeight = 32; + + #endregion Backing Fields + + #region Transient Properties + + [JsonIgnore] public float MinimumWorldScale { get; private set; } + + [JsonIgnore] public float MaximumWorldScale { get; private set; } + + #endregion Transient Properties + + #region Configurable Properties /// /// If experience can be lost in arena type maps. @@ -64,7 +86,20 @@ public bool EnableDiagonalMovement /// /// The height of the map in tiles. /// - public int MapHeight { get; set; } = DefaultMapHeight; + public int MapHeight + { + get => _mapHeight; + set + { + if (value == _mapHeight) + { + return; + } + + _mapHeight = value; + RecalculateWorldScale(); + } + } /// /// The width of map items. @@ -79,7 +114,20 @@ public bool EnableDiagonalMovement /// /// The width of the map in tiles. /// - public int MapWidth { get; set; } = DefaultMapWidth; + public int MapWidth + { + get => _mapWidth; + set + { + if (value == _mapWidth) + { + return; + } + + _mapWidth = value; + RecalculateWorldScale(); + } + } /// /// The number of movement directions available in the game for entities within the map. @@ -90,15 +138,38 @@ public bool EnableDiagonalMovement /// /// The height of each tile in pixels. /// - public int TileHeight { get; set; } = 32; + public int TileHeight + { + get => _tileHeight; + set + { + if (value == _tileHeight) + { + return; + } - [JsonIgnore] - public float TileScale => 32f / Math.Min(TileWidth, TileHeight); + _tileHeight = value; + RecalculateWorldScale(); + } + } /// /// The width of each tile in pixels. /// - public int TileWidth { get; set; } = 32; + public int TileWidth + { + get => _tileWidth; + set + { + if (value == _tileWidth) + { + return; + } + + _tileWidth = value; + RecalculateWorldScale(); + } + } /// /// The time, in milliseconds, until the map is cleaned up. @@ -110,6 +181,8 @@ public bool EnableDiagonalMovement /// public bool ZDimensionVisible { get; set; } + #endregion Configurable Properties + [OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) { @@ -131,5 +204,16 @@ public void Validate() MapItemWidth = MapItemWidth < 1 ? (uint)TileWidth : MapItemWidth; MapItemHeight = MapItemHeight < 1 ? (uint)TileHeight : MapItemHeight; + + RecalculateWorldScale(); + } + + private void RecalculateWorldScale() + { + var tileRatio = 32f / Math.Min(TileWidth, TileHeight); + var mapRatio = Math.Min(DefaultMapWidth / Math.Max(1f, _mapWidth), DefaultMapHeight / Math.Max(1f, _mapHeight)); + var combinedRatio = tileRatio * mapRatio; + MinimumWorldScale = combinedRatio; + MaximumWorldScale = Math.Max(MinimumWorldScale * 4, tileRatio * 4); } } diff --git a/Intersect.Client.Core/Core/Graphics.cs b/Intersect.Client.Core/Core/Graphics.cs index a232daebea..a872a1fe20 100644 --- a/Intersect.Client.Core/Core/Graphics.cs +++ b/Intersect.Client.Core/Core/Graphics.cs @@ -105,7 +105,9 @@ public static FloatRect CurrentView public static GameFont? UIFont; - public static float BaseWorldScale => Options.Instance?.Map?.TileScale ?? 1; + public static float MinimumWorldScale => Options.Instance?.Map?.MinimumWorldScale ?? 1; + + public static float MaximumWorldScale => Options.Instance?.Map?.MaximumWorldScale ?? 1; //Init Functions public static void InitGraphics() diff --git a/Intersect.Client.Core/Core/Input.cs b/Intersect.Client.Core/Core/Input.cs index 966623bf56..c638e88335 100644 --- a/Intersect.Client.Core/Core/Input.cs +++ b/Intersect.Client.Core/Core/Input.cs @@ -39,18 +39,18 @@ public static partial class Input private static void HandleZoomOut() { Globals.Database.WorldZoom /= 2; - if (Globals.Database.WorldZoom < Graphics.BaseWorldScale) + if (Globals.Database.WorldZoom < Graphics.MinimumWorldScale) { - Globals.Database.WorldZoom = Graphics.BaseWorldScale * 4; + Globals.Database.WorldZoom = Graphics.MaximumWorldScale; } } private static void HandleZoomIn() { Globals.Database.WorldZoom *= 2; - if (Globals.Database.WorldZoom > Graphics.BaseWorldScale * 4) + if (Globals.Database.WorldZoom > Graphics.MaximumWorldScale) { - Globals.Database.WorldZoom = Graphics.BaseWorldScale; + Globals.Database.WorldZoom = Graphics.MinimumWorldScale; } } diff --git a/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs b/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs index 72612c4e97..79c33731bc 100644 --- a/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs +++ b/Intersect.Client.Core/Interface/Shared/SettingsWindow.cs @@ -181,7 +181,7 @@ public SettingsWindow(Base parent, MainMenu? mainMenu, EscapeMenu? escapeMenu) : { Text = Strings.Settings.ShowManaAsPercentage }; - + // Game Settings - Interface: simplified escape menu. _simplifiedEscapeMenu = new LabeledCheckBox(_interfaceSettings, "SimplifiedEscapeMenu") { @@ -321,17 +321,11 @@ public SettingsWindow(Base parent, MainMenu? mainMenu, EscapeMenu? escapeMenu) : } ); - Globals.Database.WorldZoom = MathHelper.Clamp(Globals.Database.WorldZoom, 1, 4); - - var worldScaleNotches = new double[] { 1, 2, 4 }.Select(n => n * Graphics.BaseWorldScale).ToArray(); _worldScale = new LabeledHorizontalSlider(_videoSettingsContainer, "WorldScale") { + IsDisabled = !Options.IsLoaded, Label = Strings.Settings.WorldScale, - Min = worldScaleNotches.Min(), - Max = worldScaleNotches.Max(), - Notches = worldScaleNotches, SnapToNotches = false, - Value = Globals.Database.WorldZoom, }; // Video Settings - FPS Background. @@ -429,7 +423,7 @@ public SettingsWindow(Base parent, MainMenu? mainMenu, EscapeMenu? escapeMenu) : }; _keybindingRestoreBtn.Clicked += KeybindingsRestoreBtn_Clicked; - // Keybinding Settings - Controls + // Keybinding Settings - Controls var row = 0; var defaultFont = Current.GetFont("sourcesansproblack", 10); foreach (Control control in Enum.GetValues(typeof(Control))) @@ -493,6 +487,46 @@ public SettingsWindow(Base parent, MainMenu? mainMenu, EscapeMenu? escapeMenu) : IsHidden = true; } + protected override void OnVisibilityChanged(object? sender, VisibilityChangedEventArgs eventArgs) + { + base.OnVisibilityChanged(sender, eventArgs); + + if (eventArgs.IsVisible) + { + UpdateWorldScaleControls(); + } + } + + private void UpdateWorldScaleControls() + { + var worldScaleNotches = new double[] { 1, 2, 4 }.Select(n => n * Graphics.MinimumWorldScale).ToList(); + while (worldScaleNotches.Last() < Graphics.MaximumWorldScale) + { + worldScaleNotches.Add(worldScaleNotches.Last() * 2); + } + + if (Options.IsLoaded) + { + _worldScale.IsDisabled = false; + _worldScale.SetToolTipText(null); + + Globals.Database.WorldZoom = (float)MathHelper.Clamp( + Globals.Database.WorldZoom, + worldScaleNotches.Min(), + worldScaleNotches.Max() + ); + } + else + { + _worldScale.SetToolTipText(Strings.Settings.WorldScaleTooltip); + _worldScale.IsDisabled = true; + } + + _worldScale.SetRange(worldScaleNotches.Min(), worldScaleNotches.Max()); + _worldScale.Notches = worldScaleNotches.ToArray(); + _worldScale.Value = Globals.Database.WorldZoom; + } + private void GameSettingsTab_Clicked(Base sender, ClickedEventArgs arguments) { // Determine if GameSettingsContainer is currently being shown or not. @@ -642,16 +676,7 @@ private void LoadSettingsWindow() _settingsCancelBtn.Show(); _keybindingRestoreBtn.Hide(); - var worldScaleNotches = new double[] { 1, 2, 4 }.Select(n => n * Graphics.BaseWorldScale).ToArray(); - - Globals.Database.WorldZoom = (float)MathHelper.Clamp( - Globals.Database.WorldZoom, - worldScaleNotches.Min(), - worldScaleNotches.Max() - ); - _worldScale.Min = worldScaleNotches.Min(); - _worldScale.Max = worldScaleNotches.Max(); - _worldScale.Value = Globals.Database.WorldZoom; + UpdateWorldScaleControls(); } private readonly HashSet _keysDown = []; @@ -759,7 +784,6 @@ public void Show(bool returnToMenu = false) _lightingEnabledCheckbox.IsChecked = Globals.Database.EnableLighting; // _uiScale.Value = Globals.Database.UIScale; - _worldScale.Value = Globals.Database.WorldZoom; if (Graphics.Renderer?.GetValidVideoModes().Count > 0) { diff --git a/Intersect.Client.Core/Localization/Strings.cs b/Intersect.Client.Core/Localization/Strings.cs index c405c79fa9..4b032efd22 100644 --- a/Intersect.Client.Core/Localization/Strings.cs +++ b/Intersect.Client.Core/Localization/Strings.cs @@ -1991,6 +1991,10 @@ public partial struct Settings [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public static LocalizedString WorldScale = @"World Scale"; + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public static LocalizedString WorldScaleTooltip = + @"World Scale is only available after connecting to the server."; } public partial struct Parties diff --git a/Intersect.Client.Framework/Gwen/Control/LabeledHorizontalSlider.cs b/Intersect.Client.Framework/Gwen/Control/LabeledHorizontalSlider.cs index 7fd8f88f94..9aeeb2d693 100644 --- a/Intersect.Client.Framework/Gwen/Control/LabeledHorizontalSlider.cs +++ b/Intersect.Client.Framework/Gwen/Control/LabeledHorizontalSlider.cs @@ -126,11 +126,29 @@ public double Value } } + public override bool IsDisabled + { + get => base.IsDisabled; + set + { + base.IsDisabled = value; + _label.IsDisabled = value; + _slider.IsDisabled = value; + _sliderValue.IsDisabled = value; + _sliderBackground.IsDisabled = value; + } + } + /// /// Invoked when the value has been changed. /// public event GwenEventHandler ValueChanged; + public void SetRange(double min, double max) + { + _slider.SetRange(min, max); + } + public override JObject GetJson(bool isRoot = default) { var obj = base.GetJson(isRoot); @@ -142,4 +160,12 @@ public override void LoadJson(JToken obj, bool isRoot = default) { base.LoadJson(obj); } + + public override void SetToolTipText(string? text) + { + _label.SetToolTipText(text); + _slider.SetToolTipText(text); + _sliderValue.SetToolTipText(text); + _sliderBackground.SetToolTipText(text); + } } diff --git a/Intersect.Client.Framework/Gwen/Skin/TexturedBase.cs b/Intersect.Client.Framework/Gwen/Skin/TexturedBase.cs index 492166f2af..12adf61587 100644 --- a/Intersect.Client.Framework/Gwen/Skin/TexturedBase.cs +++ b/Intersect.Client.Framework/Gwen/Skin/TexturedBase.cs @@ -1641,7 +1641,7 @@ public override void DrawSlider(Control.Base control, bool horizontal, double[] else { var rect = control.RenderBounds; - Renderer.DrawColor = control.RenderColor; + Renderer.DrawColor = control.IsDisabled ? Colors.Button.Disabled : control.RenderColor; if (horizontal) {