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

@ -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);
}

View file

@ -0,0 +1,38 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpGroup.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 GumpGroup : GumpEntry
{
public GumpGroup(int group) => Group = group;
public int Group { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
if (Group == 1)
{
writer.Write("{ group 1 }"u8);
}
else
{
writer.WriteAscii($"{{ group {Group} }}");
}
}
}

View file

@ -0,0 +1,55 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpHtml.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 GumpHtml : GumpEntry
{
public GumpHtml(int x, int y, int width, int height, string text, bool background, bool scrollbar)
{
X = x;
Y = y;
Width = width;
Height = height;
Text = text;
Background = background;
Scrollbar = scrollbar;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public string Text { get; set; }
public bool Background { get; set; }
public bool Scrollbar { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var textIndex = strings.Add(Text ?? "");
var background = Background ? "1" : "0";
var scrollbar = Scrollbar ? "1" : "0";
writer.WriteAscii($"{{ htmlgump {X} {Y} {Width} {Height} {textIndex} {background} {scrollbar} }}");
}
}

View file

@ -0,0 +1,132 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpHtmlLocalized.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 GumpHtmlLocalizedType
{
Plain,
Color,
Args
}
public class GumpHtmlLocalized : GumpEntry
{
public GumpHtmlLocalized(
int x, int y, int width, int height, int number,
bool background = false, bool scrollbar = false
)
{
X = x;
Y = y;
Width = width;
Height = height;
Number = number;
Background = background;
Scrollbar = scrollbar;
Type = GumpHtmlLocalizedType.Plain;
}
public GumpHtmlLocalized(
int x, int y, int width, int height, int number, int color,
bool background = false, bool scrollbar = false
)
{
X = x;
Y = y;
Width = width;
Height = height;
Number = number;
Color = color;
Background = background;
Scrollbar = scrollbar;
Type = GumpHtmlLocalizedType.Color;
}
public GumpHtmlLocalized(
int x, int y, int width, int height, int number, string args, int color,
bool background = false, bool scrollbar = false
)
{
// Are multiple arguments unsupported? And what about non ASCII arguments?
X = x;
Y = y;
Width = width;
Height = height;
Number = number;
Args = args;
Color = color;
Background = background;
Scrollbar = scrollbar;
Type = GumpHtmlLocalizedType.Args;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Number { get; set; }
public string Args { get; set; }
public int Color { get; set; }
public bool Background { get; set; }
public bool Scrollbar { get; set; }
public GumpHtmlLocalizedType Type { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
switch (Type)
{
default:
case GumpHtmlLocalizedType.Plain:
{
writer.WriteAscii(
$"{{ xmfhtmlgump {X} {Y} {Width} {Height} {Number} {(Background ? "1" : "0")} {(Scrollbar ? "1" : "0")} }}"
);
break;
}
case GumpHtmlLocalizedType.Color:
{
writer.WriteAscii(
$"{{ xmfhtmlgumpcolor {X} {Y} {Width} {Height} {Number} {(Background ? "1" : "0")} {(Scrollbar ? "1" : "0")} {Color} }}"
);
break;
}
case GumpHtmlLocalizedType.Args:
{
writer.WriteAscii(
$"{{ xmfhtmltok {X} {Y} {Width} {Height} {(Background ? "1" : "0")} {(Scrollbar ? "1" : "0")} {Color} {Number} @{Args}@ }}"
);
break;
}
}
}
}

View file

@ -0,0 +1,65 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpImage.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 GumpImage : GumpEntry
{
public GumpImage(int x, int y, int gumpID, int hue = 0, string cls = null)
{
X = x;
Y = y;
GumpID = gumpID;
Hue = hue;
Class = cls;
}
public int X { get; set; }
public int Y { get; set; }
public int GumpID { get; set; }
public int Hue { get; set; }
public string Class { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var hasClass = !string.IsNullOrEmpty(Class);
if (Hue != 0)
{
if (hasClass)
{
writer.WriteAscii($"{{ gumppic {X} {Y} {GumpID} hue={Hue} class={Class} }}");
}
else
{
writer.WriteAscii($"{{ gumppic {X} {Y} {GumpID} hue={Hue} }}");
}
}
else if (hasClass)
{
writer.WriteAscii($"{{ gumppic {X} {Y} {GumpID} class={Class} }}");
}
else
{
writer.WriteAscii($"{{ gumppic {X} {Y} {GumpID} }}");
}
}
}

View file

@ -0,0 +1,70 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpImageTileButton.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 GumpImageTileButton : GumpEntry
{
public GumpImageTileButton(
int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param,
int itemID, int hue, int width, int height
)
{
X = x;
Y = y;
NormalID = normalID;
PressedID = pressedID;
ButtonID = buttonID;
Type = type;
Param = param;
ItemID = itemID;
Hue = hue;
Width = width;
Height = height;
}
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 int ItemID { get; set; }
public int Hue { 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(
$"{{ buttontileart {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} {ItemID} {Hue} {Width} {Height} }}"
);
}
}

View file

@ -0,0 +1,46 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpImageTiled.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 GumpImageTiled : GumpEntry
{
public GumpImageTiled(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($"{{ gumppictiled {X} {Y} {Width} {Height} {GumpID} }}");
}
}

View file

@ -0,0 +1,43 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpItem.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 GumpItem : GumpEntry
{
public GumpItem(int x, int y, int itemID, int hue = 0)
{
X = x;
Y = y;
ItemID = itemID;
Hue = hue;
}
public int X { get; set; }
public int Y { get; set; }
public int ItemID { get; set; }
public int Hue { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii(Hue == 0 ? $"{{ tilepic {X} {Y} {ItemID} }}" : $"{{ tilepichue {X} {Y} {ItemID} {Hue} }}");
}
}

View file

@ -0,0 +1,31 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpItemProperty.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 GumpItemProperty : GumpEntry
{
public GumpItemProperty(Serial serial) => Serial = serial;
public Serial Serial { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii($"{{ itemproperty {Serial.Value} }}");
}
}

View file

@ -0,0 +1,44 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpLabel.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 GumpLabel : GumpEntry
{
public GumpLabel(int x, int y, int hue, string text)
{
X = x;
Y = y;
Hue = hue;
Text = text;
}
public int X { get; set; }
public int Y { get; set; }
public int Hue { get; set; }
public string Text { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var textIndex = strings.Add(Text ?? "");
writer.WriteAscii($"{{ text {X} {Y} {Hue} {textIndex} }}");
}
}

View file

@ -0,0 +1,50 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpLabelCropped.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 GumpLabelCropped : GumpEntry
{
public GumpLabelCropped(int x, int y, int width, int height, int hue, string text)
{
X = x;
Y = y;
Width = width;
Height = height;
Hue = hue;
Text = text;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Hue { get; set; }
public string Text { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var textIndex = strings.Add(Text ?? "");
writer.WriteAscii($"{{ croppedtext {X} {Y} {Width} {Height} {Hue} {textIndex} }}");
}
}

View file

@ -0,0 +1,31 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpMasterGump.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 GumpMasterGump : GumpEntry
{
public GumpMasterGump(int gumpID) => GumpID = gumpID;
public int GumpID { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii($"{{ mastergump {GumpID} }}");
}
}

View file

@ -0,0 +1,38 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpPage.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 GumpPage : GumpEntry
{
public GumpPage(int page) => Page = page;
public int Page { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
if (Page == 0)
{
writer.Write("{ page 0 }"u8);
}
else
{
writer.WriteAscii($"{{ page {Page} }}");
}
}
}

View file

@ -0,0 +1,51 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpRadio.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 GumpRadio : GumpEntry
{
public GumpRadio(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($"{{ radio {X} {Y} {InactiveID} {ActiveID} {initialState} {SwitchID} }}");
switches++;
}
}

View file

@ -0,0 +1,52 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpSpriteImage.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 GumpSpriteImage : GumpEntry
{
public GumpSpriteImage(int x, int y, int gumpID, int width, int height, int sx, int sy)
{
X = x;
Y = y;
GumpID = gumpID;
Width = width;
Height = height;
SX = sx;
SY = sy;
}
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 int SX { get; set; }
public int SY { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii($"{{ picinpic {X} {Y} {GumpID} {Width} {Height} {SX} {SY} }}");
}
}

View file

@ -0,0 +1,54 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpTextEntry.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 GumpTextEntry : GumpEntry
{
public GumpTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText)
{
X = x;
Y = y;
Width = width;
Height = height;
Hue = hue;
EntryID = entryID;
InitialText = initialText;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Hue { get; set; }
public int EntryID { get; set; }
public string InitialText { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var textIndex = strings.Add(InitialText ?? "");
writer.WriteAscii($"{{ textentry {X} {Y} {Width} {Height} {Hue} {EntryID} {textIndex} }}");
entries++;
}
}

View file

@ -0,0 +1,59 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpTextEntryLimited.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 GumpTextEntryLimited : GumpEntry
{
public GumpTextEntryLimited(
int x, int y, int width, int height, int hue, int entryID, string initialText, int size = 0
)
{
X = x;
Y = y;
Width = width;
Height = height;
Hue = hue;
EntryID = entryID;
InitialText = initialText;
Size = size;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Hue { get; set; }
public int EntryID { get; set; }
public string InitialText { get; set; }
public int Size { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
var textIndex = strings.Add(InitialText ?? "");
writer.WriteAscii($"{{ textentrylimited {X} {Y} {Width} {Height} {Hue} {EntryID} {textIndex} {Size} }}");
entries++;
}
}

View file

@ -0,0 +1,39 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: GumpTooltip.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;
// Note, on OSI, the tooltip supports ONLY clilocs as far as I can figure out,
// and the tooltip ONLY works after the buttonTileArt (as far as I can tell from testing)
public class GumpTooltip : GumpEntry
{
public GumpTooltip(int number, string args)
{
Number = number;
Args = args;
}
public int Number { get; set; }
public string Args { get; set; }
public override void AppendTo(ref SpanWriter writer, OrderedSet<string> strings, ref int entries, ref int switches)
{
writer.WriteAscii(string.IsNullOrEmpty(Args) ? $"{{ tooltip {Number} }}" : $"{{ tooltip {Number} @{Args}@ }}");
}
}

View file

@ -0,0 +1,87 @@
using Server.Network;
using System.Collections.Generic;
namespace Server.Gumps.Base;
public readonly ref struct NetStateGumps
{
private readonly List<BaseGump> _gumps;
private readonly NetState _state;
public NetStateGumps(List<BaseGump> gumps, NetState state)
{
_gumps = gumps;
_state = state;
}
public bool Close<T>() where T : BaseGump
{
if (_state == null || _gumps == null)
{
return false;
}
for (int i = 0; i < _gumps.Count; i++)
{
if (_gumps[i] is T tGump)
{
_state.SendCloseGump(tGump.TypeID, 0);
tGump.OnServerClose(_state);
_gumps.RemoveAt(i);
return true;
}
}
return false;
}
public T Find<T>() where T : BaseGump
{
if (_state == null || _gumps == null)
{
return null;
}
for (int i = 0; i < _gumps.Count; i++)
{
if (_gumps[i] is T tGump)
{
return tGump;
}
}
return null;
}
public bool Has<T>() where T : BaseGump => Find<T>() != null;
public void Send(BaseGump gump, bool singleton = false)
{
if (_state.CannotSendPackets()) // Cannot send packets handles _state null check
{
return;
}
if (singleton || gump.Singleton)
{
for (int i = 0; i < _gumps.Count; i++)
{
BaseGump old = _gumps[i];
if (old.TypeID == gump.TypeID)
{
_state.SendCloseGump(old.TypeID, 0);
old.OnServerClose(_state);
_gumps[i] = gump;
gump.SendTo(_state);
return;
}
}
}
_gumps.Add(gump);
gump.SendTo(_state);
}
}

View file

@ -0,0 +1,100 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: OutgoingGumpPackets.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.Compression;
using Server.Logging;
using Server.Network;
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
namespace Server.Gumps;
public static class OutgoingGumpPackets
{
private static readonly ILogger _logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WritePacked(ReadOnlySpan<byte> span, ref SpanWriter writer)
{
var length = span.Length;
if (length == 0)
{
writer.Write(0);
return;
}
var dest = writer.RawBuffer[(writer.Position + 8)..];
var bytesPacked = Deflate.Standard.Pack(dest, span);
if (bytesPacked == 0)
{
_logger.Warning("Gump compression failed");
writer.Write(4);
writer.Write(0);
return;
}
writer.Write(4 + bytesPacked);
writer.Write(length);
writer.Seek(bytesPacked, SeekOrigin.Current);
}
public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption)
{
if (ns.CannotSendPackets())
{
return;
}
unknown ??= "";
caption ??= "";
var length = 15 + unknown.Length + caption.Length;
var writer = new SpanWriter(stackalloc byte[length]);
writer.Write((byte)0x8B); // Packet ID
writer.Write((ushort)length);
writer.Write(serial);
writer.Write((short)gumpId);
writer.Write((short)(unknown.Length + 1));
writer.WriteAsciiNull(unknown);
writer.Write((short)(caption.Length + 1));
writer.WriteAsciiNull(caption);
ns.Send(writer.Span);
}
public static void SendCloseGump(this NetState ns, int typeId, int buttonId)
{
if (ns.CannotSendPackets())
{
return;
}
var writer = new SpanWriter(stackalloc byte[13]);
writer.Write((byte)0xBF); // Packet ID
writer.Write((ushort)13);
writer.Write((short)0x04);
writer.Write(typeId);
writer.Write(buttonId);
ns.Send(writer.Span);
}
}

View file

@ -0,0 +1,55 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: RelayInfo.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.Text;
using System;
namespace Server.Gumps;
public readonly ref struct RelayInfo
{
private readonly ReadOnlySpan<byte> _textBlock;
private readonly ReadOnlySpan<ushort> _textIds;
private readonly ReadOnlySpan<Range> _textRanges;
public RelayInfo(
int buttonId,
ReadOnlySpan<int> switches,
ReadOnlySpan<ushort> textIds,
ReadOnlySpan<Range> textRanges,
ReadOnlySpan<byte> textBlock
)
{
ButtonID = buttonId;
Switches = switches;
_textIds = textIds;
_textRanges = textRanges;
_textBlock = textBlock;
}
public int ButtonID { get; }
public ReadOnlySpan<int> Switches { get; }
public bool IsSwitched(int switchId) => Switches.Contains(switchId);
public string GetTextEntry(int entryId)
{
int index = _textIds.IndexOf((ushort)entryId);
return index == -1
? default
: TextEncoding.GetString(_textBlock[_textRanges[index]], TextEncoding.Unicode, true);
}
}

View file

@ -0,0 +1,165 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: StaticGump.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;
using System.IO;
using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Network;
namespace Server.Gumps;
public abstract class StaticGump<TSelf> : BaseGump where TSelf : StaticGump<TSelf>
{
private static int _switches;
private static int _textEntries;
private static byte[] _compressedLayoutData;
private static byte[] _compressedStringsData;
private static bool _hasDynamicStrings;
private static int _staticStringsCount;
private static byte[] _staticStrings;
public override int Switches => _switches;
public override int TextEntries => _textEntries;
public StaticGump(int x, int y) : base(x, y)
{
}
protected abstract void BuildLayout(ref StaticGumpBuilder builder);
protected virtual void BuildStrings(ref GumpStringsBuilder builder)
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteLayout(ref SpanWriter writer, ref StaticGumpBuilder gumpBuilder)
{
var layoutPos = writer.Position;
OutgoingGumpPackets.WritePacked(gumpBuilder.LayoutData, ref writer);
var layoutLength = writer.Position - layoutPos;
_compressedLayoutData = GC.AllocateUninitializedArray<byte>(layoutLength);
writer.Span.Slice(layoutPos, layoutLength).CopyTo(_compressedLayoutData);
}
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);
if (_compressedLayoutData != null)
{
writer.Write(_compressedLayoutData);
if (_compressedStringsData != null)
{
writer.Write(_staticStringsCount);
writer.Write(_compressedStringsData);
}
else if (_hasDynamicStrings)
{
var stringsBuilder = new GumpStringsBuilder(false);
BuildStrings(ref stringsBuilder);
if (_staticStringsCount == 0)
{
writer.Write(stringsBuilder._stringsCount);
OutgoingGumpPackets.WritePacked(stringsBuilder.StringsBuffer, ref writer);
}
else
{
writer.Write(_staticStringsCount + stringsBuilder._stringsCount);
var stringsData = stringsBuilder.StringsBuffer;
var buffer = STArrayPool<byte>.Shared.Rent(_staticStrings.Length + stringsData.Length);
_staticStrings.CopyTo(buffer.AsSpan());
stringsData.CopyTo(buffer.AsSpan(_staticStrings.Length));
OutgoingGumpPackets.WritePacked(buffer, ref writer);
STArrayPool<byte>.Shared.Return(buffer);
}
}
else if (_staticStrings != null)
{
writer.Write(_staticStringsCount);
OutgoingGumpPackets.WritePacked(_staticStrings, ref writer);
}
}
else
{
StaticGumpBuilder gumpBuilder = new StaticGumpBuilder();
BuildLayout(ref gumpBuilder);
gumpBuilder.FinalizeLayout();
_switches = gumpBuilder.Switches;
_textEntries = gumpBuilder.TextEntries;
_staticStringsCount = gumpBuilder._stringsCount;
var staticStringsData = gumpBuilder.StringsData;
var hasDynamicStrings = _hasDynamicStrings = gumpBuilder.StringSlotOffsets.Length > 0;
if (hasDynamicStrings)
{
_staticStrings = GC.AllocateUninitializedArray<byte>(staticStringsData.Length);
staticStringsData.CopyTo(_staticStrings);
var stringsBuilder = new GumpStringsBuilder(true);
BuildStrings(ref stringsBuilder);
stringsBuilder.FinalizeStrings(ref gumpBuilder); // Modifies the layout
WriteLayout(ref writer, ref gumpBuilder);
writer.Write(_staticStringsCount + stringsBuilder._stringsCount);
var stringsData = stringsBuilder.StringsBuffer;
var stringsLength = staticStringsData.Length + stringsData.Length;
var buffer = STArrayPool<byte>.Shared.Rent(stringsLength);
staticStringsData.CopyTo(buffer.AsSpan());
stringsData.CopyTo(buffer.AsSpan(staticStringsData.Length));
OutgoingGumpPackets.WritePacked(buffer.AsSpan(0, stringsLength), ref writer);
STArrayPool<byte>.Shared.Return(buffer);
stringsBuilder.Dispose();
}
else
{
WriteLayout(ref writer, ref gumpBuilder);
writer.Write(_staticStringsCount);
var stringsPos = writer.Position;
OutgoingGumpPackets.WritePacked(staticStringsData, ref writer);
var stringsLength = writer.Position - stringsPos;
_compressedStringsData = GC.AllocateUninitializedArray<byte>(stringsLength);
writer.Span.Slice(stringsPos, stringsLength).CopyTo(_compressedStringsData);
}
gumpBuilder.Dispose();
}
writer.WritePacketLength();
}
}

View file

@ -0,0 +1,524 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: StaticGumpBuilder.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 StaticGumpBuilder
{
private static readonly byte[] _staticStringsBuffer = GC.AllocateUninitializedArray<byte>(0x80000);
// Position in the layout and the hash of the keyslot
private static readonly (ulong, int)[] _stringSlotOffsets = GC.AllocateUninitializedArray<(ulong, int)>(0x8000); // Assume max 65535 strings
private byte[] _stringsBuffer;
private int _stringBytesWritten;
internal int _stringsCount;
private GumpLayoutBuilder _gumpBuilder;
private int _stringOffsetCount;
internal ReadOnlySpan<byte> LayoutData => _gumpBuilder.LayoutData;
internal ReadOnlySpan<byte> StringsData => _stringsBuffer.AsSpan(0, _stringBytesWritten);
internal ReadOnlySpan<(ulong, int)> StringSlotOffsets => _stringSlotOffsets.AsSpan(0, _stringOffsetCount);
public int Switches => _gumpBuilder._switches;
public int TextEntries => _gumpBuilder._textEntries;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public StaticGumpBuilder()
{
_stringsBuffer = _staticStringsBuffer;
// For corner cases where slots are not filled, reserve the first string index for empty
_stringsBuffer.AsSpan(0, 2).Clear();
_stringBytesWritten = 2;
_stringsCount = 1;
_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 AddHtmlPlaceholder(
int x,
int y,
int width,
int height,
ReadOnlySpan<char> slotKey,
bool background = false,
bool scrollbar = false
)
{
var index = _gumpBuilder.AddHtmlPlaceholder(x, y, width, height, background, scrollbar);
WriteInternalizedStringSlot(slotKey, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHtmlPlaceholder(
int x,
int y,
int width,
int height,
ref RawInterpolatedStringHandler handler,
bool background = false,
bool scrollbar = false
)
{
AddHtmlPlaceholder(x, y, width, height, handler.Text, background, scrollbar);
handler.Clear();
}
[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
)
{
var centerHandler = handler.Text.Center();
AddHtml(x, y, width, height, ref centerHandler, 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 AddLabelPlaceholder(int x, int y, int hue, ReadOnlySpan<char> slotKey)
{
var index = _gumpBuilder.AddLabelPlaceholder(x, y, hue);
WriteInternalizedStringSlot(slotKey, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLabelPlaceholder(int x, int y, int hue, ref RawInterpolatedStringHandler handler)
{
AddHtmlPlaceholder(x, y, 0, 0, handler.Text);
handler.Clear();
}
[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 AddLabelCroppedPlaceholder(int x, int y, int width, int height, int hue, ref RawInterpolatedStringHandler handler)
{
AddLabelCroppedPlaceholder(x, y, width, height, hue, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLabelCroppedPlaceholder(int x, int y, int width, int height, int hue, ReadOnlySpan<char> slotKey)
{
var index = _gumpBuilder.AddLabelCroppedPlaceholder(x, y, width, height, hue);
WriteInternalizedStringSlot(slotKey, index);
}
[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 AddTextEntryPlaceholder(
int x, int y, int width, int height, int hue, int entryId, ref RawInterpolatedStringHandler handler
)
{
AddTextEntryPlaceholder(x, y, width, height, hue, entryId, handler.Text);
handler.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTextEntryPlaceholder(int x, int y, int width, int height, int hue, int entryId, ReadOnlySpan<char> slotKey)
{
var index = _gumpBuilder.AddTextEntryPlaceholder(x, y, width, height, hue, entryId);
WriteInternalizedStringSlot(slotKey, index);
}
[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 AddTextEntryLimitedPlaceholder(
int x, int y, int width, int height, int hue, int entryId, ReadOnlySpan<char> slotKey, int size = 0
)
{
var index = _gumpBuilder.AddTextEntryLimitedPlaceholder(x, y, width, height, hue, entryId, size);
WriteInternalizedStringSlot(slotKey, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTextEntryLimitedPlaceholder(
int x, int y, int width, int height, int hue, int entryId, ref RawInterpolatedStringHandler handler, int size = 0
)
{
AddTextEntryLimitedPlaceholder(x, y, width, height, hue, entryId, handler.Text, size);
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)]
internal 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 != _staticStringsBuffer)
{
STArrayPool<byte>.Shared.Return(toReturn);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteInternalizedStringSlot(ReadOnlySpan<char> slotKey, int index)
{
var hash = HashUtility.ComputeHash64(slotKey);
_stringSlotOffsets[_stringOffsetCount++] = (hash, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void WriteSlotIndex(int offset, int index)
{
// Left padded final index for the slot
index.TryFormat(_gumpBuilder.LayoutData[offset..], out _, "00000");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
_gumpBuilder.Dispose();
if (_stringsBuffer != _staticStringsBuffer)
{
STArrayPool<byte>.Shared.Return(_stringsBuffer);
}
this = default;
}
}

View file

@ -104,8 +104,7 @@ namespace Server.Gumps
public override void OnCancel(Mobile from)
{
from.CloseGump<CommentsGump>();
from.SendGump(new CommentsGump(m_Acct));
from.SendGump(new CommentsGump(m_Acct), true);
base.OnCancel(from);
}
@ -115,8 +114,7 @@ namespace Server.Gumps
from.SendMessage("Comment added.");
// m_Acct.AddComment( from.Name, text );
m_Acct.Comments.Add(new AccountComment(from.Name, text));
from.CloseGump<CommentsGump>();
from.SendGump(new CommentsGump(m_Acct));
from.SendGump(new CommentsGump(m_Acct), true);
}
}
}

View file

@ -9,6 +9,8 @@ namespace Server.Gumps
private readonly Type[] m_Selected;
private readonly HeritageToken m_Token;
public override bool Singleton => true;
public ConfirmHeritageGump(HeritageToken token, Type[] selected, int cliloc) : base(60, 36)
{
m_Token = token;

View file

@ -1,5 +1,4 @@
using Server.Guilds;
using Server.Items;
using Server.Mobiles;
using Server.Multis;
using Server.Network;

View file

@ -8,6 +8,8 @@ public class ConfirmReleaseGump : StaticGump<ConfirmReleaseGump>
private readonly Mobile _from;
private readonly BaseCreature _pet;
public override bool Singleton => true;
public ConfirmReleaseGump(Mobile from, BaseCreature pet) : base(50, 50)
{
_from = from;
@ -31,12 +33,6 @@ public class ConfirmReleaseGump : StaticGump<ConfirmReleaseGump>
builder.AddHtmlLocalized(170, 80, 75, 20, 1011012); // CANCEL
}
public override void SendTo(NetState ns)
{
_from.CloseGump<ConfirmReleaseGump>();
base.SendTo(ns);
}
public override void OnResponse(NetState sender, in RelayInfo info)
{
if (info.ButtonID != 2 || _pet.Deleted ||

View file

@ -25,10 +25,10 @@ public class GoGump : Gump
private readonly LocationTree _tree;
public override bool Singleton => true;
private GoGump(int page, Mobile from, LocationTree tree, GoCategory node) : base(50, 50)
{
from.CloseGump<GoGump>();
if (node == tree.Root)
{
tree.LastBranch.Remove(from);

View file

@ -102,17 +102,19 @@ namespace Server.Gumps
public static void EnsureClosed(Mobile m)
{
m.CloseGump<DeclareFealtyGump>();
m.CloseGump<GrantGuildTitleGump>();
m.CloseGump<GuildAdminCandidatesGump>();
m.CloseGump<GuildCandidatesGump>();
m.CloseGump<GuildChangeTypeGump>();
m.CloseGump<GuildCharterGump>();
m.CloseGump<GuildDismissGump>();
m.CloseGump<GuildGump>();
m.CloseGump<GuildmasterGump>();
m.CloseGump<GuildRosterGump>();
m.CloseGump<GuildWarGump>();
var gumps = m.GetGumps();
gumps.Close<DeclareFealtyGump>();
gumps.Close<GrantGuildTitleGump>();
gumps.Close<GuildAdminCandidatesGump>();
gumps.Close<GuildCandidatesGump>();
gumps.Close<GuildChangeTypeGump>();
gumps.Close<GuildCharterGump>();
gumps.Close<GuildDismissGump>();
gumps.Close<GuildGump>();
gumps.Close<GuildmasterGump>();
gumps.Close<GuildRosterGump>();
gumps.Close<GuildWarGump>();
}
public static bool BadLeader(Mobile m, Guild g)

View file

@ -7,12 +7,12 @@ namespace Server.Guilds
{
public abstract class BaseGuildGump : Gump
{
public override bool Singleton => true;
public BaseGuildGump(PlayerMobile pm, Guild g, int x = 10, int y = 10) : base(x, y)
{
guild = g;
player = pm;
pm.CloseGump<BaseGuildGump>();
}
protected Guild guild { get; }

View file

@ -6,9 +6,10 @@ namespace Server.Guilds
{
public class CreateGuildGump : Gump
{
public override bool Singleton => true;
public CreateGuildGump(PlayerMobile pm, string guildName = "Guild Name", string guildAbbrev = "") : base(10, 10)
{
pm.CloseGump<CreateGuildGump>();
pm.CloseGump<BaseGuildGump>();
AddPage(0);

View file

@ -158,8 +158,7 @@ namespace Server.Guilds
// Guild Faction
if (Guild.OrderChaos && IsLeader(pm, guild))
{
pm.CloseGump<GuildChangeTypeGump>();
pm.SendGump(new GuildChangeTypeGump(pm, guild));
pm.SendGump(new GuildChangeTypeGump(pm, guild), true);
}
break;

View file

@ -705,7 +705,6 @@ namespace Server.Gumps
if (types?.Length > 0 && cliloc > 0)
{
sender.Mobile.CloseGump<ConfirmHeritageGump>();
sender.Mobile.SendGump(new ConfirmHeritageGump(m_Token, types, cliloc));
}
else

View file

@ -11,13 +11,13 @@ namespace Server.Gumps
private readonly BaseHouse m_House;
private readonly Mobile m_Mobile;
public override bool Singleton => true;
public HouseDemolishGump(Mobile mobile, BaseHouse house) : base(110, 100)
{
m_Mobile = mobile;
m_House = house;
mobile.CloseGump<HouseDemolishGump>();
Closable = false;
AddPage(0);

View file

@ -189,10 +189,12 @@ namespace Server.Gumps
if (m_List.Count > 0)
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseRemoveGump(m_Number, m_List, m_House, m_AccountOf));
var gumps = from.GetGumps();
gumps.Close<HouseGump>();
gumps.Close<HouseListGump>();
gumps.Close<HouseRemoveGump>();
gumps.Send(new HouseRemoveGump(m_Number, m_List, m_House, m_AccountOf));
return;
}
}
@ -206,6 +208,8 @@ namespace Server.Gumps
{
private readonly BaseHouse m_House;
public override bool Singleton => true;
public HouseGump(Mobile from, BaseHouse house) : base(20, 30)
{
if (house.Deleted)
@ -215,9 +219,10 @@ namespace Server.Gumps
m_House = house;
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
var gumps = from.GetGumps();
gumps.Close<HouseListGump>();
gumps.Close<HouseRemoveGump>();
var isCombatRestricted = house.IsCombatRestricted(from);
@ -453,6 +458,8 @@ namespace Server.Gumps
return;
}
var gumps = from.GetGumps();
switch (info.ButtonID)
{
case 1: // Rename sign
@ -464,10 +471,10 @@ namespace Server.Gumps
}
case 2: // List of co-owners
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseListGump(1011275, m_House.CoOwners, m_House, false));
gumps.Close<HouseGump>();
gumps.Close<HouseRemoveGump>();
gumps.Close<HouseListGump>();
gumps.Send(new HouseListGump(1011275, m_House.CoOwners, m_House, false));
break;
}
@ -491,10 +498,10 @@ namespace Server.Gumps
{
if (isOwner)
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseRemoveGump(1011274, m_House.CoOwners, m_House, false));
gumps.Close<HouseGump>();
gumps.Close<HouseListGump>();
gumps.Close<HouseRemoveGump>();
gumps.Send(new HouseRemoveGump(1011274, m_House.CoOwners, m_House, false));
}
else
{
@ -520,10 +527,10 @@ namespace Server.Gumps
}
case 6: // List friends
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseListGump(1011273, m_House.Friends, m_House, false));
gumps.Close<HouseGump>();
gumps.Close<HouseRemoveGump>();
gumps.Close<HouseListGump>();
gumps.Send(new HouseListGump(1011273, m_House.Friends, m_House, false));
break;
}
@ -547,10 +554,10 @@ namespace Server.Gumps
{
if (isCoOwner)
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseRemoveGump(1011272, m_House.Friends, m_House, false));
gumps.Close<HouseGump>();
gumps.Close<HouseListGump>();
gumps.Close<HouseRemoveGump>();
gumps.Send(new HouseRemoveGump(1011272, m_House.Friends, m_House, false));
}
else
{
@ -590,19 +597,19 @@ namespace Server.Gumps
}
case 12: // List bans
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseListGump(1011271, m_House.Bans, m_House, true));
gumps.Close<HouseGump>();
gumps.Close<HouseRemoveGump>();
gumps.Close<HouseListGump>();
gumps.Send(new HouseListGump(1011271, m_House.Bans, m_House, true));
break;
}
case 13: // Remove ban
{
from.CloseGump<HouseGump>();
from.CloseGump<HouseListGump>();
from.CloseGump<HouseRemoveGump>();
from.SendGump(new HouseRemoveGump(1011269, m_House.Bans, m_House, true));
gumps.Close<HouseGump>();
gumps.Close<HouseListGump>();
gumps.Close<HouseRemoveGump>();
gumps.Send(new HouseRemoveGump(1011269, m_House.Bans, m_House, true));
break;
}
@ -630,8 +637,7 @@ namespace Server.Gumps
}
else
{
from.CloseGump<HouseDemolishGump>();
from.SendGump(new HouseDemolishGump(from, m_House));
gumps.Send(new HouseDemolishGump(from, m_House));
}
}
else

View file

@ -73,15 +73,13 @@ namespace Server.Gumps
private List<Mobile> m_List;
public override bool Singleton => true;
public HouseGumpAOS(HouseGumpPageAOS page, Mobile from, BaseHouse house) : base(50, 40)
{
m_House = house;
m_Page = page;
from.CloseGump<HouseGumpAOS>();
// from.CloseGump( typeof( HouseListGump ) );
// from.CloseGump( typeof( HouseRemoveGump ) );
var isCombatRestricted = house.IsCombatRestricted(from);
var isOwner = house.IsOwner(from);
@ -1427,8 +1425,7 @@ namespace Server.Gumps
}
else
{
from.CloseGump<HouseDemolishGump>();
from.SendGump(new HouseDemolishGump(from, m_House));
from.SendGump(new HouseDemolishGump(from, m_House), true);
}
}

View file

@ -9,6 +9,8 @@ namespace Server.Gumps
private readonly BaseHouse m_House;
private readonly Mobile m_To;
public override bool Singleton => true;
public HouseTransferGump(Mobile from, Mobile to, BaseHouse house) : base(110, 100)
{
m_From = from;

View file

@ -8,10 +8,10 @@ public class PetResurrectGump : StaticGump<PetResurrectGump>
private readonly double _hitsScalar;
private readonly BaseCreature _pet;
public override bool Singleton => true;
public PetResurrectGump(Mobile from, BaseCreature pet, double hitsScalar = 0.0) : base(50, 50)
{
from.CloseGump<PetResurrectGump>();
_pet = pet;
_hitsScalar = hitsScalar;
}

View file

@ -12,6 +12,8 @@ namespace Server.Gumps
private readonly PlayerVendor m_Vendor;
private readonly VendorItem m_VI;
public override bool Singleton => true;
public PlayerVendorBuyGump(PlayerVendor vendor, VendorItem vi) : base(100, 200)
{
m_Vendor = vendor;
@ -497,13 +499,13 @@ namespace Server.Gumps
private readonly Mobile m_Vendor;
public override bool Singleton => true;
public PlayerVendorCustomizeGump(Mobile v, Mobile from) : base(30, 40)
{
m_Vendor = v;
int x, y;
from.CloseGump<PlayerVendorCustomizeGump>();
AddPage(0);
AddBackground(0, 0, 585, 393, 5054);
AddBackground(195, 36, 387, 275, 3000);

View file

@ -14,6 +14,9 @@ public class PricedResurrectGump : StaticGump<PricedResurrectGump>
{
_healer = healer;
_price = price;
// Close this gump when
TypeID = GetTypeId(typeof(ResurrectGump));
}
protected override void BuildLayout(ref StaticGumpBuilder builder)

View file

@ -10,6 +10,8 @@ namespace Server.Gumps
private readonly BaseHouse m_House;
private readonly List<Mobile> m_Vendors;
public override bool Singleton => true;
public ReclaimVendorGump(BaseHouse house) : base(50, 50)
{
m_House = house;

View file

@ -23,6 +23,8 @@ public class ResurrectGump : DynamicGump
private readonly double _hitsScalar;
private readonly ResurrectMessage _resurrectMessage;
public override bool Singleton => true;
public static void TryGiveStatLoss(PlayerMobile player)
{
if (Core.AOS || player.ShortTermMurders < ShortMurdersForStatLoss)

View file

@ -15,6 +15,8 @@ public class RunebookGump : DynamicGump
private readonly Runebook _book;
public Runebook Book => _book;
public override bool Singleton => true;
public RunebookGump(Runebook book) : base(150, 200) => _book = book;
protected override void BuildLayout(ref DynamicGumpBuilder builder)

View file

@ -13,6 +13,8 @@ namespace Server.Gumps
{
private readonly ISecurable m_Info;
public override bool Singleton => true;
public SetSecureLevelGump(Mobile owner, ISecurable info, BaseHouse house) : base(50, 50)
{
m_Info = info;

View file

@ -31,6 +31,8 @@ namespace Server.Gumps
private readonly int m_ToTEras;
public override bool Singleton => true;
public ToTAdminGump() : base(30, 50)
{
Closable = true;
@ -123,11 +125,7 @@ namespace Server.Gumps
[Usage("ToTAdmin"), Description("Displays a menu to configure Treasures of Tokuno.")]
public static void ToTAdmin_OnCommand(CommandEventArgs e)
{
ToTAdminGump tg;
tg = new ToTAdminGump();
e.Mobile.CloseGump<ToTAdminGump>();
e.Mobile.SendGump(tg);
e.Mobile.SendGump(new ToTAdminGump());
}
}
}

View file

@ -11,6 +11,8 @@ namespace Server.Gumps
private readonly BaseHouse m_House;
private readonly List<VendorInventory> m_Inventories;
public override bool Singleton => true;
public VendorInventoryGump(BaseHouse house, Mobile from) : base(50, 50)
{
m_House = house;

View file

@ -606,6 +606,8 @@ namespace Server.Gumps
private readonly int m_RefundAmount;
private readonly RentedVendor m_Vendor;
public override bool Singleton => true;
public VendorRentalRefundGump(RentedVendor vendor, Mobile landlord, int refundAmount) : base(50, 50)
{
m_Vendor = vendor;

View file

@ -16,14 +16,14 @@ namespace Server.Gumps
private readonly List<BaseHouse> m_List;
private readonly BaseHouse m_Selection;
public override bool Singleton => true;
public ViewHousesGump(Mobile from, List<BaseHouse> list, BaseHouse sel) : base(50, 40)
{
m_From = from;
m_List = list;
m_Selection = sel;
from.CloseGump<ViewHousesGump>();
AddPage(0);
AddBackground(0, 0, 240, 360, 5054);

View file

@ -0,0 +1,248 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: VirtualCheckGump.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.Items;
using Server.Network;
namespace Server.Gumps;
public sealed class VirtualCheckGump : Gump, IVirtualCheckGump
{
public static unsafe void Configure()
{
VirtualCheck.GumpActivator = &Activator;
}
private static VirtualCheckGump Activator(Mobile from, VirtualCheck check) => new(from, check);
public enum Buttons
{
Close,
Clear,
Accept,
AllPlat,
AllGold
}
private int _plat, _gold;
public override bool Singleton => true;
public VirtualCheckGump(Mobile user, VirtualCheck check) : base(50, 50)
{
User = user;
Check = check;
_plat = Check.Plat;
_gold = Check.Gold;
Closable = true;
Disposable = true;
Draggable = true;
Resizable = false;
CompileLayout();
}
public Mobile User { get; }
public VirtualCheck Check { get; private set; }
public override void OnServerClose(NetState owner)
{
base.OnServerClose(owner);
if (Check?.Deleted == false)
{
Check.UpdateTrade(User);
}
}
public void Close()
{
User.CloseGump<VirtualCheckGump>();
if (Check?.Deleted == false)
{
Check.UpdateTrade(User);
}
else
{
Check = null;
}
}
public void Send()
{
if (Check?.Deleted == false)
{
User.SendGump(this);
}
else
{
Close();
}
}
public void Refresh(bool recompile)
{
if (Check?.Deleted != false)
{
Close();
return;
}
if (recompile)
{
CompileLayout();
}
Close();
Send();
}
private void CompileLayout()
{
if (Check?.Deleted != false)
{
return;
}
Entries.ForEach(e => e.Parent = null);
Entries.Clear();
AddPage(0);
AddBackground(0, 0, 400, 160, 3500);
// Title
AddImageTiled(25, 35, 350, 3, 96);
AddImage(10, 8, 113);
AddImage(360, 8, 113);
AddHtml(40, 15, 320, 20, $"BANK OF {User.RawName.ToUpper()}".Center(0x2F4F4F));
// Platinum Row
AddBackground(15, 60, 175, 20, 9300);
AddBackground(20, 45, 165, 30, 9350);
AddItem(20, 45, 3826); // Plat
AddLabel(60, 50, 0, User.Account.TotalPlat.ToString("#,0"));
AddButton(195, 50, 95, 95, (int)Buttons.AllPlat); // ->
AddBackground(210, 60, 175, 20, 9300);
AddBackground(215, 45, 165, 30, 9350);
AddTextEntry(225, 50, 145, 20, 0, 0, _plat.ToString(), User.Account.TotalPlat.ToString().Length);
// Gold Row
AddBackground(15, 100, 175, 20, 9300);
AddBackground(20, 85, 165, 30, 9350);
AddItem(20, 85, 3823); // Gold
AddLabel(60, 90, 0, User.Account.TotalGold.ToString("#,0"));
AddButton(195, 90, 95, 95, (int)Buttons.AllGold); // ->
AddBackground(210, 100, 175, 20, 9300);
AddBackground(215, 85, 165, 30, 9350);
AddTextEntry(225, 90, 145, 20, 0, 1, _gold.ToString(), User.Account.TotalGold.ToString().Length);
// Buttons
AddButton(20, 128, 12006, 12007, (int)Buttons.Close);
AddButton(215, 128, 12003, 12004, (int)Buttons.Clear);
AddButton(305, 128, 12000, 12002, (int)Buttons.Accept);
}
public override void OnResponse(NetState sender, in RelayInfo info)
{
if (Check?.Deleted != false || sender.Mobile != User)
{
Close();
return;
}
var refresh = false;
var updated = false;
switch ((Buttons)info.ButtonID)
{
case Buttons.Clear:
{
_plat = _gold = 0;
refresh = true;
break;
}
case Buttons.Accept:
{
var platText = info.GetTextEntry(0);
var goldText = info.GetTextEntry(1);
if (!int.TryParse(platText, out _plat))
{
User.SendMessage("That is not a valid amount of platinum.");
refresh = true;
}
else if (!int.TryParse(goldText, out _gold))
{
User.SendMessage("That is not a valid amount of gold.");
refresh = true;
}
else
{
var totalPlat = User.Account.TotalPlat;
var totalGold = User.Account.TotalGold;
if (totalPlat < _plat || totalGold < _gold)
{
_plat = User.Account.TotalPlat;
_gold = User.Account.TotalGold;
User.SendMessage("You do not have that much currency.");
refresh = true;
}
else
{
Check.Plat = _plat;
Check.Gold = _gold;
updated = true;
}
}
break;
}
case Buttons.AllPlat:
{
_plat = User.Account.TotalPlat;
refresh = true;
break;
}
case Buttons.AllGold:
{
_gold = User.Account.TotalGold;
refresh = true;
break;
}
}
if (updated)
{
User.SendMessage("Your offer has been updated.");
}
if (refresh && Check?.Deleted == false)
{
Refresh(true);
return;
}
Close();
}
}

View file

@ -25,6 +25,8 @@ public class WhoGump : DynamicGump
private readonly List<Mobile> _mobiles;
private readonly int _page;
public override bool Singleton => true;
public WhoGump(Mobile owner, string filter) : this(BuildList(owner, filter))
{
}
@ -33,8 +35,6 @@ public class WhoGump : DynamicGump
{
_mobiles = list;
_page = page;
// Initialize(page);
}
public static void Configure()