From 873c25bbc5156aa3a30a6cf5dc3d12ddaf97d3c2 Mon Sep 17 00:00:00 2001 From: Guyute Date: Fri, 9 Aug 2024 21:39:36 -0400 Subject: [PATCH] fix: Migrates RunebookGump to DynamicGump (#1915) --- Projects/UOContent/Gumps/RunebookGump.cs | 803 ++++++++---------- .../Items/Skill Items/Magical/Runebook.cs | 17 +- 2 files changed, 370 insertions(+), 450 deletions(-) diff --git a/Projects/UOContent/Gumps/RunebookGump.cs b/Projects/UOContent/Gumps/RunebookGump.cs index 90f688ecd..d7d2c0dbb 100644 --- a/Projects/UOContent/Gumps/RunebookGump.cs +++ b/Projects/UOContent/Gumps/RunebookGump.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Server.Items; using Server.Multis; using Server.Network; @@ -7,504 +8,418 @@ using Server.Spells.Chivalry; using Server.Spells.Fourth; using Server.Spells.Seventh; -namespace Server.Gumps +namespace Server.Gumps; + +public class RunebookGump : DynamicGump { - public class RunebookGump : Gump + private readonly Runebook _book; + public Runebook Book => _book; + + public RunebookGump(Runebook book) : base(150, 200) => _book = book; + + protected override void BuildLayout(ref DynamicGumpBuilder builder) { - public RunebookGump(Mobile from, Runebook book) : base(150, 200) + AddBackground(ref builder); + AddIndex(ref builder); + + for (var page = 0; page < 8; ++page) { - Book = book; + builder.AddPage(2 + page); - AddBackground(); - AddIndex(); + builder.AddButton(125, 14, 2205, 2205, 0, GumpButtonType.Page, 1 + page); - for (var page = 0; page < 8; ++page) + if (page < 7) { - AddPage(2 + page); + builder.AddButton(393, 14, 2206, 2206, 0, GumpButtonType.Page, 3 + page); + } - AddButton(125, 14, 2205, 2205, 0, GumpButtonType.Page, 1 + page); - - if (page < 7) - { - AddButton(393, 14, 2206, 2206, 0, GumpButtonType.Page, 3 + page); - } - - for (var half = 0; half < 2; ++half) - { - AddDetails(page * 2 + half, half); - } + for (var half = 0; half < 2; ++half) + { + AddDetails(ref builder, page * 2 + half, half); } } + } - public Runebook Book { get; } - - public int GetMapHue(Map map) + public static int GetMapHue(Map map) + { + if (map == Map.Trammel) { - if (map == Map.Trammel) - { - return 10; - } - - if (map == Map.Felucca) - { - return 81; - } - - if (map == Map.Ilshenar) - { - return 1102; - } - - if (map == Map.Malas) - { - return 1102; - } - - if (map == Map.Tokuno) - { - return 1154; - } - - return 0; + return 10; } - public string GetName(string name) + if (map == Map.Felucca) { - if (name == null || (name = name.Trim()).Length <= 0) - { - return "(indescript)"; - } - - return name; + return 81; } - private void AddBackground() + if (map == Map.Ilshenar) { - AddPage(0); - - // Background image - AddImage(100, 10, 2200); - - // Two separators - for (var i = 0; i < 2; ++i) - { - var xOffset = 125 + i * 165; - - AddImage(xOffset, 50, 57); - xOffset += 20; - - for (var j = 0; j < 6; ++j, xOffset += 15) - { - AddImage(xOffset, 50, 58); - } - - AddImage(xOffset - 5, 50, 59); - } - - // First four page buttons - for (int i = 0, xOffset = 130, gumpID = 2225; i < 4; ++i, xOffset += 35, ++gumpID) - { - AddButton(xOffset, 187, gumpID, gumpID, 0, GumpButtonType.Page, 2 + i); - } - - // Next four page buttons - for (int i = 0, xOffset = 300, gumpID = 2229; i < 4; ++i, xOffset += 35, ++gumpID) - { - AddButton(xOffset, 187, gumpID, gumpID, 0, GumpButtonType.Page, 6 + i); - } - - // Charges - AddHtmlLocalized(140, 40, 80, 18, 1011296); // Charges: - AddHtml(220, 40, 30, 18, Book.CurCharges.ToString()); - - // Max charges - AddHtmlLocalized(300, 40, 100, 18, 1011297); // Max Charges: - AddHtml(400, 40, 30, 18, Book.MaxCharges.ToString()); + return 1102; } - private void AddIndex() + if (map == Map.Malas) { - // Index - AddPage(1); - - // Rename button - AddButton(125, 15, 2472, 2473, 1); - AddHtmlLocalized(158, 22, 100, 18, 1011299); // Rename book - - // List of entries - var entries = Book.Entries; - - for (var i = 0; i < 16; ++i) - { - string desc; - int hue; - - if (i < entries.Count) - { - desc = GetName(entries[i].Description); - hue = GetMapHue(entries[i].Map); - } - else - { - desc = "Empty"; - hue = 0; - } - - // Use charge button - AddButton(130 + i / 8 * 160, 65 + i % 8 * 15, 2103, 2104, 2 + i * 6 + 0); - - // Description label - AddLabelCropped(145 + i / 8 * 160, 60 + i % 8 * 15, 115, 17, hue, desc); - } - - // Turn page button - AddButton(393, 14, 2206, 2206, 0, GumpButtonType.Page, 2); + return 1102; } - private void AddDetails(int index, int half) + if (map == Map.Tokuno) { + return 1154; + } + + return 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan GetName(ReadOnlySpan name) => name.IsEmpty ? "(indescript)" : name.Trim(); + + private void AddBackground(ref DynamicGumpBuilder builder) + { + builder.AddPage(); + + // Background image + builder.AddImage(100, 10, 2200); + + // Two separators + for (var i = 0; i < 2; ++i) + { + var xOffset = 125 + i * 165; + + builder.AddImage(xOffset, 50, 57); + xOffset += 20; + + for (var j = 0; j < 6; ++j, xOffset += 15) + { + builder.AddImage(xOffset, 50, 58); + } + + builder.AddImage(xOffset - 5, 50, 59); + } + + // First four page buttons + for (int i = 0, xOffset = 130, gumpID = 2225; i < 4; ++i, xOffset += 35, ++gumpID) + { + builder.AddButton(xOffset, 187, gumpID, gumpID, 0, GumpButtonType.Page, 2 + i); + } + + // Next four page buttons + for (int i = 0, xOffset = 300, gumpID = 2229; i < 4; ++i, xOffset += 35, ++gumpID) + { + builder.AddButton(xOffset, 187, gumpID, gumpID, 0, GumpButtonType.Page, 6 + i); + } + + // Charges + builder.AddHtmlLocalized(140, 40, 80, 18, 1011296); // Charges: + builder.AddHtml(220, 40, 30, 18, $"{_book.CurCharges}"); + + // Max charges + builder.AddHtmlLocalized(300, 40, 100, 18, 1011297); // Max Charges: + builder.AddHtml(400, 40, 30, 18, $"{_book.MaxCharges}"); + } + + private void AddIndex(ref DynamicGumpBuilder builder) + { + // Index + builder.AddPage(1); + + // Rename button + builder.AddButton(125, 15, 2472, 2473, 1); + builder.AddHtmlLocalized(158, 22, 100, 18, 1011299); // Rename book + + // List of entries + var entries = _book.Entries; + + for (var i = 0; i < 16; ++i) + { + var entry = entries[i]; + // Use charge button - AddButton(130 + half * 160, 65, 2103, 2104, 2 + index * 6 + 0); + builder.AddButton(130 + i / 8 * 160, 65 + i % 8 * 15, 2103, 2104, 2 + i * 6 + 0); - string desc; - int hue; - - if (index < Book.Entries.Count) + // Description label + if (i < entries.Count) { - var e = Book.Entries[index]; - - desc = GetName(e.Description); - hue = GetMapHue(e.Map); - - // Location labels - int xLong = 0, yLat = 0; - int xMins = 0, yMins = 0; - bool xEast = false, ySouth = false; - - if (Sextant.Format(e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth)) - { - AddLabel(135 + half * 160, 80, 0, $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}"); - AddLabel(135 + half * 160, 95, 0, $"{xLong}° {xMins}'{(xEast ? "E" : "W")}"); - } - - // Drop rune button - AddButton(135 + half * 160, 115, 2437, 2438, 2 + index * 6 + 1); - AddHtmlLocalized(150 + half * 160, 115, 100, 18, 1011298); // Drop rune - - // Set as default button - var defButtonID = e != Book.Default ? 2361 : 2360; - - AddButton(160 + half * 140, 20, defButtonID, defButtonID, 2 + index * 6 + 2); - AddHtmlLocalized(175 + half * 140, 15, 100, 18, 1011300); // Set default - - if (Core.AOS) - { - AddButton(135 + half * 160, 140, 2103, 2104, 2 + index * 6 + 3); - AddHtmlLocalized(150 + half * 160, 136, 110, 20, 1062722); // Recall - - AddButton(135 + half * 160, 158, 2103, 2104, 2 + index * 6 + 4); - AddHtmlLocalized(150 + half * 160, 154, 110, 20, 1062723); // Gate Travel - - AddButton(135 + half * 160, 176, 2103, 2104, 2 + index * 6 + 5); - AddHtmlLocalized(150 + half * 160, 172, 110, 20, 1062724); // Sacred Journey - } - else - { - // Recall button - AddButton(135 + half * 160, 140, 2271, 2271, 2 + index * 6 + 3); - - // Gate button - AddButton(205 + half * 160, 140, 2291, 2291, 2 + index * 6 + 4); - } + builder.AddLabelCropped( + 145 + i / 8 * 160, + 60 + i % 8 * 15, + 115, + 17, + GetMapHue(entry.Map), + GetName(entry.Description) + ); } else { - desc = "Empty"; - hue = 0; + builder.AddLabelCropped(145 + i / 8 * 160, 60 + i % 8 * 15, 115, 17, 0, "Empty"); + } + } + + // Turn page button + builder.AddButton(393, 14, 2206, 2206, 0, GumpButtonType.Page, 2); + } + + private void AddDetails(ref DynamicGumpBuilder builder, int index, int half) + { + // Use charge button + builder.AddButton(130 + half * 160, 65, 2103, 2104, 2 + index * 6 + 0); + + if (index < _book.Entries.Count) + { + var e = _book.Entries[index]; + + // Location labels + var xLong = 0; + var yLat = 0; + var xMins = 0; + var yMins = 0; + var xEast = false; + var ySouth = false; + + if (Sextant.Format(e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth)) + { + builder.AddLabel(135 + half * 160, 80, 0, $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}"); + builder.AddLabel(135 + half * 160, 95, 0, $"{xLong}° {xMins}'{(xEast ? "E" : "W")}"); + } + + // Drop rune button + builder.AddButton(135 + half * 160, 115, 2437, 2438, 2 + index * 6 + 1); + builder.AddHtmlLocalized(150 + half * 160, 115, 100, 18, 1011298); // Drop rune + + // Set as default button + var defButtonID = e != _book.Default ? 2361 : 2360; + + builder.AddButton(160 + half * 140, 20, defButtonID, defButtonID, 2 + index * 6 + 2); + builder.AddHtmlLocalized(175 + half * 140, 15, 100, 18, 1011300); // Set default + + if (Core.AOS) + { + builder.AddButton(135 + half * 160, 140, 2103, 2104, 2 + index * 6 + 3); + builder.AddHtmlLocalized(150 + half * 160, 136, 110, 20, 1062722); // Recall + + builder.AddButton(135 + half * 160, 158, 2103, 2104, 2 + index * 6 + 4); + builder.AddHtmlLocalized(150 + half * 160, 154, 110, 20, 1062723); // Gate Travel + + builder.AddButton(135 + half * 160, 176, 2103, 2104, 2 + index * 6 + 5); + builder.AddHtmlLocalized(150 + half * 160, 172, 110, 20, 1062724); // Sacred Journey + } + else + { + // Recall button + builder.AddButton(135 + half * 160, 140, 2271, 2271, 2 + index * 6 + 3); + + // Gate button + builder.AddButton(205 + half * 160, 140, 2291, 2291, 2 + index * 6 + 4); } // Description label - AddLabelCropped(145 + half * 160, 60, 115, 17, hue, desc); + builder.AddLabelCropped(145 + half * 160, 60, 115, 17, GetMapHue(e.Map), GetName(e.Description)); + } + else + { + // Description label + builder.AddLabelCropped(145 + half * 160, 60, 115, 17, 0, "Empty"); + } + } + + public static bool HasSpell(Mobile from, int spellID) => Spellbook.Find(from, spellID)?.HasSpell(spellID) == true; + + public override void OnResponse(NetState state, in RelayInfo info) + { + var from = state.Mobile; + + _book.Openers.Remove(from); + + if (_book.Deleted || !from.InRange(_book.GetWorldLocation(), Core.ML ? 3 : 1) || !DesignContext.Check(from)) + { + return; } - public static bool HasSpell(Mobile from, int spellID) => Spellbook.Find(from, spellID)?.HasSpell(spellID) == true; + var buttonID = info.ButtonID; - public override void OnResponse(NetState state, in RelayInfo info) + if (buttonID == 1) // Rename book { - var from = state.Mobile; - - if (Book.Deleted || !from.InRange(Book.GetWorldLocation(), Core.ML ? 3 : 1) || !DesignContext.Check(from)) + if (!_book.IsLockedDown || from.AccessLevel >= AccessLevel.GameMaster) { - Book.Openers.Remove(from); - return; - } - - var buttonID = info.ButtonID; - - if (buttonID == 1) // Rename book - { - if (!Book.IsLockedDown || from.AccessLevel >= AccessLevel.GameMaster) - { - from.SendLocalizedMessage(502414); // Please enter a title for the runebook: - from.Prompt = new InternalPrompt(Book); - } - else - { - Book.Openers.Remove(from); - - from.SendLocalizedMessage(502413, null, 0x35); // That cannot be done while the book is locked down. - } + from.SendLocalizedMessage(502414); // Please enter a title for the runebook: + from.Prompt = new InternalPrompt(_book); } else { - buttonID -= 2; + from.SendLocalizedMessage(502413, null, 0x35); // That cannot be done while the book is locked down. + } - var index = buttonID / 6; - var type = buttonID % 6; + return; + } - if (index >= 0 && index < Book.Entries.Count) + buttonID -= 2; + + var index = Math.DivRem(buttonID, 6, out var type); + + if (index < 0 || index > _book.Entries.Count) + { + return; + } + + var e = _book.Entries[index]; + + switch (type) + { + case 0: // Use charges { - var e = Book.Entries[index]; - - switch (type) + if (_book.CurCharges <= 0) { - case 0: // Use charges - { - if (Book.CurCharges <= 0) - { - from.CloseGump(); - from.SendGump(new RunebookGump(from, Book)); - - from.SendLocalizedMessage(502412); // There are no charges left on that item. - } - else - { - int xLong = 0, yLat = 0; - int xMins = 0, yMins = 0; - bool xEast = false, ySouth = false; - - if (Sextant.Format( - e.Location, - e.Map, - ref xLong, - ref yLat, - ref xMins, - ref yMins, - ref xEast, - ref ySouth - )) - { - var location = - $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}' {xMins}'{(xEast ? "E" : "W")}"; - from.SendMessage(location); - } - - Book.OnTravel(); - new RecallSpell(from, e, Book, Book).Cast(); - - Book.Openers.Remove(from); - } - - break; - } - case 1: // Drop rune - { - if (!Book.IsLockedDown || from.AccessLevel >= AccessLevel.GameMaster) - { - Book.DropRune(from, e, index); - - from.CloseGump(); - if (!Core.ML) - { - from.SendGump(new RunebookGump(from, Book)); - } - } - else - { - Book.Openers.Remove(from); - - from.SendLocalizedMessage( - 502413, - null, - 0x35 - ); // That cannot be done while the book is locked down. - } - - break; - } - case 2: // Set default - { - if (Book.CheckAccess(from)) - { - Book.Default = e; - - from.CloseGump(); - from.SendGump(new RunebookGump(from, Book)); - - from.SendLocalizedMessage(502417); // New default location set. - } - - break; - } - case 3: // Recall - { - if (HasSpell(from, 31)) - { - int xLong = 0, yLat = 0; - int xMins = 0, yMins = 0; - bool xEast = false, ySouth = false; - - if (Sextant.Format( - e.Location, - e.Map, - ref xLong, - ref yLat, - ref xMins, - ref yMins, - ref xEast, - ref ySouth - )) - { - var location = - $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}' {xMins}'{(xEast ? "E" : "W")}"; - from.SendMessage(location); - } - - Book.OnTravel(); - new RecallSpell(from, e).Cast(); - } - else - { - from.SendLocalizedMessage(500015); // You do not have that spell! - } - - Book.Openers.Remove(from); - - break; - } - case 4: // Gate - { - if (HasSpell(from, 51)) - { - int xLong = 0, yLat = 0; - int xMins = 0, yMins = 0; - bool xEast = false, ySouth = false; - - if (Sextant.Format( - e.Location, - e.Map, - ref xLong, - ref yLat, - ref xMins, - ref yMins, - ref xEast, - ref ySouth - )) - { - var location = - $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}' {xMins}'{(xEast ? "E" : "W")}"; - from.SendMessage(location); - } - - Book.OnTravel(); - new GateTravelSpell(from, e).Cast(); - } - else - { - from.SendLocalizedMessage(500015); // You do not have that spell! - } - - Book.Openers.Remove(from); - - break; - } - case 5: // Sacred Journey - { - if (Core.AOS) - { - if (HasSpell(from, 209)) - { - int xLong = 0, yLat = 0; - int xMins = 0, yMins = 0; - bool xEast = false, ySouth = false; - - if (Sextant.Format( - e.Location, - e.Map, - ref xLong, - ref yLat, - ref xMins, - ref yMins, - ref xEast, - ref ySouth - )) - { - var location = - $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}' {xMins}'{(xEast ? "E" : "W")}"; - from.SendMessage(location); - } - - Book.OnTravel(); - new SacredJourneySpell(from, e).Cast(); - } - else - { - from.SendLocalizedMessage(500015); // You do not have that spell! - } - } - - Book.Openers.Remove(from); - - break; - } + from.SendLocalizedMessage(502412); // There are no charges left on that item. } + else + { + SendTargetCoordinates(from, e.Location, e.Map); + + _book.OnTravel(); + new RecallSpell(from, e, _book, _book).Cast(); + } + + break; } - else + case 1: // Drop rune { - Book.Openers.Remove(from); + if (!_book.IsLockedDown || from.AccessLevel >= AccessLevel.GameMaster) + { + _book.DropRune(from, e, index); + + if (!Core.ML) + { + _book.SendGumpTo(from); + } + } + else + { + // That cannot be done while the book is locked down. + from.SendLocalizedMessage(502413, null, 0x35); + } + + break; } + case 2: // Set default + { + if (_book.CheckAccess(from)) + { + _book.Default = e; + + // Resend the runebook gump + _book.SendGumpTo(from); + + from.SendLocalizedMessage(502417); // New default location set. + } + + break; + } + case 3: // Recall + { + if (!HasSpell(from, 31)) + { + from.SendLocalizedMessage(500015); // You do not have that spell! + break; + } + + SendTargetCoordinates(from, e.Location, e.Map); + + _book.OnTravel(); + new RecallSpell(from, e).Cast(); + break; + } + case 4: // Gate + { + if (!HasSpell(from, 51)) + { + from.SendLocalizedMessage(500015); // You do not have that spell! + break; + } + + SendTargetCoordinates(from, e.Location, e.Map); + + _book.OnTravel(); + new GateTravelSpell(from, e).Cast(); + break; + } + case 5: // Sacred Journey + { + if (Core.AOS) + { + if (!HasSpell(from, 209)) + { + from.SendLocalizedMessage(500015); // You do not have that spell! + _book.Openers.Remove(from); + break; + } + + SendTargetCoordinates(from, e.Location, e.Map); + + _book.OnTravel(); + new SacredJourneySpell(from, e).Cast(); + } + break; + } + } + } + + private static void SendTargetCoordinates(Mobile to, Point3D location, Map map) + { + var xLong = 0; + var yLat = 0; + var xMins = 0; + var yMins = 0; + bool xEast = false, ySouth = false; + + if (Sextant.Format( + location, + map, + ref xLong, + ref yLat, + ref xMins, + ref yMins, + ref xEast, + ref ySouth + )) + { + var locationString = + $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}' {xMins}'{(xEast ? "E" : "W")}"; + to.SendMessage(locationString); + } + } + + private class InternalPrompt : Prompt + { + private readonly Runebook _book; + + public InternalPrompt(Runebook book) => _book = book; + + public override void OnResponse(Mobile from, string text) + { + if (_book.Deleted || !from.InRange(_book.GetWorldLocation(), Core.ML ? 3 : 1)) + { + return; + } + + if (_book.CheckAccess(from)) + { + _book.Description = text.AsSpan().Trim().FixHtml(); + _book.SendGumpTo(from); + + from.SendMessage("The book's title has been changed."); + } + else + { + from.SendLocalizedMessage(502416); // That cannot be done while the book is locked down. } } - private class InternalPrompt : Prompt + public override void OnCancel(Mobile from) { - private readonly Runebook m_Book; + from.SendLocalizedMessage(502415); // Request cancelled. - public InternalPrompt(Runebook book) => m_Book = book; - - public override void OnResponse(Mobile from, string text) + if (!_book.Deleted && from.InRange(_book.GetWorldLocation(), Core.ML ? 3 : 1)) { - if (m_Book.Deleted || !from.InRange(m_Book.GetWorldLocation(), Core.ML ? 3 : 1)) - { - return; - } - - if (m_Book.CheckAccess(from)) - { - m_Book.Description = text.AsSpan().Trim().FixHtml(); - - from.CloseGump(); - from.SendGump(new RunebookGump(from, m_Book)); - - from.SendMessage("The book's title has been changed."); - } - else - { - m_Book.Openers.Remove(from); - - from.SendLocalizedMessage(502416); // That cannot be done while the book is locked down. - } - } - - public override void OnCancel(Mobile from) - { - from.SendLocalizedMessage(502415); // Request cancelled. - - if (!m_Book.Deleted && from.InRange(m_Book.GetWorldLocation(), Core.ML ? 3 : 1)) - { - from.CloseGump(); - from.SendGump(new RunebookGump(from, m_Book)); - } + _book.SendGumpTo(from); } } } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs index 622988d96..40954b4e3 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs @@ -73,7 +73,7 @@ public partial class Runebook : Item, ISecurable, ICraftable [CommandProperty(AccessLevel.GameMaster)] public DateTime NextUse { get; set; } - public List Openers { get; set; } = new(); + public HashSet Openers { get; } = new(); public override int LabelNumber => 1041267; // runebook @@ -262,13 +262,18 @@ public partial class Runebook : Item, ISecurable, ICraftable return; } - from.CloseGump(); - from.SendGump(new RunebookGump(from, this)); - - Openers.Add(from); + SendGumpTo(from); } } + public void SendGumpTo(Mobile from) + { + from.CloseGump(); + from.SendGump(new RunebookGump(this)); + + Openers.Add(from); + } + public virtual void OnTravel() { if (!Core.SA) @@ -408,7 +413,7 @@ public partial class RunebookEntry public RunebookEntry( Runebook runebook, - Point3D loc = new(), + Point3D loc = default, Map map = null, string description = null, BaseHouse house = null