feat: Moves gumps out of the core (#1916)

> [!Important]
> **Developer Note**
> This code change will **completely move gumps out of the core**


### Summary

- Adds `GetGumps()` convenience which exposes methods to Find/Close/Send multiple gumps. This helper is a performance improvement by eliminating the Dictionary<Player, List> lookup for gumps.
This commit is contained in:
Kamron Batman 2024-08-09 19:07:32 -07:00 • committed by GitHub
parent 40d99f6d1c
commit 8282b00ca2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
246 changed files with 1722 additions and 1383 deletions

View file

@ -307,9 +307,10 @@ public class AddonGenerator
private const int GreenHue = 0x40;
private readonly PickerState _state;
public override bool Singleton => true;
public InternalGump(Mobile m, PickerState state) : base(100, 50)
{
m.CloseGump<InternalGump>();
_state = state;
MakeGump();
}

View file

@ -524,11 +524,11 @@ namespace Server.Commands.Generic
if (match.Length < 3)
{
e.Mobile.SendMessage("Invalid search string.");
e.Mobile.SendGump(new AddGump(e.Mobile, match, 0, Type.EmptyTypes, false));
e.Mobile.SendGump(new AddGump(match, 0, Type.EmptyTypes, false));
}
else
{
e.Mobile.SendGump(new AddGump(e.Mobile, match, 0, AddGump.Match(match), true));
e.Mobile.SendGump(new AddGump(match, 0, AddGump.Match(match), true));
}
}
else

View file

@ -290,18 +290,20 @@ public static class HelpInfo
public override void OnResponse(NetState sender, in RelayInfo info)
{
var m = sender.Mobile;
var gumps = m.GetGumps();
switch (info.ButtonID)
{
case 0:
{
m.CloseGump<CommandInfoGump>();
gumps.Close<CommandInfoGump>();
break;
}
case 1:
{
if (_page > 0)
{
m.SendGump(new CommandListGump(_page - 1, m, _list));
gumps.Send(new CommandListGump(_page - 1, m, _list));
}
break;
@ -310,7 +312,7 @@ public static class HelpInfo
{
if ((_page + 1) * EntriesPerPage < SortedHelpInfo.Count)
{
m.SendGump(new CommandListGump(_page + 1, m, _list));
gumps.Send(new CommandListGump(_page + 1, m, _list));
}
break;
@ -325,13 +327,13 @@ public static class HelpInfo
if (m.AccessLevel >= c.AccessLevel)
{
m.SendGump(new CommandInfoGump(c));
m.SendGump(new CommandListGump(_page, m, _list));
gumps.Send(new CommandInfoGump(c));
gumps.Send(new CommandListGump(_page, m, _list));
}
else
{
m.SendMessage("You no longer have access to that command.");
m.SendGump(new CommandListGump(_page, m, null));
gumps.Send(new CommandListGump(_page, m, null));
}
}

View file

@ -14,14 +14,14 @@ public class AddGump : DynamicGump
private readonly string _searchString;
private readonly bool _explicitSearch;
public AddGump(Mobile from, string searchString, int page, Type[] searchResults, bool explicitSearch) : base(50, 50)
public override bool Singleton => true;
public AddGump(string searchString, int page, Type[] searchResults, bool explicitSearch) : base(50, 50)
{
_searchString = searchString;
_searchResults = searchResults;
_explicitSearch = explicitSearch;
_page = page;
from.CloseGump<AddGump>();
}
protected override void BuildLayout(ref DynamicGumpBuilder builder) {
@ -120,7 +120,7 @@ public class AddGump : DynamicGump
explicitSearch = true;
}
e.Mobile.SendGump(new AddGump(e.Mobile, val, 0, types, explicitSearch));
e.Mobile.SendGump(new AddGump(val, 0, types, explicitSearch));
}
private static void Match(string match, Type[] types, HashSet<Type> results)
@ -212,11 +212,11 @@ public class AddGump : DynamicGump
if (match.Length < 3)
{
from.SendMessage("Invalid search string.");
from.SendGump(new AddGump(from, match, _page, _searchResults, false));
from.SendGump(new AddGump(match, _page, _searchResults, false));
}
else
{
from.SendGump(new AddGump(from, match, 0, Match(match), true));
from.SendGump(new AddGump(match, 0, Match(match), true));
}
break;
@ -225,7 +225,7 @@ public class AddGump : DynamicGump
{
if (_page > 0)
{
from.SendGump(new AddGump(from, _searchString, _page - 1, _searchResults, true));
from.SendGump(new AddGump(_searchString, _page - 1, _searchResults, true));
}
break;
@ -234,7 +234,7 @@ public class AddGump : DynamicGump
{
if ((_page + 1) * 10 < _searchResults.Length)
{
from.SendGump(new AddGump(from, _searchString, _page + 1, _searchResults, true));
from.SendGump(new AddGump(_searchString, _page + 1, _searchResults, true));
}
break;
@ -307,7 +307,7 @@ public class AddGump : DynamicGump
{
if (cancelType == TargetCancelType.Canceled)
{
from.SendGump(new AddGump(from, m_SearchString, m_Page, m_SearchResults, true));
from.SendGump(new AddGump(m_SearchString, m_Page, m_SearchResults, true));
}
}
}

View file

@ -34,14 +34,14 @@ namespace Server.Gumps
private readonly Mobile m_Owner;
private int m_Page;
public override bool Singleton => true;
public CategorizedAddGump(Mobile owner) : this(owner, CAGCategory.Root)
{
}
public CategorizedAddGump(Mobile owner, CAGCategory category, int page = 0) : base(GumpOffsetX, GumpOffsetY)
{
owner.CloseGump<WhoGump>();
m_Owner = owner;
m_Category = category;

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace Server.Diagnostics;
public class GumpProfile : BaseProfile
{
private static readonly Dictionary<Type, GumpProfile> _profiles = new();
public GumpProfile(Type type) : base(type.FullName)
{
}
public static IEnumerable<GumpProfile> Profiles => _profiles.Values;
public static GumpProfile Acquire(Type type)
{
if (!Core.Profiling)
{
return null;
}
if (!_profiles.TryGetValue(type, out var prof))
{
_profiles.Add(type, prof = new GumpProfile(type));
}
return prof;
}
}

View file

@ -1,3 +1,5 @@
using Server.Gumps;
namespace Server.Engines.AdvancedSearch;
public static class AdvancedSearchCommand
@ -14,7 +16,6 @@ public static class AdvancedSearchCommand
{
var from = e.Mobile;
from.CloseGump<AdvancedSearchGump>();
from.SendGump(new AdvancedSearchGump(from));
from.SendGump(new AdvancedSearchGump(from), true);
}
}

View file

@ -4,11 +4,11 @@ using Server.Network;
namespace Server.Engines.BulkOrders
{
public class BOBFilterGump : Gump
public sealed class BOBFilterGump : DynamicGump
{
private const int LabelColor = 0x7FFF;
private static readonly int[,] m_MaterialFilters =
private static readonly int[,] _materialFilters =
{
{ 1044067, 1 }, // Blacksmithy
{ 1062226, 3 }, // Iron
@ -30,21 +30,21 @@ namespace Server.Engines.BulkOrders
{ 1062238, 16 } // Barbed
};
private static readonly int[,] m_TypeFilters =
private static readonly int[,] _typeFilters =
{
{ 1062229, 0 }, // All
{ 1062224, 1 }, // Small
{ 1062225, 2 } // Large
};
private static readonly int[,] m_QualityFilters =
private static readonly int[,] _qualityFilters =
{
{ 1062229, 0 }, // All
{ 1011542, 1 }, // Normal
{ 1060636, 2 } // Exceptional
};
private static readonly int[,] m_AmountFilters =
private static readonly int[,] _amountFilters =
{
{ 1062229, 0 }, // All
{ 1049706, 1 }, // 10
@ -52,74 +52,77 @@ namespace Server.Engines.BulkOrders
{ 1062239, 3 } // 20
};
private static readonly int[][,] m_Filters =
private static readonly int[][,] _filters =
{
m_TypeFilters,
m_QualityFilters,
m_MaterialFilters,
m_AmountFilters
_typeFilters,
_qualityFilters,
_materialFilters,
_amountFilters
};
private static readonly int[] m_XOffsets_Type = { 0, 75, 170 };
private static readonly int[] m_XOffsets_Quality = { 0, 75, 170 };
private static readonly int[] m_XOffsets_Amount = { 0, 75, 180, 275 };
private static readonly int[] m_XOffsets_Material = { 0, 105, 210, 305, 390, 485 };
private static readonly int[] _xOffsets_Type = [0, 75, 170];
private static readonly int[] _xOffsets_Quality = [0, 75, 170];
private static readonly int[] _xOffsets_Amount = [0, 75, 180, 275];
private static readonly int[] _xOffsets_Material = [0, 105, 210, 305, 390, 485];
private static readonly int[] m_XWidths_Small = { 50, 50, 70, 50 };
private static readonly int[] m_XWidths_Large = { 80, 50, 50, 50, 50, 50 };
private readonly BulkOrderBook m_Book;
private readonly PlayerMobile m_From;
private static readonly int[] _xWidths_Small = [50, 50, 70, 50];
private static readonly int[] _xWidths_Large = [80, 50, 50, 50, 50, 50];
private readonly BulkOrderBook _book;
private readonly PlayerMobile _from;
public override bool Singleton => true;
public BOBFilterGump(PlayerMobile from, BulkOrderBook book) : base(12, 24)
{
from.CloseGump<BOBGump>();
from.CloseGump<BOBFilterGump>();
m_From = from;
m_Book = book;
var f = from.UseOwnFilter ? from.BOBFilter : book.Filter;
AddPage(0);
AddBackground(10, 10, 600, 439, 5054);
AddImageTiled(18, 20, 583, 420, 2624);
AddAlphaRegion(18, 20, 583, 420);
AddImage(5, 5, 10460);
AddImage(585, 5, 10460);
AddImage(5, 424, 10460);
AddImage(585, 424, 10460);
AddHtmlLocalized(270, 32, 200, 32, 1062223, LabelColor); // Filter Preference
AddHtmlLocalized(26, 64, 120, 32, 1062228, LabelColor); // Bulk Order Type
AddFilterList(25, 96, m_XOffsets_Type, 40, m_TypeFilters, m_XWidths_Small, f.Type, 0);
AddHtmlLocalized(320, 64, 50, 32, 1062215, LabelColor); // Quality
AddFilterList(320, 96, m_XOffsets_Quality, 40, m_QualityFilters, m_XWidths_Small, f.Quality, 1);
AddHtmlLocalized(26, 160, 120, 32, 1062232, LabelColor); // Material Type
AddFilterList(25, 192, m_XOffsets_Material, 40, m_MaterialFilters, m_XWidths_Large, f.Material, 2);
AddHtmlLocalized(26, 320, 120, 32, 1062217, LabelColor); // Amount
AddFilterList(25, 352, m_XOffsets_Amount, 40, m_AmountFilters, m_XWidths_Small, f.Quantity, 3);
AddHtmlLocalized(75, 416, 120, 32, 1062477, from.UseOwnFilter ? LabelColor : 16927); // Set Book Filter
AddButton(40, 416, 4005, 4007, 1);
AddHtmlLocalized(235, 416, 120, 32, 1062478, from.UseOwnFilter ? 16927 : LabelColor); // Set Your Filter
AddButton(200, 416, 4005, 4007, 2);
AddHtmlLocalized(405, 416, 120, 32, 1062231, LabelColor); // Clear Filter
AddButton(370, 416, 4005, 4007, 3);
AddHtmlLocalized(540, 416, 50, 32, 1011046, LabelColor); // APPLY
AddButton(505, 416, 4017, 4018, 0);
_from = from;
_book = book;
}
private void AddFilterList(
protected override void BuildLayout(ref DynamicGumpBuilder builder)
{
var f = _from.UseOwnFilter ? _from.BOBFilter : _book.Filter;
builder.AddPage();
builder.AddBackground(10, 10, 600, 439, 5054);
builder.AddImageTiled(18, 20, 583, 420, 2624);
builder.AddAlphaRegion(18, 20, 583, 420);
builder.AddImage(5, 5, 10460);
builder.AddImage(585, 5, 10460);
builder.AddImage(5, 424, 10460);
builder.AddImage(585, 424, 10460);
builder.AddHtmlLocalized(270, 32, 200, 32, 1062223, LabelColor); // Filter Preference
builder.AddHtmlLocalized(26, 64, 120, 32, 1062228, LabelColor); // Bulk Order Type
AddFilterList(ref builder, 25, 96, _xOffsets_Type, 40, _typeFilters, _xWidths_Small, f.Type, 0);
builder.AddHtmlLocalized(320, 64, 50, 32, 1062215, LabelColor); // Quality
AddFilterList(ref builder, 320, 96, _xOffsets_Quality, 40, _qualityFilters, _xWidths_Small, f.Quality, 1);
builder.AddHtmlLocalized(26, 160, 120, 32, 1062232, LabelColor); // Material Type
AddFilterList(ref builder, 25, 192, _xOffsets_Material, 40, _materialFilters, _xWidths_Large, f.Material, 2);
builder.AddHtmlLocalized(26, 320, 120, 32, 1062217, LabelColor); // Amount
AddFilterList(ref builder, 25, 352, _xOffsets_Amount, 40, _amountFilters, _xWidths_Small, f.Quantity, 3);
builder.AddHtmlLocalized(75, 416, 120, 32, 1062477, _from.UseOwnFilter ? LabelColor : 16927); // Set Book Filter
builder.AddButton(40, 416, 4005, 4007, 1);
builder.AddHtmlLocalized(235, 416, 120, 32, 1062478, _from.UseOwnFilter ? 16927 : LabelColor); // Set Your Filter
builder.AddButton(200, 416, 4005, 4007, 2);
builder.AddHtmlLocalized(405, 416, 120, 32, 1062231, LabelColor); // Clear Filter
builder.AddButton(370, 416, 4005, 4007, 3);
builder.AddHtmlLocalized(540, 416, 50, 32, 1011046, LabelColor); // APPLY
builder.AddButton(505, 416, 4017, 4018, 0);
}
private void AddFilterList(ref DynamicGumpBuilder builder,
int x, int y, int[] xOffsets, int yOffset, int[,] filters, int[] xWidths, int filterValue,
int filterIndex
)
@ -136,7 +139,7 @@ namespace Server.Engines.BulkOrders
var isSelected = filters[i, 1] == filterValue ||
i % xOffsets.Length == 0 && filterValue == 0;
AddHtmlLocalized(
builder.AddHtmlLocalized(
x + 35 + xOffsets[i % xOffsets.Length],
y + i / xOffsets.Length * yOffset,
xWidths[i % xOffsets.Length],
@ -144,7 +147,8 @@ namespace Server.Engines.BulkOrders
number,
isSelected ? 16927 : LabelColor
);
AddButton(
builder.AddButton(
x + xOffsets[i % xOffsets.Length],
y + i / xOffsets.Length * yOffset,
4005,
@ -156,7 +160,7 @@ namespace Server.Engines.BulkOrders
public override void OnResponse(NetState sender, in RelayInfo info)
{
var f = m_From.UseOwnFilter ? m_From.BOBFilter : m_Book.Filter;
var f = _from.UseOwnFilter ? _from.BOBFilter : _book.Filter;
var index = info.ButtonID;
@ -164,28 +168,28 @@ namespace Server.Engines.BulkOrders
{
case 0: // Apply
{
m_From.SendGump(new BOBGump(m_From, m_Book));
_from.SendGump(new BOBGump(_from, _book));
break;
}
case 1: // Set Book Filter
{
m_From.UseOwnFilter = false;
m_From.SendGump(new BOBFilterGump(m_From, m_Book));
_from.UseOwnFilter = false;
_from.SendGump(new BOBFilterGump(_from, _book));
break;
}
case 2: // Set Your Filter
{
m_From.UseOwnFilter = true;
m_From.SendGump(new BOBFilterGump(m_From, m_Book));
_from.UseOwnFilter = true;
_from.SendGump(new BOBFilterGump(_from, _book));
break;
}
case 3: // Clear Filter
{
f.Clear();
m_From.SendGump(new BOBFilterGump(m_From, m_Book));
_from.SendGump(new BOBFilterGump(_from, _book));
break;
}
@ -196,9 +200,9 @@ namespace Server.Engines.BulkOrders
var type = index % 4;
index /= 4;
if (type >= 0 && type < m_Filters.Length)
if (type >= 0 && type < _filters.Length)
{
var filters = m_Filters[type];
var filters = _filters[type];
if (index >= 0 && index < filters.GetLength(0))
{
@ -223,7 +227,7 @@ namespace Server.Engines.BulkOrders
break;
}
m_From.SendGump(new BOBFilterGump(m_From, m_Book));
_from.SendGump(new BOBFilterGump(_from, _book));
}
}

View file

@ -11,20 +11,19 @@ namespace Server.Engines.BulkOrders
public class BOBGump : Gump
{
private const int LabelColor = 0x7FFF;
private readonly BulkOrderBook m_Book;
private readonly PlayerMobile m_From;
private readonly List<IBOBEntry> m_List;
private readonly BulkOrderBook _book;
private readonly PlayerMobile _from;
private readonly List<IBOBEntry> _list;
private int m_Page;
private int _page;
public override bool Singleton => true;
public BOBGump(PlayerMobile from, BulkOrderBook book, int page = 0, List<IBOBEntry> list = null) : base(12, 24)
{
from.CloseGump<BOBGump>();
from.CloseGump<BOBFilterGump>();
m_From = from;
m_Book = book;
m_Page = page;
_from = from;
_book = book;
_page = page;
if (list == null)
{
@ -41,7 +40,7 @@ namespace Server.Engines.BulkOrders
}
}
m_List = list;
_list = list;
var index = GetIndexForPage(page);
var count = GetCountForIndex(index);
@ -313,7 +312,7 @@ namespace Server.Engines.BulkOrders
Type itemType
)
{
var f = m_From.UseOwnFilter ? m_From.BOBFilter : m_Book.Filter;
var f = _from.UseOwnFilter ? _from.BOBFilter : _book.Filter;
if (f.IsDefault)
{
@ -394,7 +393,7 @@ namespace Server.Engines.BulkOrders
var slots = 0;
var count = 0;
var list = m_List;
var list = _list;
for (var i = index; i >= 0 && i < list.Count; ++i)
{
@ -429,7 +428,7 @@ namespace Server.Engines.BulkOrders
var page = 0;
int i;
var list = m_List;
var list = _list;
for (i = 0; i < index && i < list.Count; i++)
{
var entry = list[i];
@ -525,6 +524,13 @@ namespace Server.Engines.BulkOrders
return "Invalid";
}
public override void SendTo(NetState ns)
{
ns.CloseGump<BOBFilterGump>();
base.SendTo(ns);
}
public override void OnResponse(NetState sender, in RelayInfo info)
{
var index = info.ButtonID;
@ -537,34 +543,34 @@ namespace Server.Engines.BulkOrders
}
case 1: // Set Filter
{
m_From.SendGump(new BOBFilterGump(m_From, m_Book));
_from.SendGump(new BOBFilterGump(_from, _book));
break;
}
case 2: // Previous page
{
if (m_Page > 0)
if (_page > 0)
{
m_From.SendGump(new BOBGump(m_From, m_Book, m_Page - 1, m_List));
_from.SendGump(new BOBGump(_from, _book, _page - 1, _list));
}
return;
}
case 3: // Next page
{
if (GetIndexForPage(m_Page + 1) < m_List.Count)
if (GetIndexForPage(_page + 1) < _list.Count)
{
m_From.SendGump(new BOBGump(m_From, m_Book, m_Page + 1, m_List));
_from.SendGump(new BOBGump(_from, _book, _page + 1, _list));
}
break;
}
case 4: // Price all
{
if (m_Book.IsChildOf(m_From.Backpack))
if (_book.IsChildOf(_from.Backpack))
{
m_From.Prompt = new SetPricePrompt(m_Book, null, m_Page, m_List);
m_From.SendMessage("Type in a price for all deeds in the book:");
_from.Prompt = new SetPricePrompt(_book, null, _page, _list);
_from.SendMessage("Type in a price for all deeds in the book:");
}
break;
@ -576,28 +582,28 @@ namespace Server.Engines.BulkOrders
var type = index % 2;
index /= 2;
if (index < 0 || index >= m_List.Count)
if (index < 0 || index >= _list.Count)
{
break;
}
var bobEntry = m_List[index];
var bobEntry = _list[index];
if (!m_Book.Entries.Contains(bobEntry))
if (!_book.Entries.Contains(bobEntry))
{
m_From.SendLocalizedMessage(1062382); // The deed selected is not available.
_from.SendLocalizedMessage(1062382); // The deed selected is not available.
break;
}
if (type == 0) // Drop
{
if (m_Book.IsChildOf(m_From.Backpack))
if (_book.IsChildOf(_from.Backpack))
{
var item = bobEntry.Reconstruct();
var pack = m_From.Backpack;
var pack = _from.Backpack;
if (pack?.CheckHold(
m_From,
_from,
item,
true,
true,
@ -605,37 +611,37 @@ namespace Server.Engines.BulkOrders
item.PileWeight + item.TotalWeight
) != true)
{
m_From.SendLocalizedMessage(503204); // You do not have room in your backpack for this
m_From.SendGump(new BOBGump(m_From, m_Book, m_Page));
_from.SendLocalizedMessage(503204); // You do not have room in your backpack for this
_from.SendGump(new BOBGump(_from, _book, _page));
}
else
{
if (m_Book.IsChildOf(m_From.Backpack))
if (_book.IsChildOf(_from.Backpack))
{
var sizeOfDroppedBod = bobEntry is BOBLargeEntry entry ? entry.Entries.Length : 1;
m_From.AddToBackpack(item);
_from.AddToBackpack(item);
// The bulk order deed has been placed in your backpack.
m_From.SendLocalizedMessage(1045152);
_from.SendLocalizedMessage(1045152);
m_Book.Entries.Remove(bobEntry);
m_Book.InvalidateProperties();
_book.Entries.Remove(bobEntry);
_book.InvalidateProperties();
if (m_Book.Entries.Count / 5 < m_Book.ItemCount)
if (_book.Entries.Count / 5 < _book.ItemCount)
{
m_Book.ItemCount--;
m_Book.InvalidateItems();
_book.ItemCount--;
_book.InvalidateItems();
}
if (m_Book.Entries.Count > 0)
if (_book.Entries.Count > 0)
{
m_Page = GetPageForIndex(index, sizeOfDroppedBod);
m_From.SendGump(new BOBGump(m_From, m_Book, m_Page));
_page = GetPageForIndex(index, sizeOfDroppedBod);
_from.SendGump(new BOBGump(_from, _book, _page));
}
else
{
m_From.SendLocalizedMessage(1062381); // The book is empty.
_from.SendLocalizedMessage(1062381); // The book is empty.
}
}
}
@ -643,14 +649,14 @@ namespace Server.Engines.BulkOrders
}
else // Set Price | Buy
{
if (m_Book.IsChildOf(m_From.Backpack))
if (_book.IsChildOf(_from.Backpack))
{
m_From.Prompt = new SetPricePrompt(m_Book, bobEntry, m_Page, m_List);
m_From.SendLocalizedMessage(1062383); // Type in a price for the deed:
_from.Prompt = new SetPricePrompt(_book, bobEntry, _page, _list);
_from.SendLocalizedMessage(1062383); // Type in a price for the deed:
}
else if (m_Book.RootParent is PlayerVendor pv)
else if (_book.RootParent is PlayerVendor pv)
{
var vi = pv.GetVendorItem(m_Book);
var vi = pv.GetVendorItem(_book);
if (vi?.IsForSale != false)
{
@ -662,18 +668,18 @@ namespace Server.Engines.BulkOrders
if (price == 0)
{
m_From.SendLocalizedMessage(1062382); // The deed selected is not available.
_from.SendLocalizedMessage(1062382); // The deed selected is not available.
}
else
{
if (m_Book.Entries.Count > 0)
if (_book.Entries.Count > 0)
{
m_Page = GetPageForIndex(index, sizeOfDroppedBod);
m_From.SendGump(new BODBuyGump(m_From, m_Book, bobEntry, m_Page, price));
_page = GetPageForIndex(index, sizeOfDroppedBod);
_from.SendGump(new BODBuyGump(_from, _book, bobEntry, _page, price));
}
else
{
m_From.SendLocalizedMessage(1062381); // The book is emptz
_from.SendLocalizedMessage(1062381); // The book is emptz
}
}
}

View file

@ -1,4 +1,5 @@
using ModernUO.Serialization;
using Server.Gumps;
using Server.Mobiles;
namespace Server.Engines.BulkOrders

View file

@ -8,14 +8,13 @@ namespace Server.Engines.BulkOrders
private readonly LargeBOD m_Deed;
private readonly Mobile m_From;
public override bool Singleton => true;
public LargeBODAcceptGump(Mobile from, LargeBOD deed) : base(50, 50)
{
m_From = from;
m_Deed = deed;
m_From.CloseGump<LargeBODAcceptGump>();
m_From.CloseGump<SmallBODAcceptGump>();
var entries = deed.Entries;
AddPage(0);

View file

@ -8,14 +8,13 @@ namespace Server.Engines.BulkOrders
private readonly LargeBOD m_Deed;
private readonly Mobile m_From;
public override bool Singleton => true;
public LargeBODGump(Mobile from, LargeBOD deed) : base(25, 25)
{
m_From = from;
m_Deed = deed;
m_From.CloseGump<LargeBODGump>();
m_From.CloseGump<SmallBODGump>();
var entries = deed.Entries;
AddPage(0);

View file

@ -1,4 +1,5 @@
using System;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;

View file

@ -8,14 +8,13 @@ namespace Server.Engines.BulkOrders
private readonly SmallBOD m_Deed;
private readonly Mobile m_From;
public override bool Singleton => true;
public SmallBODAcceptGump(Mobile from, SmallBOD deed) : base(50, 50)
{
m_From = from;
m_Deed = deed;
m_From.CloseGump<LargeBODAcceptGump>();
m_From.CloseGump<SmallBODAcceptGump>();
AddPage(0);
AddBackground(25, 10, 430, 264, 5054);

View file

@ -8,14 +8,13 @@ namespace Server.Engines.BulkOrders
private readonly SmallBOD m_Deed;
private readonly Mobile m_From;
public override bool Singleton => true;
public SmallBODGump(Mobile from, SmallBOD deed) : base(25, 25)
{
m_From = from;
m_Deed = deed;
m_From.CloseGump<LargeBODGump>();
m_From.CloseGump<SmallBODGump>();
AddPage(0);
AddBackground(50, 10, 455, 260, 5054);

View file

@ -22,6 +22,8 @@ namespace Server.Engines.ConPVP
private bool m_Active = true;
public override bool Singleton => true;
public AcceptDuelGump(Mobile challenger, Mobile challenged, DuelContext context, Participant p, int slot) : base(
50,
50
@ -33,8 +35,6 @@ namespace Server.Engines.ConPVP
m_Participant = p;
m_Slot = slot;
challenged.CloseGump<AcceptDuelGump>();
Closable = false;
AddPage(0);
@ -244,23 +244,18 @@ namespace Server.Engines.ConPVP
m_Challenger.SendMessage($"{m_Challenged.Name} has accepted the request.");
m_Challenged.SendMessage($"You have accepted the request from {m_Challenger.Name}.");
var ns = m_Challenger.NetState;
if (ns != null)
foreach (var g in m_Challenger.GetAllGumps())
{
foreach (var g in ns.Gumps)
if (g is ParticipantGump pg && pg.Participant == m_Participant)
{
if (g is ParticipantGump pg && pg.Participant == m_Participant)
{
m_Challenger.SendGump(new ParticipantGump(m_Challenger, m_Context, m_Participant));
break;
}
m_Challenger.SendGump(new ParticipantGump(m_Challenger, m_Context, m_Participant));
break;
}
if (g is DuelContextGump dcg && dcg.Context == m_Context)
{
m_Challenger.SendGump(new DuelContextGump(m_Challenger, m_Context));
break;
}
if (g is DuelContextGump dcg && dcg.Context == m_Context)
{
m_Challenger.SendGump(new DuelContextGump(m_Challenger, m_Context));
break;
}
}
}

View file

@ -135,9 +135,9 @@ namespace Server.Engines.ConPVP
string title = move switch
{
NinjaMove => "Bushido",
NinjaMove => "Bushido",
SamuraiMove => "Ninjitsu",
_ => null
_ => null
};
if (title == null || name == null || Ruleset.GetOption(title, name))
@ -199,15 +199,15 @@ namespace Server.Engines.ConPVP
case MagerySpell magerySpell:
title = magerySpell.Circle switch
{
SpellCircle.First => "1st Circle",
SpellCircle.Second => "2nd Circle",
SpellCircle.Third => "3rd Circle",
SpellCircle.Fourth => "4th Circle",
SpellCircle.Fifth => "5th Circle",
SpellCircle.Sixth => "6th Circle",
SpellCircle.First => "1st Circle",
SpellCircle.Second => "2nd Circle",
SpellCircle.Third => "3rd Circle",
SpellCircle.Fourth => "4th Circle",
SpellCircle.Fifth => "5th Circle",
SpellCircle.Sixth => "6th Circle",
SpellCircle.Seventh => "7th Circle",
SpellCircle.Eighth => "8th Circle",
_ => null
SpellCircle.Eighth => "8th Circle",
_ => null
};
option = magerySpell.Name;
@ -1431,7 +1431,6 @@ namespace Server.Engines.ConPVP
if (prefs != null)
{
e.Mobile.CloseGump<PreferencesGump>();
e.Mobile.SendGump(new PreferencesGump(e.Mobile, prefs));
}
}
@ -1542,23 +1541,18 @@ namespace Server.Engines.ConPVP
p.Nullify(pl);
pm.DuelPlayer = null;
var ns = init.NetState;
if (ns != null)
foreach (var g in init.GetAllGumps())
{
foreach (var g in ns.Gumps)
if (g is ParticipantGump pg && pg.Participant == p)
{
if (g is ParticipantGump pg && pg.Participant == p)
{
init.SendGump(new ParticipantGump(init, dc, p));
break;
}
init.SendGump(new ParticipantGump(init, dc, p));
break;
}
if (g is DuelContextGump dcg && dcg.Context == dc)
{
init.SendGump(new DuelContextGump(init, dc));
break;
}
if (g is DuelContextGump dcg && dcg.Context == dc)
{
init.SendGump(new DuelContextGump(init, dc));
break;
}
}
}
@ -1580,34 +1574,29 @@ namespace Server.Engines.ConPVP
p.Nullify(pl);
pm.DuelPlayer = null;
var ns = init.NetState;
var send = true;
if (ns != null)
foreach (var g in init.GetAllGumps())
{
var send = true;
foreach (var g in ns.Gumps)
if (g is ParticipantGump pg && pg.Participant == p)
{
if (g is ParticipantGump pg && pg.Participant == p)
{
init.SendGump(new ParticipantGump(init, dc, p));
send = false;
break;
}
if (g is DuelContextGump dcg && dcg.Context == dc)
{
init.SendGump(new DuelContextGump(init, dc));
send = false;
break;
}
init.SendGump(new ParticipantGump(init, dc, p));
send = false;
break;
}
if (send)
if (g is DuelContextGump dcg && dcg.Context == dc)
{
init.SendGump(new DuelContextGump(init, dc));
send = false;
break;
}
}
if (send)
{
init.SendGump(new DuelContextGump(init, dc));
}
}
}
else
@ -1629,34 +1618,29 @@ namespace Server.Engines.ConPVP
p.Nullify(pl);
pm.DuelPlayer = null;
var ns = init.NetState;
var send = true;
if (ns != null)
foreach (var g in init.GetAllGumps())
{
var send = true;
foreach (var g in ns.Gumps)
if (g is ParticipantGump pg && pg.Participant == p)
{
if (g is ParticipantGump pg && pg.Participant == p)
{
init.SendGump(new ParticipantGump(init, dc, p));
send = false;
break;
}
if (g is DuelContextGump dcg && dcg.Context == dc)
{
init.SendGump(new DuelContextGump(init, dc));
send = false;
break;
}
init.SendGump(new ParticipantGump(init, dc, p));
send = false;
break;
}
if (send)
if (g is DuelContextGump dcg && dcg.Context == dc)
{
init.SendGump(new DuelContextGump(init, dc));
send = false;
break;
}
}
if (send)
{
init.SendGump(new DuelContextGump(init, dc));
}
}
}
}
@ -1714,13 +1698,15 @@ namespace Server.Engines.ConPVP
public void CloseAllGumps(DuelPlayer pl)
{
pl.Mobile.CloseGump<BeginGump>();
pl.Mobile.CloseGump<DuelContextGump>();
pl.Mobile.CloseGump<ParticipantGump>();
pl.Mobile.CloseGump<PickRulesetGump>();
pl.Mobile.CloseGump<ReadyGump>();
pl.Mobile.CloseGump<ReadyUpGump>();
pl.Mobile.CloseGump<RulesetGump>();
var gumps = pl.Mobile.GetGumps();
gumps.Close<BeginGump>();
gumps.Close<DuelContextGump>();
gumps.Close<ParticipantGump>();
gumps.Close<PickRulesetGump>();
gumps.Close<ReadyGump>();
gumps.Close<ReadyUpGump>();
gumps.Close<RulesetGump>();
}
public void CloseAllGumps()
@ -1799,9 +1785,11 @@ namespace Server.Engines.ConPVP
}
// Close all of them?
mob.CloseGump<DuelContextGump>();
mob.CloseGump<ReadyUpGump>();
mob.CloseGump<ReadyGump>();
var gumps = mob.GetGumps();
gumps.Close<DuelContextGump>();
gumps.Close<ReadyUpGump>();
gumps.Close<ReadyGump>();
}
}
@ -2194,10 +2182,12 @@ namespace Server.Engines.ConPVP
{
if (count == 10)
{
mob.CloseGump<ReadyGump>();
mob.CloseGump<ReadyUpGump>();
mob.CloseGump<BeginGump>();
mob.SendGump(new BeginGump(count));
var gumps = mob.GetGumps();
gumps.Close<ReadyGump>();
gumps.Close<ReadyUpGump>();
gumps.Close<BeginGump>();
gumps.Send(new BeginGump(count));
}
mob.Frozen = true;
@ -2256,8 +2246,7 @@ namespace Server.Engines.ConPVP
if (mob != null && m_Tournament == null)
{
mob.CloseGump<ReadyUpGump>();
mob.SendGump(new ReadyUpGump(mob, this));
mob.SendGump(new ReadyUpGump(mob, this), true);
}
}
}
@ -2512,8 +2501,7 @@ namespace Server.Engines.ConPVP
{
if (m_Tournament == null)
{
mob.CloseGump<ReadyGump>();
mob.SendGump(new ReadyGump(mob, this, count));
mob.SendGump(new ReadyGump(mob, this, count), true);
}
}
else

View file

@ -1057,7 +1057,6 @@ namespace Server.Engines.ConPVP
{
if (m_TeamInfo?.Game != null)
{
from.CloseGump<BRBoardGump>();
from.SendGump(new BRBoardGump(from, m_TeamInfo.Game));
}
}
@ -1082,7 +1081,7 @@ namespace Server.Engines.ConPVP
private const int LabelColor32 = 0xFFFFFF;
private const int BlackColor32 = 0x000000;
// private BRGame m_Game;
public override bool Singleton => true;
public BRBoardGump(Mobile mob, BRGame game, BRTeamInfo section = null) : base(60, 60)
{
@ -1719,7 +1718,6 @@ namespace Server.Engines.ConPVP
}
}
mob.CloseGump<BRBoardGump>();
mob.SendGump(new BRBoardGump(mob, this));
m_Context.Requip(mob, corpse);
@ -1918,7 +1916,6 @@ namespace Server.Engines.ConPVP
if (dp?.Mobile != null)
{
dp.Mobile.CloseGump<BRBoardGump>();
dp.Mobile.SendGump(new BRBoardGump(dp.Mobile, this));
}
}

View file

@ -29,7 +29,6 @@ namespace Server.Engines.ConPVP
{
if (m_TeamInfo?.Game != null)
{
from.CloseGump<CTFBoardGump>();
from.SendGump(new CTFBoardGump(from, m_TeamInfo.Game));
}
}
@ -56,6 +55,8 @@ namespace Server.Engines.ConPVP
private CTFGame m_Game;
public override bool Singleton => true;
public CTFBoardGump(Mobile mob, CTFGame game, CTFTeamInfo section = null)
: base(60, 60)
{
@ -1059,7 +1060,6 @@ namespace Server.Engines.ConPVP
}
}
mob.CloseGump<CTFBoardGump>();
mob.SendGump(new CTFBoardGump(mob, this));
m_Context.Requip(mob, corpse);
@ -1258,7 +1258,6 @@ namespace Server.Engines.ConPVP
if (dp?.Mobile != null)
{
dp.Mobile.CloseGump<CTFBoardGump>();
dp.Mobile.SendGump(new CTFBoardGump(dp.Mobile, this));
}
}

View file

@ -27,7 +27,6 @@ namespace Server.Engines.ConPVP
{
if (m_TeamInfo?.Game != null)
{
from.CloseGump<DDBoardGump>();
from.SendGump(new DDBoardGump(from, m_TeamInfo.Game));
}
}
@ -52,7 +51,7 @@ namespace Server.Engines.ConPVP
private const int LabelColor32 = 0xFFFFFF;
private const int BlackColor32 = 0x000000;
// private DDGame m_Game;
public override bool Singleton => true;
public DDBoardGump(Mobile mob, DDGame game, DDTeamInfo section = null)
: base(60, 60)
@ -605,7 +604,6 @@ namespace Server.Engines.ConPVP
}
}
mob.CloseGump<DDBoardGump>();
mob.SendGump(new DDBoardGump(mob, this));
m_Context.Requip(mob, corpse);
@ -820,7 +818,6 @@ namespace Server.Engines.ConPVP
if (dp?.Mobile != null)
{
dp.Mobile.CloseGump<DDBoardGump>();
dp.Mobile.SendGump(new DDBoardGump(dp.Mobile, this));
}
}

View file

@ -311,7 +311,6 @@ namespace Server.Engines.ConPVP
{
if (m_Game != null)
{
from.CloseGump<KHBoardGump>();
from.SendGump(new KHBoardGump(from, m_Game));
}
else
@ -353,6 +352,8 @@ namespace Server.Engines.ConPVP
private KHGame m_Game;
public override bool Singleton => true;
public KHBoardGump(Mobile mob, KHGame game)
: base(60, 60)
{
@ -989,7 +990,6 @@ namespace Server.Engines.ConPVP
}
}
mob.CloseGump<KHBoardGump>();
mob.SendGump(new KHBoardGump(mob, this));
m_Context.Requip(mob, corpse);
@ -1199,7 +1199,6 @@ namespace Server.Engines.ConPVP
if (dp?.Mobile != null)
{
dp.Mobile.CloseGump<KHBoardGump>();
dp.Mobile.SendGump(new KHBoardGump(dp.Mobile, this));
}
}

View file

@ -50,7 +50,6 @@ namespace Server.Engines.ConPVP
return false;
}
from.CloseGump<ArenaGump>();
from.SendGump(new ArenaGump(from, this));
if (!from.Hidden || from.AccessLevel == AccessLevel.Player)
@ -84,6 +83,8 @@ namespace Server.Engines.ConPVP
private int m_ColumnX = 12;
public override bool Singleton => true;
public ArenaGump(Mobile from, ArenasMoongate gate) : base(50, 50)
{
m_From = from;

View file

@ -19,6 +19,8 @@ namespace Server.Engines.ConPVP
private readonly Mobile m_Registrar;
private readonly Tournament m_Tournament;
public override bool Singleton => true;
public ConfirmSignupGump(Mobile from, Mobile registrar, Tournament tourney, List<Mobile> players) : base(50, 50)
{
m_From = from;
@ -26,10 +28,11 @@ namespace Server.Engines.ConPVP
m_Tournament = tourney;
m_Players = players;
m_From.CloseGump<AcceptTeamGump>();
m_From.CloseGump<AcceptDuelGump>();
m_From.CloseGump<DuelContextGump>();
m_From.CloseGump<ConfirmSignupGump>();
var gumps = m_From.GetGumps();
gumps.Close<AcceptTeamGump>();
gumps.Close<AcceptDuelGump>();
gumps.Close<DuelContextGump>();
var ruleset = tourney.Ruleset;
var basedef = ruleset.Base;

View file

@ -5,14 +5,17 @@ namespace Server.Engines.ConPVP
{
public class DuelContextGump : Gump
{
public override bool Singleton => true;
public DuelContextGump(Mobile from, DuelContext context) : base(50, 50)
{
From = from;
Context = context;
from.CloseGump<RulesetGump>();
from.CloseGump<DuelContextGump>();
from.CloseGump<ParticipantGump>();
var gumps = from.GetGumps();
gumps.Close<RulesetGump>();
gumps.Close<ParticipantGump>();
var count = context.Participants.Count;

View file

@ -52,8 +52,7 @@ namespace Server.Engines.ConPVP
if (ladder != null)
{
from.CloseGump<LadderGump>();
from.SendGump(new LadderGump(ladder));
from.SendGump(new LadderGump(ladder), true);
}
}
else

View file

@ -7,15 +7,18 @@ namespace Server.Engines.ConPVP
{
public class ParticipantGump : Gump
{
public override bool Singleton => true;
public ParticipantGump(Mobile from, DuelContext context, Participant p) : base(50, 50)
{
From = from;
Context = context;
Participant = p;
from.CloseGump<RulesetGump>();
from.CloseGump<DuelContextGump>();
from.CloseGump<ParticipantGump>();
var gumps = from.GetGumps();
gumps.Close<RulesetGump>();
gumps.Close<DuelContextGump>();
var count = p.Players.Length;

View file

@ -12,6 +12,8 @@ namespace Server.Engines.ConPVP
private readonly bool m_ReadOnly;
private readonly Ruleset m_Ruleset;
public override bool Singleton => true;
public RulesetGump(Mobile from, Ruleset ruleset, RulesetLayout page, DuelContext duelContext, bool readOnly = false)
: base(readOnly ? 310 : 50, 50)
{
@ -23,9 +25,10 @@ namespace Server.Engines.ConPVP
Draggable = !readOnly;
from.CloseGump<RulesetGump>();
from.CloseGump<DuelContextGump>();
from.CloseGump<ParticipantGump>();
var gumps = from.GetGumps();
gumps.Close<DuelContextGump>();
gumps.Close<ParticipantGump>();
var depthCounter = page;

View file

@ -179,6 +179,8 @@ namespace Server.Engines.ConPVP
private readonly PreferencesEntry m_Entry;
private int m_ColumnX = 12;
public override bool Singleton => true;
public PreferencesGump(Mobile from, Preferences prefs) : base(50, 50)
{
m_Entry = prefs.Find(from);

View file

@ -1,3 +1,5 @@
using Server.Gumps;
namespace Server.Engines.ConPVP
{
public class TournamentBracketItem : Item
@ -26,8 +28,7 @@ namespace Server.Engines.ConPVP
if (tourney != null)
{
from.CloseGump<TournamentBracketGump>();
from.SendGump(new TournamentBracketGump(from, tourney, TourneyBracketGumpType.Index));
from.SendGump(new TournamentBracketGump(from, tourney, TourneyBracketGumpType.Index), true);
}
}
}

View file

@ -66,9 +66,11 @@ namespace Server.Engines.ConPVP
{
if (from.AccessLevel >= AccessLevel.GameMaster && Tournament != null)
{
from.CloseGump<PickRulesetGump>();
from.CloseGump<RulesetGump>();
from.SendGump(new PickRulesetGump(from, null, Tournament.Ruleset));
var gumps = from.GetGumps();
gumps.Close<RulesetGump>();
gumps.Close<PickRulesetGump>();
gumps.Send(new PickRulesetGump(from, null, Tournament.Ruleset));
}
}

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Server.Factions;
using Server.Gumps;
using Server.Mobiles;
namespace Server.Engines.ConPVP
@ -146,7 +147,6 @@ namespace Server.Engines.ConPVP
}
else if (!tourney.HasParticipant(from))
{
from.CloseGump<ConfirmSignupGump>();
from.SendGump(new ConfirmSignupGump(from, Registrar, tourney, new List<Mobile> { from }));
}
else

View file

@ -24,6 +24,8 @@ public class CraftGump : DynamicGump
private readonly BaseTool _tool;
private readonly TextDefinition _notice;
public override bool Singleton => true;
public CraftGump(
Mobile from, CraftSystem craftSystem, BaseTool tool, TextDefinition notice, CraftPage page = CraftPage.None
) : base(40, 40)
@ -419,7 +421,6 @@ public class CraftGump : DynamicGump
public override void SendTo(NetState ns)
{
_from.CloseGump<CraftGump>();
_from.CloseGump<CraftGumpItem>();
base.SendTo(ns);

View file

@ -27,6 +27,8 @@ namespace Server.Engines.Craft
private bool m_ShowExceptionalChance;
public override bool Singleton => true;
public CraftGumpItem(Mobile from, CraftSystem craftSystem, CraftItem craftItem, BaseTool tool) : base(40, 40)
{
m_From = from;
@ -34,9 +36,6 @@ namespace Server.Engines.Craft
m_CraftItem = craftItem;
m_Tool = tool;
from.CloseGump<CraftGump>();
from.CloseGump<CraftGumpItem>();
AddPage(0);
AddBackground(0, 0, 530, 417, 5054);
AddImageTiled(10, 10, 510, 22, 2624);

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Server.Commands;
using Server.Factions;
using Server.Gumps;
using Server.Items;
using Server.Logging;
using Server.Mobiles;

View file

@ -1,4 +1,5 @@
using System;
using Server.Gumps;
using Server.Items;
using Server.Targeting;

View file

@ -14,13 +14,13 @@ namespace Server.Engines.Craft
private readonly BaseTool m_Tool;
private readonly Type m_TypeRes;
public override bool Singleton => true;
public QueryMakersMarkGump(
int quality, Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes,
BaseTool tool
) : base(100, 200)
{
from.CloseGump<QueryMakersMarkGump>();
m_Quality = quality;
m_From = from;
m_CraftItem = craftItem;

View file

@ -1,4 +1,5 @@
using System;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;

View file

@ -1,4 +1,5 @@
using Server.Ethics;
using Server.Gumps;
using Server.Items;
using Server.Targeting;

View file

@ -1,5 +1,6 @@
using System;
using Server.Factions;
using Server.Gumps;
using Server.Items;
using Server.Targeting;

View file

@ -5,6 +5,7 @@ using Server.Commands.Generic;
using Server.Engines.ConPVP;
using Server.Ethics;
using Server.Guilds;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Prompts;

View file

@ -1,3 +1,4 @@
using Server.Gumps;
using Server.Mobiles;
namespace Server.Factions;

View file

@ -1,3 +1,4 @@
using Server.Gumps;
using Server.Mobiles;
namespace Server.Factions;

View file

@ -1,3 +1,4 @@
using Server.Gumps;
using Server.Mobiles;
namespace Server.Factions;

View file

@ -1,3 +1,4 @@
using Server.Gumps;
using Server.Mobiles;
namespace Server.Factions;

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;

View file

@ -58,6 +58,8 @@ public sealed class HelpGump : DynamicGump
{
private readonly Mobile _from;
public override bool Singleton => true;
public HelpGump(Mobile from) : base(0, 0) => _from = from;
protected override void BuildLayout(ref DynamicGumpBuilder builder)
@ -206,12 +208,6 @@ public sealed class HelpGump : DynamicGump
builder.AddHtmlLocalized(180, y + 150, 335, 40, 1001015); // NO - I meant to ask for help with another matter.
}
public override void SendTo(NetState ns)
{
_from.CloseGump<HelpGump>();
base.SendTo(ns);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddOption(
ref DynamicGumpBuilder builder, int y, int buttonId, int localizedName, GumpButtonType type = GumpButtonType.Reply,
@ -237,12 +233,9 @@ public sealed class HelpGump : DynamicGump
public static void HelpRequest(Mobile m)
{
foreach (var gump in m.NetState.Gumps)
if (m.HasGump<HelpGump>())
{
if (gump is HelpGump)
{
return;
}
return;
}
if (!PageQueue.CheckAllowedToPage(m))

View file

@ -8,6 +8,8 @@ public sealed class PagePromptGump : StaticGump<PagePromptGump>
private readonly Mobile _from;
private readonly PageType _type;
public override bool Singleton => true;
public PagePromptGump(Mobile from, PageType type) : base(0, 0)
{
_from = from;
@ -31,12 +33,6 @@ public sealed class PagePromptGump : StaticGump<PagePromptGump>
builder. AddButton(405, 355, 2073, 2072, 0); // Cancel
}
public override void SendTo(NetState ns)
{
_from.CloseGump<PagePromptGump>();
base.SendTo(ns);
}
public override void OnResponse(NetState sender, in RelayInfo info)
{
if (info.ButtonID == 0)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Server.Gumps;
using Server.Misc;
using Server.Mobiles;
using Server.Network;

View file

@ -118,9 +118,7 @@ namespace Server.Engines.Help
{
if (PageQueue.List.IndexOf(m_List[info.ButtonID - 1]) >= 0)
{
var g = new PageEntryGump(state.Mobile, m_List[info.ButtonID - 1]);
g.SendTo(state);
state.SendGump(new PageEntryGump(state.Mobile, m_List[info.ButtonID - 1]));
}
else
{
@ -224,13 +222,13 @@ namespace Server.Engines.Help
private readonly Mobile m_From;
private readonly PredefinedResponse m_Response;
public override bool Singleton => true;
public PredefGump(Mobile from, PredefinedResponse response) : base(30, 30)
{
m_From = from;
m_Response = response;
from.CloseGump<PredefGump>();
var canEdit = from.AccessLevel >= AccessLevel.GameMaster;
AddPage(0);
@ -593,9 +591,7 @@ namespace Server.Engines.Help
public void Resend(NetState state)
{
var g = new PageEntryGump(m_Mobile, m_Entry);
g.SendTo(state);
state.SendGump(new PageEntryGump(m_Mobile, m_Entry));
}
public override void OnResponse(NetState state, in RelayInfo info)
@ -613,9 +609,7 @@ namespace Server.Engines.Help
{
if (m_Entry.Handler != state.Mobile)
{
var g = new PageQueueGump();
g.SendTo(state);
state.SendGump(new PageQueueGump());
}
break;
@ -722,10 +716,7 @@ namespace Server.Engines.Help
PageQueue.Remove(m_Entry);
state.Mobile.SendMessage("You delete the page.");
var g = new PageQueueGump();
g.SendTo(state);
state.SendGump(new PageQueueGump());
}
else
{
@ -764,10 +755,7 @@ namespace Server.Engines.Help
m_Entry.Handler = null;
state.Mobile.SendMessage("You mark the page as handled, and remove it from the queue.");
var g = new PageQueueGump();
g.SendTo(state);
state.SendGump(new PageQueueGump());
}
else
{

View file

@ -290,9 +290,11 @@ namespace Server.Items
);
}
from.CloseGump<PuzzleGump>();
from.CloseGump<StatusGump>();
from.SendGump(new PuzzleGump(from, this, solution, 0));
var gumps = from.GetGumps();
gumps.Close<StatusGump>();
gumps.Close<PuzzleGump>();
gumps.Send(new PuzzleGump(from, this, solution, 0));
return true;
}

View file

@ -268,12 +268,13 @@ namespace Server.Engines.MLQuests.Gumps
*/
public static void CloseOtherGumps(PlayerMobile pm)
{
pm.CloseGump<InfoNPCGump>();
pm.CloseGump<QuestRewardGump>();
pm.CloseGump<QuestConversationGump>();
pm.CloseGump<QuestReportBackGump>();
// pm.CloseGump( typeof( UnknownGump807 ) );
pm.CloseGump<QuestCancelConfirmGump>();
var gumps = pm.GetGumps();
gumps.Close<InfoNPCGump>();
gumps.Close<QuestRewardGump>();
gumps.Close<QuestConversationGump>();
gumps.Close<QuestReportBackGump>();
gumps.Close<QuestCancelConfirmGump>();
}
private struct ButtonInfo

View file

@ -8,6 +8,8 @@ namespace Server.Engines.MLQuests.Gumps
private readonly bool m_CloseGumps;
private readonly MLQuestInstance m_Instance;
public override bool Singleton => true;
public QuestLogDetailedGump(MLQuestInstance instance, bool closeGumps = true)
: base(1046026) // Quest Log
{
@ -20,7 +22,6 @@ namespace Server.Engines.MLQuests.Gumps
if (closeGumps)
{
CloseOtherGumps(pm);
pm.CloseGump<QuestLogDetailedGump>();
}
SetTitle(quest.Title);

View file

@ -9,6 +9,8 @@ namespace Server.Engines.MLQuests.Gumps
private readonly bool m_CloseGumps;
private readonly PlayerMobile m_Owner;
public override bool Singleton => true;
public QuestLogGump(PlayerMobile pm, bool closeGumps = true)
: base(1046026) // Quest Log
{
@ -17,7 +19,6 @@ namespace Server.Engines.MLQuests.Gumps
if (closeGumps)
{
pm.CloseGump<QuestLogGump>();
pm.CloseGump<QuestLogDetailedGump>();
}

View file

@ -9,6 +9,8 @@ namespace Server.Engines.MLQuests.Gumps
private readonly MLQuest m_Quest;
private readonly IQuestGiver m_Quester;
public override bool Singleton => true;
public QuestOfferGump(MLQuest quest, IQuestGiver quester, PlayerMobile pm)
: base(1049010) // Quest Offer
{
@ -16,7 +18,6 @@ namespace Server.Engines.MLQuests.Gumps
m_Quester = quester;
CloseOtherGumps(pm);
pm.CloseGump<QuestOfferGump>();
SetTitle(quest.Title);
RegisterButton(ButtonPosition.Left, ButtonGraphic.Accept, 1);

View file

@ -27,11 +27,11 @@ namespace Server.Engines.MLQuests.Gumps
private readonly IRaceChanger m_Owner;
private readonly Race m_Race;
public override bool Singleton => true;
public RaceChangeConfirmGump(IRaceChanger owner, PlayerMobile from, Race targetRace)
: base(50, 50)
{
from.CloseGump<RaceChangeConfirmGump>();
m_Owner = owner;
m_From = from;
m_Race = targetRace;

View file

@ -5,6 +5,7 @@ using Server.Engines.MLQuests.Gumps;
using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Gumps;
using Server.Mobiles;
namespace Server.Engines.MLQuests

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Server.Engines.MLQuests.Gumps;
using Server.Engines.MLQuests.Objectives;
using Server.Gumps;
using Server.Mobiles;
namespace Server.Engines.MLQuests

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Engines.MLQuests.Definitions;
using Server.Engines.MLQuests.Gumps;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;

View file

@ -1,3 +1,4 @@
using Server.Gumps;
using Server.Targeting;
namespace Server.Engines.Plants
@ -21,12 +22,7 @@ namespace Server.Engines.Plants
if (!m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant &&
from.InRange(m_Plant.GetWorldLocation(), 3) && m_Plant.IsUsableBy(from))
{
if (from.HasGump<MainPlantGump>())
{
from.CloseGump<MainPlantGump>();
}
from.SendGump(new MainPlantGump(m_Plant));
from.SendGump(new MainPlantGump(m_Plant), true);
}
}
}

View file

@ -1,3 +1,4 @@
using Server.Gumps;
using Server.Targeting;
namespace Server.Engines.Plants

View file

@ -55,7 +55,6 @@ public partial class Impresario : BaseQuester
if (obj.IsInRightTheater())
{
player.CloseGump<SheetMusicOfferGump>();
player.SendGump(new SheetMusicOfferGump());
}
else
@ -68,6 +67,8 @@ public partial class Impresario : BaseQuester
public class SheetMusicOfferGump : BaseQuestGump
{
public override bool Singleton => true;
public SheetMusicOfferGump() : base(75, 25)
{
Closable = false;

View file

@ -130,6 +130,8 @@ namespace Server.Engines.Quests
{
private readonly QuestSystem m_System;
public override bool Singleton => true;
public QuestLogUpdatedGump(QuestSystem system) : base(3, 30)
{
m_System = system;

View file

@ -264,7 +264,6 @@ namespace Server.Engines.Quests
public virtual void ShowQuestLogUpdated()
{
From.CloseGump<QuestLogUpdatedGump>();
From.SendGump(new QuestLogUpdatedGump(this));
}
@ -272,18 +271,19 @@ namespace Server.Engines.Quests
{
if (Objectives.Count > 0)
{
From.CloseGump<QuestItemInfoGump>();
From.CloseGump<QuestLogUpdatedGump>();
From.CloseGump<QuestObjectivesGump>();
From.CloseGump<QuestConversationsGump>();
var gumps = From.GetGumps();
gumps.Close<QuestItemInfoGump>();
gumps.Close<QuestLogUpdatedGump>();
gumps.Close<QuestConversationsGump>();
gumps.Close<QuestObjectivesGump>();
From.SendGump(new QuestObjectivesGump(Objectives));
gumps.Send(new QuestObjectivesGump(Objectives));
var last = Objectives[^1];
if (last.Info != null)
{
From.SendGump(new QuestItemInfoGump(last.Info));
gumps.Send(new QuestItemInfoGump(last.Info));
}
}
}
@ -292,11 +292,13 @@ namespace Server.Engines.Quests
{
if (Conversations.Count > 0)
{
From.CloseGump<QuestItemInfoGump>();
From.CloseGump<QuestObjectivesGump>();
From.CloseGump<QuestConversationsGump>();
var gumps = From.GetGumps();
From.SendGump(new QuestConversationsGump(Conversations));
gumps.Close<QuestItemInfoGump>();
gumps.Close<QuestObjectivesGump>();
gumps.Close<QuestConversationsGump>();
gumps.Send(new QuestConversationsGump(Conversations));
var last = Conversations[^1];
@ -389,10 +391,10 @@ namespace Server.Engines.Quests
Conversations.Add(conv);
}
From.CloseGump<QuestItemInfoGump>();
From.CloseGump<QuestObjectivesGump>();
From.CloseGump<QuestConversationsGump>();
From.SendGump(conv.Logged ? new QuestConversationsGump(Conversations) : new QuestConversationsGump(conv));
var gumps = From.GetGumps();
gumps.Close<QuestItemInfoGump>();
gumps.Close<QuestObjectivesGump>();
gumps.Send(conv.Logged ? new QuestConversationsGump(Conversations) : new QuestConversationsGump(conv));
if (conv.Info != null)
{

View file

@ -186,7 +186,6 @@ public partial class Mardoth : BaseQuester
m.PlaySound(0x214);
m.FixedEffect(0x376A, 10, 16);
m.CloseGump<ResurrectGump>();
m.SendGump(new ResurrectGump(m, ResurrectMessage.Healer));
}
}

View file

@ -203,7 +203,6 @@ public partial class Emino : BaseQuester
m.PlaySound(0x214);
m.FixedEffect(0x376A, 10, 16);
m.CloseGump<ResurrectGump>();
m.SendGump(new ResurrectGump(m, ResurrectMessage.Healer));
}
}

View file

@ -105,7 +105,6 @@ public partial class Zoel : BaseQuester
m.PlaySound(0x214);
m.FixedEffect(0x376A, 10, 16);
m.CloseGump<ResurrectGump>();
m.SendGump(new ResurrectGump(m, ResurrectMessage.Healer));
}
}

View file

@ -1,4 +1,5 @@
using System;
using Server.Gumps;
using Server.Regions;
using Server.Mobiles;

View file

@ -220,7 +220,6 @@ public partial class Chyloth : BaseQuester
AngryAt = null;
}
member.CloseGump<ChylothPartyGump>();
member.SendGump(new ChylothPartyGump(from, member));
}
}

View file

@ -130,7 +130,6 @@ public partial class Schmendrick : BaseQuester
m.PlaySound(0x214);
m.FixedEffect(0x376A, 10, 16);
m.CloseGump<ResurrectGump>();
m.SendGump(new ResurrectGump(m, ResurrectMessage.Healer));
}
}

View file

@ -391,7 +391,6 @@ public partial class Uzeraan : BaseQuester
m.PlaySound(0x214);
m.FixedEffect(0x376A, 10, 16);
m.CloseGump<ResurrectGump>();
m.SendGump(new ResurrectGump(m, ResurrectMessage.Healer));
}
}

View file

@ -4,6 +4,7 @@ using System.Reflection;
using System.Text.Json;
using ModernUO.Serialization;
using Server.Commands;
using Server.Gumps;
using Server.Items;
using Server.Json;
using Server.Mobiles;

View file

@ -291,6 +291,8 @@ namespace Server.Mobiles
{
if (m.Alive && m is PlayerMobile pm)
{
var gumps = pm.GetGumps();
if (pm.Alive && (Z - pm.Z).Abs() < 16 && InRange(m, 3) && !InRange(oldLocation, 3))
{
if (pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward)
@ -298,11 +300,11 @@ namespace Server.Mobiles
// Congratulations! You have turned in enough minor treasures to earn a greater reward.
SayTo(pm, 1070980);
pm.CloseGump<ToTTurnInGump>(); // Sanity
gumps.Close<ToTTurnInGump>(); // Sanity
if (!pm.HasGump<ToTRedeemGump>())
{
pm.SendGump(new ToTRedeemGump(this, false));
gumps.Send(new ToTRedeemGump(this, false));
}
}
else
@ -325,7 +327,7 @@ namespace Server.Mobiles
if (buttons?.Count > 0 && !pm.HasGump<ToTTurnInGump>())
{
pm.SendGump(new ToTTurnInGump(this, buttons));
gumps.Send(new ToTTurnInGump(this, buttons));
}
}
}
@ -334,8 +336,8 @@ namespace Server.Mobiles
if (!InRange(m, leaveRange) && InRange(oldLocation, leaveRange))
{
pm.CloseGump<ToTRedeemGump>();
pm.CloseGump<ToTTurnInGump>();
gumps.Close<ToTRedeemGump>();
gumps.Close<ToTTurnInGump>();
}
}
}
@ -412,16 +414,18 @@ namespace Server.Gumps
item.Delete();
var gumps = pm.GetGumps();
if (++pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward)
{
// Congratulations! You have turned in enough minor treasures to earn a greater reward.
m_Collector.SayTo(pm, 1070980);
pm.CloseGump<ToTTurnInGump>(); // Sanity
gumps.Close<ToTTurnInGump>(); // Sanity
if (!pm.HasGump<ToTRedeemGump>())
if (!gumps.Has<ToTRedeemGump>())
{
pm.SendGump(new ToTRedeemGump(m_Collector, false));
gumps.Send(new ToTRedeemGump(m_Collector, false));
}
}
else
@ -434,11 +438,11 @@ namespace Server.Gumps
var buttons = FindRedeemableItems(pm);
pm.CloseGump<ToTTurnInGump>(); // Sanity
gumps.Close<ToTTurnInGump>(); // Sanity
if (buttons?.Count > 0)
{
pm.SendGump(new ToTTurnInGump(m_Collector, buttons));
gumps.Send(new ToTTurnInGump(m_Collector, buttons));
}
}
}
@ -594,9 +598,7 @@ namespace Server.Gumps
if (t.Type == typeof(PigmentsOfTokuno)) // Special case of course.
{
pm.CloseGump<ToTTurnInGump>(); // Sanity
pm.CloseGump<ToTRedeemGump>();
pm.SendGump(new ToTRedeemGump(m_Collector, true));
pm.SendGump(new ToTRedeemGump(m_Collector, true), true);
return;
}

View file

@ -591,8 +591,7 @@ public class CharacterStatueTarget : Target
_maker.Delete();
statue.Sculpt(from);
from.CloseGump<CharacterStatueGump>();
from.SendGump(new CharacterStatueGump(_maker, statue, from));
from.SendGump(new CharacterStatueGump(_maker, statue, from), true);
return;
}

View file

@ -8,12 +8,12 @@ namespace Server.Engines.VeteranRewards
{
private readonly Mobile m_From;
public override bool Singleton => true;
public RewardChoiceGump(Mobile from) : base(0, 0)
{
m_From = from;
from.CloseGump<RewardChoiceGump>();
RenderBackground();
RenderCategories();
}

View file

@ -9,13 +9,13 @@ namespace Server.Engines.VeteranRewards
private readonly RewardEntry m_Entry;
private readonly Mobile m_From;
public override bool Singleton => true;
public RewardConfirmGump(Mobile from, RewardEntry entry) : base(0, 0)
{
m_From = from;
m_Entry = entry;
from.CloseGump<RewardConfirmGump>();
AddPage(0);
AddBackground(10, 10, 500, 300, 2600);

View file

@ -8,6 +8,8 @@ namespace Server.Gumps
{
private readonly IAddon m_Addon;
public override bool Singleton => true;
public RewardDemolitionGump(IAddon addon, int question) : base(150, 50)
{
m_Addon = addon;

View file

@ -7,12 +7,12 @@ namespace Server.Engines.VeteranRewards
{
private readonly Mobile m_From;
public override bool Singleton => true;
public RewardNoticeGump(Mobile from) : base(0, 0)
{
m_From = from;
from.CloseGump<RewardNoticeGump>();
AddPage(0);
AddBackground(10, 10, 500, 135, 2600);

View file

@ -14,6 +14,8 @@ namespace Server.Gumps
private readonly IRewardOption m_Option;
private readonly RewardOptionList m_Options = new();
public override bool Singleton => true;
public RewardOptionGump(IRewardOption option, int title = 0) : base(60, 36)
{
m_Option = option;

View file

@ -1,5 +1,6 @@
using System;
using Server.Accounting;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;

View file

@ -74,7 +74,6 @@ public static class SacrificeVirtue
* We need to wait for them to accept the gump or they can just use
* Sacrifice and cancel to have items in their backpack for free.
*/
from.CloseGump<ResurrectGump>();
from.SendGump(new ResurrectGump(from, fromSacrifice: true));
}
else

View file

@ -36,8 +36,7 @@ public class VirtueGump : Gump
}
else if (beholder.Map == beheld.Map && beholder.InRange(beheld, 12))
{
beholder.CloseGump<VirtueGump>();
beholder.SendGump(new VirtueGump(beholder, beheld));
beholder.SendGump(new VirtueGump(beholder, beheld), true);
}
}

View file

@ -74,13 +74,13 @@ namespace Server.Gumps
private readonly AdminGumpPage m_PageType;
private readonly object m_State;
public override bool Singleton => true;
public AdminGump(
Mobile from, AdminGumpPage pageType, int listPage = 0, List<object> list = null, string notice = null,
object state = null
) : base(50, 40)
{
from.CloseGump<AdminGump>();
m_From = from;
m_PageType = pageType;
m_ListPage = listPage;

View file

@ -0,0 +1,89 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: BaseGump.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using Server.Network;
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
namespace Server.Gumps;
public abstract class BaseGump
{
private static readonly byte[] _packetBuffer = GC.AllocateUninitializedArray<byte>(0x10000);
private static Serial nextSerial = (Serial)1;
public int TypeID { get; protected set; }
public Serial Serial { get; protected set; }
public abstract int Switches { get; }
public abstract int TextEntries { get; }
public int X { get; set; }
public int Y { get; set; }
/**
* If true, only one instance of this gump can be open at a time per player.
*/
public virtual bool Singleton => false;
public BaseGump(int x, int y) : this()
{
X = x;
Y = y;
}
public BaseGump()
{
Serial = nextSerial++;
TypeID = GetTypeId(GetType());
}
public virtual void SendTo(NetState ns)
{
var writer = new SpanWriter(_packetBuffer);
Compile(ref writer);
ns.Send(writer.Span);
writer.Dispose();
}
public abstract void Compile(ref SpanWriter writer);
public virtual void OnResponse(NetState sender, in RelayInfo info)
{
}
public virtual void OnServerClose(NetState owner)
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetTypeId(Type type)
{
unchecked
{
// To use the original .NET Framework deterministic hash code (with really terrible performance)
// change the next line to use HashUtility.GetNetFrameworkHashCode
var hash = (int)HashUtility.ComputeHash32(type?.FullName);
const int primeMulti = 0x108B76F1;
// Virtue Gump
return hash == 461 ? hash * primeMulti : hash;
}
}
}

View file

@ -0,0 +1,62 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: DynamicGump.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using System.IO;
using Server.Network;
namespace Server.Gumps;
public abstract class DynamicGump : BaseGump
{
private int _switches;
private int _textEntries;
public override int Switches => _switches;
public override int TextEntries => _textEntries;
public DynamicGump(int x, int y) : base(x, y)
{
}
protected abstract void BuildLayout(ref DynamicGumpBuilder builder);
public override void Compile(ref SpanWriter writer)
{
writer.Write((byte)0xDD); // Packet ID
writer.Seek(2, SeekOrigin.Current);
writer.Write(Serial);
writer.Write(TypeID);
writer.Write(X);
writer.Write(Y);
DynamicGumpBuilder gumpBuilder = new DynamicGumpBuilder();
BuildLayout(ref gumpBuilder);
gumpBuilder.FinalizeLayout();
_switches = gumpBuilder.Switches;
_textEntries = gumpBuilder.TextEntries;
OutgoingGumpPackets.WritePacked(gumpBuilder.LayoutData, ref writer);
writer.Write(gumpBuilder._stringsCount);
OutgoingGumpPackets.WritePacked(gumpBuilder.StringsData, ref writer);
gumpBuilder.Dispose();
writer.WritePacketLength();
}
}

View file

@ -0,0 +1,404 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: DynamicGumpBuilder.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Text;
namespace Server.Gumps;
public ref struct DynamicGumpBuilder
{
private static readonly byte[] _staticStringsBuffer = GC.AllocateUninitializedArray<byte>(0x80000);
private byte[] _stringsBuffer;
private int _stringBytesWritten;
internal int _stringsCount;
private GumpLayoutBuilder _gumpBuilder;
public ReadOnlySpan<byte> LayoutData => _gumpBuilder.LayoutData;
public ReadOnlySpan<byte> StringsData => _stringsBuffer.AsSpan(0, _stringBytesWritten);
public int Switches => _gumpBuilder._switches;
public int TextEntries => _gumpBuilder._textEntries;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DynamicGumpBuilder()
{
_stringsBuffer = _staticStringsBuffer;
_gumpBuilder = new GumpLayoutBuilder();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoClose() => _gumpBuilder.SetNoClose();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoMove() => _gumpBuilder.SetNoMove();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoResize() => _gumpBuilder.SetNoResize();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoDispose() => _gumpBuilder.SetNoDispose();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddAlphaRegion(int x, int y, int width, int height) =>
_gumpBuilder.AddAlphaRegion(x, y, width, height);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddBackground(int x, int y, int width, int height, int gumpID) =>
_gumpBuilder.AddBackground(x, y, width, height, gumpID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddButton(
int x, int y, int normalID, int pressedId, int buttonId, GumpButtonType type = GumpButtonType.Reply, int param = 0
) => _gumpBuilder.AddButton(x, y, normalID, pressedId, buttonId, type, param);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddCheckbox(int x, int y, int inactiveID, int activeID, bool selected, int switchId) =>
_gumpBuilder.AddCheckbox(x, y, inactiveID, activeID, selected, switchId);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddGroup(int groupId) => _gumpBuilder.AddGroup(groupId);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtml(
int x,
int y,
int width,
int height,
ReadOnlySpan<char> text,
bool background = false,
bool scrollbar = false
)
{
WriteInternalizedString(text);
_gumpBuilder.AddHtml(x, y, width, height, _stringsCount++, background, scrollbar);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtml(
int x, int y, int width, int height, ref RawInterpolatedStringHandler handler,
bool background = false, bool scrollbar = false
)
{
AddHtml(x, y, width, height, handler.Text, background, scrollbar);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtml(
int x,
int y,
int width,
int height,
int color,
ReadOnlySpan<char> text,
bool background = false,
bool scrollbar = false
)
{
var handler = text.Color(color);
AddHtml(x, y, width, height, ref handler, background, scrollbar);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtml(
int x,
int y,
int width,
int height,
int color,
ref RawInterpolatedStringHandler handler,
bool background = false,
bool scrollbar = false
)
{
AddHtml(x, y, width, height, color, handler.Text, background, scrollbar);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlCentered(
int x, int y, int width, int height, ReadOnlySpan<char> text, bool background = false, bool scrollbar = false
)
{
var handler = text.Center();
AddHtml(x, y, width, height, ref handler, background, scrollbar);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlCentered(
int x,
int y,
int width,
int height,
ref RawInterpolatedStringHandler handler,
bool background = false,
bool scrollbar = false
)
{
AddHtmlCentered(x, y, width, height, handler.Text, background, scrollbar);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlCentered(
int x,
int y,
int width,
int height,
int color,
ReadOnlySpan<char> text,
bool background = false,
bool scrollbar = false
)
{
var handler = text.Center(color);
AddHtml(x, y, width, height, ref handler, background, scrollbar);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlCentered(
int x,
int y,
int width,
int height,
int color,
ref RawInterpolatedStringHandler handler,
bool background = false,
bool scrollbar = false
)
{
AddHtmlCentered(x, y, width, height, color, handler.Text, background, scrollbar);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, bool background = false, bool scrollbar = false
) => _gumpBuilder.AddHtmlLocalized(x, y, width, height, number, background, scrollbar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, int color, bool background = false, bool scrollbar = false
) => _gumpBuilder.AddHtmlLocalized(x, y, width, height, number, color, background, scrollbar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlLocalized(
int x,
int y,
int width,
int height,
int number,
ReadOnlySpan<char> args,
int color,
bool background = false,
bool scrollbar = false
) => _gumpBuilder.AddHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, ref RawInterpolatedStringHandler handler, int color,
bool background = false, bool scrollbar = false
) => _gumpBuilder.AddHtmlLocalized(x, y, width, height, number, ref handler, color, background, scrollbar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImage(int x, int y, int gumpId, int hue = 0, ReadOnlySpan<char> cls = default) =>
_gumpBuilder.AddImage(x, y, gumpId, hue, cls);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImage(int x, int y, int gumpId, ref RawInterpolatedStringHandler handler) =>
_gumpBuilder.AddImage(x, y, gumpId, ref handler);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImage(int x, int y, int gumpId, int hue, ref RawInterpolatedStringHandler handler) =>
_gumpBuilder.AddImage(x, y, gumpId, hue, ref handler);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImageTiledButton(
int x,
int y,
int normalId,
int pressedId,
int buttonId,
GumpButtonType type,
int param,
int itemId,
int hue,
int width,
int height,
int localizedTooltip = -1
) => _gumpBuilder.AddImageTiledButton(
x, y, normalId, pressedId, buttonId, type, param, itemId, hue, width, height, localizedTooltip
);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImageTiled(int x, int y, int width, int height, int gumpId) =>
_gumpBuilder.AddImageTiled(x, y, width, height, gumpId);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddItem(int x, int y, int itemId, int hue = 0) => _gumpBuilder.AddItem(x, y, itemId, hue);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddItemProperty(Serial serial) => _gumpBuilder.AddItemProperty(serial);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLabel(int x, int y, int hue, ReadOnlySpan<char> text)
{
WriteInternalizedString(text);
_gumpBuilder.AddLabel(x, y, hue, _stringsCount++);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLabel(int x, int y, int hue, ref RawInterpolatedStringHandler handler)
{
AddLabel(x, y, hue, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLabelCropped(int x, int y, int width, int height, int hue, ReadOnlySpan<char> text)
{
WriteInternalizedString(text);
_gumpBuilder.AddLabelCropped(x, y, width, height, hue, _stringsCount++);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLabelCropped(int x, int y, int width, int height, int hue, ref RawInterpolatedStringHandler handler)
{
AddLabelCropped(x, y, width, height, hue, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddGumpIdOverride(int gumpId) => _gumpBuilder.AddGumpIdOverride(gumpId);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddPage(int page = 0) => _gumpBuilder.AddPage(page);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddRadio(int x, int y, int inactiveId, int activeId, bool selected, int switchId) =>
_gumpBuilder.AddRadio(x, y, inactiveId, activeId, selected, switchId);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddSpriteImage(int x, int y, int gumpId, int width, int height, int sx, int sy) =>
_gumpBuilder.AddSpriteImage(x, y, gumpId, width, height, sx, sy);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTextEntry(
int x, int y, int width, int height, int hue, int entryId, ReadOnlySpan<char> initialText = default
)
{
WriteInternalizedString(initialText);
_gumpBuilder.AddTextEntry(x, y, width, height, hue, entryId, _stringsCount++);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTextEntry(
int x, int y, int width, int height, int hue, int entryId, ref RawInterpolatedStringHandler handler
)
{
AddTextEntry(x, y, width, height, hue, entryId, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTextEntryLimited(
int x, int y, int width, int height, int hue, int entryId, ReadOnlySpan<char> initialText = default, int size = 0
)
{
WriteInternalizedString(initialText);
_gumpBuilder.AddTextEntryLimited(x, y, width, height, hue, entryId, _stringsCount++, size);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTextEntryLimited(
int x, int y, int width, int height, int hue, int entryId, ref RawInterpolatedStringHandler handler, int size = 0
)
{
AddTextEntryLimited(x, y, width, height, hue, entryId, handler.Text, size);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTooltip(int number) => _gumpBuilder.AddTooltip(number);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTooltip(int number, ReadOnlySpan<char> args) => _gumpBuilder.AddTooltip(number, args);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTooltip(int number, ref RawInterpolatedStringHandler handler)
{
_gumpBuilder.AddTooltip(number, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FinalizeLayout() => _gumpBuilder.FinalizeLayout();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteInternalizedString(ReadOnlySpan<char> text)
{
if (text.Length > ushort.MaxValue)
{
text = text[..ushort.MaxValue];
}
GrowStringsBufferIfNeeded(2 + text.Length * 2);
BinaryPrimitives.WriteUInt16BigEndian(_stringsBuffer.AsSpan(_stringBytesWritten), (ushort)text.Length);
_stringBytesWritten += 2;
_stringBytesWritten += text.GetBytesBigUni(_stringsBuffer.AsSpan(_stringBytesWritten));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void GrowStringsBufferIfNeeded(int needed)
{
if (needed + _stringBytesWritten <= _stringsBuffer.Length)
{
return;
}
var newSize = Math.Max(_stringBytesWritten + needed, _stringsBuffer.Length * 2);
byte[] poolArray = STArrayPool<byte>.Shared.Rent(newSize);
_stringsBuffer.AsSpan(0, _stringBytesWritten).CopyTo(poolArray);
byte[] toReturn = _stringsBuffer;
_stringsBuffer = poolArray;
if (toReturn != null && toReturn != _staticStringsBuffer)
{
STArrayPool<byte>.Shared.Return(toReturn);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
_gumpBuilder.Dispose();
if (_stringsBuffer != _staticStringsBuffer)
{
STArrayPool<byte>.Shared.Return(_stringsBuffer);
}
this = default;
}
}

View file

@ -0,0 +1,77 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Grid.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Collections.Generic;
namespace Server.Gumps;
public enum GridHues
{
White = 2049, // Change to 0x480 if you have old hues.mul
Black = 0,
}
public static class GridColors
{
public const string Gold = "#ffc959";
public const string White = "#ffffff";
public const string Blue = "#1D63DC";
public const string LightBlue = "#7799EE";
public const string Green = "#80E732";
public const string Orange = "#F28B00";
public const string Purple = "#F3ACF6";
public const string Red = "#B52B2D";
public const string LightGreen = "#E6FFC0";
public const string Yellow = "#FFFFBB";
}
public class ListItem
{
public int X { get; set; }
public int Y { get; set; }
public int Index { get; set; }
public Col[] Cols { get; set; }
}
public class Col
{
public int X { get; set; }
public int Width { get; set; }
public int HCenter => X + Width / 2;
public int HEnd => X + Width;
}
public class Row
{
public int Y { get; set; }
public int Height { get; set; }
public int VCenter => Y + Height / 2;
public int VEnd => Y + Height;
}
public class Grid
{
public string Name { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public List<Col> Columns { get; set; } = new();
public List<Row> Rows { get; set; } = new();
}
public class Swap
{
public int Index { get; set; }
public int Size { get; set; }
}

View file

@ -0,0 +1,525 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpGrid.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Collections.Generic;
using Server.Collections;
namespace Server.Gumps;
public class GumpGrid : Gump
{
private Dictionary<string, Grid> _grids = new();
public GumpGrid(int x, int y) : base(x, y)
{
}
private void AddColumn(string name, int x, int width)
{
_grids[name].Columns.Add(new Col { X = x, Width = width, });
}
private void AddRow(string name, int y, int height)
{
_grids[name].Rows.Add(new Row { Y = y, Height = height });
}
private void InitColumn(string name, int column, int w, int x)
{
var width = w / column;
for (int i = 0; i < column; i++)
{
AddColumn(name, i * width + x, width);
}
}
private void InitRow(string name, int row, int h, int y)
{
var height = h / row;
for (int i = 0; i < row; i++)
{
AddRow(name, i * height + y, height);
}
}
private static Swap Exist(ref PooledRefList<Swap> list, int index)
{
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
if (item.Index == index)
{
return item;
}
}
return null;
}
private static int CalculateCustom(int column, int len, string[] sizes, ref PooledRefList<Swap> list)
{
var cropSize = 0;
for (int i = 0; i < column; i++)
{
if (sizes[i] == "*")
{
continue;
}
//percent
var percent = sizes[i].RemoveOrdinal("*");
var size = sizes[i] != percent ? len / 100 * int.Parse(percent) : int.Parse(percent);
list.Add(new Swap { Index = i, Size = size });
cropSize += size;
}
return cropSize;
}
private void InitCustomSizeRow(string name, int height, int row, string[] sizes, int y)
{
var list = PooledRefList<Swap>.Create();
var cropHeight = CalculateCustom(row, height, sizes, ref list);
var Height = height - cropHeight;
var factor = row - list.Count;
for (int i = 0; i < row; i++)
{
var swap = Exist(ref list, i);
if (swap != null)
{
var size = swap.Size;
if (i == 0)
{
AddRow(name, y, size);
}
else
{
var col = _grids[name].Rows[i - 1];
AddRow(name, col.Height + col.Y + y, size);
}
}
else
{
var size = Height / factor;
if (i == 0)
{
AddRow(name, y, size);
}
else
{
var col = _grids[name].Rows[i - 1];
AddRow(name, col.Height + col.Y + y, size);
}
}
}
list.Dispose();
}
private void InitCustomSizeColumn(string name, int width, int column, string[] sizes, int x)
{
var list = PooledRefList<Swap>.Create();
var cropWidth = CalculateCustom(column, width, sizes, ref list);
var Width = width - cropWidth;
var factor = column - list.Count;
for (int i = 0; i < column; i++)
{
var swap = Exist(ref list, i);
if (swap != null)
{
var size = swap.Size;
if (i == 0)
{
AddColumn(name, x, size);
}
else
{
var col = _grids[name].Columns[i - 1];
AddColumn(name, col.Width + col.X + x, size);
}
}
else
{
var size = Width / factor;
if (i == 0)
{
AddColumn(name, x, size);
}
else
{
var col = _grids[name].Columns[i - 1];
AddColumn(name, col.Width + col.X + x, size);
}
}
}
}
public Grid SubGrid(
string name,
string destGridName,
int columnStart,
int rowStart,
int createColumnsCount,
int createRowsCount,
int columnSpan = 0,
int rowSpan = 0,
string createColumnSize = "",
string createRowSize = "",
int marginX = 0,
int marginY = 0
)
{
if (CalculateCord(
destGridName,
columnStart,
rowStart,
columnSpan,
rowSpan,
out var x,
out var y,
out var width,
out var height
))
{
Grid(
name,
width,
height,
createColumnsCount,
createRowsCount,
createColumnSize,
createRowSize,
x + marginX,
y + marginY
);
}
return CreateGrid(name, 0, 0);
}
public Grid CreateGrid(string name, int width, int height)
{
if (!_grids.TryGetValue(name, out var grid))
{
grid = new Grid { Name = name, Width = width, Height = height };
_grids.Add(name, grid);
}
return grid;
}
public Grid Grid(
string name,
int width,
int height,
int columns,
int rows,
string columnSize = "",
string rowSize = "",
int x = 0,
int y = 0
)
{
if (columns < 1)
{
throw new Exception(nameof(columns));
}
if (rows < 1)
{
throw new Exception(nameof(columns));
}
var Grid = CreateGrid(name, width, height);
if (columnSize.Length > 0)
{
var buffer = columnSize.Split(' ');
if (buffer.Length == columns)
{
InitCustomSizeColumn(name, width, columns, buffer, x);
}
else
{
throw new Exception(nameof(columnSize));
}
}
else
{
InitColumn(name, columns, width, x);
}
if (rowSize.Length > 0)
{
var buffer = rowSize.Split(' ');
if (buffer.Length == rows)
{
InitCustomSizeRow(name, height, rows, buffer, y);
}
else
{
throw new Exception(nameof(rowSize));
}
}
else
{
InitRow(name, rows, height, y);
}
return Grid;
}
public bool CalculateCord(
string name,
int column,
int row,
int columnSpan,
int rowSpan,
out int x,
out int y,
out int width,
out int height
)
{
x = 0; y = 0; width = 0; height = 0;
if (_grids.TryGetValue(name, out var grid))
{
var count = columnSpan == 0 ? column + 1 : row + columnSpan;
for (int i = column; i < count; i++)
{
if (i >= grid.Columns.Count)
{
break;
}
var dcol = grid.Columns[i];
if (i == column)
{
x = dcol.X;
}
width += dcol.Width;
}
count = rowSpan == 0 ? row + 1 : row + rowSpan;
for (int i = row; i < count; i++)
{
if (i >= grid.Rows.Count)
{
break;
}
var drow = grid.Rows[i];
if (i == row)
{
y = drow.Y;
}
height += drow.Height;
}
if (width == 0)
{
width = grid.Width;
}
if (height == 0)
{
height = grid.Height;
}
return true;
}
return false;
}
public class ListView
{
public int LineCount { get; }
public int ItemsCount { get; }
public ListItem[] Items { get; }
public ListItem Header { get; }
public int Page { get; }
public int TotalPages { get; }
public int ColHeight { get; }
public bool CanNext { get; }
public bool CanBack { get; }
public int ColsCount { get; }
public ListView(
int itemsCount,
int columns,
int heightPerItem,
int page,
int x,
int y,
int height,
int pageItemCount,
int[] ColWidth,
int marginX = 0,
int marginY = 0,
int headerHeight = 0
)
{
ItemsCount = itemsCount;
var itemsPerPageCount = pageItemCount == 0 ? (height - marginY) / heightPerItem : pageItemCount;
LineCount = itemsPerPageCount;
if (itemsCount > 0)
{
TotalPages = itemsCount / itemsPerPageCount;
if (itemsCount % itemsPerPageCount == 0)
{
TotalPages--;
}
}
Page = page > TotalPages ? TotalPages : page;
ColsCount = columns;
ColHeight = heightPerItem;
var index = Page * itemsPerPageCount > 0 ? Page * itemsPerPageCount : 0;
var nowPageItems = index + itemsPerPageCount;
if (index > 0)
{
CanBack = true;
}
if (nowPageItems < itemsCount)
{
CanNext = true;
}
if (nowPageItems > itemsCount)
{
itemsPerPageCount = Page > 0 ? itemsCount - Page * itemsPerPageCount : itemsCount;
}
//header
Header = new ListItem
{
X = x,
Y = y + marginY,
Index = -1,
Cols = new Col[columns],
};
var length = x + marginX;
for (int col = 0; col < columns; col++)
{
Header.Cols[col] = new Col();
Header.Cols[col].Width = ColWidth[col];
Header.Cols[col].X = length;
length += ColWidth[col];
}
Items = new ListItem[itemsPerPageCount];
for (int i = 0; i < itemsPerPageCount; i++)
{
Items[i] = new ListItem
{
X = x,
Y = y + i * heightPerItem + marginY + headerHeight,
Index = index + i,
Cols = Header.Cols
};
}
}
}
public ListView AddListView(
string name,
int column,
int row,
int itemsCount,
int page,
int heightPerItem,
int createColumn,
int columnSpan = 0,
int rowSpan = 0,
int width = 0,
int height = 0,
int pageItemCount = 0,
int marginX = 0,
int marginY = 0,
int headerHeight = 0,
string colSize = ""
)
{
if (CalculateCord(name, column, row, columnSpan, rowSpan, out var x, out var y, out var w, out var h))
{
w = width > 0 ? width : w;
h = height > 0 ? height : h;
int[] colWidth = new int[createColumn];
if (colSize == string.Empty)
{
for (int i = 0; i < createColumn; i++)
{
colWidth[i] = w / createColumn;
}
}
else
{
var buffer = colSize.Split(' ');
if (buffer.Length > 0)
{
const string listName = "listView";
_grids.Add(listName, new Grid());
if (createColumn < buffer.Length)
{
Array.Resize(ref buffer, createColumn);
}
InitCustomSizeColumn(listName, w, createColumn, buffer, x);
var cols = _grids[listName].Columns;
for (var i = 0; i < createColumn; i++)
{
colWidth[i] = cols[i].Width;
}
_grids.Remove(listName);
}
}
return new ListView(
itemsCount,
createColumn,
heightPerItem,
page,
x,
y,
h,
pageItemCount,
colWidth,
marginX,
marginY,
headerHeight
);
}
return null;
}
}

View file

@ -0,0 +1,27 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpFlags.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
namespace Server.Gumps;
[Flags]
public enum GumpFlags
{
NoDispose = 0x1,
NoResize = 0x2,
NoMove = 0x4,
NoClose = 0x8
}

View file

@ -0,0 +1,642 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpLayoutBuilder.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Text;
namespace Server.Gumps;
public ref struct GumpLayoutBuilder
{
private static readonly byte[] _staticLayoutBuffer = GC.AllocateUninitializedArray<byte>(0x80000);
private byte[] _layoutBuffer;
private int _bytesWritten;
internal GumpFlags _flags;
internal int _switches;
internal int _textEntries;
internal Span<byte> LayoutData => _layoutBuffer.AsSpan(0, _bytesWritten);
// This struct uses static fields and is therefore not thread safe!
// Do not instantiate/process multiple gumps at the same time!
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GumpLayoutBuilder() => _layoutBuffer = _staticLayoutBuffer;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoClose()
{
if ((_flags & GumpFlags.NoClose) == 0)
{
GrowIfNeeded(11);
Write("{ noclose }"u8);
_flags |= GumpFlags.NoClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoMove()
{
if ((_flags & GumpFlags.NoMove) == 0)
{
GrowIfNeeded(10);
Write("{ nomove }"u8);
_flags |= GumpFlags.NoMove;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoResize()
{
if ((_flags & GumpFlags.NoResize) == 0)
{
GrowIfNeeded(12);
Write("{ noresize }"u8);
_flags |= GumpFlags.NoResize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetNoDispose()
{
if ((_flags & GumpFlags.NoDispose) == 0)
{
GrowIfNeeded(13);
Write("{ nodispose }"u8);
_flags |= GumpFlags.NoDispose;
}
}
public void AddAlphaRegion(int x, int y, int width, int height)
{
GrowIfNeeded(8 + 12 + 36);
WriteStart("checkertrans"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteEnd();
}
public void AddBackground(int x, int y, int width, int height, int gumpId)
{
GrowIfNeeded(9 + 9 + 45);
WriteStart("resizepic"u8);
WriteValue(x);
WriteValue(y);
WriteValue(gumpId);
WriteValue(width);
WriteValue(height);
WriteEnd();
}
public void AddButton(
int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type = GumpButtonType.Reply, int param = 0
)
{
GrowIfNeeded(11 + 6 + 54 + 2);
WriteStart("button"u8);
WriteValue(x);
WriteValue(y);
WriteValue(normalId);
WriteValue(pressedId);
WriteValue(type == GumpButtonType.Reply);
WriteValue(param);
WriteValue(buttonId);
WriteEnd();
}
public void AddCheckbox(int x, int y, int inactiveId, int activeId, bool selected, int switchId)
{
GrowIfNeeded(10 + 8 + 45 + 2);
WriteStart("checkbox"u8);
WriteValue(x);
WriteValue(y);
WriteValue(inactiveId);
WriteValue(activeId);
WriteValue(selected);
WriteValue(switchId);
WriteEnd();
_switches++;
}
public void AddGroup(int groupId)
{
if (groupId == 1)
{
GrowIfNeeded(11);
Write("{ group 1 }"u8);
return;
}
GrowIfNeeded(5 + 7 + 9);
WriteStart("group"u8);
WriteValue(groupId);
WriteEnd();
}
public int AddHtmlPlaceholder(int x, int y, int width, int height,
bool background = false, bool scrollbar = false)
{
GrowIfNeeded(11 + 8 + 36 + 10);
WriteStart("htmlgump"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
var position = _bytesWritten;
Write(" "u8);
WriteValue(background);
WriteValue(scrollbar);
WriteEnd();
return position;
}
public void AddHtml(int x, int y, int width, int height, int text,
bool background = false, bool scrollbar = false)
{
GrowIfNeeded(11 + 8 + 45 + 4);
WriteStart("htmlgump"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(text);
WriteValue(background);
WriteValue(scrollbar);
WriteEnd();
}
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, bool background = false, bool scrollbar = false
)
{
GrowIfNeeded(11 + 11 + 45 + 4);
WriteStart("xmfhtmlgump"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(number);
WriteValue(background);
WriteValue(scrollbar);
WriteEnd();
}
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, int color, bool background = false, bool scrollbar = false
)
{
GrowIfNeeded(12 + 16 + 45 + 5 + 4);
WriteStart("xmfhtmlgumpcolor"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(number);
WriteValue(background);
WriteValue(scrollbar);
WriteValue(color);
WriteEnd();
}
public void AddHtmlLocalized(int x, int y, int width, int height, int number, ReadOnlySpan<char> args, int color,
bool background = false, bool scrollbar = false)
{
GrowIfNeeded(12 + 10 + 45 + 5 + 4 + (args.Length > 0 ? 3 + args.Length : 0));
WriteStart("xmfhtmltok"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(background);
WriteValue(scrollbar);
WriteValue(color);
WriteValue(number);
if (args.Length > 0)
{
Write("@"u8);
WriteValue(args);
Write("@ "u8);
}
WriteEnd();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, ref RawInterpolatedStringHandler handler, int color,
bool background = false, bool scrollbar = false
)
{
AddHtmlLocalized(x, y, width, height, number, handler.Text, color, background, scrollbar);
// Dispose of the handler
handler.Clear();
}
public void AddImage(int x, int y, int gumpId, int hue = 0, ReadOnlySpan<char> cls = default)
{
GrowIfNeeded(7 + 7 + 36 + (hue != 0 ? 14 : 0) + (cls.Length > 0 ? 7 + cls.Length : 0));
WriteStart("gumppic"u8);
WriteValue(x);
WriteValue(y);
WriteValue(gumpId);
if (hue != 0)
{
Write("hue="u8);
WriteValue(hue);
Write(" "u8);
}
if (!cls.IsEmpty)
{
Write("class="u8);
WriteValue(cls);
Write(" "u8);
}
WriteEnd();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImage(int x, int y, int gumpId, ref RawInterpolatedStringHandler handler)
{
AddImage(x, y, gumpId, 0, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddImage(int x, int y, int gumpId, int hue, ref RawInterpolatedStringHandler handler)
{
AddImage(x, y, gumpId, hue, handler.Text);
handler.Clear();
}
public void AddImageTiledButton(int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type, int param,
int itemId, int hue, int width, int height, int localizedTooltip = -1)
{
GrowIfNeeded(15 + 13 + 90 + 2);
WriteStart("buttontileart"u8);
WriteValue(x);
WriteValue(y);
WriteValue(normalId);
WriteValue(pressedId);
WriteValue(type == GumpButtonType.Reply);
WriteValue(param);
WriteValue(buttonId);
WriteValue(itemId);
WriteValue(hue);
WriteValue(width);
WriteValue(height);
WriteEnd();
if (localizedTooltip <= 0)
{
return;
}
AddTooltip(localizedTooltip);
}
public void AddImageTiled(int x, int y, int width, int height, int gumpId)
{
GrowIfNeeded(9 + 12 + 45);
WriteStart("gumppictiled"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(gumpId);
WriteEnd();
}
public void AddItem(int x, int y, int itemId, int hue = 0)
{
GrowIfNeeded(7 + 36 + (hue != 0 ? 20 : 7));
WriteStart(hue == 0 ? "tilepic"u8 : "tilepichue"u8);
WriteValue(x);
WriteValue(y);
WriteValue(itemId);
if (hue != 0)
{
WriteValue(hue);
}
WriteEnd();
}
public void AddItemProperty(Serial serial)
{
GrowIfNeeded(5 + 12 + 9);
WriteStart("itemproperty"u8);
WriteValue(serial.Value);
WriteEnd();
}
public int AddLabelPlaceholder(int x, int y, int hue)
{
GrowIfNeeded(8 + 4 + 27 + 6);
WriteStart("text"u8);
WriteValue(x);
WriteValue(y);
WriteValue(hue);
var position = _bytesWritten;
Write(" "u8);
WriteEnd();
return position;
}
public void AddLabel(int x, int y, int hue, int text)
{
GrowIfNeeded(8 + 4 + 36 + 6);
WriteStart("text"u8);
WriteValue(x);
WriteValue(y);
WriteValue(hue);
WriteValue(text);
WriteEnd();
}
public int AddLabelCroppedPlaceholder(int x, int y, int width, int height, int hue)
{
GrowIfNeeded(10 + 11 + 45 + 6);
WriteStart("croppedtext"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(hue);
var position = _bytesWritten;
Write(" "u8);
WriteEnd();
return position;
}
public void AddLabelCropped(int x, int y, int width, int height, int hue, int text)
{
GrowIfNeeded(10 + 11 + 54 + 6);
WriteStart("croppedtext"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(hue);
WriteValue(text);
WriteEnd();
}
public void AddGumpIdOverride(int gumpId)
{
GrowIfNeeded(5 + 10 + 9);
WriteStart("mastergump"u8);
WriteValue(gumpId);
WriteEnd();
}
public void AddPage(int page = 0)
{
if (page == 0)
{
GrowIfNeeded(10);
Write("{ page 0 }"u8);
return;
}
GrowIfNeeded(5 + 4 + 9);
WriteStart("page"u8);
WriteValue(page);
WriteEnd();
}
public void AddRadio(int x, int y, int inactiveId, int activeId, bool selected, int switchId)
{
GrowIfNeeded(10 + 5 + 45 + 2);
WriteStart("radio"u8);
WriteValue(x);
WriteValue(y);
WriteValue(inactiveId);
WriteValue(activeId);
WriteValue(selected);
WriteValue(switchId);
WriteEnd();
_switches++;
}
public void AddSpriteImage(int x, int y, int gumpId, int width, int height, int sx, int sy)
{
GrowIfNeeded(11 + 8 + 63);
WriteStart("picinpic"u8);
WriteValue(x);
WriteValue(y);
WriteValue(gumpId);
WriteValue(width);
WriteValue(height);
WriteValue(sx);
WriteValue(sy);
WriteEnd();
}
public int AddTextEntryPlaceholder(
int x, int y, int width, int height, int hue, int entryId
)
{
GrowIfNeeded(11 + 9 + 54 + 6);
WriteStart("textentry"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(hue);
WriteValue(entryId);
var position = _bytesWritten;
Write(" "u8);
WriteEnd();
_textEntries++;
return position;
}
public void AddTextEntry(
int x, int y, int width, int height, int hue, int entryId, int initialText
)
{
GrowIfNeeded(11 + 9 + 63 + 6);
WriteStart("textentry"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(hue);
WriteValue(entryId);
WriteValue(initialText);
WriteEnd();
_textEntries++;
}
public int AddTextEntryLimitedPlaceholder(
int x, int y, int width, int height, int hue, int entryId, int size = 0
)
{
GrowIfNeeded(12 + 16 + 63 + 6);
WriteStart("textentrylimited"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(hue);
WriteValue(entryId);
var position = _bytesWritten;
Write(" "u8);
WriteValue(size);
WriteEnd();
_textEntries++;
return position;
}
public void AddTextEntryLimited(
int x, int y, int width, int height, int hue, int entryId, int initialText, int size = 0
)
{
GrowIfNeeded(12 + 16 + 72);
WriteStart("textentrylimited"u8);
WriteValue(x);
WriteValue(y);
WriteValue(width);
WriteValue(height);
WriteValue(hue);
WriteValue(entryId);
WriteValue(initialText);
WriteValue(size);
WriteEnd();
_textEntries++;
}
public void AddTooltip(int number)
{
GrowIfNeeded(5 + 7 + 9);
WriteStart("tooltip"u8);
WriteValue(number);
WriteEnd();
}
public void AddTooltip(int number, ReadOnlySpan<char> args)
{
GrowIfNeeded(5 + 7 + 9 + (args.Length > 0 ? 3 + args.Length : 0));
WriteStart("tooltip"u8);
WriteValue(number);
if (args.Length > 0)
{
Write("@"u8);
WriteValue(args);
Write("@ "u8);
}
WriteEnd();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteStart(ReadOnlySpan<byte> value)
{
Write("{ "u8);
Write(value);
Write(" "u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteEnd() => Write("}"u8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteValue(bool value) => Write(value ? "1 "u8 : "0 "u8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteValue(ReadOnlySpan<char> value)
{
if (!value.IsEmpty)
{
_bytesWritten += value.GetBytesAscii(_layoutBuffer.AsSpan(_bytesWritten));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteValue<T>(T value, ReadOnlySpan<char> format = default) where T : IUtf8SpanFormattable
{
if (!value.TryFormat(_layoutBuffer.AsSpan(_bytesWritten), out int bytesWritten, format, null))
{
throw new InvalidOperationException($"Failed to format '{value}' with the given format '{format}'");
}
_bytesWritten += bytesWritten;
Write(" "u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Write(ReadOnlySpan<byte> span)
{
span.CopyTo(_layoutBuffer.AsSpan(_bytesWritten));
_bytesWritten += span.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void FinalizeLayout()
{
GrowIfNeeded(1);
_layoutBuffer[_bytesWritten++] = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void GrowIfNeeded(int count)
{
if (_bytesWritten + count <= _layoutBuffer.Length)
{
return;
}
var newSize = Math.Max(_bytesWritten + count, _layoutBuffer.Length * 2);
byte[] poolArray = STArrayPool<byte>.Shared.Rent(newSize);
_layoutBuffer.AsSpan(0, _bytesWritten).CopyTo(poolArray);
byte[] toReturn = _layoutBuffer;
_layoutBuffer = poolArray;
if (toReturn != _staticLayoutBuffer)
{
STArrayPool<byte>.Shared.Return(toReturn);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_layoutBuffer != _staticLayoutBuffer)
{
STArrayPool<byte>.Shared.Return(_layoutBuffer);
}
this = default;
}
}

View file

@ -0,0 +1,129 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpStringsBuilder.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Text;
namespace Server.Gumps;
public ref struct GumpStringsBuilder
{
private static readonly byte[] _staticStringsBuffer = GC.AllocateUninitializedArray<byte>(0x80000);
private static readonly Dictionary<ulong, int> _hashes = new();
private readonly bool _finalizeLayout;
private byte[] _stringsBuffer;
private int _stringBytesWritten;
internal int _stringsCount;
internal ReadOnlySpan<byte> StringsBuffer => _stringsBuffer.AsSpan(0, _stringBytesWritten);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GumpStringsBuilder(bool finalizeLayout)
{
_finalizeLayout = finalizeLayout;
_stringsBuffer = _staticStringsBuffer;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void GrowStringsBufferIfNeeded(int needed)
{
var newLength = needed + _stringBytesWritten;
if (newLength <= _stringsBuffer.Length)
{
return;
}
var newSize = Math.Max(newLength, _stringsBuffer.Length * 2);
byte[] poolArray = STArrayPool<byte>.Shared.Rent(newSize);
_stringsBuffer.AsSpan(0, _stringBytesWritten).CopyTo(poolArray);
byte[] toReturn = _stringsBuffer;
_stringsBuffer = poolArray;
if (toReturn != _staticStringsBuffer)
{
STArrayPool<byte>.Shared.Return(toReturn);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetStringSlot(ReadOnlySpan<char> slotKey, ref RawInterpolatedStringHandler handler)
{
SetStringSlot(slotKey, handler.Text);
handler.Clear();
}
public void SetStringSlot(ReadOnlySpan<char> slotKey, ReadOnlySpan<char> text)
{
var hash = HashUtility.ComputeHash64(slotKey);
if (text.Length > ushort.MaxValue)
{
text = text[..ushort.MaxValue];
}
GrowStringsBufferIfNeeded(2 + text.Length * 2);
BinaryPrimitives.WriteUInt16BigEndian(_stringsBuffer.AsSpan(_stringBytesWritten), (ushort)text.Length);
_stringBytesWritten += 2;
if (text.Length > 0)
{
_stringBytesWritten += text.GetBytesBigUni(_stringsBuffer.AsSpan(_stringBytesWritten));
}
if (_finalizeLayout)
{
_hashes[hash] = _stringsCount;
}
_stringsCount++;
}
public void FinalizeStrings(ref StaticGumpBuilder builder)
{
var startingIndex = builder._stringsCount;
var stringSlotOffsets = builder.StringSlotOffsets;
for (var i = 0; i < stringSlotOffsets.Length; i++)
{
var (hash, offset) = stringSlotOffsets[i];
if (hash != 0 && _hashes.TryGetValue(hash, out var index))
{
builder.WriteSlotIndex(offset, index + startingIndex);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_finalizeLayout)
{
_hashes.Clear();
}
if (_stringsBuffer != _staticStringsBuffer)
{
STArrayPool<byte>.Shared.Return(_stringsBuffer);
}
}
}

View file

@ -0,0 +1,198 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: IncomingGumpPackets.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using Server.Diagnostics;
using Server.Engines.Virtues;
using Server.Exceptions;
using Server.Mobiles;
using Server.Network;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace Server.Gumps;
public static partial class GumpSystem
{
public static void DisplayGumpResponse(NetState state, SpanReader reader)
{
var serial = (Serial)reader.ReadUInt32();
var typeId = reader.ReadInt32();
var buttonId = reader.ReadInt32();
BaseGump baseGump = null;
foreach (var g in GetAll(state))
{
if (g.Serial != serial || g.TypeID != typeId)
{
continue;
}
baseGump = g;
break;
}
if (baseGump != null)
{
if (baseGump is Gump gump)
{
var buttonExists = buttonId == 0; // 0 is always 'close'
if (!buttonExists)
{
foreach (var e in gump.Entries)
{
if ((e as GumpButton)?.ButtonID == buttonId)
{
buttonExists = true;
break;
}
if ((e as GumpImageTileButton)?.ButtonID == buttonId)
{
buttonExists = true;
break;
}
}
}
if (!buttonExists)
{
state.LogInfo("Invalid gump response, disconnecting...");
var exception = new InvalidGumpResponseException($"Button {buttonId} doesn't exist");
exception.SetStackTrace(new StackTrace());
NetState.TraceException(exception);
return;
}
}
var switchCount = reader.ReadInt32();
if (switchCount < 0 || switchCount > baseGump.Switches)
{
state.LogInfo("Invalid gump response, disconnecting...");
var exception = new InvalidGumpResponseException($"Bad switch count {switchCount}");
exception.SetStackTrace(new StackTrace());
NetState.TraceException(exception);
return;
}
int switchByteCount = switchCount * 4;
// Read all the integers
ReadOnlySpan<int> switchBlock =
MemoryMarshal.Cast<byte, int>(reader.Buffer.Slice(reader.Position, switchByteCount));
reader.Seek(switchByteCount, SeekOrigin.Current);
scoped ReadOnlySpan<int> switches;
// Swap the endianness if necessary
if (BitConverter.IsLittleEndian)
{
Span<int> reversedSwitches = stackalloc int[switchCount];
BinaryPrimitives.ReverseEndianness(switchBlock, reversedSwitches);
switches = reversedSwitches;
}
else
{
switches = switchBlock;
}
var textCount = reader.ReadInt32();
if (textCount < 0 || textCount > baseGump.TextEntries)
{
state.LogInfo("Invalid gump response, disconnecting...");
var exception = new InvalidGumpResponseException($"Bad text entry count {textCount}");
exception.SetStackTrace(new StackTrace());
NetState.TraceException(exception);
return;
}
Span<ushort> textIds = stackalloc ushort[textCount];
Span<Range> textFields = stackalloc Range[textCount];
var textOffset = reader.Position;
for (var i = 0; i < textCount; i++)
{
var textId = reader.ReadUInt16();
var textLength = reader.ReadUInt16();
if (textLength > 239)
{
state.LogInfo("Invalid gump response, disconnecting...");
var exception = new InvalidGumpResponseException($"Text entry {i} is too long ({textLength})");
exception.SetStackTrace(new StackTrace());
NetState.TraceException(exception);
return;
}
textIds[i] = textId;
var offset = reader.Position - textOffset;
var length = textLength * 2;
textFields[i] = offset..(offset + length);
reader.Seek(length, SeekOrigin.Current);
}
var textBlock = reader.Buffer.Slice(textOffset, reader.Position - textOffset);
Remove(state, baseGump);
var prof = GumpProfile.Acquire(baseGump.GetType());
prof?.Start();
var relayInfo = new RelayInfo(
buttonId,
switches,
textIds,
textFields,
textBlock
);
baseGump.OnResponse(state, relayInfo);
prof?.Finish();
}
if (typeId == 461)
{
// Virtue gump
var switchCount = reader.Remaining >= 4 ? reader.ReadInt32() : 0;
if (buttonId == 1 && switchCount > 0)
{
var beheld = World.FindEntity<PlayerMobile>((Serial)reader.ReadUInt32());
if (beheld != null)
{
VirtueGump.RequestVirtueGump((PlayerMobile)state.Mobile, beheld);
}
}
else
{
var beheld = World.FindMobile(serial);
if (beheld != null)
{
VirtueGump.RequestVirtueItem((PlayerMobile)state.Mobile, beheld, buttonId);
}
}
}
}
}

View file

@ -0,0 +1,289 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpSystem.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using Server.Gumps.Base;
using Server.Logging;
using Server.Network;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Server.Gumps;
public static partial class GumpSystem
{
private const int GumpCap = 512;
private const int InitialCapacity = 4;
private static readonly Dictionary<NetState, List<BaseGump>> _gumps = [];
private static readonly ILogger _logger = LogFactory.GetLogger(typeof(GumpSystem));
public static unsafe void Configure()
{
IncomingPackets.Register(0xB1, 0, true, &DisplayGumpResponse);
EventSink.Disconnected += EventSink_Disconnected;
}
private static void EventSink_Disconnected(Mobile m)
{
if (m.NetState != null && _gumps.Remove(m.NetState, out var gumps))
{
gumps.Clear();
}
}
private static ReadOnlySpan<BaseGump> GetAll(NetState ns) =>
_gumps.TryGetValue(ns, out var gumps) ? CollectionsMarshal.AsSpan(gumps) : [];
private static T Find<T>(NetState ns) where T : BaseGump
{
if (ns == null || !_gumps.TryGetValue(ns, out var gumps))
{
return null;
}
var gumpsSpan = CollectionsMarshal.AsSpan(gumps);
for (int i = 0; i < gumpsSpan.Length; i++)
{
if (gumpsSpan[i] is T tGump)
{
return tGump;
}
}
return null;
}
private static void Add(NetState ns, BaseGump gump)
{
if (ns == null || gump == null)
{
return;
}
if (!_gumps.TryGetValue(ns, out var gumps))
{
gumps = new List<BaseGump>(InitialCapacity);
_gumps.Add(ns, gumps);
}
if (gumps.Count < GumpCap)
{
gumps.Add(gump);
}
else
{
_logger.Information("Exceeded gump cap, disconnecting...");
ns.Disconnect("Exceeded gump cap.");
}
}
private static void Remove(NetState ns, BaseGump gump)
{
if (_gumps.TryGetValue(ns, out var gumps))
{
for (int i = 0; i < gumps.Count; i++)
{
if (gumps[i] == gump)
{
gumps.RemoveAt(i);
return;
}
}
}
}
private static bool Remove<T>(NetState ns, out T gump) where T : BaseGump
{
if (ns != null && _gumps.TryGetValue(ns, out var gumps))
{
var gumpsSpan = CollectionsMarshal.AsSpan(gumps);
for (int i = 0; i < gumpsSpan.Length; i++)
{
if (gumpsSpan[i] is T tGump)
{
gumps.RemoveAt(i);
gump = tGump;
return true;
}
}
}
gump = null;
return false;
}
private static void Send(NetState ns, BaseGump gump, bool singleton)
{
if (ns.CannotSendPackets()) // Handles ns null check too
{
return;
}
ref List<BaseGump> list = ref CollectionsMarshal.GetValueRefOrAddDefault(_gumps, ns, out bool exists);
if (exists)
{
bool replaced = false;
if (singleton || gump.Singleton)
{
for (int i = 0; i < list.Count; i++)
{
BaseGump old = list[i];
if (old.TypeID == gump.TypeID)
{
ns.SendCloseGump(old.TypeID, 0);
old.OnServerClose(ns);
list[i] = gump;
replaced = true;
break;
}
}
}
if (!replaced)
{
list.Add(gump);
}
}
else
{
list = [gump];
}
gump.SendTo(ns);
}
private static bool Close<T>(NetState ns) where T : BaseGump
{
if (Remove<T>(ns, out var gump))
{
ns.SendCloseGump(gump.TypeID, 0);
gump.OnServerClose(ns);
return true;
}
return false;
}
private static readonly List<BaseGump> _emptyList = [];
private static NetStateGumps Get(NetState ns)
{
if (ns == null)
{
return new NetStateGumps(_emptyList, null);
}
ref List<BaseGump> list = ref CollectionsMarshal.GetValueRefOrAddDefault(_gumps, ns, out bool exists);
if (!exists)
{
list = [];
}
return new NetStateGumps(list, ns);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasGump<T>([DisallowNull] this Mobile m) where T : BaseGump
{
ArgumentNullException.ThrowIfNull(m);
return Find<T>(m.NetState) != null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T FindGump<T>([DisallowNull] this Mobile m) where T : BaseGump
{
ArgumentNullException.ThrowIfNull(m);
return Find<T>(m.NetState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CloseGump<T>([DisallowNull] this Mobile m) where T : BaseGump
{
ArgumentNullException.ThrowIfNull(m);
return Close<T>(m.NetState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SendGump([DisallowNull] this Mobile m, BaseGump g, bool singleton = false)
{
ArgumentNullException.ThrowIfNull(m);
Send(m.NetState, g, singleton);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<BaseGump> GetAllGumps([DisallowNull] this Mobile m)
{
ArgumentNullException.ThrowIfNull(m);
return GetAll(m.NetState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NetStateGumps GetGumps([DisallowNull] this Mobile m)
{
ArgumentNullException.ThrowIfNull(m);
return Get(m.NetState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasGump<T>([DisallowNull] this NetState ns) where T : BaseGump
{
ArgumentNullException.ThrowIfNull(ns);
return Find<T>(ns) != null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SendGump([DisallowNull] this NetState ns, BaseGump g, bool singleton = false)
{
ArgumentNullException.ThrowIfNull(ns);
Send(ns, g, singleton);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CloseGump<T>([DisallowNull] this NetState ns) where T : BaseGump
{
ArgumentNullException.ThrowIfNull(ns);
return Close<T>(ns);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<BaseGump> GetAllGumps([DisallowNull] this NetState ns)
{
ArgumentNullException.ThrowIfNull(ns);
return GetAll(ns);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddGump([DisallowNull] this NetState ns, BaseGump gump)
{
ArgumentNullException.ThrowIfNull(ns);
Add(ns, gump);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NetStateGumps GetGumps([DisallowNull] this NetState ns)
{
ArgumentNullException.ThrowIfNull(ns);
return Get(ns);
}
}

View file

@ -0,0 +1,25 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: InvalidGumpResponseException.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
namespace Server.Gumps;
public class InvalidGumpResponseException : Exception
{
public InvalidGumpResponseException(string reason) : base(reason)
{
}
}

View file

@ -0,0 +1,324 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Gump.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using Server.Collections;
using Server.Network;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
namespace Server.Gumps;
public class Gump : BaseGump
{
private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly OrderedSet<string> _stringsList = new();
private int _switches;
private int _textEntries;
public Gump(int x, int y)
{
X = x;
Y = y;
Entries = [];
Strings = [];
}
public List<string> Strings { get; }
public List<GumpEntry> Entries { get; }
public bool Disposable { get; set; } = true;
public bool Resizable { get; set; } = true;
public bool Draggable { get; set; } = true;
public bool Closable { get; set; } = true;
public override int Switches => _switches;
public override int TextEntries => _textEntries;
public void AddPage(int page)
{
Add(new GumpPage(page));
}
public void AddAlphaRegion(int x, int y, int width, int height)
{
Add(new GumpAlphaRegion(x, y, width, height));
}
public void AddBackground(int x, int y, int width, int height, int gumpID)
{
Add(new GumpBackground(x, y, width, height, gumpID));
}
public void AddButton(
int x, int y, int normalID, int pressedID, int buttonID,
GumpButtonType type = GumpButtonType.Reply, int param = 0
)
{
Add(new GumpButton(x, y, normalID, pressedID, buttonID, type, param));
}
public void AddCheck(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
Add(new GumpCheck(x, y, inactiveID, activeID, initialState, switchID));
}
public void AddGroup(int group)
{
Add(new GumpGroup(group));
}
public void AddTooltip(int number, string args = null)
{
Add(new GumpTooltip(number, args));
}
public void AddHtml(
int x, int y, int width, int height, string text, bool background = false, bool scrollbar = false
)
{
Add(new GumpHtml(x, y, width, height, text, background, scrollbar));
}
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, bool background = false,
bool scrollbar = false
)
{
Add(new GumpHtmlLocalized(x, y, width, height, number, background, scrollbar));
}
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, int color, bool background = false,
bool scrollbar = false
)
{
Add(new GumpHtmlLocalized(x, y, width, height, number, color, background, scrollbar));
}
public void AddHtmlLocalized(
int x, int y, int width, int height, int number, string args, int color,
bool background = false, bool scrollbar = false
)
{
Add(new GumpHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar));
}
public void AddImage(int x, int y, int gumpID, int hue = 0)
{
Add(new GumpImage(x, y, gumpID, hue));
}
public void AddImageTiled(int x, int y, int width, int height, int gumpID)
{
Add(new GumpImageTiled(x, y, width, height, gumpID));
}
public void AddImageTiledButton(
int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type,
int param, int itemID, int hue, int width, int height
)
{
Add(
new GumpImageTileButton(
x,
y,
normalID,
pressedID,
buttonID,
type,
param,
itemID,
hue,
width,
height
)
);
}
public void AddItem(int x, int y, int itemID, int hue = 0)
{
Add(new GumpItem(x, y, itemID, hue));
}
public void AddLabel(int x, int y, int hue, string text)
{
Add(new GumpLabel(x, y, hue, text));
}
public void AddLabelHtml(int x, int y, int width, int height, string text, string hue, int size = 4, bool center = true)
{
AddHtml(x, y, width, height, center ? text.Center(hue, size) : text.Color(hue, size));
}
public void AddLabelCropped(int x, int y, int width, int height, int hue, string text)
{
Add(new GumpLabelCropped(x, y, width, height, hue, text));
}
public void AddRadio(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
Add(new GumpRadio(x, y, inactiveID, activeID, initialState, switchID));
}
public void AddTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText)
{
Add(new GumpTextEntry(x, y, width, height, hue, entryID, initialText));
}
public void AddTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText, int size)
{
Add(new GumpTextEntryLimited(x, y, width, height, hue, entryID, initialText, size));
}
public void AddItemProperty(Serial serial)
{
Add(new GumpItemProperty(serial));
}
public void AddSpriteImage(int x, int y, int gumpID, int width, int height, int sx, int sy)
{
Add(new GumpSpriteImage(x, y, gumpID, width, height, sx, sy));
}
public void AddECHandleInput()
{
Add(new GumpECHandleInput());
}
public void AddGumpIDOverride(int gumpID)
{
Add(new GumpMasterGump(gumpID));
}
public void Add(GumpEntry g)
{
if (g.Parent != this)
{
g.Parent = this;
}
else if (!Entries.Contains(g))
{
Entries.Add(g);
}
}
public void Remove(GumpEntry g)
{
if (g == null || !Entries.Contains(g))
{
return;
}
Entries.Remove(g);
g.Parent = null;
}
public int Intern(string value)
{
var indexOf = Strings.IndexOf(value);
if (indexOf >= 0)
{
return indexOf;
}
Strings.Add(value);
return Strings.Count - 1;
}
public override void Compile(ref SpanWriter writer)
{
_textEntries = 0;
_switches = 0;
var layoutWriter = new SpanWriter(_layoutBuffer);
if (!Draggable)
{
layoutWriter.Write("{ nomove }"u8);
}
if (!Closable)
{
layoutWriter.Write("{ noclose }"u8);
}
if (!Disposable)
{
layoutWriter.Write("{ nodispose }"u8);
}
if (!Resizable)
{
layoutWriter.Write("{ noresize }"u8);
}
foreach (var entry in Entries)
{
entry.AppendTo(ref layoutWriter, _stringsList, ref _textEntries, ref _switches);
}
var stringsWriter = new SpanWriter(_stringsBuffer);
foreach (var str in _stringsList)
{
var s = str ?? "";
stringsWriter.Write((ushort)s.Length);
stringsWriter.WriteBigUni(s);
}
writer.Write((byte)0xDD); // Packet ID
writer.Seek(2, SeekOrigin.Current);
writer.Write(Serial);
writer.Write(TypeID);
writer.Write(X);
writer.Write(Y);
layoutWriter.Write((byte)0); // Layout text terminator
OutgoingGumpPackets.WritePacked(layoutWriter.Span, ref writer);
writer.Write(_stringsList.Count);
OutgoingGumpPackets.WritePacked(stringsWriter.Span, ref writer);
writer.WritePacketLength();
layoutWriter.Dispose(); // Just in case
stringsWriter.Dispose(); // Just in case
if (_stringsList.Count > 0)
{
_stringsList.Clear();
}
}
protected void Reset()
{
_switches = 0;
_textEntries = 0;
Entries.Clear();
Strings.Clear();
}
}

View file

@ -0,0 +1,43 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpAlphaRegion.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using Server.Collections;
namespace Server.Gumps;
public class GumpAlphaRegion : GumpEntry
{
public GumpAlphaRegion(int x, int y, int width, int height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii($"{{ checkertrans {X} {Y} {Width} {Height} }}");
}
}

View file

@ -0,0 +1,46 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpBackground.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using Server.Collections;
namespace Server.Gumps;
public class GumpBackground : GumpEntry
{
public GumpBackground(int x, int y, int width, int height, int gumpID)
{
X = x;
Y = y;
Width = width;
Height = height;
GumpID = gumpID;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int GumpID { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii($"{{ resizepic {X} {Y} {GumpID} {Width} {Height} }}");
}
}

View file

@ -0,0 +1,61 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpButton.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using Server.Collections;
namespace Server.Gumps;
public enum GumpButtonType
{
Page = 0,
Reply = 1
}
public class GumpButton : GumpEntry
{
public GumpButton(
int x, int y, int normalID, int pressedID, int buttonID,
GumpButtonType type = GumpButtonType.Reply, int param = 0
)
{
X = x;
Y = y;
NormalID = normalID;
PressedID = pressedID;
ButtonID = buttonID;
Type = type;
Param = param;
}
public int X { get; set; }
public int Y { get; set; }
public int NormalID { get; set; }
public int PressedID { get; set; }
public int ButtonID { get; set; }
public GumpButtonType Type { get; set; }
public int Param { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii($"{{ button {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} }}");
}
}

View file

@ -0,0 +1,51 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpCheck.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using Server.Collections;
namespace Server.Gumps;
public class GumpCheck : GumpEntry
{
public GumpCheck(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
X = x;
Y = y;
InactiveID = inactiveID;
ActiveID = activeID;
InitialState = initialState;
SwitchID = switchID;
}
public int X { get; set; }
public int Y { get; set; }
public int InactiveID { get; set; }
public int ActiveID { get; set; }
public bool InitialState { get; set; }
public int SwitchID { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var initialState = InitialState ? "1" : "0";
writer.WriteAscii($"{{ checkbox {X} {Y} {InactiveID} {ActiveID} {initialState} {SwitchID} }}");
switches++;
}
}

View file

@ -0,0 +1,27 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpECHandleInput.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using Server.Collections;
namespace Server.Gumps;
public class GumpECHandleInput : GumpEntry
{
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.Write("{ echandleinput }"u8);
}
}

View file

@ -0,0 +1,27 @@
using System.Buffers;
using Server.Collections;
namespace Server.Gumps;
public abstract class GumpEntry
{
private Gump m_Parent;
public Gump Parent
{
get => m_Parent;
set
{
if (m_Parent != value)
{
m_Parent?.Remove(this);
m_Parent = value;
m_Parent?.Add(this);
}
}
}
public abstract void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches);
}

Some files were not shown because too many files have changed in this diff Show more