perf: Migrate SoulStone and TMap chest gumps to DynamicGump (#2417)

## Summary

Migrates the five-step SoulStone wizard and the TreasureMapChest remove-confirmation dialog from legacy `Gump` to the modern builder API.

Per-gump base type:

- **`SelectSkillGump` -> `DynamicGump`** -- the skill picker iterates the player's skill list and emits one button per non-zero skill, so the layout shape varies per instance.
- **`ConfirmSkillGump` -> `DynamicGump`** -- skill name uses `AosSkillBonuses.GetLabel(...)` which returns dynamic clilocs in the `1044060 + (int)skill` range, plus current/cap skill values rendered as text labels.
- **`ConfirmTransferGump` -> `DynamicGump`** -- same dynamic skill cliloc plus per-instance Base/Cap/Stored values.
- **`ConfirmRemovalGump` -> `StaticGump<ConfirmRemovalGump>`** -- only fixed clilocs (warning text, Continue, Cancel), so the layout caches.
- **`ErrorGump` -> `DynamicGump`** -- title and message clilocs are constructor parameters that vary per call site.
- **`TreasureMapChest.RemoveGump` -> `StaticGump<RemoveGump>`** -- fixed-cliloc confirmation prompt (no item list, despite the name); `Closable=false`/`Disposable=false` are now `builder.SetNoClose()`/`builder.SetNoDispose()`.

All six gumps are now `Singleton => true`, have private constructors, and expose a static `DisplayTo` entry point that validates `from`, `NetState`, and the underlying entity before constructing -- prevents the empty-gump leak. Wizard navigation between steps now goes through `DisplayTo` (e.g. `ConfirmSkillGump.DisplayTo(from, _stone, skill)` from the skill picker, `ErrorGump.DisplayTo(...)` from absorption pre-checks, `SelectSkillGump.DisplayTo(...)` from the "make another selection" button on `ConfirmSkillGump` and from `ErrorGump` bounce-back). Because each gump is Singleton, sending the same type again automatically closes any prior instance instead of stacking; the explicit `gumps.Close<T>()` chain on `OnDoubleClick` is preserved so opening the soulstone still resets any orphaned step from another wizard.

`OnResponse` now uses `in RelayInfo info`. All inline `AddX(...)` calls move to `builder.AddX(...)` inside `BuildLayout`. `Skill.Base.ToString("F1")` etc. are converted to `$"{value:F1}"` interpolation passed to `AddLabel(ReadOnlySpan<char>)`. Skill picker pagination still uses client-side `AddPage` / `GumpButtonType.Page` -- no server-state pagination to migrate.
This commit is contained in:
Kamron Batman 2026-04-25 20:38:24 -07:00 committed by GitHub
parent b90ac0d481
commit 4e565ca6da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 232 additions and 139 deletions

View file

@ -430,12 +430,7 @@ public partial class TreasureMapChest : LockableContainer
public void BeginRemove(Mobile from)
{
if (!from.Alive)
{
return;
}
from.SendGump(new RemoveGump(from, this));
RemoveGump.DisplayTo(from, this);
}
public void EndRemove(Mobile from)
@ -449,42 +444,50 @@ public partial class TreasureMapChest : LockableContainer
Delete();
}
private class RemoveGump : Gump
private class RemoveGump : StaticGump<RemoveGump>
{
private readonly TreasureMapChest _chest;
private readonly Mobile _from;
public override bool Singleton => true;
public RemoveGump(Mobile from, TreasureMapChest chest) : base(15, 15)
private RemoveGump(TreasureMapChest chest) : base(15, 15) => _chest = chest;
public static void DisplayTo(Mobile from, TreasureMapChest chest)
{
_from = from;
_chest = chest;
if (from?.NetState == null || !from.Alive || chest == null || chest.Deleted)
{
return;
}
Closable = false;
Disposable = false;
from.SendGump(new RemoveGump(chest));
}
AddPage(0);
protected override void BuildLayout(ref StaticGumpBuilder builder)
{
builder.AddPage();
AddBackground(30, 0, 240, 240, 2620);
builder.SetNoClose();
builder.SetNoDispose();
builder.AddBackground(30, 0, 240, 240, 2620);
// When this treasure chest is removed, any items still inside of it will be lost.
AddHtmlLocalized(45, 15, 200, 80, 1048125, 0x7FFF);
builder.AddHtmlLocalized(45, 15, 200, 80, 1048125, 0x7FFF);
// Are you certain you're ready to remove this chest?
AddHtmlLocalized(45, 95, 200, 60, 1048126, 0x7FFF);
builder.AddHtmlLocalized(45, 95, 200, 60, 1048126, 0x7FFF);
AddButton(40, 153, 4005, 4007, 1);
AddHtmlLocalized(75, 155, 180, 40, 1048127, 0x7FFF); // Remove the Treasure Chest
builder.AddButton(40, 153, 4005, 4007, 1);
builder.AddHtmlLocalized(75, 155, 180, 40, 1048127, 0x7FFF); // Remove the Treasure Chest
AddButton(40, 195, 4005, 4007, 2);
AddHtmlLocalized(75, 197, 180, 35, 1006045, 0x7FFF); // Cancel
builder.AddButton(40, 195, 4005, 4007, 2);
builder.AddHtmlLocalized(75, 197, 180, 35, 1006045, 0x7FFF); // Cancel
}
public override void OnResponse(NetState sender, in RelayInfo info)
{
if (info.ButtonID == 1)
{
_chest.EndRemove(_from);
_chest.EndRemove(sender.Mobile);
}
}
}