From 31fb29cf0b7817019a156f59c00e10bfbdcab0a9 Mon Sep 17 00:00:00 2001 From: nullptr-w8 <79784082+nullptr-w8@users.noreply.github.com> Date: Fri, 1 Jul 2022 10:22:51 +0500 Subject: [PATCH] feat: Adds [Spawn command & Gump Grids (#917) Adds `[spawn` command Screen Shot 2022-06-30 at 10 13 15 PM --- Projects/Server/Gumps/Grid/Grid.cs | 77 +++ Projects/Server/Gumps/Grid/GumpGrid.cs | 525 ++++++++++++++++ Projects/Server/Gumps/Gump.cs | 8 +- Projects/Server/Utilities/Html.cs | 48 ++ .../UOContent/Engines/Spawners/BaseSpawner.cs | 5 + .../Engines/Spawners/SpawnerControllerGump.cs | 580 ++++++++++++++++++ Projects/UOContent/Gumps/BaseGridGump.cs | 8 +- 7 files changed, 1248 insertions(+), 3 deletions(-) create mode 100644 Projects/Server/Gumps/Grid/Grid.cs create mode 100644 Projects/Server/Gumps/Grid/GumpGrid.cs create mode 100644 Projects/Server/Utilities/Html.cs create mode 100644 Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs diff --git a/Projects/Server/Gumps/Grid/Grid.cs b/Projects/Server/Gumps/Grid/Grid.cs new file mode 100644 index 000000000..2cdb8b075 --- /dev/null +++ b/Projects/Server/Gumps/Grid/Grid.cs @@ -0,0 +1,77 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - 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 . * + *************************************************************************/ + +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 Columns { get; set; } = new(); + public List Rows { get; set; } = new(); +} + +public class Swap +{ + public int Index { get; set; } + public int Size { get; set; } +} diff --git a/Projects/Server/Gumps/Grid/GumpGrid.cs b/Projects/Server/Gumps/Grid/GumpGrid.cs new file mode 100644 index 000000000..1c5d87118 --- /dev/null +++ b/Projects/Server/Gumps/Grid/GumpGrid.cs @@ -0,0 +1,525 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - 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 . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using Server.Collections; + +namespace Server.Gumps; + +public class GumpGrid : Gump +{ + private Dictionary _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 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 list) + { + var cropSize = 0; + + for (int i = 0; i < column; i++) + { + if (sizes[i] == "*") + { + continue; + } + + //percent + var percent = sizes[i].Replace("*", ""); + 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.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.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; + } +} diff --git a/Projects/Server/Gumps/Gump.cs b/Projects/Server/Gumps/Gump.cs index f3ccb13a4..163faada3 100644 --- a/Projects/Server/Gumps/Gump.cs +++ b/Projects/Server/Gumps/Gump.cs @@ -2,10 +2,11 @@ using System; using System.Collections.Generic; using Server.Network; using Server.Text; +using Server.Utilities; namespace Server.Gumps; -public class Gump +public partial class Gump { private static Serial _nextSerial = (Serial)1; @@ -165,6 +166,11 @@ public class Gump 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)); diff --git a/Projects/Server/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs new file mode 100644 index 000000000..8de7a6de9 --- /dev/null +++ b/Projects/Server/Utilities/Html.cs @@ -0,0 +1,48 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Html.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 . * + *************************************************************************/ + +using System.Runtime.CompilerServices; + +namespace Server.Utilities; + +public static class Html +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, int color) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, int color, int size) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, string color) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, string color, int size) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, int color) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, int color, int size) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, string color) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, string color, int size) => $"
{text}
"; +} diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs index 130e21c24..e4faa6332 100644 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs @@ -250,6 +250,11 @@ public abstract class BaseSpawner : Item, ISpawner Region ISpawner.Region => Region.Find(Location, Map); + public void UpdateEntries(List entries) + { + Entries = entries; + } + public void Remove(ISpawnable spawn) { Defrag(); diff --git a/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs b/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs new file mode 100644 index 000000000..48c113c50 --- /dev/null +++ b/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs @@ -0,0 +1,580 @@ +using System; +using Server.Collections; +using Server.Network; +using Server.Engines.Spawners; + +namespace Server.Gumps; + +public enum SpawnSearchType +{ + Creature = 0, + Coords, + Props, + Name +} +public class SpawnSearch +{ + public SpawnSearchType Type { get; set; } = SpawnSearchType.Creature; + public string SearchPattern { get; set; } = ""; +} + +public class SpawnerControllerGump : GumpGrid +{ + private BaseSpawner _copy; + private int _page; + private const int EntryCount = 5; + private Mobile _mobile; + private SpawnSearch _search; + private int _lineCount; + private BaseSpawner[] _spawners = Array.Empty(); + private Grid _main; + private const int TypeCount = 15; + + public static int GetButtonID(int type, int index) => 1 + type + index * TypeCount; + + public static void Initialize() + { + CommandSystem.Register("Spawn", AccessLevel.GameMaster, OpenSpawnGump_OnCommand); + } + + public static void OpenSpawnGump_OnCommand(CommandEventArgs e) + { + e.Mobile.SendGump(new SpawnerControllerGump(e.Mobile)); + } + + public void DrawBorder(ListView list) + { + AddBackground(0, 0, _main.Width, _main.Height, 1755); + + var row1Y = _main.Rows[1].Y; + AddImageTiled(0, row1Y, _main.Width, 3, 1756); + AddAlphaRegion(0, row1Y, _main.Width, 3); + + AddImageTiled(0, row1Y + 21, _main.Width, 3, 1756); + AddAlphaRegion(0, row1Y + 21, _main.Width, 3); + + for (int i = 0; i < list.ColsCount - 1; i++) + { + var x = list.Header.Cols[i].HEnd - 8; + var y = list.Header.Y + 10; + var height = 20; + AddImageTiled(x, y, 3, height, 1758); + AddAlphaRegion(x, y, 3, height); + } + + AddImageTiled(0, _main.Rows[2].Y - 11, _main.Width, 6, 1756); + AddImageTiled(0, _main.Rows[2].Y + 55, _main.Width, 6, 1756); + } + + private static string ExtractCoords(Point3D cord) => $"X {cord.X} Y {cord.Y}"; + + public void DrawSearch() + { + const int deltaX = 140; + var search = SubGrid("search", _main.Name, 0, 0, 2, 1); + + var col0X = search.Columns[0].X; + var col1X = search.Columns[1].X; + var col1HCenter = search.Columns[1].HCenter; + var row0Y = search.Rows[0].Y; + var row0VCenter = search.Rows[0].VCenter; + + AddLabelHtml(col0X, row0Y + 8, col1X, 30, "Search type", GridColors.Gold); + AddLabelHtml(col1X, row0Y + 8, col1X, 30, "Search Match", GridColors.Gold); + + AddImageTiled(0, row0Y + 30, _main.Width, 3, 1756); + AddImageTiled(col1X, row0Y, 3, search.Height, 1758); + + var creature = _search.Type == SpawnSearchType.Creature ? 9021 : 9020; + var cords = _search.Type == SpawnSearchType.Coords ? 9021 : 9020; + var props = _search.Type == SpawnSearchType.Props ? 9021 : 9020; + var args = _search.Type == SpawnSearchType.Name ? 9021 : 9020; + + AddButton(col0X + 10, row0Y + 45, creature, creature, GetButtonID(1, 0)); + AddLabelHtml(col0X + 15, row0Y + 48, 100, 30, "Creature", GridColors.White); + + + AddButton(col0X + 10 + deltaX - 40, row0Y + 45, cords, cords, GetButtonID(1, 1)); + AddLabelHtml(col0X + 40 + deltaX - 40, row0Y + 48, 150, 30, "Range", GridColors.White, 4, false); + + AddButton(col0X + 10 + deltaX * 2 - 100, row0Y + 45, props, props, GetButtonID(1, 2)); + AddLabelHtml(col0X + 40 + deltaX * 2 - 100, row0Y + 48, 150, 30, "Creature Props", GridColors.White, 4, false); + + AddButton(col0X + 10 + deltaX * 3 - 100, row0Y + 45, args, args, GetButtonID(1, 3)); + AddLabelHtml(col0X + 40 + deltaX * 3 - 100, row0Y + 48, 150, 30, "Spawner Name", GridColors.White, 4, false); + + AddImageTiled(col1HCenter - 100, row0VCenter + 16, 200, 1, 9357); + AddTextEntry(col1HCenter - 100, row0VCenter, 200, 20, (int)GridHues.White, 0xFFFF, _search.SearchPattern, GetButtonID(1, 4)); + AddButton(col1HCenter + 120, row0VCenter, 4023, 4024, GetButtonID(1, 4)); + + var type = (int)_search.Type; + if (type == 0) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by creature name", GridColors.White); + } + else if (type == 1) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by range (number)", GridColors.White); + } + else if (type == 2) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by entry property field", GridColors.White); + } + else if (type == 3) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by spawner name", GridColors.White); + } + } + + private void SearchSpawner() + { + if (_search.SearchPattern.Length < 1) + { + return; + } + + using var queue = PooledRefQueue.Create(); + + if (!(_search.Type == SpawnSearchType.Coords && int.TryParse(_search.SearchPattern, out var range))) + { + range = -1; + } + + foreach (var item in World.Items.Values) + { + if (item is not BaseSpawner spawner) + { + continue; + } + + bool enqueue = _search.Type switch + { + SpawnSearchType.Creature => SearchSpawnerCreatures(spawner, _search.SearchPattern), + SpawnSearchType.Coords => _mobile.InRange(spawner.Location, range), + SpawnSearchType.Props => SearchSpawnerProperties(spawner, _search.SearchPattern), + SpawnSearchType.Name => spawner.Name?.InsensitiveContains(_search.SearchPattern) == true, + _ => false + }; + + if (enqueue) + { + queue.Enqueue(spawner); + } + } + + _spawners = queue.ToArray(); + } + + private static bool SearchSpawnerCreatures(BaseSpawner spawner, string searchPattern) + { + foreach (var entry in spawner.Entries) + { + if (entry.SpawnedName?.InsensitiveContains(searchPattern) == true) + { + return true; + } + } + + return false; + } + + private static bool SearchSpawnerProperties(BaseSpawner spawner, string searchPattern) + { + foreach (var entry in spawner.Entries) + { + if (entry.Properties?.InsensitiveContains(searchPattern) == true) + { + return true; + } + } + + return false; + } + + private void DrawFooter(ListView list) + { + if (list.CanNext) + { + AddButton(_main.Columns[0].HCenter + 60, _main.Rows[2].VCenter + 16, 4005, 4006, GetButtonID(10, 0)); + } + + if (list.CanBack) + { + AddButton(_main.Columns[0].HCenter + -60, _main.Rows[2].VCenter + 16, 4014, 4015, GetButtonID(11, 0)); + } + + var col0X = _main.Columns[0].X; + var col0Width = _main.Columns[0].Width; + var row2Y = _main.Rows[2].Y; + + //past to all spawners + AddButton(col0X + 15, row2Y, 4029, 4031, GetButtonID(1, 5)); + AddLabelHtml(col0X + 55, row2Y + 4, col0Width, 20, $"Paste props for {_spawners.Length} spawners", GridColors.Orange, 4, false); + + //paste target + AddButton(col0X + 15, row2Y + 28, 4029, 4031, GetButtonID(1, 6)); + AddLabelHtml(col0X + 55, row2Y + 32, col0Width, 20, $"Paste entry for {_spawners.Length} spawners", GridColors.Orange, 4, false); + + //paste ground + AddButton(col0X + 15 + 260, row2Y, 4029, 4031, GetButtonID(1, 7)); + AddLabelHtml(col0X + 55 + 260, row2Y + 4, col0Width, 20, "Paste copy to the ground", GridColors.Yellow, 4, false); + + AddLabelHtml(col0X + 16, _main.Rows[2].VCenter + 17, col0Width, 20, $"Pages {list.Page + 1}/{list.TotalPages + 1}", GridColors.White); + AddLabelHtml(col0X + 20, _main.Rows[2].VCenter + 17, col0Width, 20, $"Total Spawners {_spawners.Length}", GridColors.White, 4, false); + } + + + public void DrawSpawner(ListView list) + { + for (int i = 0; i < list.Items.Length; i++) + { + const int vCenter = 15; + const int perItem = 45; + + var spawner = _spawners[list.Items[i].Index]; + var item = list.Items[i]; + + AddButton(item.Cols[0].X + 15, list.Items[i].Y + 5, 4011, 4012, GetButtonID(2, item.Index)); + AddLabelHtml(item.Cols[0].X, list.Items[i].Y + 26, item.Cols[0].Width, 30, "copy", GridColors.White, 3); + + AddButton(item.Cols[1].X + 2, list.Items[i].Y + 5, 4029, 4031, GetButtonID(3, item.Index)); + AddLabelHtml(item.Cols[1].X + 3, list.Items[i].Y + 26, item.Cols[1].Width, 30, "props", GridColors.White, 3, false); + + AddButton(item.Cols[1].HCenter - 2, list.Items[i].Y + 5, 4029, 4031, GetButtonID(4, item.Index)); + AddLabelHtml(item.Cols[1].HCenter - 3, list.Items[i].Y + 26, 55, 30, "entry", GridColors.White, 3, false); + + //props + AddLabelHtml(item.Cols[2].X, list.Items[i].Y + vCenter + 10, list.Header.Cols[2].Width, 30, spawner.Serial.ToString(), GridColors.White); + + //map + AddLabelHtml(item.Cols[3].X, list.Items[i].Y + vCenter, list.Header.Cols[3].Width, 30, spawner.Map.ToString(), GridColors.White); + + //teleport button + AddButton(item.Cols[4].X, list.Items[i].Y + vCenter - 4, 0x10, 0x10, GetButtonID(5, item.Index)); + AddButton(item.Cols[4].X + 20, list.Items[i].Y + vCenter - 4, 0x10, 0x10, GetButtonID(5, item.Index)); + AddImage(item.Cols[4].X, list.Items[i].Y + vCenter - 5, 0x638, 936); + AddImage(item.Cols[4].X + 50, list.Items[i].Y + vCenter - 5, 0x638, 936); + AddLabelHtml(item.Cols[4].X, list.Items[i].Y + vCenter, list.Header.Cols[4].Width, 30, ExtractCoords(spawner.Location), GridColors.White); + + //open entry button + AddButton(item.Cols[5].X + 6, list.Items[i].Y + vCenter - 4, 0x2635, 0x2635, GetButtonID(7, item.Index)); + AddImage(item.Cols[5].X + 6, list.Items[i].Y + vCenter - 7, 0x2635, 827); + AddLabelHtml(item.Cols[5].X, list.Items[i].Y + vCenter, list.Header.Cols[5].Width, 30, spawner.Entries?.Count.ToString(), GridColors.White); + + + //entry + var entry = i * EntryCount; + + AddImageTiled(item.Cols[2].X - 2, list.Items[i].Y + 18, item.Cols[2].Width, 1, 9357); + AddTextEntry(item.Cols[2].X, list.Items[i].Y + 2, list.Header.Cols[2].Width, 80, (int)GridHues.White, entry, spawner.Name, 20); + AddButton(item.Cols[2].X, list.Items[i].Y + vCenter + 9, 5837, 5838, GetButtonID(6, item.Index)); + + AddImageTiled(item.Cols[6].X + 8, list.Items[i].Y + 29, item.Cols[6].Width / 2, 1, 9357); + AddTextEntry(item.Cols[6].X + 12, list.Items[i].Y + 13, list.Header.Cols[6].Width, 80, (int)GridHues.White, entry + 1, spawner.WalkingRange.ToString(), 2); + + AddImageTiled(item.Cols[7].X + 8, list.Items[i].Y + 29, item.Cols[7].Width / 2, 1, 9357); + AddTextEntry(item.Cols[7].X + 12, list.Items[i].Y + 13, list.Header.Cols[7].Width, 80, (int)GridHues.White, entry + 2, spawner.HomeRange.ToString(), 2); + + AddImageTiled(item.Cols[8].X + 6, list.Items[i].Y + 29, item.Cols[8].Width - 20, 1, 9357); + AddTextEntry(item.Cols[8].X + 8, list.Items[i].Y + 13, list.Header.Cols[8].Width, 80, (int)GridHues.White, entry + 3, spawner.MinDelay.ToString(), 8); + + AddImageTiled(item.Cols[9].X + 6, list.Items[i].Y + 29, item.Cols[9].Width - 20, 1, 9357); + AddTextEntry(item.Cols[9].X + 8, list.Items[i].Y + 13, list.Header.Cols[9].Width, 80, (int)GridHues.White, entry + 4, spawner.MaxDelay.ToString(), 8); + //end entry + + AddLabelHtml(item.Cols[10].X, list.Items[i].Y + vCenter, list.Header.Cols[10].Width, 30, spawner.NextSpawn.ToString(@"hh\:mm\:ss"), GridColors.White); + + AddButton(item.Cols[11].X, list.Items[i].Y + 5, 4023, 4025, GetButtonID(8, item.Index)); + AddLabelHtml(item.Cols[11].X + 4, list.Items[i].Y + 26, 55, 30, "save", GridColors.White, 3, false); + + AddButton(item.Cols[11].X + perItem, list.Items[i].Y + 5, 4017, 4018, GetButtonID(9, item.Index)); + AddLabelHtml(item.Cols[11].X + perItem, list.Items[i].Y + 26, 55, 30, "delete", GridColors.White, 3, false); + + AddImageTiled(0, list.Items[i].Y + list.ColHeight, _main.Width, 1, 1756); + AddAlphaRegion(0, list.Items[i].Y + list.ColHeight, _main.Width, 1); + } + } + + public void DrawColumnName(ListView list) + { + AddLabelHtml(list.Header.Cols[0].X, list.Header.Y + 10, list.Header.Cols[0].Width, 30, "Copy", GridColors.Gold); + AddLabelHtml(list.Header.Cols[1].X, list.Header.Y + 10, list.Header.Cols[1].Width, 30, "Paste", GridColors.Gold); + AddLabelHtml(list.Header.Cols[2].X, list.Header.Y + 10, list.Header.Cols[2].Width, 30, "Name/Serial", GridColors.Gold); + AddLabelHtml(list.Header.Cols[3].X, list.Header.Y + 10, list.Header.Cols[3].Width, 30, "Map", GridColors.Gold); + AddLabelHtml(list.Header.Cols[4].X, list.Header.Y + 10, list.Header.Cols[4].Width, 30, "Coords", GridColors.Gold); + AddLabelHtml(list.Header.Cols[5].X, list.Header.Y + 10, list.Header.Cols[5].Width, 30, "Entry", GridColors.Gold); + AddLabelHtml(list.Header.Cols[6].X, list.Header.Y + 10, list.Header.Cols[6].Width, 30, "Walk", GridColors.Gold); + AddLabelHtml(list.Header.Cols[7].X, list.Header.Y + 10, list.Header.Cols[7].Width, 30, "Home", GridColors.Gold); + AddLabelHtml(list.Header.Cols[8].X, list.Header.Y + 10, list.Header.Cols[8].Width, 30, "Min Delay", GridColors.Gold); + AddLabelHtml(list.Header.Cols[9].X, list.Header.Y + 10, list.Header.Cols[9].Width, 30, "Max Delay", GridColors.Gold); + AddLabelHtml(list.Header.Cols[10].X, list.Header.Y + 10, list.Header.Cols[10].Width, 30, "Next Spawn", GridColors.Gold); + AddLabelHtml(list.Header.Cols[11].X, list.Header.Y + 10, list.Header.Cols[11].Width, 30, "Actions", GridColors.Gold); + } + + public SpawnerControllerGump(Mobile mobile, int page = 0, BaseSpawner copy = null, SpawnSearch search = null) : base(20, 30) + { + _main = Grid("main", 1000, 800, 1, 3, rowSize: "10* * 100"); + _mobile = mobile; + _page = page; + _copy = copy; + _search = search ?? new SpawnSearch(); + + SearchSpawner(); + + var _list = AddListView( + _main.Name, + 0, + 1, + _spawners.Length, + page, + 45, + 12, + colSize: "6* 8* 18* 6* 12* 6* 5* 5* 8* 8* 9* *", + headerHeight: 30, + marginY: -7 + ); + + _lineCount = _list.LineCount; + DrawBorder(_list); + DrawSearch(); + DrawSpawner(_list); + DrawColumnName(_list); + DrawFooter(_list); + + } + public void CopyProperty(BaseSpawner spawner, BaseSpawner target) + { + target.Name = spawner.Name; + target.MinDelay = spawner.MinDelay; + target.MaxDelay = spawner.MaxDelay; + target.WalkingRange = spawner.WalkingRange; + target.HomeRange = spawner.HomeRange; + target.Group = spawner.Group; + target.Count = spawner.Count; + } + public void CopyEntry(BaseSpawner spawner, BaseSpawner target) + { + if (spawner.Entries?.Count > 0) + { + target.Entries?.Clear(); + + for (int i = 0; i < spawner.Entries.Count; i++) + { + var item = spawner.Entries[i]; + var targetEntry = target.AddEntry(item.SpawnedName, item.SpawnedProbability, item.SpawnedMaxCount); + targetEntry.Properties = item.Properties; + targetEntry.Parameters = item.Parameters; + } + + } + } + public void FullCopy(BaseSpawner spawner, BaseSpawner target) + { + CopyProperty(spawner, target); + CopyEntry(spawner, target); + } + public void Refresh(bool update = true) + { + if (update) + { + _mobile.SendGump(new SpawnerControllerGump(_mobile, _page, _copy, _search)); + } + else + { + _mobile.SendGump(this); + } + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + var buttonID = info.ButtonID - 1; + var type = buttonID % TypeCount; + var index = buttonID / TypeCount; + + switch (type) + { + case -1: + { + return; + } + case 1: + { + _page = 0; + if (index < 4) + { + _search.Type = (SpawnSearchType)index; + var isNumeric = int.TryParse(_search.SearchPattern, out _); + if (index == 1 && !isNumeric || index != 1 && isNumeric) + { + _search.SearchPattern = ""; + } + } + else if (index == 4) + { + var entry = info.GetTextEntry(0xFFFF); + + if (entry?.Text?.Length > 0) + { + _search.SearchPattern = entry.Text; + } + } + else if (index == 5 && _copy != null && _spawners?.Length > 0) + { + foreach (var spawner in _spawners) + { + CopyProperty(_copy, spawner); + } + } + else if (index == 6 && _copy != null && _spawners?.Length > 0) + { + foreach (var spawner in _spawners) + { + CopyEntry(_copy, spawner); + } + } + else if (index == 7 && _copy != null) + { + var newSpawner = new Spawner(); + + FullCopy(_copy, newSpawner); + newSpawner.Map = _mobile.Map; + newSpawner.Location = _mobile.Location; + newSpawner.Stop(); + newSpawner.Start(); + newSpawner.Respawn(); + } + + break; + } + //copy + case 2: + { + if (index < _spawners.Length) + { + _copy = _spawners[index]; + } + + break; + } + //paste props + case 3: + { + if (index < _spawners.Length && _copy != null) + { + CopyProperty(_copy, _spawners[index]); + } + + break; + } + //paste entry + case 4: + { + if (index < _spawners.Length && _copy != null) + { + CopyEntry(_copy, _spawners[index]); + } + + break; + } + //save + case 8: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + + var indexEntry = index >= _lineCount ? (index - _lineCount * _page) * EntryCount : index * EntryCount; + + var name = info.GetTextEntry(indexEntry); + if (name?.Text.Length > 0) + { + spawner.Name = name.Text; + } + + if (int.TryParse(info.GetTextEntry(indexEntry + 1)?.Text, out var walkRange)) + { + spawner.WalkingRange = walkRange; + } + + if (int.TryParse(info.GetTextEntry(indexEntry + 2)?.Text, out var homeHange)) + { + spawner.HomeRange = homeHange; + } + + if (TimeSpan.TryParse(info.GetTextEntry(indexEntry + 3)?.Text, out var minDelay)) + { + spawner.MinDelay = minDelay; + } + + if (TimeSpan.TryParse(info.GetTextEntry(indexEntry + 4)?.Text, out var maxDelay)) + { + spawner.MaxDelay = maxDelay; + } + } + + break; + } + //go + case 5: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + _mobile.Map = spawner.Map; + _mobile.Location = spawner.Location; + } + Refresh(false); + return; + } + //open entry + case 6: + case 7: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + Refresh(); + + if (type == 7) + { + _mobile.SendGump(new SpawnerGump(spawner)); + } + else + { + _mobile.SendGump(new PropertiesGump(_mobile, spawner)); + } + + return; + } + + break; + } + //delete + case 9: + { + if (index < _spawners.Length) + { + _spawners[index].Delete(); + } + + break; + } + case 10: + { + _page++; + break; + } + case 11: + { + if (_page > 0) + { + _page--; + } + + break; + } + } + + Refresh(); + } +} diff --git a/Projects/UOContent/Gumps/BaseGridGump.cs b/Projects/UOContent/Gumps/BaseGridGump.cs index c755cde62..c5d69ec15 100644 --- a/Projects/UOContent/Gumps/BaseGridGump.cs +++ b/Projects/UOContent/Gumps/BaseGridGump.cs @@ -1,3 +1,6 @@ +using System.Runtime.CompilerServices; +using Server.Utilities; + namespace Server.Gumps { public abstract class BaseGridGump : Gump @@ -37,9 +40,10 @@ namespace Server.Gumps public virtual int TextHue => 0; public virtual int TextOffsetX => 2; - public string Center(string text) => $"
{text}
"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string Center(string text) => text.Center(); - public string Color(string text, int color) => $"{text}"; + public string Color(string text, int color) => text.Color(color); public int GetButtonID(int typeCount, int type, int index) => 1 + index * typeCount + type;