From 0d510c027c85e761e149ee9268ce31dad5c8f11c Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 3 May 2026 10:22:52 -0700 Subject: [PATCH] perf: Migrate RewardGump to DynamicGump (#2432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Converts `RewardGump` and the inner `RewardConfirmGump` from legacy `Gump` to `DynamicGump` with the static `DisplayTo` entry-point pattern. Both gumps have variable per-instance layout — the reward grid loops over `_rewards` and adds `AddItem(itemID, hue)` / `AddTooltip(tooltipID)` calls whose values are baked into the layout buffer per entry, so a cached static layout would be incorrect. DynamicGump is the right choice and avoids the placeholder dance for non-text values. Constructors are private; `DisplayTo` validates `NetState`, null `rewards`/`onPicked`, and empty arrays before constructing. Builder labels use `$"{value}"` interpolated-string-handler form for zero-allocation text. --- Projects/UOContent/Gumps/RewardGump.cs | 374 +++++++++++++------------ 1 file changed, 194 insertions(+), 180 deletions(-) diff --git a/Projects/UOContent/Gumps/RewardGump.cs b/Projects/UOContent/Gumps/RewardGump.cs index 53a3f8038..9a53de1f3 100644 --- a/Projects/UOContent/Gumps/RewardGump.cs +++ b/Projects/UOContent/Gumps/RewardGump.cs @@ -1,213 +1,227 @@ using System; using Server.Network; -namespace Server.Gumps +namespace Server.Gumps; + +/* + * A generic version of the EA Clean Up Britannia reward gump. + */ + +public interface IRewardEntry { - /* - * A generic version of the EA Clean Up Britannia reward gump. - */ + int Price { get; } + int ItemID { get; } + int Hue { get; } + int Tooltip { get; } + TextDefinition Description { get; } +} - public interface IRewardEntry +public delegate void RewardPickedHandler(Mobile from, int index); + +public class RewardGump : DynamicGump +{ + private readonly TextDefinition _title; + private readonly IRewardEntry[] _rewards; + private readonly int _points; + private readonly RewardPickedHandler _onPicked; + + public override bool Singleton => true; + + public TextDefinition Title => _title; + public IRewardEntry[] Rewards => _rewards; + public int Points => _points; + public RewardPickedHandler OnPicked => _onPicked; + + private RewardGump(TextDefinition title, IRewardEntry[] rewards, int points, RewardPickedHandler onPicked) + : base(250, 50) { - int Price { get; } - int ItemID { get; } - int Hue { get; } - int Tooltip { get; } - TextDefinition Description { get; } + _title = title; + _rewards = rewards; + _points = points; + _onPicked = onPicked; } - public delegate void RewardPickedHandler(Mobile from, int index); - - public class RewardGump : Gump + public static void DisplayTo(Mobile from, TextDefinition title, IRewardEntry[] rewards, int points, RewardPickedHandler onPicked) { - public RewardGump(TextDefinition title, IRewardEntry[] rewards, int points, RewardPickedHandler onPicked) - : base(250, 50) + if (from?.NetState != null && rewards != null && rewards.Length != 0 && onPicked != null) { - Title = title; - Rewards = rewards; - Points = points; - OnPicked = onPicked; - - AddPage(0); - - AddImage(0, 0, 0x1F40); - AddImageTiled(20, 37, 300, 308, 0x1F42); - AddImage(20, 325, 0x1F43); - AddImage(35, 8, 0x39); - AddImageTiled(65, 8, 257, 10, 0x3A); - AddImage(290, 8, 0x3B); - AddImage(32, 33, 0x2635); - AddImageTiled(70, 55, 230, 2, 0x23C5); - - if (Title.String != null) - { - AddHtml(70, 35, 270, 20, Title.String); - } - else if (Title.Number != 0) - { - AddHtmlLocalized(70, 35, 270, 20, Title.Number, 1); - } - - AddHtmlLocalized(50, 65, 150, 20, 1072843, 1); // Your Reward Points: - AddLabel(230, 65, 0x64, Points.ToString()); - AddImageTiled(35, 85, 270, 2, 0x23C5); - AddHtmlLocalized(35, 90, 270, 20, 1072844, 1); // Please Choose a Reward: - - AddPage(1); - - var offset = 110; - var page = 1; - - for (var i = 0; i < Rewards.Length; ++i) - { - var entry = Rewards[i]; - - var bounds = ItemBounds.Bounds[entry.ItemID]; - var height = Math.Max(36, bounds.Height); - - if (offset + height > 320) - { - AddHtmlLocalized(240, 335, 60, 20, 1072854, 1); //
Next
- AddButton(300, 335, 0x15E1, 0x15E5, 51, GumpButtonType.Page, page + 1); - - AddPage(++page); - - AddButton(150, 335, 0x15E3, 0x15E7, 52, GumpButtonType.Page, page - 1); - AddHtmlLocalized(170, 335, 60, 20, 1074880, 1); // Previous - - offset = 110; - } - - var available = entry.Price <= Points; - var half = offset + height / 2; - - if (available) - { - AddButton(35, half - 6, 0x837, 0x838, 100 + i); - } - - AddItem( - 83 - bounds.Width / 2 - bounds.X, - half - bounds.Height / 2 - bounds.Y, - entry.ItemID, - available ? entry.Hue : 995 - ); - - if (entry.Tooltip != 0) - { - AddTooltip(entry.Tooltip); - } - - AddLabel(133, half - 10, available ? 0x64 : 0x21, entry.Price.ToString()); - - if (entry.Description != null) - { - if (entry.Description.String != null) - { - AddHtml(190, offset, 114, height, entry.Description.String); - } - else if (entry.Description.Number != 0) - { - AddHtmlLocalized(190, offset, 114, height, entry.Description.Number, 1); - } - } - - offset += height + 10; - } - } - - public TextDefinition Title { get; } - - public IRewardEntry[] Rewards { get; } - - public int Points { get; } - - public RewardPickedHandler OnPicked { get; } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var choice = info.ButtonID; - - if (choice == 0) - { - return; // Close - } - - choice -= 100; - - if (choice >= 0 && choice < Rewards.Length) - { - var entry = Rewards[choice]; - - if (entry.Price <= Points) - { - sender.Mobile.SendGump(new RewardConfirmGump(this, choice, entry)); - } - } + from.SendGump(new RewardGump(title, rewards, points, onPicked)); } } - public class RewardConfirmGump : Gump + protected override void BuildLayout(ref DynamicGumpBuilder builder) { - private readonly int m_Index; - private readonly RewardGump m_Parent; + builder.AddPage(); - public RewardConfirmGump(RewardGump parent, int index, IRewardEntry entry) - : base(120, 50) + builder.AddImage(0, 0, 0x1F40); + builder.AddImageTiled(20, 37, 300, 308, 0x1F42); + builder.AddImage(20, 325, 0x1F43); + builder.AddImage(35, 8, 0x39); + builder.AddImageTiled(65, 8, 257, 10, 0x3A); + builder.AddImage(290, 8, 0x3B); + builder.AddImage(32, 33, 0x2635); + builder.AddImageTiled(70, 55, 230, 2, 0x23C5); + + _title.AddHtmlText(ref builder, 70, 35, 270, 20, numberColor: 1); + + builder.AddHtmlLocalized(50, 65, 150, 20, 1072843, 1); // Your Reward Points: + builder.AddLabel(230, 65, 0x64, $"{_points}"); + builder.AddImageTiled(35, 85, 270, 2, 0x23C5); + builder.AddHtmlLocalized(35, 90, 270, 20, 1072844, 1); // Please Choose a Reward: + + builder.AddPage(1); + + var offset = 110; + var page = 1; + + for (var i = 0; i < _rewards.Length; ++i) { - m_Parent = parent; - m_Index = index; + var entry = _rewards[i]; - Closable = false; + var bounds = ItemBounds.Bounds[entry.ItemID]; + var height = Math.Max(36, bounds.Height); - AddPage(0); + if (offset + height > 320) + { + builder.AddHtmlLocalized(240, 335, 60, 20, 1072854, 1); //
Next
+ builder.AddButton(300, 335, 0x15E1, 0x15E5, 51, GumpButtonType.Page, page + 1); - AddImageTiled(0, 0, 348, 262, 0xA8E); - AddAlphaRegion(0, 0, 348, 262); - AddImage(0, 15, 0x27A8); - AddImageTiled(0, 30, 17, 200, 0x27A7); - AddImage(0, 230, 0x27AA); - AddImage(15, 0, 0x280C); - AddImageTiled(30, 0, 300, 17, 0x280A); - AddImage(315, 0, 0x280E); - AddImage(15, 244, 0x280C); - AddImageTiled(30, 244, 300, 17, 0x280A); - AddImage(315, 244, 0x280E); - AddImage(330, 15, 0x27A8); - AddImageTiled(330, 30, 17, 200, 0x27A7); - AddImage(330, 230, 0x27AA); - AddImage(333, 2, 0x2716); - AddImage(333, 248, 0x2716); - AddImage(2, 248, 0x2716); - AddImage(2, 2, 0x2716); + builder.AddPage(++page); - AddItem(140, 120, entry.ItemID, entry.Hue); + builder.AddButton(150, 335, 0x15E3, 0x15E7, 52, GumpButtonType.Page, page - 1); + builder.AddHtmlLocalized(170, 335, 60, 20, 1074880, 1); // Previous + + offset = 110; + } + + var available = entry.Price <= _points; + var half = offset + height / 2; + + if (available) + { + builder.AddButton(35, half - 6, 0x837, 0x838, 100 + i); + } + + builder.AddItem( + 83 - bounds.Width / 2 - bounds.X, + half - bounds.Height / 2 - bounds.Y, + entry.ItemID, + available ? entry.Hue : 995 + ); if (entry.Tooltip != 0) { - AddTooltip(entry.Tooltip); + builder.AddTooltip(entry.Tooltip); } - AddHtmlLocalized(25, 22, 200, 20, 1074974, 0x7D00); // Confirm Selection - AddImage(25, 40, 0xBBF); - AddHtmlLocalized(25, 55, 300, 120, 1074975, 0x7FFF); // Are you sure you wish to select this? - AddRadio(25, 175, 0x25F8, 0x25FB, true, 1); - AddRadio(25, 210, 0x25F8, 0x25FB, false, 0); - AddHtmlLocalized(60, 180, 280, 20, 1074976, 0x7FFF); // Yes - AddHtmlLocalized(60, 215, 280, 20, 1074977, 0x7FFF); // No - AddButton(265, 220, 0xF7, 0xF8, 7); + builder.AddLabel(133, half - 10, available ? 0x64 : 0x21, $"{entry.Price}"); + + if (entry.Description != null) + { + if (entry.Description.String != null) + { + builder.AddHtml(190, offset, 114, height, entry.Description.String); + } + else if (entry.Description.Number != 0) + { + builder.AddHtmlLocalized(190, offset, 114, height, entry.Description.Number, 1); + } + } + + offset += height + 10; } + } - public override void OnResponse(NetState sender, in RelayInfo info) + public override void OnResponse(NetState sender, in RelayInfo info) + { + var choice = info.ButtonID - 100; + + if (choice >= 0 && choice < _rewards.Length) { - if (info.ButtonID == 7 && info.IsSwitched(1)) + var entry = _rewards[choice]; + + if (entry.Price <= _points) { - m_Parent.OnPicked(sender.Mobile, m_Index); - } - else - { - sender.Mobile.SendGump(new RewardGump(m_Parent.Title, m_Parent.Rewards, m_Parent.Points, m_Parent.OnPicked)); + RewardConfirmGump.DisplayTo(sender.Mobile, this, choice, entry); } } } } + +public class RewardConfirmGump : DynamicGump +{ + private readonly int _index; + private readonly RewardGump _parent; + private readonly IRewardEntry _entry; + + public override bool Singleton => true; + + private RewardConfirmGump(RewardGump parent, int index, IRewardEntry entry) : base(120, 50) + { + _parent = parent; + _index = index; + _entry = entry; + } + + public static void DisplayTo(Mobile from, RewardGump parent, int index, IRewardEntry entry) + { + if (from?.NetState != null && parent != null && entry != null) + { + from.SendGump(new RewardConfirmGump(parent, index, entry)); + } + } + + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + builder.SetNoClose(); + + builder.AddPage(); + + builder.AddImageTiled(0, 0, 348, 262, 0xA8E); + builder.AddAlphaRegion(0, 0, 348, 262); + builder.AddImage(0, 15, 0x27A8); + builder.AddImageTiled(0, 30, 17, 200, 0x27A7); + builder.AddImage(0, 230, 0x27AA); + builder.AddImage(15, 0, 0x280C); + builder.AddImageTiled(30, 0, 300, 17, 0x280A); + builder.AddImage(315, 0, 0x280E); + builder.AddImage(15, 244, 0x280C); + builder.AddImageTiled(30, 244, 300, 17, 0x280A); + builder.AddImage(315, 244, 0x280E); + builder.AddImage(330, 15, 0x27A8); + builder.AddImageTiled(330, 30, 17, 200, 0x27A7); + builder.AddImage(330, 230, 0x27AA); + builder.AddImage(333, 2, 0x2716); + builder.AddImage(333, 248, 0x2716); + builder.AddImage(2, 248, 0x2716); + builder.AddImage(2, 2, 0x2716); + + builder.AddItem(140, 120, _entry.ItemID, _entry.Hue); + + if (_entry.Tooltip != 0) + { + builder.AddTooltip(_entry.Tooltip); + } + + builder.AddHtmlLocalized(25, 22, 200, 20, 1074974, 0x7D00); // Confirm Selection + builder.AddImage(25, 40, 0xBBF); + builder.AddHtmlLocalized(25, 55, 300, 120, 1074975, 0x7FFF); // Are you sure you wish to select this? + builder.AddRadio(25, 175, 0x25F8, 0x25FB, true, 1); + builder.AddRadio(25, 210, 0x25F8, 0x25FB, false, 0); + builder.AddHtmlLocalized(60, 180, 280, 20, 1074976, 0x7FFF); // Yes + builder.AddHtmlLocalized(60, 215, 280, 20, 1074977, 0x7FFF); // No + builder.AddButton(265, 220, 0xF7, 0xF8, 7); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (info.ButtonID == 7 && info.IsSwitched(1)) + { + _parent.OnPicked(sender.Mobile, _index); + } + else + { + RewardGump.DisplayTo(sender.Mobile, _parent.Title, _parent.Rewards, _parent.Points, _parent.OnPicked); + } + } +}