perf: Migrate Travel/Moongate gumps to DynamicGump (#2412)

## Summary

Migrates the three player-facing travel/moongate gumps from legacy `Gump` to the modern `DynamicGump` system.

- `GoGump` (Gumps/Go/GoGump.cs) → `DynamicGump`. The category-tree layout's row count varies with the current `GoCategory`'s child count and pagination. Refreshes via `SendGump(this)` after mutating `_node`/`_page` instead of allocating a new instance per nav/page click.
- `MoongateGump` (Items/Misc/PublicMoongate.cs) → `DynamicGump`. The destination tab strip and per-map pages are gated by ruleset (sigil bearer, murderer, faction facet) and expansion/young flag, plus the configured map selection. The set of pages and the active-map swap make the layout shape per-instance.
- `MoongateConfirmGump` (Items/Skill Items/Magical/Misc/Moongate.cs) → `DynamicGump`. Per the **dynamic-cliloc rule**, the gump bakes one of two different cliloc numbers (1062050 Felucca-warning vs 1062049 generic confirm) and selects between an AOS and pre-AOS layout shape — both characteristics force `DynamicGump` rather than `StaticGump<T>` because cached layout bytes would otherwise lock in the wrong cliloc/shape.

All three now use a `private` constructor with a `public static DisplayTo(...)` entry point that validates prerequisites before any gump is allocated (empty-gump rule, CLAUDE.md §13). All are `Singleton` and use `SendGump(this)` self-refresh on internal navigation. Updated callers: `PublicMoongate.UseGate` and `Moongate.BeginConfirmation` now call `DisplayTo(...)`.
This commit is contained in:
Kamron Batman 2026-04-25 17:03:24 -07:00 committed by GitHub
parent 2423950cca
commit 83f771c99a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 218 additions and 188 deletions

View file

@ -5,15 +5,15 @@ using static Server.Gumps.PropsConfig;
namespace Server.Gumps;
public class GoGump : Gump
public class GoGump : DynamicGump
{
private const int EntryWidth = 180;
private const int EntryCount = 15;
private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private readonly GoCategory _node;
private readonly int _page;
private GoCategory _node;
private int _page;
private readonly LocationTree _tree;
@ -33,46 +33,61 @@ public class GoGump : Gump
_page = page;
_tree = tree;
_node = node;
var count = Math.Clamp(node.Categories.Length + node.Locations.Length - page * EntryCount, 0, EntryCount);
AddPage(0);
this.AddPropsFrame(TotalWidth, count + 1, out var x, out var y);
this.AddPropsHeaderWithBack(
TotalWidth, ref x, ref y, node.Name,
node.Parent != null, 1,
page > 0, 2,
(page + 1) * EntryCount < node.Categories.Length + node.Locations.Length, 3,
nextType: GumpButtonType.Reply, nextParam: 1
);
var totalEntryCount = node.Categories.Length + node.Locations.Length;
for (int i = 0, index = page * EntryCount; i < EntryCount && index < totalEntryCount; ++i, ++index)
{
PropsLayout.NextRow(ref x, ref y);
var name = index >= node.Categories.Length
? node.Locations[index - node.Categories.Length].Name
: node.Categories[index].Name;
this.AddPropsEntryButton(ref x, ref y, EntryWidth, name, true, index + 4);
}
}
public static void DisplayTo(Mobile from)
{
if (from?.NetState == null)
{
return;
}
var tree = GoLocations.GetLocations(from.Map);
if (tree == null)
{
return;
}
if (!tree.LastBranch.TryGetValue(from, out var branch))
{
branch = tree.Root;
}
if (branch != null)
if (branch == null)
{
from.SendGump(new GoGump(0, from, tree, branch));
return;
}
from.SendGump(new GoGump(0, from, tree, branch));
}
protected override void BuildLayout(ref DynamicGumpBuilder builder)
{
var count = Math.Clamp(_node.Categories.Length + _node.Locations.Length - _page * EntryCount, 0, EntryCount);
builder.AddPage();
builder.AddPropsFrame(TotalWidth, count + 1, out var x, out var y);
builder.AddPropsHeaderWithBack(
TotalWidth, ref x, ref y, _node.Name,
_node.Parent != null, 1,
_page > 0, 2,
(_page + 1) * EntryCount < _node.Categories.Length + _node.Locations.Length, 3,
nextType: GumpButtonType.Reply, nextParam: 1
);
var totalEntryCount = _node.Categories.Length + _node.Locations.Length;
for (int i = 0, index = _page * EntryCount; i < EntryCount && index < totalEntryCount; ++i, ++index)
{
PropsLayout.NextRow(ref x, ref y);
var name = index >= _node.Categories.Length
? _node.Locations[index - _node.Categories.Length].Name
: _node.Categories[index].Name;
builder.AddPropsEntryButton(ref x, ref y, EntryWidth, name, true, index + 4);
}
}
@ -86,7 +101,19 @@ public class GoGump : Gump
{
if (_node.Parent != null)
{
from.SendGump(new GoGump(0, from, _tree, _node.Parent));
_page = 0;
_node = _node.Parent;
if (_node == _tree.Root)
{
_tree.LastBranch.Remove(from);
}
else
{
_tree.LastBranch[from] = _node;
}
from.SendGump(this);
}
break;
@ -95,7 +122,8 @@ public class GoGump : Gump
{
if (_page > 0)
{
from.SendGump(new GoGump(_page - 1, from, _tree, _node));
_page--;
from.SendGump(this);
}
break;
@ -104,7 +132,8 @@ public class GoGump : Gump
{
if ((_page + 1) * EntryCount < _node.Categories.Length + _node.Locations.Length)
{
from.SendGump(new GoGump(_page + 1, from, _tree, _node));
_page++;
from.SendGump(this);
}
break;
@ -120,7 +149,10 @@ public class GoGump : Gump
if (index < _node.Categories.Length)
{
from.SendGump(new GoGump(0, from, _tree, _node.Categories[index]));
_page = 0;
_node = _node.Categories[index];
_tree.LastBranch[from] = _node;
from.SendGump(this);
}
else
{

View file

@ -84,7 +84,7 @@ public partial class PublicMoongate : Item
return false;
}
m.SendGump(new MoongateGump(m, this));
MoongateGump.DisplayTo(m, this);
if (!m.Hidden || m.AccessLevel == AccessLevel.Player)
{
@ -176,13 +176,12 @@ public class PMEntry
public class PMList
{
public static PMList Trammel =
public static readonly PMList Trammel =
new(
1012000,
1012012,
Map.Trammel,
new[]
{
[
new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow
new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain
new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom
@ -193,16 +192,15 @@ public class PMList
/* Dynamic Z for Magincia to support both old and new maps. */
new PMEntry(new Point3D(3563, 2139, Map.Trammel.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia
new PMEntry(new Point3D(3450, 2677, 25), 1078098) // New Haven
}
]
);
public static PMList Felucca =
public static readonly PMList Felucca =
new(
1012001,
1012013,
Map.Felucca,
new[]
{
[
new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow
new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain
new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom
@ -213,16 +211,15 @@ public class PMList
/* Dynamic Z for Magincia to support both old and new maps. */
new PMEntry(new Point3D(3563, 2139, Map.Felucca.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia
new PMEntry(new Point3D(2711, 2234, 0), 1019001) // Buccaneer's Den
}
]
);
public static PMList Ilshenar =
public static readonly PMList Ilshenar =
new(
1012002,
1012014,
Map.Ilshenar,
new[]
{
[
new PMEntry(new Point3D(1215, 467, -13), 1012015), // Compassion
new PMEntry(new Point3D(722, 1366, -60), 1012016), // Honesty
new PMEntry(new Point3D(744, 724, -28), 1012017), // Honor
@ -232,72 +229,68 @@ public class PMList
new PMEntry(new Point3D(1532, 1340, -3), 1012021), // Spirituality
new PMEntry(new Point3D(528, 216, -45), 1012022), // Valor
new PMEntry(new Point3D(1721, 218, 96), 1019000) // Chaos
}
]
);
public static PMList Malas =
public static readonly PMList Malas =
new(
1060643,
1062039,
Map.Malas,
new[]
{
[
new PMEntry(new Point3D(1015, 527, -65), 1060641), // Luna
new PMEntry(new Point3D(1997, 1386, -85), 1060642) // Umbra
}
]
);
public static PMList Tokuno =
public static readonly PMList Tokuno =
new(
1063258,
1063415,
Map.Tokuno,
new[]
{
[
new PMEntry(new Point3D(1169, 998, 41), 1063412), // Isamu-Jima
new PMEntry(new Point3D(802, 1204, 25), 1063413), // Makoto-Jima
new PMEntry(new Point3D(270, 628, 15), 1063414) // Homare-Jima
}
]
);
public static PMList TerMur =
public static readonly PMList TerMur =
new(
1113602,
1113604,
Map.TerMur,
new[]
{
new PMEntry(new Point3D(850, 3525, -38), 1113603), // Royal City
}
[
new PMEntry(new Point3D(850, 3525, -38), 1113603) // Royal City
]
);
public static PMList TerMurEodon =
public static readonly PMList TerMurEodon =
new(
1113602,
1113604,
Map.TerMur,
new[]
{
[
new PMEntry(new Point3D(850, 3525, -38), 1113603), // Royal City
new PMEntry(new Point3D(719, 1863, 40), 1156262) // Valley of Eodon
}
]
);
public static PMList[] NoTrammelLists = { Felucca };
public static PMList[] T2ALists = { Trammel, Felucca };
public static PMList[] T2AListsYoung = { Trammel };
public static PMList[] LBRLists = { Trammel, Felucca, Ilshenar };
public static PMList[] LBRListsYoung = { Trammel, Ilshenar };
public static PMList[] AOSLists = { Trammel, Felucca, Ilshenar, Malas };
public static PMList[] AOSListsYoung = { Trammel, Ilshenar, Malas };
public static PMList[] SELists = { Trammel, Felucca, Ilshenar, Malas, Tokuno };
public static PMList[] SEListsYoung = { Trammel, Ilshenar, Malas, Tokuno };
public static PMList[] SALists = { Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMur };
public static PMList[] SAListsYoung = { Trammel, Ilshenar, Malas, Tokuno, TerMur };
public static PMList[] TOLLists = { Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMurEodon };
public static PMList[] TOLListsYoung = { Trammel, Ilshenar, Malas, Tokuno, TerMurEodon };
public static PMList[] RedLists = { Felucca };
public static PMList[] SigilLists = { Felucca };
public static readonly PMList[] NoTrammelLists = [Felucca];
public static readonly PMList[] T2ALists = [Trammel, Felucca];
public static readonly PMList[] T2AListsYoung = [Trammel];
public static readonly PMList[] LBRLists = [Trammel, Felucca, Ilshenar];
public static readonly PMList[] LBRListsYoung = [Trammel, Ilshenar];
public static readonly PMList[] AOSLists = [Trammel, Felucca, Ilshenar, Malas];
public static readonly PMList[] AOSListsYoung = [Trammel, Ilshenar, Malas];
public static readonly PMList[] SELists = [Trammel, Felucca, Ilshenar, Malas, Tokuno];
public static readonly PMList[] SEListsYoung = [Trammel, Ilshenar, Malas, Tokuno];
public static readonly PMList[] SALists = [Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMur];
public static readonly PMList[] SAListsYoung = [Trammel, Ilshenar, Malas, Tokuno, TerMur];
public static readonly PMList[] TOLLists = [Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMurEodon];
public static readonly PMList[] TOLListsYoung = [Trammel, Ilshenar, Malas, Tokuno, TerMurEodon];
public static readonly PMList[] RedLists = [Felucca];
public static readonly PMList[] SigilLists = [Felucca];
public PMList(int number, int selNumber, Map map, PMEntry[] entries)
{
@ -316,18 +309,27 @@ public class PMList
public PMEntry[] Entries { get; }
}
public class MoongateGump : Gump
public class MoongateGump : DynamicGump
{
private PMList[] _lists;
private Mobile _mobile;
private Item _moongate;
private readonly PMList[] _lists;
private readonly PMList[] _tabOrder;
private readonly Item _moongate;
public override bool Singleton => true;
public MoongateGump(Mobile mobile, Item moongate) : base(100, 100)
private MoongateGump(Item moongate, PMList[] lists, PMList[] tabOrder) : base(100, 100)
{
_mobile = mobile;
_moongate = moongate;
_lists = lists;
_tabOrder = tabOrder;
}
public static void DisplayTo(Mobile mobile, Item moongate)
{
if (mobile?.NetState == null || moongate == null || moongate.Deleted)
{
return;
}
PMList[] checkLists;
@ -376,71 +378,80 @@ public class MoongateGump : Gump
}
var mapCount = filteredBySelectedMaps.Count;
_lists = new PMList[mapCount];
for (var i = 0; i < mapCount; i++)
if (mapCount == 0)
{
_lists[i] = filteredBySelectedMaps[i];
return;
}
for (var i = 0; i < _lists.Length; ++i)
var lists = filteredBySelectedMaps.ToArray();
var tabOrder = filteredBySelectedMaps.ToArray();
for (var i = 0; i < lists.Length; ++i)
{
if (_lists[i].Map == mobile.Map)
if (lists[i].Map == mobile.Map)
{
(_lists[i], _lists[0]) = (_lists[0], _lists[i]);
(lists[i], lists[0]) = (lists[0], lists[i]);
break;
}
}
AddPage(0);
mobile.SendGump(new MoongateGump(moongate, lists, tabOrder));
}
AddBackground(0, 0, 380, 280, 5054);
protected override void BuildLayout(ref DynamicGumpBuilder builder)
{
builder.AddPage();
AddButton(10, 210, 4005, 4007, 1);
AddHtmlLocalized(45, 210, 140, 25, 1011036); // OKAY
builder.AddBackground(0, 0, 380, 280, 5054);
AddButton(10, 235, 4005, 4007, 0);
AddHtmlLocalized(45, 235, 140, 25, 1011012); // CANCEL
builder.AddButton(10, 210, 4005, 4007, 1);
builder.AddHtmlLocalized(45, 210, 140, 25, 1011036); // OKAY
AddHtmlLocalized(5, 5, 200, 20, 1012011); // Pick your destination:
builder.AddButton(10, 235, 4005, 4007, 0);
builder.AddHtmlLocalized(45, 235, 140, 25, 1011012); // CANCEL
for (var i = 0; i < filteredBySelectedMaps.Count; ++i)
builder.AddHtmlLocalized(5, 5, 200, 20, 1012011); // Pick your destination:
// Tab buttons on page 0 — rendered in original (pre-swap) order so the active map appears at its expected slot.
for (var i = 0; i < _tabOrder.Length; ++i)
{
AddButton(10, 35 + i * 25, 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf(_lists, filteredBySelectedMaps[i]) + 1);
AddHtmlLocalized(30, 35 + i * 25, 150, 20, filteredBySelectedMaps[i].Number);
builder.AddButton(10, 35 + i * 25, 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf(_lists, _tabOrder[i]) + 1);
builder.AddHtmlLocalized(30, 35 + i * 25, 150, 20, _tabOrder[i].Number);
}
for (var i = 0; i < _lists.Length; ++i)
{
RenderPage(i, filteredBySelectedMaps.IndexOf(_lists[i]));
RenderPage(ref builder, i, Array.IndexOf(_tabOrder, _lists[i]));
}
}
private void RenderPage(int index, int offset)
private void RenderPage(ref DynamicGumpBuilder builder, int index, int offset)
{
var list = _lists[index];
AddPage(index + 1);
builder.AddPage(index + 1);
AddButton(10, 35 + offset * 25, 2117, 2118, 0, GumpButtonType.Page, index + 1);
AddHtmlLocalized(30, 35 + offset * 25, 150, 20, list.SelNumber);
builder.AddButton(10, 35 + offset * 25, 2117, 2118, 0, GumpButtonType.Page, index + 1);
builder.AddHtmlLocalized(30, 35 + offset * 25, 150, 20, list.SelNumber);
var entries = list.Entries;
for (var i = 0; i < entries.Length; ++i)
{
AddRadio(200, 35 + i * 25, 210, 211, false, index * 100 + i);
AddHtmlLocalized(225, 35 + i * 25, 150, 20, entries[i].Number);
builder.AddRadio(200, 35 + i * 25, 210, 211, false, index * 100 + i);
builder.AddHtmlLocalized(225, 35 + i * 25, 150, 20, entries[i].Number);
}
}
public override void OnResponse(NetState state, in RelayInfo info)
{
if (info.ButtonID == 0) // Cancel
if (info.ButtonID == 0 || _moongate.Deleted)
{
return;
}
if (_mobile.Deleted || _moongate.Deleted || _mobile.Map == null)
var from = state.Mobile;
if (from.Map == null)
{
return;
}
@ -470,43 +481,40 @@ public class MoongateGump : Gump
var entry = list.Entries[listEntry];
if (!_mobile.InRange(_moongate.GetWorldLocation(), 1) || _mobile.Map != _moongate.Map)
if (!from.InRange(_moongate.GetWorldLocation(), 1) || from.Map != _moongate.Map)
{
_mobile.SendLocalizedMessage(1019002); // You are too far away to use the gate.
from.SendLocalizedMessage(1019002); // You are too far away to use the gate.
}
else if (_mobile.Player && _mobile.Murderer && list.Map != Map.Felucca)
else if (from.Player && from.Murderer && list.Map != Map.Felucca ||
Sigil.ExistsOn(from) && list.Map != Faction.Facet)
{
_mobile.SendLocalizedMessage(1019004); // You are not allowed to travel there.
from.SendLocalizedMessage(1019004); // You are not allowed to travel there.
}
else if (Sigil.ExistsOn(_mobile) && list.Map != Faction.Facet)
else if (from.Criminal)
{
_mobile.SendLocalizedMessage(1019004); // You are not allowed to travel there.
from.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily.
}
else if (_mobile.Criminal)
else if (SpellHelper.CheckCombat(from))
{
_mobile.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily.
from.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle??
}
else if (SpellHelper.CheckCombat(_mobile))
else if (from.Spell != null)
{
_mobile.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle??
from.SendLocalizedMessage(1049616); // You are too busy to do that at the moment.
}
else if (_mobile.Spell != null)
else if (from.Map == list.Map && from.InRange(entry.Location, 1))
{
_mobile.SendLocalizedMessage(1049616); // You are too busy to do that at the moment.
}
else if (_mobile.Map == list.Map && _mobile.InRange(entry.Location, 1))
{
_mobile.SendLocalizedMessage(1019003); // You are already there.
from.SendLocalizedMessage(1019003); // You are already there.
}
else
{
BaseCreature.TeleportPets(_mobile, entry.Location, list.Map);
BaseCreature.TeleportPets(from, entry.Location, list.Map);
_mobile.Combatant = null;
_mobile.Warmode = false;
_mobile.Hidden = true;
from.Combatant = null;
from.Warmode = false;
from.Hidden = true;
_mobile.MoveToWorld(entry.Location, list.Map);
from.MoveToWorld(entry.Location, list.Map);
Effects.PlaySound(entry.Location, list.Map, 0x1FE);
}

View file

@ -157,7 +157,7 @@ public partial class Moongate : Item
from.SendSound(0x20E, from);
}
from.SendGump(new MoongateConfirmGump(from, this));
MoongateConfirmGump.DisplayTo(from, this);
}
else
{
@ -301,90 +301,80 @@ public partial class ConfirmationMoongate : Moongate
}
}
public class MoongateConfirmGump : Gump
public class MoongateConfirmGump : DynamicGump
{
private Mobile _from;
private Moongate _gate;
private readonly Mobile _from;
private readonly Moongate _gate;
public override bool Singleton => true;
public MoongateConfirmGump(Mobile from, Moongate gate) : base(Core.AOS ? 110 : 20, Core.AOS ? 100 : 30)
private MoongateConfirmGump(Mobile from, Moongate gate) : base(Core.AOS ? 110 : 20, Core.AOS ? 100 : 30)
{
_from = from;
_gate = gate;
}
public static void DisplayTo(Mobile from, Moongate gate)
{
if (from?.NetState == null || gate == null || gate.Deleted)
{
return;
}
from.SendGump(new MoongateConfirmGump(from, gate));
}
protected override void BuildLayout(ref DynamicGumpBuilder builder)
{
builder.AddPage();
if (Core.AOS)
{
Closable = false;
builder.SetNoClose();
AddPage(0);
builder.AddBackground(0, 0, 420, 280, 5054);
AddBackground(0, 0, 420, 280, 5054);
builder.AddImageTiled(10, 10, 400, 20, 2624);
builder.AddAlphaRegion(10, 10, 400, 20);
AddImageTiled(10, 10, 400, 20, 2624);
AddAlphaRegion(10, 10, 400, 20);
builder.AddHtmlLocalized(10, 10, 400, 20, 1062051, 30720); // Gate Warning
AddHtmlLocalized(10, 10, 400, 20, 1062051, 30720); // Gate Warning
builder.AddImageTiled(10, 40, 400, 200, 2624);
builder.AddAlphaRegion(10, 40, 400, 200);
AddImageTiled(10, 40, 400, 200, 2624);
AddAlphaRegion(10, 40, 400, 200);
if (from.Map != Map.Felucca && gate.TargetMap == Map.Felucca && gate.ShowFeluccaWarning)
if (_from.Map != Map.Felucca && _gate.TargetMap == Map.Felucca && _gate.ShowFeluccaWarning)
{
AddHtmlLocalized(
10,
40,
400,
200,
1062050, // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here
32512,
false,
true
);
// This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here
builder.AddHtmlLocalized(10, 40, 400, 200, 1062050, 32512, false, true);
}
else
{
AddHtmlLocalized(
10,
40,
400,
200,
1062049, // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here
32512,
false,
true
);
// Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here
builder.AddHtmlLocalized(10, 40, 400, 200, 1062049, 32512, false, true);
}
AddImageTiled(10, 250, 400, 20, 2624);
AddAlphaRegion(10, 250, 400, 20);
builder.AddImageTiled(10, 250, 400, 20, 2624);
builder.AddAlphaRegion(10, 250, 400, 20);
AddButton(10, 250, 4005, 4007, 1);
AddHtmlLocalized(40, 250, 170, 20, 1011036, 32767); // OKAY
builder.AddButton(10, 250, 4005, 4007, 1);
builder.AddHtmlLocalized(40, 250, 170, 20, 1011036, 32767); // OKAY
AddButton(210, 250, 4005, 4007, 0);
AddHtmlLocalized(240, 250, 170, 20, 1011012, 32767); // CANCEL
builder.AddButton(210, 250, 4005, 4007, 0);
builder.AddHtmlLocalized(240, 250, 170, 20, 1011012, 32767); // CANCEL
}
else
{
AddPage(0);
builder.AddBackground(0, 0, 420, 400, 5054);
builder.AddBackground(10, 10, 400, 380, 3000);
AddBackground(0, 0, 420, 400, 5054);
AddBackground(10, 10, 400, 380, 3000);
// Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here
builder.AddHtmlLocalized(20, 40, 380, 60, 1062049);
AddHtmlLocalized(
20,
40,
380,
60,
1062049 // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here
);
builder.AddHtmlLocalized(55, 110, 290, 20, 1011012); // CANCEL
builder.AddButton(20, 110, 4005, 4007, 0);
AddHtmlLocalized(55, 110, 290, 20, 1011012); // CANCEL
AddButton(20, 110, 4005, 4007, 0);
AddHtmlLocalized(55, 140, 290, 40, 1011011); // CONTINUE
AddButton(20, 140, 4005, 4007, 1);
builder.AddHtmlLocalized(55, 140, 290, 40, 1011011); // CONTINUE
builder.AddButton(20, 140, 4005, 4007, 1);
}
}