From 50599e04534788bdd1de5d942c0d29599099ba7b Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:51:42 -0700 Subject: [PATCH] perf: Migrate NPC and Skill UI gumps to DynamicGump/StaticGump (#2414) ## Summary Migrates five legacy `Gump`-derived UI dialogs in the NPC and Skill domains to the modern `DynamicGump` builder pipeline. All five gumps were chosen as `DynamicGump` rather than `StaticGump` because their layout shape varies per instance, and several of them carry per-instance localization numbers (cliloc IDs) that the static cache cannot bake (see CLAUDE.md gump-system rule and `dev-docs/gump-system.md`). Per-gump rationale: - **`TownCrierGump` (`Mobiles/Townfolk/TownCrier.cs`)** - DynamicGump. Announcement count varies, expiration text is rebuilt per render via `ValueStringBuilder`, and one button per entry is emitted in a loop. - **`ClaimListGump` (`Mobiles/Vendors/NPC/AnimalTrainer.cs`)** - DynamicGump. The pet list and resulting background/alpha-region heights vary per stabling player. - **`AnimalLoreGump` (`Skills/AnimalLore.cs`)** - DynamicGump. Page count itself varies (3 pages pre-AOS, 5 pages on AOS) and several `AddHtmlLocalized` calls use cliloc IDs computed from per-creature data (loyalty rating `1049595 + c.Loyalty / 10`, food preference, pack instinct), which violates the StaticGump cliloc-bake rule. - **`DisguiseGump` (`Items/Skill Items/Thief/DisguiseKit.cs`)** - DynamicGump. Page count and entry order shift on `from.Female`, `Body.IsFemale`, and `startAtHair`. - **`CommentsGump` (`Gumps/CommentsGump.cs`)** - DynamicGump. Comment list and pagination depend on `Account.Comments` size; the title label encodes the variable account username string. All five are now `Singleton => true`, have `private` constructors, and expose static `DisplayTo(...)` entry points that validate prerequisites before constructing - guaranteeing no empty gumps (CLAUDE.md Sec.13). All internal refresh paths (prompts, `OnDoubleClick`, command handlers, target callbacks) were updated to call `DisplayTo` rather than `new XGump(...)`. Legacy `m_`-prefixed fields renamed to `_camelCase` (CLAUDE.md Sec.12), and `DisguiseEntry`'s `m_`-prefixed public readonly fields converted to PascalCase auto-properties. `OnResponse` signatures updated to `in RelayInfo info`. No external callers needed updating - all `new XGump(...)` sites lived inside the same files. --- Projects/UOContent/Gumps/CommentsGump.cs | 67 ++--- .../Items/Skill Items/Thief/DisguiseKit.cs | 131 +++++----- .../UOContent/Mobiles/Townfolk/TownCrier.cs | 135 +++++----- .../Mobiles/Vendors/NPC/AnimalTrainer.cs | 56 +++-- Projects/UOContent/Skills/AnimalLore.cs | 232 ++++++++++-------- 5 files changed, 342 insertions(+), 279 deletions(-) diff --git a/Projects/UOContent/Gumps/CommentsGump.cs b/Projects/UOContent/Gumps/CommentsGump.cs index 73fb4d44d..ac86559bb 100644 --- a/Projects/UOContent/Gumps/CommentsGump.cs +++ b/Projects/UOContent/Gumps/CommentsGump.cs @@ -5,32 +5,44 @@ using Server.Targeting; namespace Server.Gumps { - public class CommentsGump : Gump + public class CommentsGump : DynamicGump { - private readonly Account m_Acct; + private readonly Account _acct; - public CommentsGump(Account acct) : base(30, 30) + public override bool Singleton => true; + + private CommentsGump(Account acct) : base(30, 30) => _acct = acct; + + public static void DisplayTo(Mobile from, Account acct) { - m_Acct = acct; + if (from?.NetState == null || acct == null) + { + return; + } - AddPage(0); - AddImageTiled(0, 0, 410, 448, 0xA40); - AddAlphaRegion(1, 1, 408, 446); + from.SendGump(new CommentsGump(acct)); + } - var title = $"Comments for '{acct.Username}'"; + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + builder.AddPage(0); + builder.AddImageTiled(0, 0, 410, 448, 0xA40); + builder.AddAlphaRegion(1, 1, 408, 446); + + var title = $"Comments for '{_acct.Username}'"; var x = 205 - title.Length / 2 * 7; if (x < 120) { x = 120; } - AddLabel(x, 12, 2100, title); + builder.AddLabel(x, 12, 2100, title); - AddPage(1); - AddButton(12, 12, 0xFA8, 0xFAA, 0x7F); - AddLabel(48, 12, 2100, "Add Comment"); + builder.AddPage(1); + builder.AddButton(12, 12, 0xFA8, 0xFAA, 0x7F); + builder.AddLabel(48, 12, 2100, "Add Comment"); - var list = acct.Comments; + var list = _acct.Comments; if (list.Count > 0) { for (var i = 0; i < list.Count; ++i) @@ -39,21 +51,21 @@ namespace Server.Gumps if (i >= 5 && i % 5 == 0) { - AddButton(368, 12, 0xFA5, 0xFA7, 0, GumpButtonType.Page, i / 5 + 1); - AddLabel(298, 12, 2100, "Next Page"); - AddPage(i / 5 + 1); - AddButton(12, 12, 0xFAE, 0xFB0, 0, GumpButtonType.Page, i / 5); - AddLabel(48, 12, 2100, "Prev Page"); + builder.AddButton(368, 12, 0xFA5, 0xFA7, 0, GumpButtonType.Page, i / 5 + 1); + builder.AddLabel(298, 12, 2100, "Next Page"); + builder.AddPage(i / 5 + 1); + builder.AddButton(12, 12, 0xFAE, 0xFB0, 0, GumpButtonType.Page, i / 5); + builder.AddLabel(48, 12, 2100, "Prev Page"); } var html = $"[Added By: {comment.AddedBy} on {comment.LastModified.ToString("H:mm M/d/yy")}]
{comment.Content}"; - AddHtml(12, 44 + i % 5 * 80, 386, 70, html, true, true); + builder.AddHtml(12, 44 + i % 5 * 80, 386, 70, html, background: true, scrollbar: true); } } else { - AddLabel(12, 44, 2100, "There are no comments for this account."); + builder.AddLabel(12, 44, 2100, "There are no comments for this account."); } } @@ -83,7 +95,7 @@ namespace Server.Gumps } else { - from.SendGump(new CommentsGump((Account)m.Account)); + DisplayTo(from, (Account)m.Account); } } @@ -92,19 +104,19 @@ namespace Server.Gumps if (info.ButtonID == 0x7F) { state.Mobile.SendMessage("Enter the text for the account comment (or press [Esc] to cancel):"); - state.Mobile.Prompt = new CommentPrompt(m_Acct); + state.Mobile.Prompt = new CommentPrompt(_acct); } } public class CommentPrompt : Prompt { - private readonly Account m_Acct; + private readonly Account _acct; - public CommentPrompt(Account acct) => m_Acct = acct; + public CommentPrompt(Account acct) => _acct = acct; public override void OnCancel(Mobile from) { - from.SendGump(new CommentsGump(m_Acct), true); + DisplayTo(from, _acct); base.OnCancel(from); } @@ -112,9 +124,8 @@ namespace Server.Gumps { base.OnResponse(from, text); from.SendMessage("Comment added."); - // m_Acct.AddComment( from.Name, text ); - m_Acct.Comments.Add(new AccountComment(from.Name, text)); - from.SendGump(new CommentsGump(m_Acct), true); + _acct.Comments.Add(new AccountComment(from.Name, text)); + DisplayTo(from, _acct); } } } diff --git a/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs b/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs index 1d9bb3230..319412466 100644 --- a/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs +++ b/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs @@ -71,14 +71,14 @@ public partial class DisguiseKit : Item { if (ValidateUse(from)) { - from.SendGump(new DisguiseGump(from, this, true, false)); + DisguiseGump.DisplayTo(from, this, true, false); } } } -public class DisguiseGump : Gump +public class DisguiseGump : DynamicGump { - private static readonly DisguiseEntry[] m_HairEntries = + private static readonly DisguiseEntry[] _hairEntries = { new(8251, 50700, 0, 5, 1011052), // Short new(8261, 60710, 0, 3, 1011047), // Pageboy @@ -92,7 +92,7 @@ public class DisguiseGump : Gump new(0, 0, 0, 0, 1011051) // None }; - private static readonly DisguiseEntry[] m_BeardEntries = + private static readonly DisguiseEntry[] _beardEntries = { new(8269, 50906, 0, 0, 1011401), // Vandyke new(8257, 50808, 0, -2, 1011062), // Mustache @@ -104,55 +104,70 @@ public class DisguiseGump : Gump new(0, 0, 0, 0, 1011051) // None }; - private readonly Mobile m_From; - private readonly DisguiseKit m_Kit; - private readonly bool m_Used; + private readonly Mobile _from; + private readonly DisguiseKit _kit; + private readonly bool _startAtHair; + private readonly bool _used; public override bool Singleton => true; - public DisguiseGump(Mobile from, DisguiseKit kit, bool startAtHair, bool used) : base(50, 50) + private DisguiseGump(Mobile from, DisguiseKit kit, bool startAtHair, bool used) : base(50, 50) { - m_From = from; - m_Kit = kit; - m_Used = used; + _from = from; + _kit = kit; + _startAtHair = startAtHair; + _used = used; + } - AddPage(0); + public static void DisplayTo(Mobile from, DisguiseKit kit, bool startAtHair, bool used) + { + if (from?.NetState == null || kit == null || kit.Deleted) + { + return; + } - AddBackground(100, 10, 400, 385, 2600); + from.SendGump(new DisguiseGump(from, kit, startAtHair, used)); + } + + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + builder.AddPage(0); + + builder.AddBackground(100, 10, 400, 385, 2600); //
THIEF DISGUISE KIT
- AddHtmlLocalized(100, 25, 400, 35, 1011045); + builder.AddHtmlLocalized(100, 25, 400, 35, 1011045); - AddButton(140, 353, 4005, 4007, 0); - AddHtmlLocalized(172, 355, 90, 35, 1011036); // OKAY + builder.AddButton(140, 353, 4005, 4007, 0); + builder.AddHtmlLocalized(172, 355, 90, 35, 1011036); // OKAY - AddButton(257, 353, 4005, 4007, 1); - AddHtmlLocalized(289, 355, 90, 35, 1011046); // APPLY + builder.AddButton(257, 353, 4005, 4007, 1); + builder.AddHtmlLocalized(289, 355, 90, 35, 1011046); // APPLY - if (from.Female || from.Body.IsFemale) + if (_from.Female || _from.Body.IsFemale) { - DrawEntries(0, 1, -1, m_HairEntries, -1); + DrawEntries(ref builder, 0, 1, -1, _hairEntries, -1); } - else if (startAtHair) + else if (_startAtHair) { - DrawEntries(0, 1, 2, m_HairEntries, 1011056); - DrawEntries(1, 2, 1, m_BeardEntries, 1011059); + DrawEntries(ref builder, 0, 1, 2, _hairEntries, 1011056); + DrawEntries(ref builder, 1, 2, 1, _beardEntries, 1011059); } else { - DrawEntries(1, 1, 2, m_BeardEntries, 1011059); - DrawEntries(0, 2, 1, m_HairEntries, 1011056); + DrawEntries(ref builder, 1, 1, 2, _beardEntries, 1011059); + DrawEntries(ref builder, 0, 2, 1, _hairEntries, 1011056); } } - private void DrawEntries(int index, int page, int nextPage, DisguiseEntry[] entries, int nextNumber) + private static void DrawEntries(ref DynamicGumpBuilder builder, int index, int page, int nextPage, DisguiseEntry[] entries, int nextNumber) { - AddPage(page); + builder.AddPage(page); if (nextPage != -1) { - AddButton(155, 320, 250 + index * 2, 251 + index * 2, 0, GumpButtonType.Page, nextPage); - AddHtmlLocalized(180, 320, 150, 35, nextNumber); + builder.AddButton(155, 320, 250 + index * 2, 251 + index * 2, 0, GumpButtonType.Page, nextPage); + builder.AddHtmlLocalized(180, 320, 150, 35, nextNumber); } for (var i = 0; i < entries.Length; ++i) @@ -167,14 +182,14 @@ public class DisguiseGump : Gump var x = i % 2 * 205; var y = i / 2 * 55; - if (entry.m_GumpID != 0) + if (entry.GumpID != 0) { - AddBackground(220 + x, 60 + y, 50, 50, 2620); - AddImage(153 + x + entry.m_OffsetX, 15 + y + entry.m_OffsetY, entry.m_GumpID); + builder.AddBackground(220 + x, 60 + y, 50, 50, 2620); + builder.AddImage(153 + x + entry.OffsetX, 15 + y + entry.OffsetY, entry.GumpID); } - AddHtmlLocalized(140 + x, 72 + y, 80, 35, entry.m_Number); - AddRadio(118 + x, 73 + y, 208, 209, false, i * 2 + index); + builder.AddHtmlLocalized(140 + x, 72 + y, 80, 35, entry.Number); + builder.AddRadio(118 + x, 73 + y, 208, 209, false, i * 2 + index); } } @@ -182,13 +197,13 @@ public class DisguiseGump : Gump { if (info.ButtonID == 0) { - if (m_Used) + if (_used) { - m_From.SendLocalizedMessage(501706); // Disguises wear off after 2 hours. + _from.SendLocalizedMessage(501706); // Disguises wear off after 2 hours. } else { - m_From.SendLocalizedMessage(501707); // You're looking good. + _from.SendLocalizedMessage(501707); // You're looking good. } return; @@ -207,7 +222,7 @@ public class DisguiseGump : Gump var hair = type == 0; - var entries = hair ? m_HairEntries : m_BeardEntries; + var entries = hair ? _hairEntries : _beardEntries; if (index >= 0 && index < entries.Length) { @@ -218,54 +233,54 @@ public class DisguiseGump : Gump return; } - if (!m_Kit.ValidateUse(m_From)) + if (!_kit.ValidateUse(_from)) { return; } - if (!hair && (m_From.Female || m_From.Body.IsFemale)) + if (!hair && (_from.Female || _from.Body.IsFemale)) { return; } - m_From.NameMod = NameList.RandomName(m_From.Female ? "female" : "male"); + _from.NameMod = NameList.RandomName(_from.Female ? "female" : "male"); - if (m_From is PlayerMobile pm) + if (_from is PlayerMobile pm) { if (hair) { - pm.SetHairMods(entry.m_ItemID, -2); + pm.SetHairMods(entry.ItemID, -2); } else { - pm.SetHairMods(-2, entry.m_ItemID); + pm.SetHairMods(-2, entry.ItemID); } } - m_From.SendGump(new DisguiseGump(m_From, m_Kit, hair, true)); + DisguiseGump.DisplayTo(_from, _kit, hair, true); - DisguisePersistence.RemoveTimer(m_From); + DisguisePersistence.RemoveTimer(_from); - DisguisePersistence.CreateTimer(m_From, TimeSpan.FromHours(2.0)); - DisguisePersistence.StartTimer(m_From); + DisguisePersistence.CreateTimer(_from, TimeSpan.FromHours(2.0)); + DisguisePersistence.StartTimer(_from); } } private class DisguiseEntry { - public readonly int m_GumpID; - public readonly int m_ItemID; - public readonly int m_Number; - public readonly int m_OffsetX; - public readonly int m_OffsetY; + public int GumpID { get; } + public int ItemID { get; } + public int Number { get; } + public int OffsetX { get; } + public int OffsetY { get; } public DisguiseEntry(int itemID, int gumpID, int ox, int oy, int name) { - m_ItemID = itemID; - m_GumpID = gumpID; - m_OffsetX = ox; - m_OffsetY = oy; - m_Number = name; + ItemID = itemID; + GumpID = gumpID; + OffsetX = ox; + OffsetY = oy; + Number = name; } } } diff --git a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs index 849f44f6e..8343a0667 100644 --- a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs +++ b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs @@ -88,7 +88,7 @@ public class GlobalTownCrierEntryList : ITownCrierEntryList [Usage("TownCriers"), Description("Manages the global town crier list.")] public static void TownCriers_OnCommand(CommandEventArgs e) { - e.Mobile.SendGump(new TownCrierGump(e.Mobile, Instance)); + TownCrierGump.DisplayTo(e.Mobile, Instance); } } @@ -119,7 +119,7 @@ public class TownCrierDurationPrompt : Prompt if (!TimeSpan.TryParse(text, out var ts)) { from.SendMessage("Value was not properly formatted. Use: "); - from.SendGump(new TownCrierGump(from, m_Owner)); + TownCrierGump.DisplayTo(from, m_Owner); return; } @@ -137,7 +137,7 @@ public class TownCrierDurationPrompt : Prompt public override void OnCancel(Mobile from) { from.SendLocalizedMessage(502980); // Message entry cancelled. - from.SendGump(new TownCrierGump(from, m_Owner)); + TownCrierGump.DisplayTo(from, m_Owner); } } @@ -188,96 +188,104 @@ public class TownCrierLinesPrompt : Prompt } } - from.SendGump(new TownCrierGump(from, m_Owner)); + TownCrierGump.DisplayTo(from, m_Owner); } } -public class TownCrierGump : Gump +public class TownCrierGump : DynamicGump { - private readonly Mobile m_From; - private readonly ITownCrierEntryList m_Owner; + private readonly ITownCrierEntryList _owner; public override bool Singleton => true; - public TownCrierGump(Mobile from, ITownCrierEntryList owner) : base(50, 50) + private TownCrierGump(ITownCrierEntryList owner) : base(50, 50) => _owner = owner; + + public static void DisplayTo(Mobile from, ITownCrierEntryList owner) { - m_From = from; - m_Owner = owner; + if (from?.NetState == null || owner == null) + { + return; + } - AddPage(0); + from.SendGump(new TownCrierGump(owner)); + } - var entries = owner.Entries; + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + builder.AddPage(); - owner.GetRandomEntry(); // force expiration checks + _owner.GetRandomEntry(); // force expiration checks + var entries = _owner.Entries; var count = entries?.Count ?? 0; - AddImageTiled(0, 0, 300, 38 + (count == 0 ? 20 : count * 85), 0xA40); - AddAlphaRegion(1, 1, 298, 36 + (count == 0 ? 20 : count * 85)); + builder.AddImageTiled(0, 0, 300, 38 + (count == 0 ? 20 : count * 85), 0xA40); + builder.AddAlphaRegion(1, 1, 298, 36 + (count == 0 ? 20 : count * 85)); - AddHtml(8, 8, 300 - 8 - 30, 20, "
TOWN CRIER MESSAGES
"); + builder.AddHtml(8, 8, 300 - 8 - 30, 20, "
TOWN CRIER MESSAGES
"); - AddButton(300 - 8 - 30, 8, 0xFAB, 0xFAD, 1); + builder.AddButton(300 - 8 - 30, 8, 0xFAB, 0xFAD, 1); if (count == 0) { - AddHtml(8, 30, 284, 20, "The crier has no news."); + builder.AddHtml(8, 30, 284, 20, "The crier has no news."); + return; } - else + + for (var i = 0; i < entries!.Count; ++i) { - for (var i = 0; i < entries!.Count; ++i) + var tce = entries[i]; + + var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero); + + using var sb = ValueStringBuilder.Create(512); + + sb.Append("[Expires: "); + + if (toExpire.TotalHours >= 1) { - var tce = entries[i]; - - var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero); - - using var sb = ValueStringBuilder.Create(512); - - sb.Append("[Expires: "); - - if (toExpire.TotalHours >= 1) - { - sb.Append((int)toExpire.TotalHours); - sb.Append(':'); - sb.Append(toExpire.Minutes.ToString("D2")); - } - else - { - sb.Append(toExpire.Minutes); - } - + sb.Append((int)toExpire.TotalHours); sb.Append(':'); - sb.Append(toExpire.Seconds.ToString("D2")); + sb.Append(toExpire.Minutes.ToString("D2")); + } + else + { + sb.Append(toExpire.Minutes); + } - sb.Append("] "); + sb.Append(':'); + sb.Append(toExpire.Seconds.ToString("D2")); - for (var j = 0; j < tce.Lines.Length; ++j) + sb.Append("] "); + + for (var j = 0; j < tce.Lines.Length; ++j) + { + if (j > 0) { - if (j > 0) - { - sb.Append("
"); - } - - sb.Append(tce.Lines[j]); + sb.Append("
"); } - AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), true, true); - - AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i); + sb.Append(tce.Lines[j]); } + + builder.AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), background: true, scrollbar: true); + + builder.AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i); } } public override void OnResponse(NetState sender, in RelayInfo info) { + var from = sender.Mobile; + if (info.ButtonID == 1) { - m_From.SendMessage("Enter the duration for the new message. Format: "); - m_From.Prompt = new TownCrierDurationPrompt(m_Owner); + from.SendMessage("Enter the duration for the new message. Format: "); + from.Prompt = new TownCrierDurationPrompt(_owner); } else if (info.ButtonID > 1) { - var entries = m_Owner.Entries; + var entries = _owner.Entries; var index = info.ButtonID - 2; if (index < entries?.Count) @@ -285,9 +293,9 @@ public class TownCrierGump : Gump var tce = entries[index]; var ts = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero); - m_From.SendMessage($"Editing entry #{index + 1}."); - m_From.SendMessage("Enter the first line to shout:"); - m_From.Prompt = new TownCrierLinesPrompt(m_Owner, tce, new List(), ts); + from.SendMessage($"Editing entry #{index + 1}."); + from.SendMessage("Enter the first line to shout:"); + from.Prompt = new TownCrierLinesPrompt(_owner, tce, new List(), ts); } } } @@ -352,11 +360,6 @@ public partial class TownCrier : Mobile, ITownCrierEntryList Utility.AssignRandomHair(this); } - public TownCrier(Serial serial) : base(serial) - { - Instances.Add(this); - } - public static List Instances { get; } = new(); public List Entries { get; private set; } @@ -467,7 +470,7 @@ public partial class TownCrier : Mobile, ITownCrierEntryList { if (from.AccessLevel >= AccessLevel.GameMaster) { - from.SendGump(new TownCrierGump(from, this)); + TownCrierGump.DisplayTo(from, this); } else { @@ -510,4 +513,10 @@ public partial class TownCrier : Mobile, ITownCrierEntryList Instances.Remove(this); base.OnDelete(); } + + [AfterDeserialization] + private void AfterDeserialization() + { + Instances.Add(this); + } } diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs b/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs index 467ea007b..64ee7d45c 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs @@ -142,7 +142,7 @@ namespace Server.Mobiles if (list.Count > 0) { - from.SendGump(new ClaimListGump(this, from, list)); + ClaimListGump.DisplayTo(this, from, list); } else { @@ -421,38 +421,50 @@ namespace Server.Mobiles } } - private class ClaimListGump : Gump + private class ClaimListGump : DynamicGump { - private readonly Mobile m_From; - private readonly List m_List; - private readonly AnimalTrainer m_Trainer; + private readonly List _list; + private readonly AnimalTrainer _trainer; public override bool Singleton => true; - public ClaimListGump(AnimalTrainer trainer, Mobile from, List list) : base(50, 50) + private ClaimListGump(AnimalTrainer trainer, List list) : base(50, 50) { - m_Trainer = trainer; - m_From = from; - m_List = list; + _trainer = trainer; + _list = list; + } - AddPage(0); - - AddBackground(0, 0, 325, 50 + list.Count * 20, 9250); - AddAlphaRegion(5, 5, 315, 40 + list.Count * 20); - - AddHtml(15, 15, 275, 20, "Select a pet to retrieve from the stables:"); - - for (var i = 0; i < list.Count; ++i) + public static void DisplayTo(AnimalTrainer trainer, Mobile from, List list) + { + if (from?.NetState == null || trainer == null || list == null || list.Count == 0) { - var pet = list[i]; + return; + } + + from.SendGump(new ClaimListGump(trainer, list)); + } + + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + builder.AddPage(); + + builder.AddBackground(0, 0, 325, 50 + _list.Count * 20, 9250); + builder.AddAlphaRegion(5, 5, 315, 40 + _list.Count * 20); + + // Select a pet to retrieve from the stables: + builder.AddHtmlLocalized(15, 15, 275, 20, 1080333, 0x7FFF); + + for (var i = 0; i < _list.Count; ++i) + { + var pet = _list[i]; if (pet?.Deleted != false) { continue; } - AddButton(15, 39 + i * 20, 10006, 10006, i + 1); - AddHtml(32, 35 + i * 20, 275, 18, pet.Name.Color(0xC0C0EE)); + builder.AddButton(15, 39 + i * 20, 10006, 10006, i + 1); + builder.AddHtml(32, 35 + i * 20, 275, 18, pet.Name.Color(0xC0C0EE)); } } @@ -460,9 +472,9 @@ namespace Server.Mobiles { var index = info.ButtonID - 1; - if (index >= 0 && index < m_List.Count) + if (index >= 0 && index < _list.Count) { - m_Trainer.EndClaimList(m_From, m_List[index]); + _trainer.EndClaimList(sender.Mobile, _list[index]); } } } diff --git a/Projects/UOContent/Skills/AnimalLore.cs b/Projects/UOContent/Skills/AnimalLore.cs index e53220844..a02bec8a4 100644 --- a/Projects/UOContent/Skills/AnimalLore.cs +++ b/Projects/UOContent/Skills/AnimalLore.cs @@ -62,60 +62,76 @@ namespace Server.SkillHandlers } else { - from.SendGump(new AnimalLoreGump(c)); + AnimalLoreGump.DisplayTo(from, c); } } } } - public class AnimalLoreGump : Gump + public class AnimalLoreGump : DynamicGump { private const int LabelColor = 0x24E5; + private readonly BaseCreature _creature; + public override bool Singleton => true; - public AnimalLoreGump(BaseCreature c) : base(250, 50) + private AnimalLoreGump(BaseCreature c) : base(250, 50) => _creature = c; + + public static void DisplayTo(Mobile from, BaseCreature c) { - AddPage(0); + if (from?.NetState == null || c == null || c.Deleted) + { + return; + } - AddImage(100, 100, 2080); - AddImage(118, 137, 2081); - AddImage(118, 207, 2081); - AddImage(118, 277, 2081); - AddImage(118, 347, 2083); + from.SendGump(new AnimalLoreGump(c)); + } - AddHtml(147, 108, 210, 18, $"
{c.Name}
"); + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + var c = _creature; - AddButton(240, 77, 2093, 2093, 2); + builder.AddPage(0); - AddImage(140, 138, 2091); - AddImage(140, 335, 2091); + builder.AddImage(100, 100, 2080); + builder.AddImage(118, 137, 2081); + builder.AddImage(118, 207, 2081); + builder.AddImage(118, 277, 2081); + builder.AddImage(118, 347, 2083); + + builder.AddHtml(147, 108, 210, 18, $"
{c.Name}
"); + + builder.AddButton(240, 77, 2093, 2093, 2); + + builder.AddImage(140, 138, 2091); + builder.AddImage(140, 335, 2091); var pages = Core.AOS ? 5 : 3; var page = 0; - AddPage(++page); + builder.AddPage(++page); - AddImage(128, 152, 2086); - AddHtmlLocalized(147, 150, 160, 18, 1049593, 200); // Attributes + builder.AddImage(128, 152, 2086); + builder.AddHtmlLocalized(147, 150, 160, 18, 1049593, 200); // Attributes - AddHtmlLocalized(153, 168, 160, 18, 1049578, LabelColor); // Hits - AddHtml(280, 168, 75, 18, FormatAttributes(c.Hits, c.HitsMax)); + builder.AddHtmlLocalized(153, 168, 160, 18, 1049578, LabelColor); // Hits + builder.AddHtml(280, 168, 75, 18, FormatAttributes(c.Hits, c.HitsMax)); - AddHtmlLocalized(153, 186, 160, 18, 1049579, LabelColor); // Stamina - AddHtml(280, 186, 75, 18, FormatAttributes(c.Stam, c.StamMax)); + builder.AddHtmlLocalized(153, 186, 160, 18, 1049579, LabelColor); // Stamina + builder.AddHtml(280, 186, 75, 18, FormatAttributes(c.Stam, c.StamMax)); - AddHtmlLocalized(153, 204, 160, 18, 1049580, LabelColor); // Mana - AddHtml(280, 204, 75, 18, FormatAttributes(c.Mana, c.ManaMax)); + builder.AddHtmlLocalized(153, 204, 160, 18, 1049580, LabelColor); // Mana + builder.AddHtml(280, 204, 75, 18, FormatAttributes(c.Mana, c.ManaMax)); - AddHtmlLocalized(153, 222, 160, 18, 1028335, LabelColor); // Strength - AddHtml(320, 222, 35, 18, FormatStat(c.Str)); + builder.AddHtmlLocalized(153, 222, 160, 18, 1028335, LabelColor); // Strength + builder.AddHtml(320, 222, 35, 18, FormatStat(c.Str)); - AddHtmlLocalized(153, 240, 160, 18, 3000113, LabelColor); // Dexterity - AddHtml(320, 240, 35, 18, FormatStat(c.Dex)); + builder.AddHtmlLocalized(153, 240, 160, 18, 3000113, LabelColor); // Dexterity + builder.AddHtml(320, 240, 35, 18, FormatStat(c.Dex)); - AddHtmlLocalized(153, 258, 160, 18, 3000112, LabelColor); // Intelligence - AddHtml(320, 258, 35, 18, FormatStat(c.Int)); + builder.AddHtmlLocalized(153, 258, 160, 18, 3000112, LabelColor); // Intelligence + builder.AddHtml(320, 258, 35, 18, FormatStat(c.Int)); if (Core.AOS) { @@ -129,17 +145,17 @@ namespace Server.SkillHandlers bd = 0; } - AddHtmlLocalized(153, 276, 160, 18, 1070793, LabelColor); // Barding Difficulty - AddHtml(320, y, 35, 18, FormatDouble(bd)); + builder.AddHtmlLocalized(153, 276, 160, 18, 1070793, LabelColor); // Barding Difficulty + builder.AddHtml(320, y, 35, 18, FormatDouble(bd)); y += 18; } - AddImage(128, y + 2, 2086); - AddHtmlLocalized(147, y, 160, 18, 1049594, 200); // Loyalty Rating + builder.AddImage(128, y + 2, 2086); + builder.AddHtmlLocalized(147, y, 160, 18, 1049594, 200); // Loyalty Rating y += 18; - AddHtmlLocalized( + builder.AddHtmlLocalized( 153, y, 160, @@ -150,122 +166,122 @@ namespace Server.SkillHandlers } else { - AddImage(128, 278, 2086); - AddHtmlLocalized(147, 276, 160, 18, 3001016, 200); // Miscellaneous + builder.AddImage(128, 278, 2086); + builder.AddHtmlLocalized(147, 276, 160, 18, 3001016, 200); // Miscellaneous - AddHtmlLocalized(153, 294, 160, 18, 1049581, LabelColor); // Armor Rating - AddHtml(320, 294, 35, 18, FormatStat(c.VirtualArmor)); + builder.AddHtmlLocalized(153, 294, 160, 18, 1049581, LabelColor); // Armor Rating + builder.AddHtml(320, 294, 35, 18, FormatStat(c.VirtualArmor)); } - AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, pages); + builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, pages); if (Core.AOS) { - AddPage(++page); + builder.AddPage(++page); - AddImage(128, 152, 2086); - AddHtmlLocalized(147, 150, 160, 18, 1061645, 200); // Resistances + builder.AddImage(128, 152, 2086); + builder.AddHtmlLocalized(147, 150, 160, 18, 1061645, 200); // Resistances - AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical - AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalResistance)); + builder.AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical + builder.AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalResistance)); - AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire - AddHtml(320, 186, 35, 18, FormatElement(c.FireResistance, "#FF0000")); + builder.AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire + builder.AddHtml(320, 186, 35, 18, FormatElement(c.FireResistance, "#FF0000")); - AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold - AddHtml(320, 204, 35, 18, FormatElement(c.ColdResistance, "#000080")); + builder.AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold + builder.AddHtml(320, 204, 35, 18, FormatElement(c.ColdResistance, "#000080")); - AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison - AddHtml(320, 222, 35, 18, FormatElement(c.PoisonResistance, "#008000")); + builder.AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison + builder.AddHtml(320, 222, 35, 18, FormatElement(c.PoisonResistance, "#008000")); - AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy - AddHtml(320, 240, 35, 18, FormatElement(c.EnergyResistance, "#BF80FF")); + builder.AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy + builder.AddHtml(320, 240, 35, 18, FormatElement(c.EnergyResistance, "#BF80FF")); - AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); - AddPage(++page); + builder.AddPage(++page); - AddImage(128, 152, 2086); - AddHtmlLocalized(147, 150, 160, 18, 1017319, 200); // Damage + builder.AddImage(128, 152, 2086); + builder.AddHtmlLocalized(147, 150, 160, 18, 1017319, 200); // Damage - AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical - AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalDamage)); + builder.AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical + builder.AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalDamage)); - AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire - AddHtml(320, 186, 35, 18, FormatElement(c.FireDamage, "#FF0000")); + builder.AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire + builder.AddHtml(320, 186, 35, 18, FormatElement(c.FireDamage, "#FF0000")); - AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold - AddHtml(320, 204, 35, 18, FormatElement(c.ColdDamage, "#000080")); + builder.AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold + builder.AddHtml(320, 204, 35, 18, FormatElement(c.ColdDamage, "#000080")); - AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison - AddHtml(320, 222, 35, 18, FormatElement(c.PoisonDamage, "#008000")); + builder.AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison + builder.AddHtml(320, 222, 35, 18, FormatElement(c.PoisonDamage, "#008000")); - AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy - AddHtml(320, 240, 35, 18, FormatElement(c.EnergyDamage, "#BF80FF")); + builder.AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy + builder.AddHtml(320, 240, 35, 18, FormatElement(c.EnergyDamage, "#BF80FF")); if (Core.ML) { - AddHtmlLocalized(153, 258, 160, 18, 1076750, LabelColor); // Base Damage - AddHtml(300, 258, 55, 18, FormatDamage(c.DamageMin, c.DamageMax)); + builder.AddHtmlLocalized(153, 258, 160, 18, 1076750, LabelColor); // Base Damage + builder.AddHtml(300, 258, 55, 18, FormatDamage(c.DamageMin, c.DamageMax)); } - AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); } - AddPage(++page); + builder.AddPage(++page); - AddImage(128, 152, 2086); - AddHtmlLocalized(147, 150, 160, 18, 3001030, 200); // Combat Ratings + builder.AddImage(128, 152, 2086); + builder.AddHtmlLocalized(147, 150, 160, 18, 3001030, 200); // Combat Ratings - AddHtmlLocalized(153, 168, 160, 18, 1044103, LabelColor); // Wrestling - AddHtml(320, 168, 35, 18, FormatSkill(c, SkillName.Wrestling)); + builder.AddHtmlLocalized(153, 168, 160, 18, 1044103, LabelColor); // Wrestling + builder.AddHtml(320, 168, 35, 18, FormatSkill(c, SkillName.Wrestling)); - AddHtmlLocalized(153, 186, 160, 18, 1044087, LabelColor); // Tactics - AddHtml(320, 186, 35, 18, FormatSkill(c, SkillName.Tactics)); + builder.AddHtmlLocalized(153, 186, 160, 18, 1044087, LabelColor); // Tactics + builder.AddHtml(320, 186, 35, 18, FormatSkill(c, SkillName.Tactics)); - AddHtmlLocalized(153, 204, 160, 18, 1044086, LabelColor); // Magic Resistance - AddHtml(320, 204, 35, 18, FormatSkill(c, SkillName.MagicResist)); + builder.AddHtmlLocalized(153, 204, 160, 18, 1044086, LabelColor); // Magic Resistance + builder.AddHtml(320, 204, 35, 18, FormatSkill(c, SkillName.MagicResist)); - AddHtmlLocalized(153, 222, 160, 18, 1044061, LabelColor); // Anatomy - AddHtml(320, 222, 35, 18, FormatSkill(c, SkillName.Anatomy)); + builder.AddHtmlLocalized(153, 222, 160, 18, 1044061, LabelColor); // Anatomy + builder.AddHtml(320, 222, 35, 18, FormatSkill(c, SkillName.Anatomy)); if (c is CuSidhe) { - AddHtmlLocalized(153, 240, 160, 18, 1044077, LabelColor); // Healing - AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Healing)); + builder.AddHtmlLocalized(153, 240, 160, 18, 1044077, LabelColor); // Healing + builder.AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Healing)); } else { - AddHtmlLocalized(153, 240, 160, 18, 1044090, LabelColor); // Poisoning - AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Poisoning)); + builder.AddHtmlLocalized(153, 240, 160, 18, 1044090, LabelColor); // Poisoning + builder.AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Poisoning)); } // TODO: Add remaining combat skills - AddImage(128, 260, 2086); - AddHtmlLocalized(147, 258, 160, 18, 3001032, 200); // Lore & Knowledge + builder.AddImage(128, 260, 2086); + builder.AddHtmlLocalized(147, 258, 160, 18, 3001032, 200); // Lore & Knowledge - AddHtmlLocalized(153, 276, 160, 18, 1044085, LabelColor); // Magery - AddHtml(320, 276, 35, 18, FormatSkill(c, SkillName.Magery)); + builder.AddHtmlLocalized(153, 276, 160, 18, 1044085, LabelColor); // Magery + builder.AddHtml(320, 276, 35, 18, FormatSkill(c, SkillName.Magery)); - AddHtmlLocalized(153, 294, 160, 18, 1044076, LabelColor); // Evaluating Intelligence - AddHtml(320, 294, 35, 18, FormatSkill(c, SkillName.EvalInt)); + builder.AddHtmlLocalized(153, 294, 160, 18, 1044076, LabelColor); // Evaluating Intelligence + builder.AddHtml(320, 294, 35, 18, FormatSkill(c, SkillName.EvalInt)); - AddHtmlLocalized(153, 312, 160, 18, 1044106, LabelColor); // Meditation - AddHtml(320, 312, 35, 18, FormatSkill(c, SkillName.Meditation)); + builder.AddHtmlLocalized(153, 312, 160, 18, 1044106, LabelColor); // Meditation + builder.AddHtml(320, 312, 35, 18, FormatSkill(c, SkillName.Meditation)); // TODO: Add remaining skills - AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); - AddPage(++page); + builder.AddPage(++page); - AddImage(128, 152, 2086); - AddHtmlLocalized(147, 150, 160, 18, 1049563, 200); // Preferred Foods + builder.AddImage(128, 152, 2086); + builder.AddHtmlLocalized(147, 150, 160, 18, 1049563, 200); // Preferred Foods var foodPref = 3000340; @@ -296,10 +312,10 @@ namespace Server.SkillHandlers // TODO: Add 1115752 "Blackrock Stew" as a food type - AddHtmlLocalized(153, 168, 160, 18, foodPref, LabelColor); + builder.AddHtmlLocalized(153, 168, 160, 18, foodPref, LabelColor); - AddImage(128, 188, 2086); - AddHtmlLocalized(147, 186, 160, 18, 1049569, 200); // Pack Instincts + builder.AddImage(128, 188, 2086); + builder.AddHtmlLocalized(147, 186, 160, 18, 1049569, 200); // Pack Instincts var packInstinct = 3000340; @@ -336,7 +352,7 @@ namespace Server.SkillHandlers packInstinct = 1049577; // Bull } - AddHtmlLocalized(153, 204, 160, 18, packInstinct, LabelColor); + builder.AddHtmlLocalized(153, 204, 160, 18, packInstinct, LabelColor); // TODO: Add Pet Slots @@ -344,10 +360,10 @@ namespace Server.SkillHandlers if (!Core.AOS) { - AddImage(128, 224, 2086); - AddHtmlLocalized(147, 222, 160, 18, 1049594, 200); // Loyalty Rating + builder.AddImage(128, 224, 2086); + builder.AddHtmlLocalized(147, 222, 160, 18, 1049594, 200); // Loyalty Rating - AddHtmlLocalized( + builder.AddHtmlLocalized( 153, 240, 160, @@ -357,8 +373,8 @@ namespace Server.SkillHandlers ); } - AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, 1); - AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, 1); + builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); } private static string FormatSkill(BaseCreature c, SkillName name)