From 726625109b6e170954f0c5dcb1debaf2b3274572 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Mon, 24 Feb 2025 01:18:02 -0300 Subject: [PATCH 01/19] clean up shop window/item --- .../Interface/Game/GameInterface.cs | 10 +- .../Interface/Game/Shop/ShopItem.cs | 202 ++++++++++-------- .../Interface/Game/Shop/ShopWindow.cs | 128 +++-------- Intersect.Client.Core/Localization/Strings.cs | 3 + 4 files changed, 146 insertions(+), 197 deletions(-) diff --git a/Intersect.Client.Core/Interface/Game/GameInterface.cs b/Intersect.Client.Core/Interface/Game/GameInterface.cs index 4698124c2c..93590696ff 100644 --- a/Intersect.Client.Core/Interface/Game/GameInterface.cs +++ b/Intersect.Client.Core/Interface/Game/GameInterface.cs @@ -209,9 +209,7 @@ public void NotifyCloseShop() public void OpenShop() { - mShopWindow?.Close(); - - mShopWindow = new ShopWindow(GameCanvas); + mShopWindow = new ShopWindow(GameCanvas) { DeleteOnClose = true }; mShouldOpenShop = false; } @@ -392,7 +390,7 @@ public void Update(TimeSpan elapsed, TimeSpan total) GameMenu.OpenInventory(); } - if (mShopWindow != null && (!mShopWindow.IsVisible() || mShouldCloseShop)) + if (mShopWindow != null && (mShopWindow.IsHidden || mShouldCloseShop)) { CloseShop(); } @@ -521,7 +519,7 @@ public void Draw(TimeSpan elapsed, TimeSpan total) private void CloseShop() { Globals.GameShop = null; - mShopWindow?.Close(); + mShopWindow?.Hide(); mShopWindow = null; PacketSender.SendCloseShop(); } @@ -585,7 +583,7 @@ public bool CloseAllWindows() closedWindows = true; } - if (mShopWindow != null && mShopWindow.IsVisible()) + if (mShopWindow != null && !mShopWindow.IsHidden) { CloseShop(); closedWindows = true; diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs index 5a98c2dee7..2d2404a5b7 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs @@ -1,5 +1,6 @@ -using Intersect.Client.Framework.GenericClasses; -using Intersect.Client.Framework.Graphics; +using Intersect.Client.Core; +using Intersect.Client.Framework.File_Management; +using Intersect.Client.Framework.Gwen; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Gwen.Control.EventArguments; using Intersect.Client.Framework.Gwen.Input; @@ -13,57 +14,100 @@ namespace Intersect.Client.Interface.Game.Shop; - -public partial class ShopItem +public partial class ShopItem : ImagePanel { + private readonly int _mySlot; + private readonly ShopWindow _shopWindow; + public ImagePanel _iconImage; + private readonly ContextMenu _contextMenu; + private readonly MenuItem _buyMenuItem; + private ItemDescriptionWindow? _itemDescWindow; + + public ShopItem(ShopWindow shopWindow, Base parent, int index) : base(parent, nameof(ShopItem)) + { + _shopWindow = shopWindow; + _mySlot = index; - public ImagePanel Container; - - private int mCurrentItem = -2; + _iconImage = new ImagePanel(this, "ShopItemIcon"); + _iconImage.HoverEnter += _iconImage_HoverEnter; + _iconImage.HoverLeave += _iconImage_HoverLeave; + _iconImage.Clicked += _iconImage_RightClicked; + _iconImage.DoubleClicked += _iconImage_DoubleClicked; - private ItemDescriptionWindow mDescWindow; + // Generate our context menu with basic options. + _contextMenu = new ContextMenu(Interface.CurrentInterface.Root, "ShopContextMenu") + { + IsHidden = true, + IconMarginDisabled = true + }; - private bool mIsEquipped; + LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); - //Mouse Event Variables - private bool mMouseOver; + //TODO: Is this a memory leak? + _contextMenu.ClearChildren(); + _buyMenuItem = _contextMenu.AddItem(Strings.ShopContextMenu.Buy); + _buyMenuItem.Clicked += _buyMenuItem_Clicked; + _contextMenu.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); - private int mMouseX = -1; + LoadItem(); + } - private int mMouseY = -1; + private void _iconImage_HoverEnter(Base sender, EventArgs arguments) + { + if (InputHandler.MouseFocus != null) + { + return; + } - //Slot info - private int mMySlot; + if (Globals.InputManager.IsMouseButtonDown(MouseButton.Left)) + { + return; + } - //Textures - private IGameRenderTexture mSfTex; + if (_itemDescWindow != default) + { + _itemDescWindow.Dispose(); + _itemDescWindow = default; + } - //Drag/Drop References - private ShopWindow mShopWindow; + if (Globals.GameShop == default || Globals.GameShop.SellingItems.Count == 0) + { + return; + } - public ImagePanel Pnl; + if (!ItemBase.TryGet(Globals.GameShop.SellingItems[_mySlot].CostItemId, out var item)) + { + return; + } - public ShopItem(ShopWindow shopWindow, int index) - { - mShopWindow = shopWindow; - mMySlot = index; - } + if (Globals.GameShop.SellingItems[_mySlot].Item != default) + { + ItemProperties itemProperty = new ItemProperties() + { + StatModifiers = item.StatsGiven, + }; - public void Setup() - { - Pnl = new ImagePanel(Container, "ShopItemIcon"); - Pnl.HoverEnter += pnl_HoverEnter; - Pnl.HoverLeave += pnl_HoverLeave; - Pnl.Clicked += Pnl_RightClicked; - Pnl.DoubleClicked += Pnl_DoubleClicked; + _itemDescWindow = new ItemDescriptionWindow( + item: Globals.GameShop.SellingItems[_mySlot].Item, + amount: 1, + x: _shopWindow.X, + y: _shopWindow.Y, + itemProperties: itemProperty, + valueLabel: Strings.Shop.Costs.ToString(Globals.GameShop.SellingItems[_mySlot].CostItemQuantity, item.Name) + ); + } } - private void Pnl_DoubleClicked(Base sender, MouseButtonState arguments) + private void _iconImage_HoverLeave(Base sender, EventArgs arguments) { - Globals.Me?.TryBuyItem(mMySlot); + if (_itemDescWindow != null) + { + _itemDescWindow.Dispose(); + _itemDescWindow = null; + } } - private void Pnl_RightClicked(Base sender, MouseButtonState arguments) + private void _iconImage_RightClicked(Base sender, MouseButtonState arguments) { if (arguments.MouseButton != MouseButton.Right) { @@ -72,90 +116,66 @@ private void Pnl_RightClicked(Base sender, MouseButtonState arguments) if (ClientConfiguration.Instance.EnableContextMenus) { - mShopWindow.OpenContextMenu(mMySlot); + OpenContextMenu(_mySlot); } else { - Pnl_DoubleClicked(sender, arguments); + _iconImage_DoubleClicked(sender, arguments); } } - public void LoadItem() + private void _iconImage_DoubleClicked(Base sender, MouseButtonState arguments) { - var item = ItemBase.Get(Globals.GameShop.SellingItems[mMySlot].ItemId); - if (item != null) - { - var itemTex = Globals.ContentManager.GetTexture(Framework.Content.TextureType.Item, item.Icon); - if (itemTex != null) - { - Pnl.Texture = itemTex; - Pnl.RenderColor = item.Color; - } - } + Globals.Me?.TryBuyItem(_mySlot); } - - - void pnl_HoverLeave(Base sender, EventArgs arguments) + private void _buyMenuItem_Clicked(Base sender, Framework.Gwen.Control.EventArguments.MouseButtonState arguments) { - mMouseOver = false; - mMouseX = -1; - mMouseY = -1; - if (mDescWindow != null) + if (sender.Parent?.UserData is not int slot) { - mDescWindow.Dispose(); - mDescWindow = null; + return; } + + Globals.Me?.TryBuyItem(slot); } - void pnl_HoverEnter(Base sender, EventArgs arguments) + protected override void Dispose(bool disposing) { - if (InputHandler.MouseFocus != null) - { - return; - } + _contextMenu?.Close(); + base.Dispose(disposing); + } - if (Globals.InputManager.IsMouseButtonDown(MouseButton.Left)) + public void OpenContextMenu(int slot) + { + if (Globals.GameShop?.SellingItems[slot] == default) { return; } - if (mDescWindow != null) + if (!ItemBase.TryGet(Globals.GameShop.SellingItems[slot].ItemId, out var item)) { - mDescWindow.Dispose(); - mDescWindow = null; + return; } - var item = ItemBase.Get(Globals.GameShop.SellingItems[mMySlot].CostItemId); - if (item != null && Globals.GameShop.SellingItems[mMySlot].Item != null) - { - ItemProperties itemProperty = new ItemProperties() - { - StatModifiers = item.StatsGiven, - }; + _buyMenuItem.SetText(Strings.ShopContextMenu.Buy.ToString(item.Name)); - mDescWindow = new ItemDescriptionWindow( - Globals.GameShop.SellingItems[mMySlot].Item, 1, mShopWindow.X, mShopWindow.Y, itemProperty, "", - Strings.Shop.Costs.ToString(Globals.GameShop.SellingItems[mMySlot].CostItemQuantity, item.Name) - ); - } + _contextMenu.UserData = slot; + _contextMenu.SizeToChildren(); + _contextMenu.Open(Pos.None); } - public FloatRect RenderBounds() + public void LoadItem() { - var rect = new FloatRect() + if (Globals.GameShop == default || !ItemBase.TryGet(Globals.GameShop.SellingItems[_mySlot].ItemId, out var item)) { - X = Pnl.ToCanvas(new Point(0, 0)).X, - Y = Pnl.ToCanvas(new Point(0, 0)).Y, - Width = Pnl.Width, - Height = Pnl.Height - }; - - return rect; - } + return; + } - public void Update() - { + var itemTex = Globals.ContentManager?.GetTexture(Framework.Content.TextureType.Item, item.Icon); + if (itemTex != null) + { + _iconImage.Texture = itemTex; + _iconImage.RenderColor = item.Color; + } } - } diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs index 29569ba478..c740541858 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs @@ -1,129 +1,57 @@ -using Intersect.Client.Core; +using Intersect.Client.Core; using Intersect.Client.Framework.File_Management; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.General; using Intersect.Client.Localization; -using Intersect.GameObjects; namespace Intersect.Client.Interface.Game.Shop; - -public partial class ShopWindow +public partial class ShopWindow : Window { + private readonly List _items = []; + private readonly ScrollControl _slotContainer; - private static int sItemXPadding = 4; - - private static int sItemYPadding = 4; - - public List Items = new List(); - - private ScrollControl mItemContainer; - - //Controls - private WindowControl mShopWindow; - - // Context menu - private Framework.Gwen.Control.Menu mContextMenu; - - private MenuItem mBuyContextItem; - - //Init - public ShopWindow(Canvas gameCanvas) + public ShopWindow(Canvas gameCanvas) : base(gameCanvas, Globals.GameShop?.Name ?? Strings.Shop.Title, false, nameof(ShopWindow)) { - mShopWindow = new WindowControl(gameCanvas, Globals.GameShop.Name, false, "ShopWindow"); - mShopWindow.DisableResizing(); - Interface.InputBlockingComponents.Add(mShopWindow); - - mItemContainer = new ScrollControl(mShopWindow, "ItemContainer"); - mItemContainer.EnableScroll(false, true); + DisableResizing(); + Interface.InputBlockingComponents.Add(this); - mShopWindow.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); + _slotContainer = new ScrollControl(this, "ItemContainer"); + _slotContainer.EnableScroll(false, true); + } + protected override void EnsureInitialized() + { + LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); InitItemContainer(); - - // Generate our context menu with basic options. - mContextMenu = new Framework.Gwen.Control.Menu(gameCanvas, "ShopContextMenu"); - mContextMenu.IsHidden = true; - mContextMenu.IconMarginDisabled = true; - //TODO: Is this a memory leak? - mContextMenu.ClearChildren(); - mBuyContextItem = mContextMenu.AddItem(Strings.ShopContextMenu.Buy); - mBuyContextItem.Clicked += MBuyContextItem_Clicked; - mContextMenu.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); } - public void OpenContextMenu(int slot) + private void InitItemContainer() { - var item = ItemBase.Get(Globals.GameShop.SellingItems[slot].ItemId); - - // No point showing a menu for blank space. - if (item == null) + if (Globals.GameShop == default || Globals.GameShop.SellingItems.Count == 0) { return; } - mBuyContextItem.SetText(Strings.ShopContextMenu.Buy.ToString(item.Name)); - - // Set our spell slot as userdata for future reference. - mContextMenu.UserData = slot; - - mContextMenu.SizeToChildren(); - mContextMenu.Open(Framework.Gwen.Pos.None); - } - - private void MBuyContextItem_Clicked(Base sender, Framework.Gwen.Control.EventArguments.MouseButtonState arguments) - { - var slot = (int) sender.Parent.UserData; - Globals.Me.TryBuyItem(slot); - } - - //Location - public int X => mShopWindow.X; - - public int Y => mShopWindow.Y; - - public void Close() - { - mContextMenu?.Close(); - mShopWindow.Close(); - } + for (var i = 0; i < Globals.GameShop.SellingItems.Count; i++) + { + _items.Add(new ShopItem(this, _slotContainer, i)); - public bool IsVisible() - { - return !mShopWindow.IsHidden; - } + var xPadding = _items[i].Margin.Left + _items[i].Margin.Right; + var yPadding = _items[i].Margin.Top + _items[i].Margin.Bottom; - public void Hide() - { - mShopWindow.IsHidden = true; - } + var itemWidthWithPadding = _items[i].Width + xPadding; + var itemHeightWithPadding = _items[i].Height + yPadding; - private void InitItemContainer() - { - for (var i = 0; i < Globals.GameShop.SellingItems.Count; i++) - { - Items.Add(new ShopItem(this, i)); - Items[i].Container = new ImagePanel(mItemContainer, "ShopItem"); - Items[i].Setup(); + var itemsPerRow = _slotContainer.Width / itemWidthWithPadding; - Items[i].Container.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); + var column = i % itemsPerRow; + var row = i / itemsPerRow; - Items[i].LoadItem(); + var xPosition = column * itemWidthWithPadding + xPadding; + var yPosition = row * itemHeightWithPadding + yPadding; - var xPadding = Items[i].Container.Margin.Left + Items[i].Container.Margin.Right; - var yPadding = Items[i].Container.Margin.Top + Items[i].Container.Margin.Bottom; - Items[i] - .Container.SetPosition( - i % - (mItemContainer.Width / (Items[i].Container.Width + xPadding)) * - (Items[i].Container.Width + xPadding) + - xPadding, - i / - (mItemContainer.Width / (Items[i].Container.Width + xPadding)) * - (Items[i].Container.Height + yPadding) + - yPadding - ); + _items[i].SetPosition(xPosition, yPosition); } } - } diff --git a/Intersect.Client.Core/Localization/Strings.cs b/Intersect.Client.Core/Localization/Strings.cs index 51c8021bf1..149850602f 100644 --- a/Intersect.Client.Core/Localization/Strings.cs +++ b/Intersect.Client.Core/Localization/Strings.cs @@ -2456,6 +2456,9 @@ public partial struct Server public partial struct Shop { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public static LocalizedString Title = @"Shop"; + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public static LocalizedString BuyItem = @"Buy Item"; From c6850108b42acde21738eab67dbcc524d3ab0f45 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Tue, 25 Feb 2025 18:33:34 -0300 Subject: [PATCH 02/19] update shop window to the new pattern --- .../Interface/Game/Shop/ShopWindow.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs index c740541858..6205946595 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs @@ -1,5 +1,6 @@ using Intersect.Client.Core; using Intersect.Client.Framework.File_Management; +using Intersect.Client.Framework.Gwen; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.General; using Intersect.Client.Localization; @@ -16,8 +17,20 @@ public ShopWindow(Canvas gameCanvas) : base(gameCanvas, Globals.GameShop?.Name ? DisableResizing(); Interface.InputBlockingComponents.Add(this); - _slotContainer = new ScrollControl(this, "ItemContainer"); - _slotContainer.EnableScroll(false, true); + Alignment = [Alignments.Center]; + MinimumSize = new Point(x: 442, y: 469); + IsResizable = false; + IsClosable = true; + + TitleLabel.FontSize = 14; + TitleLabel.TextColorOverride = Color.White; + + _slotContainer = new ScrollControl(this, "ItemContainer") + { + Dock = Pos.Fill, + OverflowX = OverflowBehavior.Auto, + OverflowY = OverflowBehavior.Scroll, + }; } protected override void EnsureInitialized() From 8c4930e9bf94a85d4403d13e7d362faab2707e2d Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 01:16:27 -0300 Subject: [PATCH 03/19] first load min/max size before validate bounds --- .../Gwen/Control/Base.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/Control/Base.cs b/Intersect.Client.Framework/Gwen/Control/Base.cs index 7ddd25e063..1c01ff0b98 100644 --- a/Intersect.Client.Framework/Gwen/Control/Base.cs +++ b/Intersect.Client.Framework/Gwen/Control/Base.cs @@ -1586,6 +1586,16 @@ public virtual void LoadJson(JToken token, bool isRoot = default) } } + if (obj[nameof(MinimumSize)] != null) + { + MinimumSize = Point.FromString((string)obj[nameof(MinimumSize)]); + } + + if (obj[nameof(MaximumSize)] != null) + { + MaximumSize = Point.FromString((string)obj[nameof(MaximumSize)]); + } + if (obj[nameof(Bounds)] != null) { _boundsOnDisk = Rectangle.FromString((string)obj[nameof(Bounds)]); @@ -1635,16 +1645,6 @@ public virtual void LoadJson(JToken token, bool isRoot = default) ShouldDrawBackground = (bool) obj["DrawBackground"]; } - if (obj[nameof(MinimumSize)] != null) - { - MinimumSize = Point.FromString((string) obj[nameof(MinimumSize)]); - } - - if (obj[nameof(MaximumSize)] != null) - { - MaximumSize = Point.FromString((string) obj[nameof(MaximumSize)]); - } - if (obj["Disabled"] != null) { IsDisabled = (bool) obj["Disabled"]; From 635692b870db13ba73e612fee2c040b82c078a6a Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 01:17:02 -0300 Subject: [PATCH 04/19] explicit hover sound to hard code images hover sound value --- Intersect.Client.Framework/Gwen/Control/ImagePanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs index 70e9c8eafe..e81570360f 100644 --- a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs +++ b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs @@ -19,7 +19,7 @@ public partial class ImagePanel : Base private readonly float[] _uv; //Sound Effects - protected string mHoverSound; + public string HoverSound; protected string mLeftMouseClickSound; @@ -145,7 +145,7 @@ public string? TextureFilename protected override void OnMouseEntered() { base.OnMouseEntered(); - PlaySound(mHoverSound); + PlaySound(HoverSound); } protected override void OnMouseClicked(MouseButton mouseButton, Point mousePosition, bool userAction = true) @@ -181,7 +181,7 @@ protected override void Dispose(bool disposing) serializedProperties.Add(nameof(Texture), TextureFilename); serializedProperties.Add(nameof(TextureNinePatchMargin), TextureNinePatchMargin?.ToString()); - serializedProperties.Add("HoverSound", mHoverSound); + serializedProperties.Add("HoverSound", HoverSound); serializedProperties.Add("LeftMouseClickSound", mLeftMouseClickSound); serializedProperties.Add("RightMouseClickSound", mRightMouseClickSound); @@ -228,7 +228,7 @@ public override void LoadJson(JToken token, bool isRoot = default) if (obj["HoverSound"] != null) { - mHoverSound = (string) obj["HoverSound"]; + HoverSound = (string) obj["HoverSound"]; } if (obj["LeftMouseClickSound"] != null) From ea9a99357046b32e42a99b4d5ee674a2ecf7e88d Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 01:17:32 -0300 Subject: [PATCH 05/19] add set item font to hardcode default font value for context menu --- Intersect.Client.Framework/Gwen/Control/Menu.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Intersect.Client.Framework/Gwen/Control/Menu.cs b/Intersect.Client.Framework/Gwen/Control/Menu.cs index 9728acd09f..c3bee01fad 100644 --- a/Intersect.Client.Framework/Gwen/Control/Menu.cs +++ b/Intersect.Client.Framework/Gwen/Control/Menu.cs @@ -413,4 +413,12 @@ public void SetBackgroundTemplate(IGameTexture texture, string fileName) mBackgroundTemplateTex = texture; } + public void SetItemFont(IFont font, int fontSize) + { + mItemFont = font; + _itemFontSize = fontSize; + mItemFontInfo = $"{font.Name},{fontSize}"; + UpdateItemStyles(); + } + } From 0fa222190a952ec3d3eb2faa81c2e03bf4a5e610 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 01:18:07 -0300 Subject: [PATCH 06/19] add _stateSoundNames to scrollbar bar --- .../Gwen/ControlInternal/ScrollBarBar.cs | 129 +++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs b/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs index 7b8192ceb1..969d0d1657 100644 --- a/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs +++ b/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs @@ -1,18 +1,21 @@ -using Intersect.Client.Framework.GenericClasses; +using Intersect.Client.Framework.GenericClasses; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Input; +using Newtonsoft.Json.Linq; namespace Intersect.Client.Framework.Gwen.ControlInternal; - /// /// Scrollbar bar. /// public partial class ScrollBarBar : Dragger { - private bool mHorizontal; + private readonly Dictionary _stateSoundNames = []; + + private DateTime _ignoreMouseUpSoundsUntil; + /// /// Initializes a new instance of the class. /// @@ -21,6 +24,10 @@ public ScrollBarBar(Base parent) : base(parent) { RestrictToParent = true; Target = this; + + SetSound(ButtonSoundState.Hover, "octave-tap-resonant.wav"); + SetSound(ButtonSoundState.MouseDown, "octave-tap-warm.wav"); + SetSound(ButtonSoundState.MouseUp, "octave-tap-warm.wav"); } /// @@ -69,6 +76,24 @@ protected override void OnMouseMoved(int x, int y, int dx, int dy) InvalidateParent(); } + protected override void OnMouseEntered() + { + base.OnMouseEntered(); + PlaySound(ButtonSoundState.Hover); + } + + protected override void OnMouseDown(MouseButton mouseButton, Point mousePosition, bool userAction = true) + { + base.OnMouseDown(mouseButton, mousePosition, userAction); + PlaySound(ButtonSoundState.MouseDown); + } + + protected override void OnMouseUp(MouseButton mouseButton, Point mousePosition, bool userAction = true) + { + base.OnMouseUp(mouseButton, mousePosition, userAction); + PlaySound(ButtonSoundState.MouseUp); + } + protected override void OnMouseClicked(MouseButton mouseButton, Point mousePosition, bool userAction = true) { base.OnMouseClicked(mouseButton, mousePosition, userAction); @@ -77,6 +102,34 @@ protected override void OnMouseClicked(MouseButton mouseButton, Point mousePosit { InvalidateParent(); } + + PlaySound(ButtonSoundState.MouseClicked); + } + + public void PlaySound(ButtonSoundState soundState) + { + if (soundState == ButtonSoundState.MouseUp) + { + if (_ignoreMouseUpSoundsUntil > DateTime.UtcNow) + { + return; + } + } + + if (!_stateSoundNames.TryGetValue(soundState, out var soundName)) + { + return; + } + + if (!base.PlaySound(soundName)) + { + return; + } + + if (soundState == ButtonSoundState.MouseDown) + { + _ignoreMouseUpSoundsUntil = DateTime.UtcNow.AddMilliseconds(200); + } } /// @@ -100,4 +153,74 @@ protected override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds InvalidateParent(); } + + public void SetSound(ButtonSoundState state, string? soundName) + { + soundName = soundName?.Trim(); + if (string.IsNullOrEmpty(soundName)) + { + _stateSoundNames.Remove(state); + } + else + { + _stateSoundNames[state] = soundName; + } + } + + public override JObject? GetJson(bool isRoot = false, bool onlySerializeIfNotEmpty = false) + { + var serializedProperties = base.GetJson(isRoot, onlySerializeIfNotEmpty); + if (serializedProperties is null) + { + return null; + } + + serializedProperties.Add(nameof(_stateSoundNames), JObject.FromObject(_stateSoundNames)); + return FixJson(serializedProperties); + } + + public override JObject FixJson(JObject json) + { + json.Remove("HoverSound"); + json.Remove("MouseUpSound"); + json.Remove("MouseDownSound"); + return base.FixJson(json); + } + + public override void LoadJson(JToken token, bool isRoot = default) + { + base.LoadJson(token, isRoot); + + if (token is not JObject obj) + { + return; + } + + if (obj.TryGetValue(nameof(_stateSoundNames), out var tokenStateSoundNames) && tokenStateSoundNames is JObject valueStateSoundNames) + { + foreach (var (propertyName, propertyValueToken) in valueStateSoundNames) + { + if (!Enum.TryParse(propertyName, out ButtonSoundState buttonSoundState) || + buttonSoundState == ButtonSoundState.None) + { + continue; + } + + if (propertyValueToken is not JValue { Type: JTokenType.String } valuePropertyValue) + { + continue; + } + + var stringPropertyValue = valuePropertyValue.Value()?.Trim(); + if (stringPropertyValue is { Length: > 0 }) + { + _stateSoundNames[buttonSoundState] = stringPropertyValue; + } + else + { + _stateSoundNames.Remove(buttonSoundState); + } + } + } + } } From 79f53e4cebf0406f4d76e181a4e6a5f05c16bef6 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 01:19:09 -0300 Subject: [PATCH 07/19] generating default shop item --- .../Interface/Game/Shop/ShopItem.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs index 2d2404a5b7..abe397fa7f 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs @@ -28,20 +28,37 @@ public ShopItem(ShopWindow shopWindow, Base parent, int index) : base(parent, na _shopWindow = shopWindow; _mySlot = index; - _iconImage = new ImagePanel(this, "ShopItemIcon"); + MinimumSize = new Point(34, 35); + Margin = new Margin(4, 4, 4, 4); + MouseInputEnabled = true; + TextureFilename = "shopitem.png"; + + _iconImage = new ImagePanel(this, "ShopItemIcon") + { + MinimumSize = new Point(32, 32), + MouseInputEnabled = true, + Alignment = [Alignments.Center], + HoverSound = "octave-tap-resonant.wav", + }; _iconImage.HoverEnter += _iconImage_HoverEnter; _iconImage.HoverLeave += _iconImage_HoverLeave; _iconImage.Clicked += _iconImage_RightClicked; _iconImage.DoubleClicked += _iconImage_DoubleClicked; + LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); + // Generate our context menu with basic options. _contextMenu = new ContextMenu(Interface.CurrentInterface.Root, "ShopContextMenu") { IsHidden = true, - IconMarginDisabled = true + IconMarginDisabled = true, }; - LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); + var font = GameContentManager.Current.GetFont(name: "sourcesansproblack"); + if (font != default) + { + _contextMenu.SetItemFont(font, 10); + } //TODO: Is this a memory leak? _contextMenu.ClearChildren(); From 6366edc6b4fc281ae98bb7d2de628bb1d1601b11 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 11:47:54 -0300 Subject: [PATCH 08/19] more refactor and cleaning --- .../Interface/Game/Shop/ShopItem.cs | 32 +++-- .../Interface/Game/Shop/ShopWindow.cs | 29 ++-- .../Gwen/Control/Menu.cs | 130 +++++++++++++++--- 3 files changed, 142 insertions(+), 49 deletions(-) diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs index abe397fa7f..23d00faa33 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs @@ -28,7 +28,7 @@ public ShopItem(ShopWindow shopWindow, Base parent, int index) : base(parent, na _shopWindow = shopWindow; _mySlot = index; - MinimumSize = new Point(34, 35); + MinimumSize = new Point(34, 34); Margin = new Margin(4, 4, 4, 4); MouseInputEnabled = true; TextureFilename = "shopitem.png"; @@ -48,18 +48,15 @@ public ShopItem(ShopWindow shopWindow, Base parent, int index) : base(parent, na LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); // Generate our context menu with basic options. + // TODO: Refactor so shop only has 1 context menu shared between all items _contextMenu = new ContextMenu(Interface.CurrentInterface.Root, "ShopContextMenu") { IsHidden = true, IconMarginDisabled = true, + ItemFont = GameContentManager.Current.GetFont(name: "sourcesansproblack"), + ItemFontSize = 10, }; - var font = GameContentManager.Current.GetFont(name: "sourcesansproblack"); - if (font != default) - { - _contextMenu.SetItemFont(font, 10); - } - //TODO: Is this a memory leak? _contextMenu.ClearChildren(); _buyMenuItem = _contextMenu.AddItem(Strings.ShopContextMenu.Buy); @@ -87,17 +84,17 @@ private void _iconImage_HoverEnter(Base sender, EventArgs arguments) _itemDescWindow = default; } - if (Globals.GameShop == default || Globals.GameShop.SellingItems.Count == 0) + if (Globals.GameShop is not { SellingItems.Count: > 0 } gameShop) { return; } - if (!ItemBase.TryGet(Globals.GameShop.SellingItems[_mySlot].CostItemId, out var item)) + if (!ItemBase.TryGet(gameShop.SellingItems[_mySlot].CostItemId, out var item)) { return; } - if (Globals.GameShop.SellingItems[_mySlot].Item != default) + if (gameShop.SellingItems[_mySlot].Item != default) { ItemProperties itemProperty = new ItemProperties() { @@ -105,12 +102,12 @@ private void _iconImage_HoverEnter(Base sender, EventArgs arguments) }; _itemDescWindow = new ItemDescriptionWindow( - item: Globals.GameShop.SellingItems[_mySlot].Item, + item: gameShop.SellingItems[_mySlot].Item, amount: 1, x: _shopWindow.X, y: _shopWindow.Y, itemProperties: itemProperty, - valueLabel: Strings.Shop.Costs.ToString(Globals.GameShop.SellingItems[_mySlot].CostItemQuantity, item.Name) + valueLabel: Strings.Shop.Costs.ToString(gameShop.SellingItems[_mySlot].CostItemQuantity, item.Name) ); } } @@ -183,16 +180,21 @@ public void OpenContextMenu(int slot) public void LoadItem() { - if (Globals.GameShop == default || !ItemBase.TryGet(Globals.GameShop.SellingItems[_mySlot].ItemId, out var item)) + if (Globals.GameShop is not { SellingItems.Count: > 0 } gameShop) + { + return; + } + + if (!ItemBase.TryGet(gameShop.SellingItems[_mySlot].ItemId, out var itemDescriptor)) { return; } - var itemTex = Globals.ContentManager?.GetTexture(Framework.Content.TextureType.Item, item.Icon); + var itemTex = Globals.ContentManager?.GetTexture(Framework.Content.TextureType.Item, itemDescriptor.Icon); if (itemTex != null) { _iconImage.Texture = itemTex; - _iconImage.RenderColor = item.Color; + _iconImage.RenderColor = itemDescriptor.Color; } } } diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs index 6205946595..0a863b2e3c 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopWindow.cs @@ -18,7 +18,7 @@ public ShopWindow(Canvas gameCanvas) : base(gameCanvas, Globals.GameShop?.Name ? Interface.InputBlockingComponents.Add(this); Alignment = [Alignments.Center]; - MinimumSize = new Point(x: 442, y: 469); + MinimumSize = new Point(x: 435, y: 469); IsResizable = false; IsClosable = true; @@ -41,30 +41,27 @@ protected override void EnsureInitialized() private void InitItemContainer() { - if (Globals.GameShop == default || Globals.GameShop.SellingItems.Count == 0) + if (Globals.GameShop is not { SellingItems.Count: > 0 } gameShop) { return; } - for (var i = 0; i < Globals.GameShop.SellingItems.Count; i++) + float containerInnerWidth = _slotContainer.InnerPanel.InnerWidth; + for (var slotIndex = 0; slotIndex < gameShop.SellingItems.Count; slotIndex++) { - _items.Add(new ShopItem(this, _slotContainer, i)); + var slotContainer = new ShopItem(this, _slotContainer, slotIndex); + _items.Add(slotContainer); - var xPadding = _items[i].Margin.Left + _items[i].Margin.Right; - var yPadding = _items[i].Margin.Top + _items[i].Margin.Bottom; + var outerSize = slotContainer.OuterBounds.Size; + var itemsPerRow = (int)(containerInnerWidth / outerSize.X); - var itemWidthWithPadding = _items[i].Width + xPadding; - var itemHeightWithPadding = _items[i].Height + yPadding; + var column = slotIndex % itemsPerRow; + var row = slotIndex / itemsPerRow; - var itemsPerRow = _slotContainer.Width / itemWidthWithPadding; + var xPosition = column * outerSize.X + slotContainer.Margin.Left; + var yPosition = row * outerSize.Y + slotContainer.Margin.Top; - var column = i % itemsPerRow; - var row = i / itemsPerRow; - - var xPosition = column * itemWidthWithPadding + xPadding; - var yPosition = row * itemHeightWithPadding + yPadding; - - _items[i].SetPosition(xPosition, yPosition); + slotContainer.SetPosition(xPosition, yPosition); } } } diff --git a/Intersect.Client.Framework/Gwen/Control/Menu.cs b/Intersect.Client.Framework/Gwen/Control/Menu.cs index c3bee01fad..a774a3d25b 100644 --- a/Intersect.Client.Framework/Gwen/Control/Menu.cs +++ b/Intersect.Client.Framework/Gwen/Control/Menu.cs @@ -1,4 +1,3 @@ -using System.Collections; using Intersect.Client.Framework.File_Management; using Intersect.Client.Framework.Graphics; using Intersect.Client.Framework.Gwen.ControlInternal; @@ -20,12 +19,7 @@ public partial class Menu : ScrollControl private bool mDisableIconMargin; - private IFont? mItemFont; - private int _itemFontSize; - //Menu Item Stuff - private string mItemFontInfo; - protected Color mItemHoverTextColor; protected Color mItemNormalTextColor; @@ -46,6 +40,66 @@ public Menu(Base parent, string? name = default) : base(parent, name) DeleteOnClose = false; } + #region Font Handling + + private IFont? _itemFont; + + public IFont? ItemFont + { + get => _itemFont; + set => SetItemFont(value, value?.Name); + } + + private string? _itemFontName; + + public string? ItemFontName + { + get => _itemFontName; + set => SetItemFont(GameContentManager.Current.GetFont(_itemFontName), _itemFontName); + } + + private int _itemFontSize; + + public int ItemFontSize + { + get => _itemFontSize; + set + { + if (value == _itemFontSize) + { + return; + } + + var oldValue = _itemFontSize; + _itemFontSize = value; + OnFontSizeChanged(this, value, oldValue); + } + } + + private void SetItemFont(IFont? itemFont, string? itemFontName) + { + var oldValue = _itemFont; + _itemFont = itemFont; + _itemFontName = itemFontName; + + if (itemFont != oldValue) + { + OnFontChanged(this, itemFont, oldValue); + } + } + + protected virtual void OnFontChanged(Base sender, IFont? newFont, IFont? oldFont) + { + UpdateItemStyles(); + } + + protected virtual void OnFontSizeChanged(Base sender, int newSize, int oldSize) + { + UpdateItemStyles(); + } + + #endregion + internal override bool IsMenuComponent => true; public bool IconMarginDisabled @@ -343,14 +397,21 @@ public override bool SizeToChildren(SizeToChildrenArgs args) serializedProperties.Add("BackgroundTemplate", mBackgroundTemplateFilename); serializedProperties.Add("ItemTextColor", Color.ToString(mItemNormalTextColor)); serializedProperties.Add("ItemHoveredTextColor", Color.ToString(mItemHoverTextColor)); - serializedProperties.Add("ItemFont", mItemFontInfo); + serializedProperties.Add(nameof(ItemFontName), ItemFontName); + serializedProperties.Add(nameof(ItemFontSize), ItemFontSize); return base.FixJson(serializedProperties); } - public override void LoadJson(JToken obj, bool isRoot = default) + public override void LoadJson(JToken token, bool isRoot = default) { - base.LoadJson(obj); + base.LoadJson(token, isRoot); + + if (token is not JObject obj) + { + return; + } + if (obj["BackgroundTemplate"] != null) { SetBackgroundTemplate( @@ -370,12 +431,46 @@ public override void LoadJson(JToken obj, bool isRoot = default) mItemHoverTextColor = Color.FromString((string)obj["ItemHoveredTextColor"]); } - if (obj["ItemFont"] != null && obj["ItemFont"].Type != JTokenType.Null) + string? itemFontName = null; + int? itemFontSize = null; + + if (obj.TryGetValue(nameof(ItemFont), out var tokenItemFont) && + tokenItemFont is JValue { Type: JTokenType.String } valueItemFont) + { + var stringItemFont = valueItemFont.Value()?.Trim(); + if (!string.IsNullOrWhiteSpace(stringItemFont)) + { + var parts = stringItemFont.Split(','); + itemFontName = parts.FirstOrDefault(); + + if (parts.Length > 1 && int.TryParse(parts[1], out var size)) + { + itemFontSize = size; + } + } + } + + if (obj.TryGetValue(nameof(ItemFontName), out var tokenItemFontName) && + tokenItemFontName is JValue { Type: JTokenType.String } valueItemFontName) + { + itemFontName = valueItemFontName.Value(); + } + + if (obj.TryGetValue(nameof(ItemFontSize), out var tokenItemFontSize) && + tokenItemFontSize is JValue { Type: JTokenType.Integer } valueItemFontSize) + { + itemFontSize = valueItemFontSize.Value(); + } + + if (itemFontSize.HasValue) + { + ItemFontSize = itemFontSize.Value; + } + + itemFontName = itemFontName?.Trim(); + if (!string.IsNullOrWhiteSpace(itemFontName)) { - var fontArr = ((string)obj["ItemFont"]).Split(','); - mItemFontInfo = (string)obj["ItemFont"]; - _itemFontSize = int.Parse(fontArr[1]); - mItemFont = GameContentManager.Current.GetFont(fontArr[0]); + ItemFontName = itemFontName; } UpdateItemStyles(); @@ -386,9 +481,9 @@ private void UpdateItemStyles() var menuItems = Children.OfType().ToArray(); foreach (var item in menuItems) { - if (mItemFont != null) + if (_itemFont != null) { - item.Font = mItemFont; + item.Font = _itemFont; } item.FontSize = _itemFontSize; @@ -415,9 +510,8 @@ public void SetBackgroundTemplate(IGameTexture texture, string fileName) public void SetItemFont(IFont font, int fontSize) { - mItemFont = font; + _itemFont = font; _itemFontSize = fontSize; - mItemFontInfo = $"{font.Name},{fontSize}"; UpdateItemStyles(); } From 0f7a46042c8c4b4d17ce8d48d7e8592ed374aa19 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:14:34 -0300 Subject: [PATCH 09/19] cleanup image panel sounds handling --- .../Gwen/Control/ImagePanel.cs | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs index e81570360f..297d78dcdb 100644 --- a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs +++ b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs @@ -2,7 +2,6 @@ using Intersect.Client.Framework.File_Management; using Intersect.Client.Framework.GenericClasses; using Intersect.Client.Framework.Graphics; -using Intersect.Client.Framework.Gwen.ControlInternal; using Intersect.Client.Framework.Gwen.Skin.Texturing; using Intersect.Client.Framework.Input; using Newtonsoft.Json.Linq; @@ -19,11 +18,11 @@ public partial class ImagePanel : Base private readonly float[] _uv; //Sound Effects - public string HoverSound; + public string? HoverSound { get; set; } - protected string mLeftMouseClickSound; + protected string? LeftMouseClickSound { get; set; } - protected string mRightMouseClickSound; + protected string? RightMouseClickSound { get; set; } private IGameTexture? _texture { get; set; } private string? _textureName; @@ -154,8 +153,8 @@ protected override void OnMouseClicked(MouseButton mouseButton, Point mousePosit PlaySound( mouseButton switch { - MouseButton.Left => mLeftMouseClickSound, - MouseButton.Right => mRightMouseClickSound, + MouseButton.Left => LeftMouseClickSound, + MouseButton.Right => RightMouseClickSound, _ => null, } ); @@ -181,9 +180,9 @@ protected override void Dispose(bool disposing) serializedProperties.Add(nameof(Texture), TextureFilename); serializedProperties.Add(nameof(TextureNinePatchMargin), TextureNinePatchMargin?.ToString()); - serializedProperties.Add("HoverSound", HoverSound); - serializedProperties.Add("LeftMouseClickSound", mLeftMouseClickSound); - serializedProperties.Add("RightMouseClickSound", mRightMouseClickSound); + serializedProperties.Add(nameof(HoverSound), HoverSound); + serializedProperties.Add(nameof(LeftMouseClickSound), LeftMouseClickSound); + serializedProperties.Add(nameof(RightMouseClickSound), RightMouseClickSound); return base.FixJson(serializedProperties); } @@ -226,19 +225,22 @@ public override void LoadJson(JToken token, bool isRoot = default) } } - if (obj["HoverSound"] != null) + if (obj[nameof(HoverSound)] is JValue { Type: JTokenType.String } hoverSound && + hoverSound.Value()?.Trim() is { Length: > 0 }) { - HoverSound = (string) obj["HoverSound"]; + HoverSound = hoverSound?.Value(); } - if (obj["LeftMouseClickSound"] != null) + if (obj[nameof(LeftMouseClickSound)] is JValue { Type: JTokenType.String } leftMouseClickSound && + leftMouseClickSound.Value()?.Trim() is { Length: > 0 }) { - mLeftMouseClickSound = (string) obj["LeftMouseClickSound"]; + LeftMouseClickSound = leftMouseClickSound?.Value(); } - if (obj["RightMouseClickSound"] != null) + if (obj[nameof(RightMouseClickSound)] is JValue { Type: JTokenType.String } rightMouseClickSound && + rightMouseClickSound.Value()?.Trim() is { Length: > 0 }) { - mRightMouseClickSound = (string) obj["RightMouseClickSound"]; + RightMouseClickSound = rightMouseClickSound?.Value(); } } From 299315b41e050dce89cadfa67298d380c228b260 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:15:55 -0300 Subject: [PATCH 10/19] using isVisibleInTree/Parent instead IsHidden --- Intersect.Client.Core/Interface/Game/GameInterface.cs | 4 ++-- Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Intersect.Client.Core/Interface/Game/GameInterface.cs b/Intersect.Client.Core/Interface/Game/GameInterface.cs index 93590696ff..52848e5d3d 100644 --- a/Intersect.Client.Core/Interface/Game/GameInterface.cs +++ b/Intersect.Client.Core/Interface/Game/GameInterface.cs @@ -390,7 +390,7 @@ public void Update(TimeSpan elapsed, TimeSpan total) GameMenu.OpenInventory(); } - if (mShopWindow != null && (mShopWindow.IsHidden || mShouldCloseShop)) + if (mShopWindow != null && (!mShopWindow.IsVisibleInTree || mShouldCloseShop)) { CloseShop(); } @@ -583,7 +583,7 @@ public bool CloseAllWindows() closedWindows = true; } - if (mShopWindow != null && !mShopWindow.IsHidden) + if (mShopWindow is { IsVisibleInTree: false }) { CloseShop(); closedWindows = true; diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs index 23d00faa33..91aad83ddd 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs @@ -51,7 +51,7 @@ public ShopItem(ShopWindow shopWindow, Base parent, int index) : base(parent, na // TODO: Refactor so shop only has 1 context menu shared between all items _contextMenu = new ContextMenu(Interface.CurrentInterface.Root, "ShopContextMenu") { - IsHidden = true, + IsVisibleInParent = false, IconMarginDisabled = true, ItemFont = GameContentManager.Current.GetFont(name: "sourcesansproblack"), ItemFontSize = 10, From 82733f4ad1a527067fcd95db9628e28b6b79586a Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:16:22 -0300 Subject: [PATCH 11/19] remove redundant info to context menu --- .../Interface/Game/Shop/ShopItem.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs index 91aad83ddd..665a7ede87 100644 --- a/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs +++ b/Intersect.Client.Core/Interface/Game/Shop/ShopItem.cs @@ -145,12 +145,7 @@ private void _iconImage_DoubleClicked(Base sender, MouseButtonState arguments) private void _buyMenuItem_Clicked(Base sender, Framework.Gwen.Control.EventArguments.MouseButtonState arguments) { - if (sender.Parent?.UserData is not int slot) - { - return; - } - - Globals.Me?.TryBuyItem(slot); + Globals.Me?.TryBuyItem(_mySlot); } protected override void Dispose(bool disposing) @@ -161,20 +156,17 @@ protected override void Dispose(bool disposing) public void OpenContextMenu(int slot) { - if (Globals.GameShop?.SellingItems[slot] == default) + if (Globals.GameShop is not { SellingItems.Count: > 0 } gameShop) { return; } - if (!ItemBase.TryGet(Globals.GameShop.SellingItems[slot].ItemId, out var item)) + if (!ItemBase.TryGet(gameShop.SellingItems[slot].ItemId, out var item)) { return; } _buyMenuItem.SetText(Strings.ShopContextMenu.Buy.ToString(item.Name)); - - _contextMenu.UserData = slot; - _contextMenu.SizeToChildren(); _contextMenu.Open(Pos.None); } From 02348b091cd0415184b03c6de30f5831905a6690 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:18:31 -0300 Subject: [PATCH 12/19] transfer sound logic from child to parent --- .../Gwen/ControlInternal/Dragger.cs | 111 +++++++++++---- .../Gwen/ControlInternal/ScrollBarBar.cs | 131 ------------------ 2 files changed, 81 insertions(+), 161 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs index 383d18fadd..590f73c942 100644 --- a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs +++ b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs @@ -1,4 +1,4 @@ -using Intersect.Client.Framework.File_Management; +using Intersect.Client.Framework.File_Management; using Intersect.Client.Framework.GenericClasses; using Intersect.Client.Framework.Graphics; using Intersect.Client.Framework.Gwen.Control; @@ -17,9 +17,8 @@ public partial class Dragger : Base protected Point mHoldPos; //Sound Effects - private string? mHoverSound; - private string? mMouseDownSound; - private string? mMouseUpSound; + protected readonly Dictionary _stateSoundNames = []; + protected DateTime _ignoreMouseUpSoundsUntil; private IGameTexture? mClickedImage; private string? mClickedImageFilename; @@ -54,6 +53,16 @@ internal Base Target /// public event GwenEventHandler? Dragged; + protected override void OnMouseEntered() + { + base.OnMouseEntered(); + + if (ShouldDrawHover) + { + PlaySound(_stateSoundNames[ButtonSoundState.Hover]); + } + } + protected override void OnMouseDown(MouseButton mouseButton, Point mousePosition, bool userAction = true) { base.OnMouseDown(mouseButton, mousePosition, userAction); @@ -67,7 +76,7 @@ protected override void OnMouseDown(MouseButton mouseButton, Point mousePosition if (userAction) { - base.PlaySound(mMouseDownSound); + PlaySound(_stateSoundNames[ButtonSoundState.MouseDown]); } mHoldPos = _target.CanvasPosToLocal(mousePosition); @@ -80,7 +89,7 @@ protected override void OnMouseUp(MouseButton mouseButton, Point mousePosition, if (userAction) { - base.PlaySound(mMouseUpSound); + PlaySound(_stateSoundNames[ButtonSoundState.MouseUp]); } InputHandler.MouseFocus = null; @@ -169,16 +178,20 @@ protected override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds serializedProperties.Add("HoveredImage", GetImageFilename(ComponentState.Hovered)); serializedProperties.Add("ClickedImage", GetImageFilename(ComponentState.Active)); serializedProperties.Add("DisabledImage", GetImageFilename(ComponentState.Disabled)); - serializedProperties.Add("HoverSound", mHoverSound); - serializedProperties.Add("MouseUpSound", mMouseUpSound); - serializedProperties.Add("MouseDownSound", mMouseDownSound); + serializedProperties.Add(nameof(_stateSoundNames), JObject.FromObject(_stateSoundNames)); return base.FixJson(serializedProperties); } - public override void LoadJson(JToken obj, bool isRoot = default) + public override void LoadJson(JToken token, bool isRoot = default) { - base.LoadJson(obj); + base.LoadJson(token, isRoot); + + if (token is not JObject obj) + { + return; + } + if (obj["NormalImage"] != null) { SetImage( @@ -215,25 +228,50 @@ public override void LoadJson(JToken obj, bool isRoot = default) ); } - if (obj["HoverSound"] != null) + if (obj.TryGetValue(nameof(_stateSoundNames), out var tokenStateSoundNames) && tokenStateSoundNames is JObject valueStateSoundNames) { - mHoverSound = (string) obj["HoverSound"]; + foreach (var (propertyName, propertyValueToken) in valueStateSoundNames) + { + if (!Enum.TryParse(propertyName, out ButtonSoundState buttonSoundState) || + buttonSoundState == ButtonSoundState.None) + { + continue; + } + + if (propertyValueToken is not JValue { Type: JTokenType.String } valuePropertyValue) + { + continue; + } + + var stringPropertyValue = valuePropertyValue.Value()?.Trim(); + if (stringPropertyValue is { Length: > 0 }) + { + _stateSoundNames[buttonSoundState] = stringPropertyValue; + } + else + { + _stateSoundNames.Remove(buttonSoundState); + } + } } + } - if (obj["MouseUpSound"] != null) + public void SetSound(ButtonSoundState state, string? soundName) + { + soundName = soundName?.Trim(); + if (string.IsNullOrEmpty(soundName)) { - mMouseUpSound = (string) obj["MouseUpSound"]; + _stateSoundNames.Remove(state); } - - if (obj["MouseDownSound"] != null) + else { - mMouseDownSound = (string) obj["MouseDownSound"]; + _stateSoundNames[state] = soundName; } } public string GetMouseUpSound() { - return mMouseUpSound; + return _stateSoundNames[ButtonSoundState.MouseUp]; } /// @@ -303,22 +341,35 @@ public virtual string GetImageFilename(ComponentState state) } } - protected override void OnMouseEntered() + public void PlaySound(ButtonSoundState soundState) { - base.OnMouseEntered(); + if (soundState == ButtonSoundState.MouseUp) + { + if (_ignoreMouseUpSoundsUntil > DateTime.UtcNow) + { + return; + } + } - //Play Mouse Entered Sound - if (ShouldDrawHover) + if (!_stateSoundNames.TryGetValue(soundState, out var soundName)) + { + return; + } + + if (!base.PlaySound(soundName)) + { + return; + } + + if (soundState == ButtonSoundState.MouseDown) { - base.PlaySound(mHoverSound); + _ignoreMouseUpSoundsUntil = DateTime.UtcNow.AddMilliseconds(200); } } public void ClearSounds() { - mHoverSound = string.Empty; - mMouseDownSound = string.Empty; - mMouseUpSound = string.Empty; + _stateSoundNames.Clear(); } public void SetSound(string sound, ButtonSoundState state) @@ -328,13 +379,13 @@ public void SetSound(string sound, ButtonSoundState state) case ButtonSoundState.None: break; case ButtonSoundState.Hover: - mHoverSound = sound; + _stateSoundNames[ButtonSoundState.Hover] = sound; break; case ButtonSoundState.MouseDown: - mMouseDownSound = sound; + _stateSoundNames[ButtonSoundState.MouseDown] = sound; break; case ButtonSoundState.MouseUp: - mMouseUpSound = sound; + _stateSoundNames[ButtonSoundState.MouseUp] = sound; break; default: throw new ArgumentOutOfRangeException(nameof(state), state, null); diff --git a/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs b/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs index 969d0d1657..6417178359 100644 --- a/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs +++ b/Intersect.Client.Framework/Gwen/ControlInternal/ScrollBarBar.cs @@ -1,7 +1,6 @@ using Intersect.Client.Framework.GenericClasses; using Intersect.Client.Framework.Gwen.Control; using Intersect.Client.Framework.Input; -using Newtonsoft.Json.Linq; namespace Intersect.Client.Framework.Gwen.ControlInternal; @@ -12,10 +11,6 @@ public partial class ScrollBarBar : Dragger { private bool mHorizontal; - private readonly Dictionary _stateSoundNames = []; - - private DateTime _ignoreMouseUpSoundsUntil; - /// /// Initializes a new instance of the class. /// @@ -76,62 +71,6 @@ protected override void OnMouseMoved(int x, int y, int dx, int dy) InvalidateParent(); } - protected override void OnMouseEntered() - { - base.OnMouseEntered(); - PlaySound(ButtonSoundState.Hover); - } - - protected override void OnMouseDown(MouseButton mouseButton, Point mousePosition, bool userAction = true) - { - base.OnMouseDown(mouseButton, mousePosition, userAction); - PlaySound(ButtonSoundState.MouseDown); - } - - protected override void OnMouseUp(MouseButton mouseButton, Point mousePosition, bool userAction = true) - { - base.OnMouseUp(mouseButton, mousePosition, userAction); - PlaySound(ButtonSoundState.MouseUp); - } - - protected override void OnMouseClicked(MouseButton mouseButton, Point mousePosition, bool userAction = true) - { - base.OnMouseClicked(mouseButton, mousePosition, userAction); - - if (mouseButton == MouseButton.Left) - { - InvalidateParent(); - } - - PlaySound(ButtonSoundState.MouseClicked); - } - - public void PlaySound(ButtonSoundState soundState) - { - if (soundState == ButtonSoundState.MouseUp) - { - if (_ignoreMouseUpSoundsUntil > DateTime.UtcNow) - { - return; - } - } - - if (!_stateSoundNames.TryGetValue(soundState, out var soundName)) - { - return; - } - - if (!base.PlaySound(soundName)) - { - return; - } - - if (soundState == ButtonSoundState.MouseDown) - { - _ignoreMouseUpSoundsUntil = DateTime.UtcNow.AddMilliseconds(200); - } - } - /// /// Lays out the control's interior according to alignment, padding, dock etc. /// @@ -153,74 +92,4 @@ protected override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds InvalidateParent(); } - - public void SetSound(ButtonSoundState state, string? soundName) - { - soundName = soundName?.Trim(); - if (string.IsNullOrEmpty(soundName)) - { - _stateSoundNames.Remove(state); - } - else - { - _stateSoundNames[state] = soundName; - } - } - - public override JObject? GetJson(bool isRoot = false, bool onlySerializeIfNotEmpty = false) - { - var serializedProperties = base.GetJson(isRoot, onlySerializeIfNotEmpty); - if (serializedProperties is null) - { - return null; - } - - serializedProperties.Add(nameof(_stateSoundNames), JObject.FromObject(_stateSoundNames)); - return FixJson(serializedProperties); - } - - public override JObject FixJson(JObject json) - { - json.Remove("HoverSound"); - json.Remove("MouseUpSound"); - json.Remove("MouseDownSound"); - return base.FixJson(json); - } - - public override void LoadJson(JToken token, bool isRoot = default) - { - base.LoadJson(token, isRoot); - - if (token is not JObject obj) - { - return; - } - - if (obj.TryGetValue(nameof(_stateSoundNames), out var tokenStateSoundNames) && tokenStateSoundNames is JObject valueStateSoundNames) - { - foreach (var (propertyName, propertyValueToken) in valueStateSoundNames) - { - if (!Enum.TryParse(propertyName, out ButtonSoundState buttonSoundState) || - buttonSoundState == ButtonSoundState.None) - { - continue; - } - - if (propertyValueToken is not JValue { Type: JTokenType.String } valuePropertyValue) - { - continue; - } - - var stringPropertyValue = valuePropertyValue.Value()?.Trim(); - if (stringPropertyValue is { Length: > 0 }) - { - _stateSoundNames[buttonSoundState] = stringPropertyValue; - } - else - { - _stateSoundNames.Remove(buttonSoundState); - } - } - } - } } From 791c64f6609fd94469bc88d13393790fb639a2b3 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:38:19 -0300 Subject: [PATCH 13/19] fix close all windows --- Intersect.Client.Core/Interface/Game/GameInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Intersect.Client.Core/Interface/Game/GameInterface.cs b/Intersect.Client.Core/Interface/Game/GameInterface.cs index 52848e5d3d..9736d3f5cd 100644 --- a/Intersect.Client.Core/Interface/Game/GameInterface.cs +++ b/Intersect.Client.Core/Interface/Game/GameInterface.cs @@ -583,7 +583,7 @@ public bool CloseAllWindows() closedWindows = true; } - if (mShopWindow is { IsVisibleInTree: false }) + if (mShopWindow is { IsVisibleInTree: true }) { CloseShop(); closedWindows = true; From 5ec9e21fb87beb7bdeace2bfeda1530df7398ff0 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:42:04 -0300 Subject: [PATCH 14/19] expose left/right click sound --- Intersect.Client.Framework/Gwen/Control/ImagePanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs index 297d78dcdb..68186ddf98 100644 --- a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs +++ b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs @@ -20,9 +20,9 @@ public partial class ImagePanel : Base //Sound Effects public string? HoverSound { get; set; } - protected string? LeftMouseClickSound { get; set; } + public string? LeftMouseClickSound { get; set; } - protected string? RightMouseClickSound { get; set; } + public string? RightMouseClickSound { get; set; } private IGameTexture? _texture { get; set; } private string? _textureName; From fb84d3fc33be0e54d9d851b610323b15cefef7f2 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:46:18 -0300 Subject: [PATCH 15/19] better check for image panel sound load --- .../Gwen/Control/ImagePanel.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs index 68186ddf98..0390aa8012 100644 --- a/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs +++ b/Intersect.Client.Framework/Gwen/Control/ImagePanel.cs @@ -225,22 +225,22 @@ public override void LoadJson(JToken token, bool isRoot = default) } } - if (obj[nameof(HoverSound)] is JValue { Type: JTokenType.String } hoverSound && - hoverSound.Value()?.Trim() is { Length: > 0 }) + if (obj[nameof(HoverSound)] is JValue { Type: JTokenType.String } tokenHoverSound && + tokenHoverSound.Value()?.Trim() is { Length: > 0 } hoverSound) { - HoverSound = hoverSound?.Value(); + HoverSound = hoverSound; } - if (obj[nameof(LeftMouseClickSound)] is JValue { Type: JTokenType.String } leftMouseClickSound && - leftMouseClickSound.Value()?.Trim() is { Length: > 0 }) + if (obj[nameof(LeftMouseClickSound)] is JValue { Type: JTokenType.String } tokenLeftMouseClickSound && + tokenLeftMouseClickSound.Value()?.Trim() is { Length: > 0 } leftMouseClickSound) { - LeftMouseClickSound = leftMouseClickSound?.Value(); + LeftMouseClickSound = leftMouseClickSound; } - if (obj[nameof(RightMouseClickSound)] is JValue { Type: JTokenType.String } rightMouseClickSound && - rightMouseClickSound.Value()?.Trim() is { Length: > 0 }) + if (obj[nameof(RightMouseClickSound)] is JValue { Type: JTokenType.String } tokenRightMouseClickSound && + tokenRightMouseClickSound.Value()?.Trim() is { Length: > 0 } rightMouseClickSound) { - RightMouseClickSound = rightMouseClickSound?.Value(); + RightMouseClickSound = rightMouseClickSound; } } From 7dd3eaa8e77a1ada04f00f1b13dcc00a7fc91e99 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 18:56:35 -0300 Subject: [PATCH 16/19] fix KeyNotFoundException for sound handling --- .../Gwen/ControlInternal/Dragger.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs index 590f73c942..a0bbb67f30 100644 --- a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs +++ b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs @@ -57,9 +57,9 @@ protected override void OnMouseEntered() { base.OnMouseEntered(); - if (ShouldDrawHover) + if (ShouldDrawHover && _stateSoundNames.TryGetValue(ButtonSoundState.Hover, out var value)) { - PlaySound(_stateSoundNames[ButtonSoundState.Hover]); + PlaySound(value); } } @@ -74,9 +74,9 @@ protected override void OnMouseDown(MouseButton mouseButton, Point mousePosition return; } - if (userAction) + if (userAction && _stateSoundNames.TryGetValue(ButtonSoundState.MouseDown, out var value)) { - PlaySound(_stateSoundNames[ButtonSoundState.MouseDown]); + PlaySound(value); } mHoldPos = _target.CanvasPosToLocal(mousePosition); @@ -87,9 +87,9 @@ protected override void OnMouseUp(MouseButton mouseButton, Point mousePosition, { base.OnMouseUp(mouseButton, mousePosition, userAction); - if (userAction) + if (userAction && _stateSoundNames.TryGetValue(ButtonSoundState.MouseUp, out var value)) { - PlaySound(_stateSoundNames[ButtonSoundState.MouseUp]); + PlaySound(value); } InputHandler.MouseFocus = null; @@ -269,9 +269,10 @@ public void SetSound(ButtonSoundState state, string? soundName) } } - public string GetMouseUpSound() + public string? GetMouseUpSound() { - return _stateSoundNames[ButtonSoundState.MouseUp]; + _ = _stateSoundNames.TryGetValue(ButtonSoundState.MouseUp, out var value); + return value; } /// From f141fadd3e50031cf569bccda36b2e1ca564c54c Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 19:00:30 -0300 Subject: [PATCH 17/19] remove unused set item font --- Intersect.Client.Framework/Gwen/Control/Menu.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/Control/Menu.cs b/Intersect.Client.Framework/Gwen/Control/Menu.cs index a774a3d25b..ecbe139d65 100644 --- a/Intersect.Client.Framework/Gwen/Control/Menu.cs +++ b/Intersect.Client.Framework/Gwen/Control/Menu.cs @@ -507,12 +507,4 @@ public void SetBackgroundTemplate(IGameTexture texture, string fileName) mBackgroundTemplateFilename = fileName; mBackgroundTemplateTex = texture; } - - public void SetItemFont(IFont font, int fontSize) - { - _itemFont = font; - _itemFontSize = fontSize; - UpdateItemStyles(); - } - } From 25ff5cca983e294151054a8ecdab4d6039f5d5e3 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 19:54:31 -0300 Subject: [PATCH 18/19] fix: broken others context menus --- Intersect.Client.Framework/Gwen/Control/Menu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Intersect.Client.Framework/Gwen/Control/Menu.cs b/Intersect.Client.Framework/Gwen/Control/Menu.cs index ecbe139d65..23b010da3c 100644 --- a/Intersect.Client.Framework/Gwen/Control/Menu.cs +++ b/Intersect.Client.Framework/Gwen/Control/Menu.cs @@ -55,7 +55,7 @@ public IFont? ItemFont public string? ItemFontName { get => _itemFontName; - set => SetItemFont(GameContentManager.Current.GetFont(_itemFontName), _itemFontName); + set => SetItemFont(GameContentManager.Current.GetFont(value), value); } private int _itemFontSize; From 96ac3ce9f00702cc11c3f590f23741cc9b2a2307 Mon Sep 17 00:00:00 2001 From: WeylonSantana Date: Wed, 26 Feb 2025 20:12:00 -0300 Subject: [PATCH 19/19] fix property acess --- Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs index a0bbb67f30..57a86ef850 100644 --- a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs +++ b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs @@ -17,8 +17,8 @@ public partial class Dragger : Base protected Point mHoldPos; //Sound Effects - protected readonly Dictionary _stateSoundNames = []; - protected DateTime _ignoreMouseUpSoundsUntil; + private readonly Dictionary _stateSoundNames = []; + private DateTime _ignoreMouseUpSoundsUntil; private IGameTexture? mClickedImage; private string? mClickedImageFilename;