using System; using System.Collections.Generic; using ModernUO.Serialization; using Server.Collections; using Server.Factions; using Server.Gumps; using Server.Maps; using Server.Mobiles; using Server.Network; using Server.Spells; namespace Server.Items; [SerializationGenerator(0, false)] public partial class PublicMoongate : Item { [Constructible] public PublicMoongate() : base(0xF6C) { Movable = false; Light = LightType.Circle300; } public override bool ForceShowProperties => ObjectPropertyList.Enabled; public override bool HandlesOnMovement => true; public override void OnDoubleClick(Mobile from) { if (!from.Player) { return; } if (from.InRange(GetWorldLocation(), 1)) { UseGate(from); } else { from.SendLocalizedMessage(500446); // That is too far away. } } public override bool OnMoveOver(Mobile m) { // Changed so criminals are not blocked by it. if (m.Player) { UseGate(m); } return true; } public override void OnMovement(Mobile m, Point3D oldLocation) { if (m is PlayerMobile) { if (!Utility.InRange(m.Location, Location, 1) && Utility.InRange(oldLocation, Location, 1)) { m.CloseGump(); } } } public bool UseGate(Mobile m) { if (m.Criminal) { m.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily. return false; } if (SpellHelper.CheckCombat(m)) { m.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle?? return false; } if (m.Spell != null) { m.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. return false; } MoongateGump.DisplayTo(m, this); if (!m.Hidden || m.AccessLevel == AccessLevel.Player) { Effects.PlaySound(m.Location, m.Map, 0x20E); } return true; } public static void Configure() { CommandSystem.Register("MoonGen", AccessLevel.Developer, MoonGen_OnCommand); } [Usage("MoonGen"), Description("Generates public moongates. Removes all old moongates.")] public static void MoonGen_OnCommand(CommandEventArgs e) { DeleteAll(); var count = 0; count += MoonGen(PMList.Trammel, MapSelectionFlags.Trammel); count += MoonGen(PMList.Felucca, MapSelectionFlags.Felucca); count += MoonGen(PMList.Ilshenar, MapSelectionFlags.Ilshenar); count += MoonGen(PMList.Malas, MapSelectionFlags.Malas); count += MoonGen(PMList.Tokuno, MapSelectionFlags.Tokuno); World.Broadcast(0x35, true, $"{count} moongates generated."); } private static void DeleteAll() { var list = new List(); foreach (var item in World.Items.Values) { if (item is PublicMoongate) { list.Add(item); } } foreach (var item in list) { item.Delete(); } if (list.Count > 0) { World.Broadcast(0x35, true, $"{list.Count} moongates removed."); } } private static int MoonGen(PMList list, MapSelectionFlags flag) { if (!ExpansionInfo.CoreExpansion.MapSelectionFlags.Includes(flag)) { return 0; } foreach (var entry in list.Entries) { var item = new PublicMoongate(); item.MoveToWorld(entry.Location, list.Map); if (entry.Number == 1060642) // Umbra { item.Hue = 0x497; } } return list.Entries.Length; } } public class PMEntry { public PMEntry(Point3D loc, int number) { Location = loc; Number = number; } public Point3D Location { get; } public int Number { get; } } public class PMList { public static readonly PMList Trammel = new( 1012000, 1012012, Map.Trammel, [ new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom new PMEntry(new Point3D(771, 752, 5), 1012006), // Yew new PMEntry(new Point3D(2701, 692, 5), 1012007), // Minoc new PMEntry(new Point3D(1828, 2948, -20), 1012008), // Trinsic new PMEntry(new Point3D(643, 2067, 5), 1012009), // Skara Brae /* Dynamic Z for Magincia to support both old and new maps. */ new PMEntry(new Point3D(3563, 2139, Map.Trammel.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia new PMEntry(new Point3D(3450, 2677, 25), 1078098) // New Haven ] ); public static readonly PMList Felucca = new( 1012001, 1012013, Map.Felucca, [ new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom new PMEntry(new Point3D(771, 752, 5), 1012006), // Yew new PMEntry(new Point3D(2701, 692, 5), 1012007), // Minoc new PMEntry(new Point3D(1828, 2948, -20), 1012008), // Trinsic new PMEntry(new Point3D(643, 2067, 5), 1012009), // Skara Brae /* Dynamic Z for Magincia to support both old and new maps. */ new PMEntry(new Point3D(3563, 2139, Map.Felucca.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia new PMEntry(new Point3D(2711, 2234, 0), 1019001) // Buccaneer's Den ] ); public static readonly PMList Ilshenar = new( 1012002, 1012014, Map.Ilshenar, [ new PMEntry(new Point3D(1215, 467, -13), 1012015), // Compassion new PMEntry(new Point3D(722, 1366, -60), 1012016), // Honesty new PMEntry(new Point3D(744, 724, -28), 1012017), // Honor new PMEntry(new Point3D(281, 1016, 0), 1012018), // Humility new PMEntry(new Point3D(987, 1011, -32), 1012019), // Justice new PMEntry(new Point3D(1174, 1286, -30), 1012020), // Sacrifice new PMEntry(new Point3D(1532, 1340, -3), 1012021), // Spirituality new PMEntry(new Point3D(528, 216, -45), 1012022), // Valor new PMEntry(new Point3D(1721, 218, 96), 1019000) // Chaos ] ); public static readonly PMList Malas = new( 1060643, 1062039, Map.Malas, [ new PMEntry(new Point3D(1015, 527, -65), 1060641), // Luna new PMEntry(new Point3D(1997, 1386, -85), 1060642) // Umbra ] ); public static readonly PMList Tokuno = new( 1063258, 1063415, Map.Tokuno, [ new PMEntry(new Point3D(1169, 998, 41), 1063412), // Isamu-Jima new PMEntry(new Point3D(802, 1204, 25), 1063413), // Makoto-Jima new PMEntry(new Point3D(270, 628, 15), 1063414) // Homare-Jima ] ); public static readonly PMList TerMur = new( 1113602, 1113604, Map.TerMur, [ new PMEntry(new Point3D(850, 3525, -38), 1113603) // Royal City ] ); public static readonly PMList TerMurEodon = new( 1113602, 1113604, Map.TerMur, [ new PMEntry(new Point3D(850, 3525, -38), 1113603), // Royal City new PMEntry(new Point3D(719, 1863, 40), 1156262) // Valley of Eodon ] ); public static readonly PMList[] NoTrammelLists = [Felucca]; public static readonly PMList[] T2ALists = [Trammel, Felucca]; public static readonly PMList[] T2AListsYoung = [Trammel]; public static readonly PMList[] LBRLists = [Trammel, Felucca, Ilshenar]; public static readonly PMList[] LBRListsYoung = [Trammel, Ilshenar]; public static readonly PMList[] AOSLists = [Trammel, Felucca, Ilshenar, Malas]; public static readonly PMList[] AOSListsYoung = [Trammel, Ilshenar, Malas]; public static readonly PMList[] SELists = [Trammel, Felucca, Ilshenar, Malas, Tokuno]; public static readonly PMList[] SEListsYoung = [Trammel, Ilshenar, Malas, Tokuno]; public static readonly PMList[] SALists = [Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMur]; public static readonly PMList[] SAListsYoung = [Trammel, Ilshenar, Malas, Tokuno, TerMur]; public static readonly PMList[] TOLLists = [Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMurEodon]; public static readonly PMList[] TOLListsYoung = [Trammel, Ilshenar, Malas, Tokuno, TerMurEodon]; public static readonly PMList[] RedLists = [Felucca]; public static readonly PMList[] SigilLists = [Felucca]; public PMList(int number, int selNumber, Map map, PMEntry[] entries) { Number = number; SelNumber = selNumber; Map = map; Entries = entries; } public int Number { get; } public int SelNumber { get; } public Map Map { get; } public PMEntry[] Entries { get; } } public class MoongateGump : DynamicGump { private readonly PMList[] _lists; private readonly PMList[] _tabOrder; private readonly Item _moongate; public override bool Singleton => true; private MoongateGump(Item moongate, PMList[] lists, PMList[] tabOrder) : base(100, 100) { _moongate = moongate; _lists = lists; _tabOrder = tabOrder; } public static void DisplayTo(Mobile mobile, Item moongate) { if (mobile?.NetState == null || moongate == null || moongate.Deleted) { return; } PMList[] checkLists; if (!mobile.Player) { checkLists = PMList.SELists; } else if (Sigil.ExistsOn(mobile)) { checkLists = PMList.SigilLists; } else if (mobile.Murderer) { checkLists = PMList.RedLists; } else { var flags = mobile.NetState?.Flags ?? ClientFlags.None; var young = mobile is PlayerMobile { Young: true }; checkLists = Core.Expansion switch { >= Expansion.TOL when (flags & ClientFlags.TerMur) != 0 => young ? PMList.TOLListsYoung : PMList.TOLLists, >= Expansion.SA when (flags & ClientFlags.TerMur) != 0 => young ? PMList.SAListsYoung : PMList.SALists, >= Expansion.SE when (flags & ClientFlags.Tokuno) != 0 => young ? PMList.SEListsYoung : PMList.SELists, >= Expansion.AOS when (flags & ClientFlags.Malas) != 0 => young ? PMList.AOSListsYoung : PMList.AOSLists, >= Expansion.LBR when (flags & ClientFlags.Ilshenar) != 0 => young ? PMList.LBRListsYoung : PMList.LBRLists, >= Expansion.T2A when (flags & ClientFlags.Trammel) != 0 => young ? PMList.T2AListsYoung : PMList.T2ALists, _ => PMList.NoTrammelLists }; } var availableMaps = ExpansionInfo.CoreExpansion.MapSelectionFlags; using var filteredBySelectedMaps = PooledRefList.Create(); for (var i = 0; i < checkLists.Length; ++i) { var pmList = checkLists[i]; if (availableMaps.Includes(pmList.Map.ToSelectionFlag())) { filteredBySelectedMaps.Add(pmList); } } var mapCount = filteredBySelectedMaps.Count; if (mapCount == 0) { return; } var lists = filteredBySelectedMaps.ToArray(); var tabOrder = filteredBySelectedMaps.ToArray(); for (var i = 0; i < lists.Length; ++i) { if (lists[i].Map == mobile.Map) { (lists[i], lists[0]) = (lists[0], lists[i]); break; } } mobile.SendGump(new MoongateGump(moongate, lists, tabOrder)); } protected override void BuildLayout(ref DynamicGumpBuilder builder) { builder.AddPage(); builder.AddBackground(0, 0, 380, 280, 5054); builder.AddButton(10, 210, 4005, 4007, 1); builder.AddHtmlLocalized(45, 210, 140, 25, 1011036); // OKAY builder.AddButton(10, 235, 4005, 4007, 0); builder.AddHtmlLocalized(45, 235, 140, 25, 1011012); // CANCEL builder.AddHtmlLocalized(5, 5, 200, 20, 1012011); // Pick your destination: // Tab buttons on page 0 — rendered in original (pre-swap) order so the active map appears at its expected slot. for (var i = 0; i < _tabOrder.Length; ++i) { builder.AddButton(10, 35 + i * 25, 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf(_lists, _tabOrder[i]) + 1); builder.AddHtmlLocalized(30, 35 + i * 25, 150, 20, _tabOrder[i].Number); } for (var i = 0; i < _lists.Length; ++i) { RenderPage(ref builder, i, Array.IndexOf(_tabOrder, _lists[i])); } } private void RenderPage(ref DynamicGumpBuilder builder, int index, int offset) { var list = _lists[index]; builder.AddPage(index + 1); builder.AddButton(10, 35 + offset * 25, 2117, 2118, 0, GumpButtonType.Page, index + 1); builder.AddHtmlLocalized(30, 35 + offset * 25, 150, 20, list.SelNumber); var entries = list.Entries; for (var i = 0; i < entries.Length; ++i) { builder.AddRadio(200, 35 + i * 25, 210, 211, false, index * 100 + i); builder.AddHtmlLocalized(225, 35 + i * 25, 150, 20, entries[i].Number); } } public override void OnResponse(NetState state, in RelayInfo info) { if (info.ButtonID == 0 || _moongate.Deleted) { return; } var from = state.Mobile; if (from.Map == null) { return; } var switches = info.Switches; if (switches.Length == 0) { return; } var switchID = switches[0]; var listIndex = switchID / 100; var listEntry = switchID % 100; if (listIndex < 0 || listIndex >= _lists.Length) { return; } var list = _lists[listIndex]; if (listEntry < 0 || listEntry >= list.Entries.Length) { return; } var entry = list.Entries[listEntry]; if (!from.InRange(_moongate.GetWorldLocation(), 1) || from.Map != _moongate.Map) { from.SendLocalizedMessage(1019002); // You are too far away to use the gate. } else if (from.Player && from.Murderer && list.Map != Map.Felucca || Sigil.ExistsOn(from) && list.Map != Faction.Facet) { from.SendLocalizedMessage(1019004); // You are not allowed to travel there. } else if (from.Criminal) { from.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily. } else if (SpellHelper.CheckCombat(from)) { from.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle?? } else if (from.Spell != null) { from.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. } else if (from.Map == list.Map && from.InRange(entry.Location, 1)) { from.SendLocalizedMessage(1019003); // You are already there. } else { BaseCreature.TeleportPets(from, entry.Location, list.Map); from.Combatant = null; from.Warmode = false; from.Hidden = true; from.MoveToWorld(entry.Location, list.Map); Effects.PlaySound(entry.Location, list.Map, 0x1FE); } } }