diff --git a/src/Prima.Core.Server/Collections/ValueLinkList.cs b/src/Prima.Core.Server/Collections/ValueLinkList.cs new file mode 100644 index 0000000..8adcdc8 --- /dev/null +++ b/src/Prima.Core.Server/Collections/ValueLinkList.cs @@ -0,0 +1,499 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ValueLinkList.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 Prima.Core.Server.Collections; + +public interface IValueLinkListNode where T : class +{ + public T Next { get; set; } + public T Previous { get; set; } + public bool OnLinkList { get; set; } +} + +public struct ValueLinkList where T : class, IValueLinkListNode +{ + public int Count { get; internal set; } + internal T _first; + internal T _last; + + public int Version { get; private set; } + + public void Remove(T node) + { + if (node == null) + { + return; + } + + if (!node.OnLinkList) + { + throw new ArgumentException("Attempted to remove a node that is not on the list."); + } + + if (node.Previous == null) + { + // If previous is null, then it is the first element. + if (_first != node) + { + throw new ArgumentException("Attempted to remove a node that is not on the list."); + } + + if (_first == _last) + { + _last = null; + _first = null; + } + else + { + _first = node.Next; + } + + if (node.Next != null) + { + node.Next.Previous = null; + } + } + else + { + node.Previous.Next = node.Next; + + // If next is null, then it is the last element. + if (node.Next == null) + { + _last = node.Previous; + } + else + { + node.Next.Previous = node.Previous; + } + } + + node.Next = null; + node.Previous = null; + node.OnLinkList = false; + Count--; + Version++; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + } + + // Remove all entries before this node, not including this node. + public void RemoveAllBefore(T e) + { + if (e == null) + { + return; + } + + if (!e.OnLinkList) + { + throw new ArgumentException("Attempted to remove nodes before a node that is not on the list."); + } + + if (e.Previous == null) + { + return; + } + + var current = e.Previous; + e.Previous = null; + + while (current != null) + { + var previous = current.Previous; + + current.OnLinkList = false; + current.Next = null; + current.Previous = null; + Count--; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + + current = previous; + } + + _first = e; + Version++; + } + + // Remove all entries after this node, not including this node. + public void RemoveAllAfter(T e) + { + if (e == null) + { + return; + } + + if (!e.OnLinkList) + { + throw new ArgumentException("Attempted to remove nodes after a node that is not on the list."); + } + + if (e.Next == null) + { + return; + } + + var current = e.Next; + e.Next = null; + + while (current != null) + { + var next = current.Next; + + current.OnLinkList = false; + current.Next = null; + current.Previous = null; + Count--; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + + current = next; + } + + _last = e; + Version++; + } + + public void AddLast(T e) + { + if (e == null) + { + return; + } + + if (e.OnLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + if (_last != null) + { + AddAfter(_last, e); + } + else + { + _first = e; + _last = e; + Count = 1; + Version++; + e.OnLinkList = true; + } + } + + public void AddFirst(T e) + { + if (e == null) + { + return; + } + + if (e.OnLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + if (_first != null) + { + AddBefore(_first, e); + } + else + { + _first = e; + _last = e; + Count = 1; + Version++; + e.OnLinkList = true; + } + } + + public void AddBefore(T existing, T node) + { + if (node == null) + { + return; + } + + ArgumentNullException.ThrowIfNull(existing); + + if (!existing.OnLinkList) + { + throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); + } + + if (node.OnLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + node.Next = existing; + node.Previous = existing.Previous; + + if (existing.Previous != null) + { + existing.Previous.Next = node; + } + else + { + _first = node; + } + + existing.Previous = node; + node.OnLinkList = true; + Count++; + Version++; + } + + public void AddAfter(T existing, T node) + { + if (node == null) + { + return; + } + + ArgumentNullException.ThrowIfNull(existing); + + if (!existing.OnLinkList) + { + throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); + } + + if (node.OnLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + node.Previous = existing; + node.Next = existing.Next; + + if (existing.Next != null) + { + existing.Next.Previous = node; + } + else + { + _last = node; + } + + existing.Next = node; + node.OnLinkList = true; + Count++; + Version++; + } + + public void RemoveAll() + { + var current = _first; + while (current != null) + { + var next = current.Next; + + current.OnLinkList = false; + current.Next = null; + current.Previous = null; + current = next; + } + + _first = null; + _last = null; + Count = 0; + Version++; + } + + public void AddLast(ref ValueLinkList otherList, T start, T end) + { + // Should we check if start and end actually exist on the other list? + if (otherList.Count == 0 || otherList.Count == 1 && (start != end || otherList._first != start)) + { + throw new ArgumentException("Attempted to add nodes that are not on the specified linklist."); + } + + if (start.Previous != null) + { + start.Previous.Next = end.Next; + } + else + { + // Start is first + otherList._first = end.Next; + } + + if (end.Next != null) + { + end.Next.Previous = start.Previous; + } + else + { + otherList._last = start.Previous; + } + + var count = 1; + var current = start; + + // Assume start and end are in the right order, or bad things happen (crash). + while (current != end) + { + count++; + current = current.Next; + } + + otherList.Count -= count; + + if (otherList.Count < 0) + { + throw new Exception("Count is negative!"); + } + + if (_last != null) + { + _last.Next = start; + start.Previous = _last; + } + else + { + _first = start; + } + + _last = end; + Count += count; + Version++; + } + + public T[] ToArray() + { + var arr = new T[Count]; + + var index = 0; + foreach (var t in this) + { + arr[index++] = t; + } + + return arr; + } + + public ref struct ValueListEnumerator + { + private bool _started; + private T _current; + private ref readonly ValueLinkList _linkList; + private int _version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ValueListEnumerator(in ValueLinkList linkList) + { + _linkList = ref linkList; + _started = false; + _current = null; + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (!_started) + { + _current = _linkList._first; + _started = true; + _version = _linkList.Version; + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException("Collection was modified after the enumerator was instantiated."); + } + else + { + _current = _current.Next; + } + + return _current != null; + } + + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } + + public ref struct DescendingValueListEnumerator + { + private bool _started; + private T _current; + private ref readonly ValueLinkList _linkList; + private int _version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DescendingValueListEnumerator(in ValueLinkList linkList) + { + _linkList = ref linkList; + _started = false; + _current = null; + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (!_started) + { + _current = _linkList._last; + _started = true; + _version = _linkList.Version; + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException("Collection was modified after the enumerator was instantiated."); + } + else + { + _current = _current.Previous; + } + + return _current != null; + } + + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DescendingValueListEnumerator GetEnumerator() => this; + } +} + +public static class ValueLinkListExt +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueLinkList.ValueListEnumerator GetEnumerator(this in ValueLinkList linkList) + where T : class, IValueLinkListNode => new(in linkList); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueLinkList.DescendingValueListEnumerator ByDescending(this in ValueLinkList linkList) + where T : class, IValueLinkListNode => new(in linkList); +} diff --git a/src/Prima.Core.Server/Data/PrimaServerContext.cs b/src/Prima.Core.Server/Data/PrimaServerContext.cs index 48e445d..d7afd00 100644 --- a/src/Prima.Core.Server/Data/PrimaServerContext.cs +++ b/src/Prima.Core.Server/Data/PrimaServerContext.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Orion.Core.Server.Data.Directories; using Orion.Core.Server.Interfaces.Services.System; using Prima.Core.Server.Data.Session; using Prima.Core.Server.Data.Uo; @@ -10,6 +11,8 @@ public static class PrimaServerContext { public static IServiceProvider ServiceProvider { get; set; } + public static DirectoriesConfig DirectoriesConfig => ServiceProvider.GetRequiredService(); + public static IEventLoopService EventLoopService => ServiceProvider.GetRequiredService(); public static INetworkSessionService NetworkSessionService => ServiceProvider.GetRequiredService>(); diff --git a/src/Prima.Core.Server/Data/Session/NetworkSession.cs b/src/Prima.Core.Server/Data/Session/NetworkSession.cs index ea6a31f..aee15a1 100644 --- a/src/Prima.Core.Server/Data/Session/NetworkSession.cs +++ b/src/Prima.Core.Server/Data/Session/NetworkSession.cs @@ -1,7 +1,6 @@ using Orion.Core.Server.Interfaces.Sessions; using Prima.Core.Server.Data.Uo; using Prima.Network.Interfaces.Packets; -using Prima.Network.Packets; namespace Prima.Core.Server.Data.Session; diff --git a/src/Prima.Core.Server/Handlers/Base/BasePacketListenerHandler.cs b/src/Prima.Core.Server/Handlers/Base/BasePacketListenerHandler.cs index b6c43f7..3f26dd5 100644 --- a/src/Prima.Core.Server/Handlers/Base/BasePacketListenerHandler.cs +++ b/src/Prima.Core.Server/Handlers/Base/BasePacketListenerHandler.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using Orion.Core.Server.Interfaces.Services.Base; using Orion.Core.Server.Interfaces.Services.System; +using Orion.Core.Server.Listeners.EventBus; using Prima.Core.Server.Data.Session; using Prima.Core.Server.Interfaces.Listeners; using Prima.Core.Server.Interfaces.Services; @@ -79,4 +80,19 @@ public Task OnPacketReceived(string sessionId, IUoNetworkPacket packet) { return _serviceProvider.GetRequiredService>().GetSession(sessionId); } + + protected Task PublishEvent(TEvent eventArgs) where TEvent : class + { + return _serviceProvider.GetRequiredService().PublishAsync(eventArgs); + } + + protected void SubscribeEvent(Func handler) where TEvent : class + { + _serviceProvider.GetRequiredService().Subscribe(handler); + } + + protected void SubscribeEvent(IEventBusListener listener) where TEvent : class + { + _serviceProvider.GetRequiredService().Subscribe(listener); + } } diff --git a/src/Prima.JavaScript.Engine/Services/ScriptEngineService.cs b/src/Prima.JavaScript.Engine/Services/ScriptEngineService.cs index eb4f681..f4e3471 100644 --- a/src/Prima.JavaScript.Engine/Services/ScriptEngineService.cs +++ b/src/Prima.JavaScript.Engine/Services/ScriptEngineService.cs @@ -142,6 +142,7 @@ public Task StartAsync(CancellationToken cancellationToken = default) File.WriteAllText(Path.Combine(_directoriesConfig["Scripts"], "index.d.ts"), documentation); + GenerateEnumDefinitions(TypeScriptDocumentationGenerator.FoundEnums); ExecuteBootstrap(); @@ -149,6 +150,33 @@ public Task StartAsync(CancellationToken cancellationToken = default) return Task.CompletedTask; } + private void GenerateEnumDefinitions(List enumTypes) + { + foreach (var enumType in enumTypes) + { + var enumName = enumType.Name; + var enumDictionary = ConvertEnumToDictionary(enumType); + + _jsEngine.SetValue(enumName, enumDictionary); + } + } + + + public static Dictionary ConvertEnumToDictionary(Type enumType) + { + if (!enumType.IsEnum) + { + throw new ArgumentException($"Type {enumType.Name} is not an enum", nameof(enumType)); + } + + + return Enum.GetNames(enumType) + .ToDictionary( + name => name.ToSnakeCaseUpper(), + name => (object)Enum.Parse(enumType, name) + ); + } + public Task StopAsync(CancellationToken cancellationToken = default) { return Task.CompletedTask; diff --git a/src/Prima.JavaScript.Engine/Utils/Scripts/TypeScriptDocumentationGenerator.cs b/src/Prima.JavaScript.Engine/Utils/Scripts/TypeScriptDocumentationGenerator.cs index a6ad207..a7da7f5 100644 --- a/src/Prima.JavaScript.Engine/Utils/Scripts/TypeScriptDocumentationGenerator.cs +++ b/src/Prima.JavaScript.Engine/Utils/Scripts/TypeScriptDocumentationGenerator.cs @@ -18,6 +18,8 @@ public static class TypeScriptDocumentationGenerator private static readonly StringBuilder _enumsBuilder = new(); private static readonly List _interfaceTypesToGenerate = []; + public static List FoundEnums { get; } = []; + private static Func _nameResolver = name => name.ToSnakeCase(); public static string GenerateDocumentation( @@ -499,6 +501,8 @@ private static void GenerateEnumInterface(Type enumType) return; } + FoundEnums.Add(enumType); + _enumsBuilder.AppendLine(); _enumsBuilder.AppendLine($"/**"); _enumsBuilder.AppendLine($" * Generated enum for {enumType.FullName}"); @@ -509,8 +513,20 @@ private static void GenerateEnumInterface(Type enumType) foreach (var value in enumValues) { - var numericValue = (int)Enum.Parse(enumType, value); - _enumsBuilder.AppendLine($" {value} = {numericValue},"); + var numericValue = -1; + try + { + numericValue = Convert.ToInt32(Enum.Parse(enumType, value)); + } + catch (InvalidCastException) + { + // Handle the case where the enum value is not an integer + // This can happen if the enum is defined with a different underlying type + numericValue = (int)Enum.Parse(enumType, value); + } + + + _enumsBuilder.AppendLine($" {value.ToSnakeCaseUpper()} = {numericValue},"); } _enumsBuilder.AppendLine("}"); diff --git a/src/Prima.Network/Packets/Base/BaseUoNetworkPacket.cs b/src/Prima.Network/Packets/Base/BaseUoNetworkPacket.cs index c58f745..7e9de67 100644 --- a/src/Prima.Network/Packets/Base/BaseUoNetworkPacket.cs +++ b/src/Prima.Network/Packets/Base/BaseUoNetworkPacket.cs @@ -18,7 +18,7 @@ public abstract class BaseUoNetworkPacket : IUoNetworkPacket /// /// Gets the length of the packet data. /// - public int Length { get; } + public int Length { get; set; } /// /// Initializes a new instance of the BaseUoNetworkPacket class with the specified operation code. diff --git a/src/Prima.Server/Assets/Data/regions.json b/src/Prima.Server/Assets/Data/regions.json new file mode 100644 index 0000000..005253c --- /dev/null +++ b/src/Prima.Server/Assets/Data/regions.json @@ -0,0 +1,4202 @@ +[ + { + "$type": "GuardedRegion", + "Map": "Felucca", + "Name": "Moongates", + "Priority": 50, + "Area": [ + {"x1": 1330, "y1": 1991, "x2": 1343, "y2": 2004}, + {"x1": 1494, "y1": 3767, "x2": 1506, "y2": 3778}, + {"x1": 2694, "y1": 685, "x2": 2709, "y2": 701}, + {"x1": 1823, "y1": 2943, "x2": 1834, "y2": 2954}, + {"x1": 761, "y1": 741, "x2": 780, "y2": 762}, + {"x1": 638, "y1": 2062, "x2": 650, "y2": 2073}, + {"x1": 4459, "y1": 1276, "x2": 4475, "y2": 1292}, + {"x1": 3554, "y1": 2132, "x2": 3572, "y2": 2150} + ] + }, + { + "$type": "GuardedRegion", + "Map": "Felucca", + "Name": "T2A Arena", + "Priority": 50, + "Area": [ + {"x1": 2396, "y1": 197, "x2": 2407, "y2": 220}, + {"x1": 5717, "y1": 428, "x2": 5755, "y2": 456}, + {"x1": 6085, "y1": 3674, "x2": 6108, "y2": 3698}, + {"x1": 6063, "y1": 3698, "x2": 6111, "y2": 3750} + ], + "GoLocation": {"x": 6103, "y": 3724, "z": 25} + }, + { + "$type": "TownRegion", + "Entrance": {"x": 535, "y": 995, "z": 0}, + "Map": "Felucca", + "Name": "The Heartwood", + "Priority": 50, + "Area": [{"x1": 6911, "y1": 255, "x2": 7168, "y2": 512}], + "GoLocation": {"x": 6984, "y": 337, "z": 0}, + "Music": "ElfCity" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 762, "y": 1645, "z": 0}, + "Map": "Felucca", + "Name": "Sanctuary", + "Priority": 50, + "Area": [{"x1": 6144, "y1": 0, "x2": 6399, "y2": 255}], + "GoLocation": {"x": 6174, "y": 23, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1716, "y": 892, "z": 0}, + "Map": "Felucca", + "Name": "The Painted Caves", + "Priority": 50, + "Area": [{"x1": 6240, "y1": 850, "x2": 6315, "y2": 920}], + "GoLocation": {"x": 6308, "y": 892, "z": -1}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 3784, "y": 1097, "z": 14}, + "Map": "Felucca", + "Name": "The Prism of Light", + "Priority": 50, + "Area": [{"x1": 6400, "y1": 0, "x2": 6621, "y2": 255}], + "GoLocation": {"x": 6474, "y": 188, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 587, "y": 1641, "z": -1}, + "Map": "Felucca", + "Name": "Blighted Grove", + "Priority": 50, + "Area": [{"x1": 6440, "y1": 820, "x2": 6600, "y2": 970}], + "GoLocation": {"x": 6478, "y": 863, "z": 11}, + "Music": "MelisandesLair" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 5574, "y": 3024, "z": 31}, + "Map": "Felucca", + "Name": "The Palace of Paroxysmus", + "Priority": 50, + "Area": [{"x1": 6191, "y1": 311, "x2": 6561, "y2": 671}], + "GoLocation": {"x": 6222, "y": 335, "z": 60}, + "Music": "ParoxysmusLair" + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Bravehorn's drinking pool", + "Priority": 50, + "Area": [{"x1": 1302, "y1": 2331, "x2": 1340, "y2": 2353}] + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Huntsman's Forest", + "Priority": 50, + "Area": [{"x1": 1595, "y1": 550, "x2": 1715, "y2": 670}] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Cove", + "Priority": 50, + "Area": [ + {"x1": 2200, "y1": 1110, "x2": 2250, "y2": 1160}, + {"x1": 2200, "y1": 1160, "x2": 2286, "y2": 1246} + ], + "GoLocation": {"x": 2275, "y": 1210, "z": 0}, + "Music": "Cove" + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Britain", + "Priority": 50, + "Area": [ + {"x1": 1416, "y1": 1498, "z1": -10, "x2": 1740, "y2": 1777, "z2": 128}, + {"x1": 1500, "y1": 1408, "z1": 0, "x2": 1546, "y2": 1498, "z2": 128}, + {"x1": 1385, "y1": 1538, "z1": -10, "x2": 1416, "y2": 1777, "z2": 128}, + {"x1": 1416, "y1": 1777, "z1": 0, "x2": 1740, "y2": 1837, "z2": 128}, + {"x1": 1385, "y1": 1777, "z1": 0, "x2": 1416, "y2": 1907, "z2": 128}, + {"x1": 1093, "y1": 1538, "z1": 0, "x2": 1385, "y2": 1907, "z2": 128} + ], + "GoLocation": {"x": 1495, "y": 1629, "z": 10}, + "Music": "Britain1" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 1492, "y1": 1602, "z1": 0, "x2": 1500, "y2": 1621, "z2": 128}, + {"x1": 1500, "y1": 1610, "z1": 0, "x2": 1508, "y2": 1626, "z2": 128}, + {"x1": 1576, "y1": 1584, "z1": 0, "x2": 1600, "y2": 1600, "z2": 128}, + {"x1": 1456, "y1": 1512, "z1": 0, "x2": 1472, "y2": 1528, "z2": 128}, + {"x1": 1472, "y1": 1512, "z1": 0, "x2": 1480, "y2": 1520, "z2": 128}, + {"x1": 1486, "y1": 1684, "z1": 0, "x2": 1494, "y2": 1692, "z2": 128}, + {"x1": 1494, "y1": 1676, "z1": 0, "x2": 1502, "y2": 1700, "z2": 128}, + {"x1": 1424, "y1": 1712, "z1": 0, "x2": 1432, "y2": 1736, "z2": 128}, + {"x1": 1432, "y1": 1712, "z1": 0, "x2": 1440, "y2": 1724, "z2": 128}, + {"x1": 1608, "y1": 1584, "z1": 0, "x2": 1632, "y2": 1592, "z2": 128}, + {"x1": 1616, "y1": 1576, "z1": 0, "x2": 1624, "y2": 1584, "z2": 128}, + {"x1": 1544, "y1": 1760, "z1": 0, "x2": 1560, "y2": 1776, "z2": 128}, + {"x1": 1560, "y1": 1760, "z1": 0, "x2": 1568, "y2": 1768, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Wheatfield in Britain 1", + "Priority": 50, + "Area": [{"x1": 1120, "y1": 1776, "x2": 1152, "y2": 1808}], + "GoLocation": {"x": 1135, "y": 1791, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Wheatfield in Britain 2", + "Priority": 50, + "Area": [{"x1": 1184, "y1": 1808, "x2": 1216, "y2": 1840}], + "GoLocation": {"x": 1199, "y": 1823, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Wheatfield in Britain 3", + "Priority": 50, + "Area": [{"x1": 1216, "y1": 1872, "x2": 1248, "y2": 1904}], + "GoLocation": {"x": 1231, "y": 1887, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Carrot Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1208, "y1": 1712, "x2": 1224, "y2": 1736}], + "GoLocation": {"x": 1215, "y": 1723, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "An Onion Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1224, "y1": 1712, "x2": 1240, "y2": 1736}], + "GoLocation": {"x": 1231, "y": 1723, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Cabbage Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1176, "y1": 1672, "x2": 1192, "y2": 1695}], + "GoLocation": {"x": 1183, "y": 1683, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Turnip Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1192, "y1": 1672, "x2": 1208, "y2": 1696}], + "GoLocation": {"x": 1199, "y": 1683, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Wheatfield in Britain 4", + "Priority": 50, + "Area": [{"x1": 1104, "y1": 1608, "x2": 1136, "y2": 1640}], + "GoLocation": {"x": 1120, "y": 1624, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Wheatfield in Britain 5", + "Priority": 50, + "Area": [{"x1": 1136, "y1": 1560, "x2": 1168, "y2": 1592}], + "GoLocation": {"x": 1152, "y": 1576, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Turnip Field in Britain 2", + "Priority": 50, + "Area": [{"x1": 1208, "y1": 1592, "x2": 1224, "y2": 1616}], + "GoLocation": {"x": 1216, "y": 1604, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Britain", "Map": "Felucca" }, + "Name": "A Carrot Field in Britain 2", + "Priority": 50, + "Area": [{"x1": 1224, "y1": 1592, "x2": 1240, "y2": 1616}], + "GoLocation": {"x": 1232, "y": 1604, "z": 0} + }, + { + "$type": "NoHousingRegion", + "Map": "Felucca", + "Name": "Britain Graveyard", + "Priority": 50, + "Area": [{"x1": 1333, "y1": 1441, "x2": 1417, "y2": 1523}], + "GoLocation": {"x": 1384, "y": 1492, "z": 10} + }, + { + "$type": "NoHousingRegion", + "Map": "Felucca", + "Name": "Jhelom Islands", + "Priority": 50, + "Area": [ + {"x1": 1111, "y1": 3567, "x2": 1144, "y2": 3588}, + {"x1": 1078, "y1": 3588, "x2": 1202, "y2": 3709}, + {"x1": 1224, "y1": 3592, "x2": 1533, "y2": 4065} + ], + "GoLocation": {"x": 1383, "y": 3815, "z": 0}, + "Music": "Jhelom" + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Parent": { "Name": "Jhelom Islands", "Map": "Felucca" }, + "Name": "Jhelom", + "Priority": 50, + "Area": [ + {"x1": 1303, "y1": 3670, "z1": -20, "x2": 1492, "y2": 3895, "z2": 128}, + {"x1": 1338, "y1": 3895, "z1": -20, "x2": 1412, "y2": 3923, "z2": 128}, + {"x1": 1383, "y1": 3951, "z1": -20, "x2": 1492, "y2": 4045, "z2": 128} + ], + "GoLocation": {"x": 1383, "y": 3815, "z": 0} + }, + { + "$type": "NoHousingRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Jhelom", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 1352, "y1": 3800, "z1": -20, "x2": 1368, "y2": 3832, "z2": 128}, + {"x1": 1368, "y1": 3808, "z1": -20, "x2": 1376, "y2": 3824, "z2": 128}, + {"x1": 1432, "y1": 3768, "z1": -20, "x2": 1464, "y2": 3776, "z2": 128}, + {"x1": 1440, "y1": 3776, "z1": -20, "x2": 1464, "y2": 3784, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Minoc", + "Priority": 50, + "Area": [ + {"x1": 2411, "y1": 366, "x2": 2546, "y2": 607}, + {"x1": 2548, "y1": 495, "x2": 2620, "y2": 550}, + {"x1": 2564, "y1": 585, "x2": 2567, "y2": 627}, + {"x1": 2567, "y1": 585, "x2": 2628, "y2": 646}, + {"x1": 2499, "y1": 627, "x2": 2567, "y2": 690} + ], + "GoLocation": {"x": 2466, "y": 544, "z": 0}, + "Music": "Minoc" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Minoc", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 2457, "y1": 397, "x2": 2497, "y2": 405}, + {"x1": 2465, "y1": 405, "x2": 2473, "y2": 413}, + {"x1": 2481, "y1": 405, "x2": 2489, "y2": 413} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Ocllo", + "Priority": 50, + "Area": [ + {"x1": 3587, "y1": 2456, "x2": 3706, "y2": 2555}, + {"x1": 3706, "y1": 2460, "x2": 3708, "y2": 2555}, + {"x1": 3587, "y1": 2555, "x2": 3693, "y2": 2628}, + {"x1": 3590, "y1": 2628, "x2": 3693, "y2": 2686}, + {"x1": 3693, "y1": 2555, "x2": 3754, "y2": 2699}, + {"x1": 3754, "y1": 2558, "x2": 3761, "y2": 2699}, + {"x1": 3761, "y1": 2555, "z1": -128, "x2": 3768, "y2": 2699, "z2": 50}, + {"x1": 3695, "y1": 2699, "x2": 3761, "y2": 2712} + ], + "GoLocation": {"x": 3650, "y": 2519, "z": 0}, + "Music": "Ocllo" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Ocllo", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 3664, "y1": 2608, "x2": 3680, "y2": 2624}, + {"x1": 3664, "y1": 2640, "x2": 3672, "y2": 2656}, + {"x1": 3672, "y1": 2648, "x2": 3680, "y2": 2656} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Trinsic", + "Priority": 50, + "Area": [ + {"x1": 1856, "y1": 2636, "x2": 1931, "y2": 2664}, + {"x1": 1816, "y1": 2664, "x2": 2099, "y2": 2895}, + {"x1": 2099, "y1": 2782, "x2": 2117, "y2": 2807}, + {"x1": 1970, "y1": 2895, "x2": 2017, "y2": 2927}, + {"x1": 1796, "y1": 2696, "z1": 0, "x2": 1816, "y2": 2763, "z2": 128}, + {"x1": 1800, "y1": 2796, "z1": 0, "x2": 1816, "y2": 2848, "z2": 128} + ], + "GoLocation": {"x": 1867, "y": 2780, "z": 0}, + "Music": "Trinsic" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Trinsic", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 1834, "y1": 2728, "x2": 1856, "y2": 2744}, + {"x1": 2024, "y1": 2784, "x2": 2040, "y2": 2804}, + {"x1": 2026, "y1": 2804, "x2": 2040, "y2": 2806}, + {"x1": 2024, "y1": 2806, "x2": 2040, "y2": 2813}, + {"x1": 1923, "y1": 2786, "x2": 1935, "y2": 2808}, + {"x1": 1935, "y1": 2786, "x2": 1942, "y2": 2800} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Vesper", + "Priority": 50, + "Area": [ + {"x1": 2893, "y1": 598, "x2": 3014, "y2": 648}, + {"x1": 2816, "y1": 648, "x2": 3065, "y2": 1013}, + {"x1": 2734, "y1": 944, "x2": 2816, "y2": 948}, + {"x1": 2728, "y1": 948, "x2": 2816, "y2": 1001} + ], + "GoLocation": {"x": 2899, "y": 676, "z": 0}, + "Music": "Vesper" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Vesper", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 2952, "y1": 864, "x2": 2968, "y2": 896}, + {"x1": 2968, "y1": 872, "x2": 2976, "y2": 888}, + {"x1": 2776, "y1": 952, "x2": 2792, "y2": 984}, + {"x1": 2768, "y1": 960, "x2": 2776, "y2": 976}, + {"x1": 2892, "y1": 901, "x2": 2908, "y2": 920}, + {"x1": 2908, "y1": 904, "x2": 2916, "y2": 912} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Yew", + "Priority": 50, + "Area": [ + {"x1": 92, "y1": 656, "z1": 0, "x2": 441, "y2": 881, "z2": 39}, + {"x1": 441, "y1": 746, "z1": 0, "x2": 657, "y2": 881, "z2": 39}, + {"x1": 258, "y1": 881, "z1": 0, "x2": 657, "y2": 1261, "z2": 39}, + {"x1": 657, "y1": 922, "z1": 0, "x2": 699, "y2": 1229, "z2": 39}, + {"x1": 657, "y1": 806, "z1": 0, "x2": 674, "y2": 834, "z2": 39}, + {"x1": 718, "y1": 874, "z1": 0, "x2": 756, "y2": 896, "z2": 39} + ], + "GoLocation": {"x": 546, "y": 992, "z": 0}, + "Music": "Yew" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Yew", "Map": "Felucca" }, + "Priority": 50, + "Area": [{"x1": 600, "y1": 808, "z1": 0, "x2": 624, "y2": 832, "z2": 39}] + }, + { + "$type": "TownRegion", + "RuneName": "A Field of Sheep in Yew", + "Map": "Felucca", + "Parent": { "Name": "Yew", "Map": "Felucca" }, + "Name": "A Field of Sheep in Yew 1", + "Priority": 50, + "Area": [{"x1": 664, "y1": 928, "x2": 686, "y2": 950}], + "GoLocation": {"x": 675, "y": 939, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Field of Sheep in Yew", + "Map": "Felucca", + "Parent": { "Name": "Yew", "Map": "Felucca" }, + "Name": "A Field of Sheep in Yew 2", + "Priority": 50, + "Area": [{"x1": 664, "y1": 1168, "x2": 686, "y2": 1190}], + "GoLocation": {"x": 675, "y": 1179, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Farm in Yew", + "Map": "Felucca", + "Parent": { "Name": "Yew", "Map": "Felucca" }, + "Name": "A Farm in Yew", + "Priority": 50, + "Area": [{"x1": 560, "y1": 1088, "x2": 582, "y2": 1110}], + "GoLocation": {"x": 571, "y": 1099, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Wheatfield in Yew", + "Map": "Felucca", + "Parent": { "Name": "Yew", "Map": "Felucca" }, + "Name": "A Wheatfield in Yew 1", + "Priority": 50, + "Area": [{"x1": 560, "y1": 1232, "x2": 576, "y2": 1248}], + "GoLocation": {"x": 567, "y": 1239, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Wheatfield in Yew", + "Map": "Felucca", + "Parent": { "Name": "Yew", "Map": "Felucca" }, + "Name": "A Wheatfield in Yew 2", + "Priority": 50, + "Area": [{"x1": 368, "y1": 1176, "x2": 382, "y2": 1208}], + "GoLocation": {"x": 375, "y": 1191, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Wind", + "Priority": 50, + "Area": [ + {"x1": 5294, "y1": 19, "x2": 5366, "y2": 139}, + {"x1": 5132, "y1": 58, "x2": 5213, "y2": 126}, + {"x1": 5197, "y1": 126, "x2": 5252, "y2": 204}, + {"x1": 5132, "y1": 3, "x2": 5202, "y2": 58}, + {"x1": 5252, "y1": 112, "x2": 5294, "y2": 170}, + {"x1": 5213, "y1": 98, "x2": 5252, "y2": 126}, + {"x1": 5279, "y1": 57, "x2": 5294, "y2": 112}, + {"x1": 5252, "y1": 170, "x2": 5284, "y2": 178}, + {"x1": 5286, "y1": 25, "x2": 5294, "y2": 57}, + {"x1": 5252, "y1": 178, "x2": 5272, "y2": 183}, + {"x1": 5252, "y1": 183, "x2": 5262, "y2": 193} + ], + "GoLocation": {"x": 5223, "y": 190, "z": 5}, + "Music": "Wind" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Wind", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 5159, "y1": 15, "x2": 5184, "y2": 24}, + {"x1": 5159, "y1": 24, "x2": 5168, "y2": 40}, + {"x1": 5175, "y1": 24, "x2": 5184, "y2": 32}, + {"x1": 5212, "y1": 159, "x2": 5221, "y2": 183}, + {"x1": 5221, "y1": 171, "x2": 5228, "y2": 183}, + {"x1": 5206, "y1": 164, "x2": 5212, "y2": 179}, + {"x1": 5303, "y1": 28, "x2": 5319, "y2": 42} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Serpent's Hold", + "Priority": 50, + "Area": [ + {"x1": 2868, "y1": 3324, "z1": 0, "x2": 3073, "y2": 3519, "z2": 128} + ], + "GoLocation": {"x": 3010, "y": 3371, "z": 15}, + "Music": "Serpents" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Serpent's Hold", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 2960, "y1": 3400, "z1": 0, "x2": 2976, "y2": 3416, "z2": 128}, + {"x1": 2968, "y1": 3416, "z1": 0, "x2": 2976, "y2": 3432, "z2": 128}, + {"x1": 3008, "y1": 3450, "z1": 0, "x2": 3022, "y2": 3464, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Skara Brae", + "Priority": 50, + "Area": [{"x1": 538, "y1": 2107, "x2": 688, "y2": 2297}], + "GoLocation": {"x": 632, "y": 2233, "z": 0}, + "Music": "Skarabra" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Skara Brae", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 600, "y1": 2232, "x2": 616, "y2": 2256}, + {"x1": 592, "y1": 2240, "x2": 600, "y2": 2256}, + {"x1": 616, "y1": 2240, "x2": 624, "y2": 2256}, + {"x1": 552, "y1": 2168, "x2": 568, "y2": 2192}, + {"x1": 568, "y1": 2168, "x2": 576, "y2": 2176} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Nujel'm", + "Priority": 50, + "Area": [ + {"x1": 3475, "y1": 1000, "z1": 0, "x2": 3835, "y2": 1435, "z2": 128} + ], + "GoLocation": {"x": 3732, "y": 1279, "z": 0}, + "Music": "Nujelm" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Nujel'm", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 3736, "y1": 1184, "z1": 0, "x2": 3752, "y2": 1207, "z2": 128}, + {"x1": 3728, "y1": 1192, "z1": 0, "x2": 3736, "y2": 1207, "z2": 128}, + {"x1": 3728, "y1": 1288, "z1": 0, "x2": 3751, "y2": 1303, "z2": 128}, + {"x1": 3728, "y1": 1303, "z1": 0, "x2": 3744, "y2": 1312, "z2": 128}, + {"x1": 3728, "y1": 1312, "z1": 0, "x2": 3740, "y2": 1320, "z2": 128}, + {"x1": 3728, "y1": 1320, "z1": 0, "x2": 3744, "y2": 1343, "z2": 128}, + {"x1": 3744, "y1": 1328, "z1": 0, "x2": 3751, "y2": 1343, "z2": 128}, + {"x1": 3760, "y1": 1216, "z1": 0, "x2": 3772, "y2": 1240, "z2": 128}, + {"x1": 3772, "y1": 1220, "z1": 0, "x2": 3776, "y2": 1236, "z2": 128}, + {"x1": 3776, "y1": 1224, "z1": 0, "x2": 3784, "y2": 1232, "z2": 128}, + {"x1": 3728, "y1": 1248, "z1": 0, "x2": 3744, "y2": 1272, "z2": 128}, + {"x1": 3744, "y1": 1264, "z1": 0, "x2": 3752, "y2": 1272, "z2": 128}, + {"x1": 3744, "y1": 1248, "z1": 0, "x2": 3752, "y2": 1256, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Moonglow", + "Priority": 50, + "Area": [ + {"x1": 4535, "y1": 844, "z1": 0, "x2": 4555, "y2": 847, "z2": 128}, + {"x1": 4530, "y1": 847, "z1": 0, "x2": 4561, "y2": 908, "z2": 128}, + {"x1": 4521, "y1": 914, "z1": 0, "x2": 4577, "y2": 963, "z2": 128}, + {"x1": 4278, "y1": 915, "z1": 0, "x2": 4332, "y2": 934, "z2": 128}, + {"x1": 4283, "y1": 944, "z1": 0, "x2": 4336, "y2": 1017, "z2": 128}, + {"x1": 4377, "y1": 1015, "z1": -10, "x2": 4436, "y2": 1052, "z2": 128}, + {"x1": 4367, "y1": 1050, "z1": 0, "x2": 4509, "y2": 1195, "z2": 128}, + {"x1": 4539, "y1": 1036, "z1": 0, "x2": 4566, "y2": 1054, "z2": 128}, + {"x1": 4517, "y1": 1053, "z1": 0, "x2": 4540, "y2": 1075, "z2": 128}, + {"x1": 4389, "y1": 1198, "z1": 0, "x2": 4436, "y2": 1237, "z2": 128}, + {"x1": 4466, "y1": 1211, "z1": 0, "x2": 4498, "y2": 1236, "z2": 128}, + {"x1": 4700, "y1": 1108, "z1": 0, "x2": 4717, "y2": 1126, "z2": 128}, + {"x1": 4656, "y1": 1127, "z1": 0, "x2": 4682, "y2": 1140, "z2": 128}, + {"x1": 4678, "y1": 1162, "z1": 0, "x2": 4703, "y2": 1187, "z2": 128}, + {"x1": 4613, "y1": 1196, "z1": 0, "x2": 4636, "y2": 1218, "z2": 128}, + {"x1": 4646, "y1": 1212, "z1": 0, "x2": 4660, "y2": 1229, "z2": 128}, + {"x1": 4677, "y1": 1214, "z1": 0, "x2": 4703, "y2": 1236, "z2": 128}, + {"x1": 4622, "y1": 1316, "z1": 0, "x2": 4644, "y2": 1340, "z2": 128}, + {"x1": 4487, "y1": 1353, "z1": 0, "x2": 4546, "y2": 1374, "z2": 128}, + {"x1": 4477, "y1": 1374, "z1": 0, "x2": 4546, "y2": 1409, "z2": 128}, + {"x1": 4659, "y1": 1387, "z1": 0, "x2": 4699, "y2": 1427, "z2": 128}, + {"x1": 4549, "y1": 1482, "z1": 0, "x2": 4578, "y2": 1509, "z2": 128}, + {"x1": 4405, "y1": 1451, "z1": 0, "x2": 4428, "y2": 1474, "z2": 128}, + {"x1": 4483, "y1": 1468, "z1": 0, "x2": 4504, "y2": 1481, "z2": 128} + ], + "GoLocation": {"x": 4442, "y": 1172, "z": 0}, + "Music": "Moonglow" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Moonglow", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 4384, "y1": 1152, "z1": 0, "x2": 4392, "y2": 1176, "z2": 128}, + {"x1": 4392, "y1": 1160, "z1": 0, "x2": 4408, "y2": 1168, "z2": 128}, + {"x1": 4400, "y1": 1152, "z1": 0, "x2": 4408, "y2": 1160, "z2": 128}, + {"x1": 4480, "y1": 1056, "z1": 0, "x2": 4488, "y2": 1072, "z2": 128}, + {"x1": 4488, "y1": 1060, "z1": 0, "x2": 4492, "y2": 1068, "z2": 128}, + {"x1": 4476, "y1": 1060, "z1": 0, "x2": 4480, "y2": 1068, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Magincia", + "Priority": 50, + "Area": [ + {"x1": 3653, "y1": 2046, "x2": 3680, "y2": 2094}, + {"x1": 3752, "y1": 2046, "x2": 3804, "y2": 2094}, + {"x1": 3680, "y1": 2045, "x2": 3752, "y2": 2094}, + {"x1": 3652, "y1": 2094, "x2": 3812, "y2": 2274}, + {"x1": 3649, "y1": 2256, "x2": 3703, "y2": 2303} + ], + "GoLocation": {"x": 3714, "y": 2220, "z": 20}, + "Music": "Magincia" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Magincia", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 3680, "y1": 2152, "x2": 3704, "y2": 2160}, + {"x1": 3720, "y1": 2216, "x2": 3736, "y2": 2232} + ] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Buccaneer's Den", + "Priority": 50, + "GuardsDisabled": true, + "Area": [ + {"x1": 2612, "y1": 2057, "x2": 2776, "y2": 2267}, + {"x1": 2604, "y1": 2065, "z1": 0, "x2": 2612, "y2": 2254, "z2": 128} + ], + "GoLocation": {"x": 2706, "y": 2163, "z": 0}, + "Music": "Bucsden" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Buccaneer's Den", "Map": "Felucca" }, + "Priority": 50, + "Area": [ + {"x1": 2712, "y1": 2080, "x2": 2728, "y2": 2096}, + {"x1": 2712, "y1": 2096, "x2": 2720, "y2": 2104}, + {"x1": 2664, "y1": 2232, "x2": 2688, "y2": 2240}, + {"x1": 2672, "y1": 2240, "x2": 2688, "y2": 2248} + ] + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2499, "y": 916, "z": 0}, + "Map": "Felucca", + "Name": "Covetous", + "Priority": 50, + "Area": [ + {"x1": 5376, "y1": 1793, "x2": 5577, "y2": 2048}, + {"x1": 5576, "y1": 1791, "x2": 5633, "y2": 2048} + ], + "GoLocation": {"x": 5456, "y": 1862, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 4111, "y": 429, "z": 0}, + "Map": "Felucca", + "Name": "Deceit", + "Priority": 50, + "Area": [{"x1": 5122, "y1": 518, "x2": 5370, "y2": 770}], + "GoLocation": {"x": 5187, "y": 635, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1296, "y": 1082, "z": 0}, + "Map": "Felucca", + "Name": "Despise", + "Priority": 50, + "Area": [{"x1": 5377, "y1": 516, "x2": 5631, "y2": 1022}], + "GoLocation": {"x": 5501, "y": 570, "z": 59}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1176, "y": 2635, "z": 0}, + "Map": "Felucca", + "Name": "Destard", + "Priority": 50, + "Area": [{"x1": 5120, "y1": 770, "x2": 5371, "y2": 1028}], + "GoLocation": {"x": 5243, "y": 1004, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 4722, "y": 3814, "z": 0}, + "Map": "Felucca", + "Name": "Hythloth", + "Priority": 50, + "Area": [{"x1": 5898, "y1": 2, "x2": 6136, "y2": 246}], + "GoLocation": {"x": 5905, "y": 22, "z": 44}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 5882, "y": 3819, "z": 0}, + "Map": "Felucca", + "Name": "Khaldun", + "Priority": 50, + "Area": [{"x1": 5381, "y1": 1284, "x2": 5628, "y2": 1509}], + "GoLocation": {"x": 5571, "y": 1302, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "JailRegion", + "Map": "Felucca", + "Name": "Jail", + "Priority": 50, + "Area": [{"x1": 5271, "y1": 1159, "x2": 5312, "y2": 1192}], + "GoLocation": {"x": 5275, "y": 1163, "z": 0} + }, + { + "$type": "GreenAcresRegion", + "Map": "Felucca", + "Name": "Green Acres", + "Priority": 1, + "Area": [{"x1": 5376, "y1": 512, "x2": 6143, "y2": 1279}], + "GoLocation": {"x": 5445, "y": 1153, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 512, "y": 1559, "z": 0}, + "Map": "Felucca", + "Name": "Shame", + "Priority": 50, + "Area": [ + {"x1": 5377, "y1": 2, "x2": 5634, "y2": 262}, + {"x1": 5635, "y1": 2, "x2": 5895, "y2": 126} + ], + "GoLocation": {"x": 5395, "y": 126, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2042, "y": 226, "z": 0}, + "Map": "Felucca", + "Name": "Wrong", + "Priority": 50, + "Area": [{"x1": 5633, "y1": 511, "x2": 5886, "y2": 1021}], + "GoLocation": {"x": 5825, "y": 599, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Cave 1", + "Priority": 50, + "Area": [ + {"x1": 2373, "y1": 900, "x2": 2395, "y2": 928}, + {"x1": 2395, "y1": 903, "x2": 2409, "y2": 919}, + {"x1": 2373, "y1": 928, "x2": 2383, "y2": 937}, + {"x1": 2359, "y1": 927, "x2": 2373, "y2": 945} + ], + "GoLocation": {"x": 2367, "y": 942, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Cave 2", + "Priority": 50, + "Area": [{"x1": 1887, "y1": 354, "x2": 1920, "y2": 377}], + "GoLocation": {"x": 1903, "y": 365, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Cave 3", + "Priority": 50, + "Area": [{"x1": 1925, "y1": 307, "x2": 1943, "y2": 325}], + "GoLocation": {"x": 1934, "y": 316, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Cave 4", + "Priority": 50, + "Area": [{"x1": 2323, "y1": 809, "x2": 2368, "y2": 851}], + "GoLocation": {"x": 2345, "y": 830, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Britain Mine 1", + "Priority": 50, + "Area": [{"x1": 1436, "y1": 1215, "x2": 1465, "y2": 1252}], + "GoLocation": {"x": 1443, "y": 1228, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Britain Mine 2", + "Priority": 50, + "Area": [{"x1": 1611, "y1": 1175, "x2": 1662, "y2": 1204}], + "GoLocation": {"x": 1629, "y": 1189, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Minoc Cave 1", + "Priority": 50, + "Area": [{"x1": 2406, "y1": 168, "x2": 2428, "y2": 184}], + "GoLocation": {"x": 2426, "y": 177, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Minoc Cave 2", + "Priority": 50, + "Area": [{"x1": 2418, "y1": 81, "x2": 2442, "y2": 113}], + "GoLocation": {"x": 2440, "y": 94, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Minoc Cave 3", + "Priority": 50, + "Area": [{"x1": 2447, "y1": 39, "x2": 2489, "y2": 67}], + "GoLocation": {"x": 2471, "y": 64, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Minoc Mine", + "Priority": 50, + "Area": [ + {"x1": 2556, "y1": 501, "x2": 2562, "y2": 504}, + {"x1": 2556, "y1": 474, "x2": 2582, "y2": 501} + ], + "GoLocation": {"x": 2558, "y": 499, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Avatar Isle Cave", + "Priority": 50, + "Area": [{"x1": 4594, "y1": 3807, "x2": 4617, "y2": 3824}], + "GoLocation": {"x": 4605, "y": 3815, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Ice Isle Cave 1", + "Priority": 50, + "Area": [{"x1": 4018, "y1": 421, "x2": 4067, "y2": 470}], + "GoLocation": {"x": 4054, "y": 440, "z": 3} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Ice Isle Cave 2", + "Priority": 50, + "Area": [ + {"x1": 4002, "y1": 310, "x2": 4045, "y2": 351}, + {"x1": 4005, "y1": 298, "x2": 4027, "y2": 310} + ], + "GoLocation": {"x": 4030, "y": 325, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "North Territory Cave", + "Priority": 50, + "Area": [{"x1": 1973, "y1": 251, "x2": 1994, "y2": 274}], + "GoLocation": {"x": 1984, "y": 262, "z": 8} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Yew Cave", + "Priority": 50, + "Area": [{"x1": 766, "y1": 1683, "x2": 778, "y2": 1697}], + "GoLocation": {"x": 773, "y": 1684, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "North Territory Mine 1", + "Priority": 50, + "Area": [{"x1": 1713, "y1": 1055, "x2": 1753, "y2": 1080}], + "GoLocation": {"x": 1723, "y": 1065, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "North Territory Mine 2", + "Priority": 50, + "Area": [{"x1": 1604, "y1": 958, "x2": 1650, "y2": 1009}], + "GoLocation": {"x": 1638, "y": 974, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Mt Kendall", + "Priority": 50, + "Area": [ + {"x1": 2552, "y1": 448, "x2": 2623, "y2": 507}, + {"x1": 2547, "y1": 380, "x2": 2590, "y2": 448}, + {"x1": 2590, "y1": 413, "x2": 2623, "y2": 448} + ], + "GoLocation": {"x": 2593, "y": 477, "z": 60} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "Covetous Mine", + "Priority": 50, + "Area": [ + {"x1": 2429, "y1": 866, "x2": 2448, "y2": 911}, + {"x1": 2448, "y1": 879, "x2": 2475, "y2": 907}, + {"x1": 2456, "y1": 907, "x2": 2475, "y2": 941} + ], + "GoLocation": {"x": 2445, "y": 880, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 5426, "y": 3120, "z": 0}, + "Map": "Felucca", + "Name": "Terathan Keep", + "Priority": 50, + "Area": [ + {"x1": 5404, "y1": 3099, "x2": 5481, "y2": 3167}, + {"x1": 5120, "y1": 1530, "x2": 5374, "y2": 1788} + ], + "GoLocation": {"x": 5451, "y": 3143, "z": -60}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2922, "y": 3402, "z": 0}, + "Map": "Felucca", + "Name": "Fire", + "Priority": 50, + "Area": [{"x1": 5635, "y1": 1285, "x2": 5880, "y2": 1520}], + "GoLocation": {"x": 5760, "y": 2908, "z": 15}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1996, "y": 80, "z": 0}, + "Map": "Felucca", + "Name": "Ice", + "Priority": 50, + "Area": [ + {"x1": 5668, "y1": 130, "x2": 5888, "y2": 268}, + {"x1": 5800, "y1": 319, "x2": 5863, "y2": 384}, + {"x1": 5654, "y1": 300, "x2": 5708, "y2": 340} + ], + "GoLocation": {"x": 5210, "y": 2322, "z": 30}, + "Music": "Dungeon9" + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Delucia", + "Priority": 50, + "Area": [ + {"x1": 5123, "y1": 3942, "x2": 5315, "y2": 4064}, + {"x1": 5147, "y1": 4064, "x2": 5272, "y2": 4084}, + {"x1": 5235, "y1": 3930, "x2": 5315, "y2": 3942} + ], + "GoLocation": {"x": 5228, "y": 3978, "z": 37} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Delucia", "Map": "Felucca" }, + "Priority": 50, + "Area": [{"x1": 5194, "y1": 4053, "x2": 5204, "y2": 4073}] + }, + { + "$type": "TownRegion", + "Map": "Felucca", + "Name": "Papua", + "Priority": 50, + "Area": [ + {"x1": 5639, "y1": 3095, "x2": 5831, "y2": 3318}, + {"x1": 5831, "y1": 3237, "x2": 5851, "y2": 3267} + ], + "GoLocation": {"x": 5769, "y": 3176, "z": 0} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Felucca", + "Parent": { "Name": "Papua", "Map": "Felucca" }, + "Priority": 50, + "Area": [{"x1": 5757, "y1": 3150, "x2": 5781, "y2": 3174}] + }, + { + "$type": "NoHousingRegion", + "Map": "Felucca", + "Name": "Wrong Entrance", + "Priority": 50, + "Area": [{"x1": 1939, "y1": 215, "x2": 2073, "y2": 352}], + "GoLocation": {"x": 2043, "y": 236, "z": 13}, + "Music": "Mountn_a" + }, + { + "$type": "NoHousingRegion", + "Map": "Felucca", + "Name": "Covetous Entrance", + "Priority": 50, + "Area": [{"x1": 2433, "y1": 846, "x2": 2561, "y2": 974}], + "GoLocation": {"x": 2499, "y": 918, "z": 0}, + "Music": "Vesper" + }, + { + "$type": "NoHousingRegion", + "Map": "Felucca", + "Name": "Despise Entrance", + "Priority": 50, + "Area": [{"x1": 1289, "y1": 1064, "x2": 1321, "y2": 1101}], + "GoLocation": {"x": 1298, "y": 1081, "z": 0} + }, + { + "$type": "NoHousingRegion", + "Map": "Felucca", + "Name": "Despise Passage", + "Priority": 50, + "Area": [ + {"x1": 1338, "y1": 1060, "x2": 1389, "y2": 1122}, + {"x1": 1354, "y1": 1122, "x2": 1396, "y2": 1243}, + {"x1": 1349, "y1": 1122, "x2": 1354, "y2": 1224} + ], + "GoLocation": {"x": 1380, "y": 1114, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1492, "y": 1641, "z": 0}, + "Map": "Felucca", + "Name": "Misc Dungeons", + "Priority": 50, + "Area": [{"x1": 5886, "y1": 1281, "x2": 6143, "y2": 1535}], + "GoLocation": {"x": 6032, "y": 1499, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1014, "y": 1434, "z": 0}, + "Map": "Felucca", + "Name": "Orc Cave", + "Priority": 50, + "Area": [ + {"x1": 5281, "y1": 1283, "x2": 5373, "y2": 1386}, + {"x1": 5267, "y1": 1955, "x2": 5364, "y2": 2046}, + {"x1": 5127, "y1": 1941, "x2": 5164, "y2": 2024} + ], + "GoLocation": {"x": 5137, "y": 2015, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "BaseRegion", + "RuneName": "A Cotton Field in Moonglow", + "Map": "Felucca", + "Name": "A Cotton Field in Moonglow", + "Priority": 50, + "Area": [{"x1": 4557, "y1": 1471, "x2": 4577, "y2": 1481}], + "GoLocation": {"x": 4567, "y": 1475, "z": 0} + }, + { + "$type": "BaseRegion", + "RuneName": "A Wheatfield in Skara Brae", + "Map": "Felucca", + "Name": "A Wheatfield in Skara Brae 1", + "Priority": 50, + "Area": [{"x1": 796, "y1": 2152, "x2": 832, "y2": 2176}], + "GoLocation": {"x": 813, "y": 2163, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "A Carrot Field in Skara Brae", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2251, "x2": 832, "y2": 2259}], + "GoLocation": {"x": 823, "y": 2254, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "An Onion Field in Skara Brae", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2261, "x2": 832, "y2": 2269}], + "GoLocation": {"x": 823, "y": 2264, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "A Cabbage Field in Skara Brae 1", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2271, "x2": 832, "y2": 2279}], + "GoLocation": {"x": 823, "y": 2264, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "A Cabbage Field in Skara Brae 2", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2281, "x2": 832, "y2": 2289}], + "GoLocation": {"x": 823, "y": 2274, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "A Wheatfield in Skara Brae 2", + "Priority": 50, + "Area": [{"x1": 835, "y1": 2344, "x2": 851, "y2": 2360}], + "GoLocation": {"x": 843, "y": 2352, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Felucca", + "Name": "A Cotton Field in Skara Brae", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2344, "x2": 832, "y2": 2368}], + "GoLocation": {"x": 823, "y": 2355, "z": 0} + }, + { + "$type": "GuardedRegion", + "Map": "Trammel", + "Name": "Moongates", + "Priority": 50, + "Area": [ + {"x1": 1330, "y1": 1991, "x2": 1343, "y2": 2004}, + {"x1": 1494, "y1": 3767, "x2": 1506, "y2": 3778}, + {"x1": 2694, "y1": 685, "x2": 2709, "y2": 701}, + {"x1": 1823, "y1": 2943, "x2": 1834, "y2": 2954}, + {"x1": 761, "y1": 741, "x2": 780, "y2": 762}, + {"x1": 638, "y1": 2062, "x2": 650, "y2": 2073}, + {"x1": 4459, "y1": 1276, "x2": 4475, "y2": 1292}, + {"x1": 3554, "y1": 2132, "x2": 3572, "y2": 2150} + ] + }, + { + "$type": "TownRegion", + "Entrance": {"x": 535, "y": 995, "z": 0}, + "Map": "Trammel", + "Name": "The Heartwood", + "Priority": 50, + "Area": [{"x1": 6911, "y1": 255, "x2": 7168, "y2": 512}], + "GoLocation": {"x": 6984, "y": 337, "z": 0}, + "Music": "ElfCity" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 762, "y": 1645, "z": 0}, + "Map": "Trammel", + "Name": "Sanctuary", + "Priority": 50, + "Area": [{"x1": 6144, "y1": 0, "x2": 6399, "y2": 255}], + "GoLocation": {"x": 6174, "y": 23, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1716, "y": 892, "z": 0}, + "Map": "Trammel", + "Name": "The Painted Caves", + "Priority": 50, + "Area": [{"x1": 6240, "y1": 850, "x2": 6315, "y2": 920}], + "GoLocation": {"x": 6308, "y": 892, "z": -1}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 3784, "y": 1097, "z": 14}, + "Map": "Trammel", + "Name": "The Prism of Light", + "Priority": 50, + "Area": [{"x1": 6400, "y1": 0, "x2": 6621, "y2": 255}], + "GoLocation": {"x": 6474, "y": 188, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 587, "y": 1641, "z": -1}, + "Map": "Trammel", + "Name": "Blighted Grove", + "Priority": 50, + "Area": [{"x1": 6440, "y1": 820, "x2": 6600, "y2": 970}], + "GoLocation": {"x": 6478, "y": 863, "z": 11}, + "Music": "MelisandesLair" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 5574, "y": 3024, "z": 31}, + "Map": "Trammel", + "Name": "The Palace of Paroxysmus", + "Priority": 50, + "Area": [{"x1": 6191, "y1": 311, "x2": 6561, "y2": 671}], + "GoLocation": {"x": 6222, "y": 335, "z": 60}, + "Music": "ParoxysmusLair" + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Bravehorn's drinking pool", + "Priority": 50, + "Area": [{"x1": 1302, "y1": 2331, "x2": 1340, "y2": 2353}] + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Huntsman's Forest", + "Priority": 50, + "Area": [{"x1": 1595, "y1": 550, "x2": 1715, "y2": 670}] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Cove", + "Priority": 50, + "Area": [ + {"x1": 2200, "y1": 1110, "z1": 0, "x2": 2250, "y2": 1160, "z2": 128}, + {"x1": 2200, "y1": 1160, "z1": 0, "x2": 2286, "y2": 1246, "z2": 128} + ], + "GoLocation": {"x": 2275, "y": 1210, "z": 0}, + "Music": "Cove" + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Britain", + "Priority": 50, + "Area": [ + {"x1": 1416, "y1": 1498, "z1": -10, "x2": 1740, "y2": 1777, "z2": 128}, + {"x1": 1500, "y1": 1408, "z1": 0, "x2": 1546, "y2": 1498, "z2": 128}, + {"x1": 1385, "y1": 1538, "z1": -10, "x2": 1416, "y2": 1777, "z2": 128}, + {"x1": 1416, "y1": 1777, "z1": 0, "x2": 1740, "y2": 1837, "z2": 128}, + {"x1": 1385, "y1": 1777, "z1": 0, "x2": 1416, "y2": 1907, "z2": 128}, + {"x1": 1093, "y1": 1538, "z1": 0, "x2": 1385, "y2": 1907, "z2": 128} + ], + "GoLocation": {"x": 1495, "y": 1629, "z": 10}, + "Music": "Britain1" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 1492, "y1": 1602, "z1": 0, "x2": 1500, "y2": 1621, "z2": 128}, + {"x1": 1500, "y1": 1610, "z1": 0, "x2": 1508, "y2": 1626, "z2": 128}, + {"x1": 1576, "y1": 1584, "z1": 0, "x2": 1600, "y2": 1600, "z2": 128}, + {"x1": 1456, "y1": 1512, "z1": 0, "x2": 1472, "y2": 1528, "z2": 128}, + {"x1": 1472, "y1": 1512, "z1": 0, "x2": 1480, "y2": 1520, "z2": 128}, + {"x1": 1424, "y1": 1712, "z1": 0, "x2": 1432, "y2": 1736, "z2": 128}, + {"x1": 1432, "y1": 1712, "z1": 0, "x2": 1440, "y2": 1724, "z2": 128}, + {"x1": 1486, "y1": 1684, "z1": 0, "x2": 1494, "y2": 1692, "z2": 128}, + {"x1": 1494, "y1": 1676, "z1": 0, "x2": 1502, "y2": 1700, "z2": 128}, + {"x1": 1608, "y1": 1584, "z1": 0, "x2": 1632, "y2": 1592, "z2": 128}, + {"x1": 1616, "y1": 1576, "z1": 0, "x2": 1624, "y2": 1584, "z2": 128}, + {"x1": 1544, "y1": 1760, "z1": 0, "x2": 1560, "y2": 1776, "z2": 128}, + {"x1": 1560, "y1": 1760, "z1": 0, "x2": 1568, "y2": 1768, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Wheatfield in Britain 1", + "Priority": 50, + "Area": [{"x1": 1120, "y1": 1776, "x2": 1152, "y2": 1808}], + "GoLocation": {"x": 1135, "y": 1791, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Wheatfield in Britain 2", + "Priority": 50, + "Area": [{"x1": 1184, "y1": 1808, "x2": 1216, "y2": 1840}], + "GoLocation": {"x": 1199, "y": 1823, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Wheatfield in Britain 3", + "Priority": 50, + "Area": [{"x1": 1216, "y1": 1872, "x2": 1248, "y2": 1904}], + "GoLocation": {"x": 1231, "y": 1887, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Carrot Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1208, "y1": 1712, "x2": 1224, "y2": 1736}], + "GoLocation": {"x": 1215, "y": 1723, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "An Onion Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1224, "y1": 1712, "x2": 1240, "y2": 1736}], + "GoLocation": {"x": 1231, "y": 1723, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Cabbage Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1176, "y1": 1672, "x2": 1192, "y2": 1695}], + "GoLocation": {"x": 1183, "y": 1683, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Turnip Field in Britain 1", + "Priority": 50, + "Area": [{"x1": 1192, "y1": 1672, "x2": 1208, "y2": 1696}], + "GoLocation": {"x": 1199, "y": 1683, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Wheatfield in Britain 4", + "Priority": 50, + "Area": [{"x1": 1104, "y1": 1608, "x2": 1136, "y2": 1640}], + "GoLocation": {"x": 1120, "y": 1624, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Wheatfield in Britain 5", + "Priority": 50, + "Area": [{"x1": 1136, "y1": 1560, "x2": 1168, "y2": 1592}], + "GoLocation": {"x": 1152, "y": 1576, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Turnip Field in Britain 2", + "Priority": 50, + "Area": [{"x1": 1208, "y1": 1592, "x2": 1224, "y2": 1616}], + "GoLocation": {"x": 1216, "y": 1604, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Britain", "Map": "Trammel" }, + "Name": "A Carrot Field in Britain 2", + "Priority": 50, + "Area": [{"x1": 1224, "y1": 1592, "x2": 1240, "y2": 1616}], + "GoLocation": {"x": 1232, "y": 1604, "z": 0} + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Britain Graveyard", + "Priority": 50, + "Area": [{"x1": 1333, "y1": 1441, "x2": 1417, "y2": 1523}], + "GoLocation": {"x": 1384, "y": 1492, "z": 10} + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Jhelom Islands", + "Priority": 50, + "Area": [ + {"x1": 1111, "y1": 3567, "x2": 1144, "y2": 3588}, + {"x1": 1078, "y1": 3588, "x2": 1202, "y2": 3709}, + {"x1": 1224, "y1": 3592, "x2": 1533, "y2": 4065} + ], + "GoLocation": {"x": 1383, "y": 3815, "z": 0}, + "Music": "Jhelom" + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Parent": { "Name": "Jhelom Islands", "Map": "Trammel" }, + "Name": "Jhelom", + "Priority": 50, + "Area": [ + {"x1": 1303, "y1": 3670, "z1": -20, "x2": 1492, "y2": 3895, "z2": 128}, + {"x1": 1338, "y1": 3895, "z1": -20, "x2": 1412, "y2": 3923, "z2": 128}, + {"x1": 1383, "y1": 3951, "z1": -20, "x2": 1492, "y2": 4045, "z2": 128} + ], + "GoLocation": {"x": 1383, "y": 3815, "z": 0} + }, + { + "$type": "NoHousingRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Jhelom", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 1352, "y1": 3800, "z1": -20, "x2": 1368, "y2": 3832, "z2": 128}, + {"x1": 1368, "y1": 3808, "z1": -20, "x2": 1376, "y2": 3824, "z2": 128}, + {"x1": 1432, "y1": 3768, "z1": -20, "x2": 1464, "y2": 3776, "z2": 128}, + {"x1": 1440, "y1": 3776, "z1": -20, "x2": 1464, "y2": 3784, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Minoc", + "Priority": 50, + "Area": [ + {"x1": 2411, "y1": 366, "x2": 2546, "y2": 607}, + {"x1": 2548, "y1": 495, "x2": 2620, "y2": 550}, + {"x1": 2564, "y1": 585, "x2": 2567, "y2": 627}, + {"x1": 2567, "y1": 585, "x2": 2628, "y2": 646}, + {"x1": 2499, "y1": 627, "x2": 2567, "y2": 690} + ], + "GoLocation": {"x": 2466, "y": 544, "z": 0}, + "Music": "Minoc" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Minoc", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 2457, "y1": 397, "x2": 2497, "y2": 405}, + {"x1": 2465, "y1": 405, "x2": 2473, "y2": 413}, + {"x1": 2481, "y1": 405, "x2": 2489, "y2": 413} + ] + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Haven Island", + "Priority": 50, + "Area": [{"x1": 3314, "y1": 2345, "x2": 3814, "y2": 3095}], + "GoLocation": {"x": 3627, "y": 2606, "z": 0} + }, + { + "$type": "GuardedRegion", + "Map": "Trammel", + "Parent": { "Name": "Haven Island", "Map": "Trammel" }, + "Name": "New Haven Moongate", + "Priority": 50, + "Area": [{"x1": 3446, "y1": 2674, "x2": 3456, "y2": 2684}] + }, + { + "$type": "TownRegion", + "RuneName": "New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "Haven Island", "Map": "Trammel" }, + "Name": "New Haven", + "Priority": 50, + "Area": [{"x1": 3416, "y1": 2480, "x2": 3545, "y2": 2648}], + "GoLocation": {"x": 3503, "y": 2574, "z": 14}, + "Music": "Ocllo" + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Little Shop of Alchemy in New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Alchemist", + "Priority": 50, + "Area": [{"x1": 3457, "y1": 2563, "x2": 3466, "y2": 2572}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Bardic Guild in New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Bard", + "Priority": 50, + "Area": [ + {"x1": 3410, "y1": 2598, "x2": 3419, "y2": 2607}, + {"x1": 3419, "y1": 2599, "x2": 3421, "y2": 2606}, + {"x1": 3408, "y1": 2607, "x2": 3415, "y2": 2612} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Warrior Guild Hall in New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "Warrior's Guild Hall", + "Priority": 50, + "Area": [ + {"x1": 3520, "y1": 2528, "x2": 3543, "y2": 2552}, + {"x1": 3520, "y1": 2552, "x2": 3534, "y2": 2563} + ] + }, + { + "$type": "NoHousingRegion", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "Warrior's Guild Hall", "Map": "Trammel" }, + "Name": "the New Haven Warrior", + "Priority": 50, + "Area": [ + {"x1": 3523, "y1": 2546, "x2": 3529, "y2": 2562}, + {"x1": 3529, "y1": 2550, "x2": 3533, "y2": 2562} + ] + }, + { + "$type": "NoHousingRegion", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "Warrior's Guild Hall", "Map": "Trammel" }, + "Name": "the New Haven Bowyer", + "Priority": 50, + "Area": [{"x1": 3529, "y1": 2531, "x2": 3541, "y2": 2550}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "A Stitch In Time in New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Tailor", + "Priority": 50, + "Area": [ + {"x1": 3492, "y1": 2546, "x2": 3502, "y2": 2557}, + {"x1": 3502, "y1": 2546, "x2": 3505, "y2": 2550} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Carpenters of New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Carpenter", + "Priority": 50, + "Area": [ + {"x1": 3439, "y1": 2632, "x2": 3448, "y2": 2643}, + {"x1": 3440, "y1": 2643, "x2": 3447, "y2": 2648} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Mapmakers of New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Mapmaker", + "Priority": 50, + "Area": [{"x1": 3490, "y1": 2588, "x2": 3497, "y2": 2595}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "New Haven Magery School", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Mage", + "Priority": 50, + "Area": [{"x1": 3464, "y1": 2488, "x2": 3491, "y2": 2504}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Bountiful Harvest Inn in New Haven", + "NoLogoutDelay": true, + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Inn", + "Priority": 50, + "Area": [ + {"x1": 3493, "y1": 2513, "x2": 3503, "y2": 2523}, + {"x1": 3503, "y1": 2513, "x2": 3510, "y2": 2530} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the docks in New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Docks", + "Priority": 50, + "Area": [ + {"x1": 3500, "y1": 2592, "x2": 3508, "y2": 2617}, + {"x1": 3508, "y1": 2592, "x2": 3516, "y2": 2599}, + {"x1": 3516, "y1": 2592, "x2": 3524, "y2": 2608} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the First Bank of New Haven", + "MinExpansion": "ML", + "Map": "Trammel", + "Parent": { "Name": "New Haven", "Map": "Trammel" }, + "Name": "the New Haven Bank", + "Priority": 50, + "Area": [{"x1": 3479, "y1": 2565, "x2": 3492, "y2": 2580}] + }, + { + "$type": "TownRegion", + "RuneName": "Haven City", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven Island", "Map": "Trammel" }, + "Name": "Haven", + "Priority": 50, + "Area": [ + {"x1": 3590, "y1": 2460, "x2": 3708, "y2": 2685}, + {"x1": 3568, "y1": 2552, "x2": 3590, "y2": 2631}, + {"x1": 3708, "y1": 2558, "x2": 3761, "y2": 2712}, + {"x1": 3695, "y1": 2685, "x2": 3708, "y2": 2712} + ], + "GoLocation": {"x": 3650, "y": 2519, "z": 0}, + "Music": "Ocllo" + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Woodsman in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Woodsman in Haven", + "Priority": 50, + "Area": [ + {"x1": 3736, "y1": 2680, "x2": 3744, "y2": 2696}, + {"x1": 3744, "y1": 2680, "x2": 3752, "y2": 2688} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Prime Cut Butcher Shop in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Prime Cut Butcher Shop in Haven", + "Priority": 50, + "Area": [{"x1": 3704, "y1": 2648, "x2": 3720, "y2": 2656}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "a farm in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Priority": 50, + "Area": [{"x1": 3728, "y1": 2632, "x2": 3736, "y2": 2640}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "a farm in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Priority": 50, + "Area": [{"x1": 3704, "y1": 2584, "x2": 3712, "y2": 2592}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "a farm in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Priority": 50, + "Area": [{"x1": 3736, "y1": 2561, "x2": 3744, "y2": 2568}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Mapmakers of Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "Mapmakers of Haven", + "Priority": 50, + "Area": [{"x1": 3624, "y1": 2632, "x2": 3640, "y2": 2640}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Haven Blacksmith", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "The Haven Blacksmith", + "Priority": 50, + "Area": [{"x1": 3641, "y1": 2613, "x2": 3646, "y2": 2624}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Second Bank of Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Second Bank of Haven", + "Priority": 50, + "Area": [ + {"x1": 3615, "y1": 2614, "x2": 3631, "y2": 2623}, + {"x1": 3615, "y1": 2609, "x2": 3624, "y2": 2614} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Uzeraan's Mansion", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "Uzeraan's Mansion", + "Priority": 50, + "Area": [ + {"x1": 3583, "y1": 2567, "x2": 3608, "y2": 2608}, + {"x1": 3608, "y1": 2572, "x2": 3613, "y2": 2603}, + {"x1": 3572, "y1": 2592, "x2": 3583, "y2": 2603}, + {"x1": 3575, "y1": 2582, "x2": 3583, "y2": 2592} + ] + }, + { + "$type": "NoHousingRegion", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Uzeraan's Mansion", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 3592, "y1": 2595, "x2": 3612, "y2": 2599}, + {"x1": 3595, "y1": 2599, "x2": 3612, "y2": 2602}, + {"x1": 3595, "y1": 2602, "x2": 3607, "y2": 2607} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Last Chance Provisioners in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "Last Chance Provisioners in Haven", + "Priority": 50, + "Area": [ + {"x1": 3666, "y1": 2592, "x2": 3681, "y2": 2600}, + {"x1": 3673, "y1": 2587, "x2": 3681, "y2": 2592} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Healers of Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Healers of Haven", + "Priority": 50, + "Area": [ + {"x1": 3659, "y1": 2577, "x2": 3674, "y2": 2584}, + {"x1": 3661, "y1": 2576, "x2": 3672, "y2": 2577}, + {"x1": 3663, "y1": 2575, "x2": 3670, "y2": 2576}, + {"x1": 3661, "y1": 2584, "x2": 3672, "y2": 2585}, + {"x1": 3663, "y1": 2585, "x2": 3670, "y2": 2586} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Little Shop of Magic in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "The Little Shop of Magic in Haven", + "Priority": 50, + "Area": [ + {"x1": 3623, "y1": 2565, "x2": 3642, "y2": 2577}, + {"x1": 3623, "y1": 2577, "x2": 3628, "y2": 2581}, + {"x1": 3637, "y1": 2577, "x2": 3642, "y2": 2581} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Shakin' Bakery in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "The Shakin' Bakery in Haven", + "Priority": 50, + "Area": [ + {"x1": 3624, "y1": 2528, "x2": 3632, "y2": 2552}, + {"x1": 3632, "y1": 2528, "x2": 3640, "y2": 2536}, + {"x1": 3632, "y1": 2544, "x2": 3640, "y2": 2552} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Bardic Guild in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Bardic Guild in Haven", + "Priority": 50, + "Area": [ + {"x1": 3672, "y1": 2528, "x2": 3679, "y2": 2544}, + {"x1": 3655, "y1": 2528, "x2": 3672, "y2": 2536} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Carpenters of Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "Carpenters of Haven", + "Priority": 50, + "Area": [ + {"x1": 3632, "y1": 2496, "x2": 3648, "y2": 2504}, + {"x1": 3640, "y1": 2504, "x2": 3648, "y2": 2512} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "Haven Clockworks and Tinker Shop", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "Haven Clockworks and Tinker Shop", + "Priority": 50, + "Area": [ + {"x1": 3656, "y1": 2504, "x2": 3672, "y2": 2512}, + {"x1": 3664, "y1": 2495, "x2": 3672, "y2": 2504} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Haven Thieves' Guild", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "The Haven Thieves' Guild", + "Priority": 50, + "Area": [ + {"x1": 3688, "y1": 2504, "x2": 3696, "y2": 2520}, + {"x1": 3696, "y1": 2504, "x2": 3704, "y2": 2512} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "A Stitch in Time in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "A Stitch in Time in Haven", + "Priority": 50, + "Area": [ + {"x1": 3688, "y1": 2464, "x2": 3696, "y2": 2488}, + {"x1": 3680, "y1": 2472, "x2": 3704, "y2": 2480} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Haven Public Library", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Haven Public Library", + "Priority": 50, + "Area": [ + {"x1": 3608, "y1": 2464, "x2": 3616, "y2": 2480}, + {"x1": 3616, "y1": 2472, "x2": 3624, "y2": 2480} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Gentle Rest Inn in New Haven", + "NoLogoutDelay": true, + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "The Gentle Rest Inn in New Haven", + "Priority": 50, + "Area": [ + {"x1": 3493, "y1": 2513, "x2": 3509, "y2": 2520}, + {"x1": 3503, "y1": 2513, "x2": 3510, "y2": 2529} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "The Albatross Bar and Grill in Haven", + "NoLogoutDelay": true, + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "The Albatross Bar and Grill in Haven", + "Priority": 50, + "Area": [ + {"x1": 3664, "y1": 2640, "x2": 3672, "y2": 2656}, + {"x1": 3672, "y1": 2648, "x2": 3680, "y2": 2656} + ], + "Music": "Tavern01" + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Proving Grounds in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Proving Grounds in Haven", + "Priority": 50, + "Area": [ + {"x1": 3728, "y1": 2575, "x2": 3736, "y2": 2592}, + {"x1": 3736, "y1": 2575, "x2": 3737, "y2": 2582} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Bullseye Marksmen in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Bullseye Marksmen in Haven", + "Priority": 50, + "Area": [{"x1": 3742, "y1": 2575, "x2": 3750, "y2": 2586}] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the docks in Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the docks in Haven", + "Priority": 50, + "Area": [ + {"x1": 3645, "y1": 2657, "x2": 3672, "y2": 2666}, + {"x1": 3645, "y1": 2666, "x2": 3651, "y2": 2684}, + {"x1": 3659, "y1": 2666, "x2": 3665, "y2": 2684} + ] + }, + { + "$type": "NoHousingRegion", + "RuneName": "the Mystic Mines of Haven", + "MaxExpansion": "SE", + "Map": "Trammel", + "Parent": { "Name": "Haven", "Map": "Trammel" }, + "Name": "the Mystic Mines of Haven", + "Priority": 50, + "Area": [{"x1": 3752, "y1": 2698, "x2": 3757, "y2": 2707}] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Trinsic", + "Priority": 50, + "Area": [ + {"x1": 1856, "y1": 2636, "x2": 1931, "y2": 2664}, + {"x1": 1816, "y1": 2664, "x2": 2099, "y2": 2895}, + {"x1": 2099, "y1": 2782, "x2": 2117, "y2": 2807}, + {"x1": 1970, "y1": 2895, "x2": 2017, "y2": 2927}, + {"x1": 1796, "y1": 2696, "z1": 0, "x2": 1816, "y2": 2763, "z2": 128}, + {"x1": 1800, "y1": 2796, "z1": 0, "x2": 1816, "y2": 2848, "z2": 128} + ], + "GoLocation": {"x": 1867, "y": 2780, "z": 0}, + "Music": "Trinsic" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Trinsic", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 1834, "y1": 2728, "x2": 1856, "y2": 2744}, + {"x1": 2024, "y1": 2784, "x2": 2040, "y2": 2804}, + {"x1": 2026, "y1": 2804, "x2": 2040, "y2": 2806}, + {"x1": 2024, "y1": 2806, "x2": 2040, "y2": 2813}, + {"x1": 1923, "y1": 2786, "x2": 1935, "y2": 2808}, + {"x1": 1935, "y1": 2786, "x2": 1942, "y2": 2800} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Vesper", + "Priority": 50, + "Area": [ + {"x1": 2893, "y1": 598, "x2": 3014, "y2": 648}, + {"x1": 2816, "y1": 648, "x2": 3065, "y2": 1013}, + {"x1": 2734, "y1": 944, "x2": 2816, "y2": 948}, + {"x1": 2728, "y1": 948, "x2": 2816, "y2": 1001} + ], + "GoLocation": {"x": 2899, "y": 676, "z": 0}, + "Music": "Vesper" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Vesper", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 2952, "y1": 864, "x2": 2968, "y2": 896}, + {"x1": 2968, "y1": 872, "x2": 2976, "y2": 888}, + {"x1": 2776, "y1": 952, "x2": 2792, "y2": 984}, + {"x1": 2768, "y1": 960, "x2": 2776, "y2": 976}, + {"x1": 2892, "y1": 901, "x2": 2908, "y2": 920}, + {"x1": 2908, "y1": 904, "x2": 2916, "y2": 912} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Yew", + "Priority": 50, + "Area": [ + {"x1": 92, "y1": 656, "z1": 0, "x2": 441, "y2": 881, "z2": 39}, + {"x1": 441, "y1": 746, "z1": 0, "x2": 657, "y2": 881, "z2": 39}, + {"x1": 258, "y1": 881, "z1": 0, "x2": 657, "y2": 1261, "z2": 39}, + {"x1": 657, "y1": 922, "z1": 0, "x2": 699, "y2": 1229, "z2": 39}, + {"x1": 657, "y1": 806, "z1": 0, "x2": 674, "y2": 834, "z2": 39}, + {"x1": 718, "y1": 874, "z1": 0, "x2": 756, "y2": 896, "z2": 39} + ], + "GoLocation": {"x": 546, "y": 992, "z": 0}, + "Music": "Yew" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Yew", "Map": "Trammel" }, + "Priority": 50, + "Area": [{"x1": 600, "y1": 808, "z1": 0, "x2": 624, "y2": 832, "z2": 39}] + }, + { + "$type": "TownRegion", + "RuneName": "A Field of Sheep in Yew", + "Map": "Trammel", + "Parent": { "Name": "Yew", "Map": "Trammel" }, + "Name": "A Field of Sheep in Yew 1", + "Priority": 50, + "Area": [{"x1": 664, "y1": 928, "x2": 686, "y2": 950}], + "GoLocation": {"x": 675, "y": 939, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Field of Sheep in Yew", + "Map": "Trammel", + "Parent": { "Name": "Yew", "Map": "Trammel" }, + "Name": "A Field of Sheep in Yew 2", + "Priority": 50, + "Area": [{"x1": 664, "y1": 1168, "x2": 686, "y2": 1190}], + "GoLocation": {"x": 675, "y": 1179, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Farm in Yew", + "Map": "Trammel", + "Parent": { "Name": "Yew", "Map": "Trammel" }, + "Name": "A Farm in Yew", + "Priority": 50, + "Area": [{"x1": 560, "y1": 1088, "x2": 582, "y2": 1110}], + "GoLocation": {"x": 571, "y": 1099, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Wheatfield in Yew", + "Map": "Trammel", + "Parent": { "Name": "Yew", "Map": "Trammel" }, + "Name": "A Wheatfield in Yew 1", + "Priority": 50, + "Area": [{"x1": 560, "y1": 1232, "x2": 576, "y2": 1248}], + "GoLocation": {"x": 567, "y": 1239, "z": 0} + }, + { + "$type": "TownRegion", + "RuneName": "A Wheatfield in Yew", + "Map": "Trammel", + "Parent": { "Name": "Yew", "Map": "Trammel" }, + "Name": "A Wheatfield in Yew 2", + "Priority": 50, + "Area": [{"x1": 368, "y1": 1176, "x2": 382, "y2": 1208}], + "GoLocation": {"x": 375, "y": 1191, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Wind", + "Priority": 50, + "Area": [ + {"x1": 5294, "y1": 19, "x2": 5366, "y2": 139}, + {"x1": 5132, "y1": 58, "x2": 5213, "y2": 126}, + {"x1": 5197, "y1": 126, "x2": 5252, "y2": 204}, + {"x1": 5132, "y1": 3, "x2": 5202, "y2": 58}, + {"x1": 5252, "y1": 112, "x2": 5294, "y2": 170}, + {"x1": 5213, "y1": 98, "x2": 5252, "y2": 126}, + {"x1": 5279, "y1": 57, "x2": 5294, "y2": 112}, + {"x1": 5252, "y1": 170, "x2": 5284, "y2": 178}, + {"x1": 5286, "y1": 25, "x2": 5294, "y2": 57}, + {"x1": 5252, "y1": 178, "x2": 5272, "y2": 183}, + {"x1": 5252, "y1": 183, "x2": 5262, "y2": 193} + ], + "GoLocation": {"x": 5223, "y": 190, "z": 5}, + "Music": "Wind" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Wind", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 5159, "y1": 15, "x2": 5184, "y2": 24}, + {"x1": 5159, "y1": 24, "x2": 5168, "y2": 40}, + {"x1": 5175, "y1": 24, "x2": 5184, "y2": 32}, + {"x1": 5212, "y1": 159, "x2": 5221, "y2": 183}, + {"x1": 5221, "y1": 171, "x2": 5228, "y2": 183}, + {"x1": 5206, "y1": 164, "x2": 5212, "y2": 179}, + {"x1": 5303, "y1": 28, "x2": 5319, "y2": 42} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Serpent's Hold", + "Priority": 50, + "Area": [ + {"x1": 2868, "y1": 3324, "z1": 0, "x2": 3073, "y2": 3519, "z2": 128} + ], + "GoLocation": {"x": 3010, "y": 3371, "z": 15}, + "Music": "Serpents" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Serpent's Hold", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 2960, "y1": 3400, "z1": 0, "x2": 2976, "y2": 3416, "z2": 128}, + {"x1": 2968, "y1": 3416, "z1": 0, "x2": 2976, "y2": 3432, "z2": 128}, + {"x1": 3008, "y1": 3450, "z1": 0, "x2": 3022, "y2": 3464, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Skara Brae", + "Priority": 50, + "Area": [{"x1": 538, "y1": 2107, "x2": 688, "y2": 2297}], + "GoLocation": {"x": 632, "y": 2233, "z": 0}, + "Music": "Skarabra" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Skara Brae", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 600, "y1": 2232, "x2": 616, "y2": 2256}, + {"x1": 592, "y1": 2240, "x2": 600, "y2": 2256}, + {"x1": 616, "y1": 2240, "x2": 624, "y2": 2256}, + {"x1": 552, "y1": 2168, "x2": 568, "y2": 2192}, + {"x1": 568, "y1": 2168, "x2": 576, "y2": 2176} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Nujel'm", + "Priority": 50, + "Area": [ + {"x1": 3475, "y1": 1000, "z1": 0, "x2": 3835, "y2": 1435, "z2": 128} + ], + "GoLocation": {"x": 3732, "y": 1279, "z": 0}, + "Music": "Nujelm" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Nujel'm", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 3736, "y1": 1184, "z1": 0, "x2": 3752, "y2": 1207, "z2": 128}, + {"x1": 3728, "y1": 1192, "z1": 0, "x2": 3736, "y2": 1207, "z2": 128}, + {"x1": 3728, "y1": 1288, "z1": 0, "x2": 3751, "y2": 1303, "z2": 128}, + {"x1": 3728, "y1": 1303, "z1": 0, "x2": 3744, "y2": 1312, "z2": 128}, + {"x1": 3728, "y1": 1312, "z1": 0, "x2": 3740, "y2": 1320, "z2": 128}, + {"x1": 3728, "y1": 1320, "z1": 0, "x2": 3744, "y2": 1343, "z2": 128}, + {"x1": 3744, "y1": 1328, "z1": 0, "x2": 3751, "y2": 1343, "z2": 128}, + {"x1": 3760, "y1": 1216, "z1": 0, "x2": 3772, "y2": 1240, "z2": 128}, + {"x1": 3772, "y1": 1220, "z1": 0, "x2": 3776, "y2": 1236, "z2": 128}, + {"x1": 3776, "y1": 1224, "z1": 0, "x2": 3784, "y2": 1232, "z2": 128}, + {"x1": 3728, "y1": 1248, "z1": 0, "x2": 3744, "y2": 1272, "z2": 128}, + {"x1": 3744, "y1": 1248, "z1": 0, "x2": 3752, "y2": 1256, "z2": 128}, + {"x1": 3744, "y1": 1264, "z1": 0, "x2": 3752, "y2": 1272, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Moonglow", + "Priority": 50, + "Area": [ + {"x1": 4535, "y1": 844, "z1": 0, "x2": 4555, "y2": 847, "z2": 128}, + {"x1": 4530, "y1": 847, "z1": 0, "x2": 4561, "y2": 908, "z2": 128}, + {"x1": 4521, "y1": 914, "z1": 0, "x2": 4577, "y2": 963, "z2": 128}, + {"x1": 4278, "y1": 915, "z1": 0, "x2": 4332, "y2": 934, "z2": 128}, + {"x1": 4283, "y1": 944, "z1": 0, "x2": 4336, "y2": 1017, "z2": 128}, + {"x1": 4377, "y1": 1015, "z1": -10, "x2": 4436, "y2": 1052, "z2": 128}, + {"x1": 4367, "y1": 1050, "z1": 0, "x2": 4509, "y2": 1195, "z2": 128}, + {"x1": 4539, "y1": 1036, "z1": 0, "x2": 4566, "y2": 1054, "z2": 128}, + {"x1": 4517, "y1": 1053, "z1": 0, "x2": 4540, "y2": 1075, "z2": 128}, + {"x1": 4389, "y1": 1198, "z1": 0, "x2": 4436, "y2": 1237, "z2": 128}, + {"x1": 4466, "y1": 1211, "z1": 0, "x2": 4498, "y2": 1236, "z2": 128}, + {"x1": 4700, "y1": 1108, "z1": 0, "x2": 4717, "y2": 1126, "z2": 128}, + {"x1": 4656, "y1": 1127, "z1": 0, "x2": 4682, "y2": 1140, "z2": 128}, + {"x1": 4678, "y1": 1162, "z1": 0, "x2": 4703, "y2": 1187, "z2": 128}, + {"x1": 4613, "y1": 1196, "z1": 0, "x2": 4636, "y2": 1218, "z2": 128}, + {"x1": 4646, "y1": 1212, "z1": 0, "x2": 4660, "y2": 1229, "z2": 128}, + {"x1": 4677, "y1": 1214, "z1": 0, "x2": 4703, "y2": 1236, "z2": 128}, + {"x1": 4622, "y1": 1316, "z1": 0, "x2": 4644, "y2": 1340, "z2": 128}, + {"x1": 4487, "y1": 1353, "z1": 0, "x2": 4546, "y2": 1374, "z2": 128}, + {"x1": 4477, "y1": 1374, "z1": 0, "x2": 4546, "y2": 1409, "z2": 128}, + {"x1": 4659, "y1": 1387, "z1": 0, "x2": 4699, "y2": 1427, "z2": 128}, + {"x1": 4549, "y1": 1482, "z1": 0, "x2": 4578, "y2": 1509, "z2": 128}, + {"x1": 4405, "y1": 1451, "z1": 0, "x2": 4428, "y2": 1474, "z2": 128}, + {"x1": 4483, "y1": 1468, "z1": 0, "x2": 4504, "y2": 1481, "z2": 128} + ], + "GoLocation": {"x": 4442, "y": 1172, "z": 0}, + "Music": "Moonglow" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Moonglow", "Map": "Trammel" }, + "Name": "The Scholar's Inn", + "Priority": 50, + "Area": [ + {"x1": 4384, "y1": 1152, "z1": 0, "x2": 4392, "y2": 1176, "z2": 128}, + {"x1": 4392, "y1": 1160, "z1": 0, "x2": 4408, "y2": 1168, "z2": 128}, + {"x1": 4400, "y1": 1152, "z1": 0, "x2": 4408, "y2": 1160, "z2": 128}, + {"x1": 4480, "y1": 1056, "z1": 0, "x2": 4488, "y2": 1072, "z2": 128}, + {"x1": 4488, "y1": 1060, "z1": 0, "x2": 4492, "y2": 1068, "z2": 128}, + {"x1": 4476, "y1": 1060, "z1": 0, "x2": 4480, "y2": 1068, "z2": 128} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Magincia", + "Priority": 50, + "Area": [ + {"x1": 3653, "y1": 2046, "x2": 3680, "y2": 2094}, + {"x1": 3752, "y1": 2046, "x2": 3804, "y2": 2094}, + {"x1": 3680, "y1": 2045, "x2": 3752, "y2": 2094}, + {"x1": 3652, "y1": 2094, "x2": 3812, "y2": 2274}, + {"x1": 3649, "y1": 2256, "x2": 3703, "y2": 2303} + ], + "GoLocation": {"x": 3714, "y": 2220, "z": 20}, + "Music": "Magincia" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Magincia", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 3680, "y1": 2152, "x2": 3704, "y2": 2160}, + {"x1": 3720, "y1": 2216, "x2": 3736, "y2": 2232} + ] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Buccaneer's Den", + "Priority": 50, + "GuardsDisabled": true, + "Area": [ + {"x1": 2612, "y1": 2057, "x2": 2776, "y2": 2267}, + {"x1": 2604, "y1": 2065, "z1": 0, "x2": 2612, "y2": 2254, "z2": 128} + ], + "GoLocation": {"x": 2706, "y": 2163, "z": 0}, + "Music": "Bucsden" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Buccaneer's Den", "Map": "Trammel" }, + "Priority": 50, + "Area": [ + {"x1": 2712, "y1": 2080, "x2": 2728, "y2": 2096}, + {"x1": 2712, "y1": 2096, "x2": 2720, "y2": 2104}, + {"x1": 2664, "y1": 2232, "x2": 2688, "y2": 2240}, + {"x1": 2672, "y1": 2240, "x2": 2688, "y2": 2248} + ] + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2499, "y": 916, "z": 0}, + "Map": "Trammel", + "Name": "Covetous", + "Priority": 50, + "Area": [ + {"x1": 5376, "y1": 1793, "x2": 5577, "y2": 2048}, + {"x1": 5576, "y1": 1791, "x2": 5633, "y2": 2048} + ], + "GoLocation": {"x": 5456, "y": 1862, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 4111, "y": 429, "z": 0}, + "Map": "Trammel", + "Name": "Deceit", + "Priority": 50, + "Area": [{"x1": 5122, "y1": 518, "x2": 5370, "y2": 770}], + "GoLocation": {"x": 5187, "y": 635, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1296, "y": 1082, "z": 0}, + "Map": "Trammel", + "Name": "Despise", + "Priority": 50, + "Area": [{"x1": 5377, "y1": 516, "x2": 5631, "y2": 1022}], + "GoLocation": {"x": 5501, "y": 570, "z": 59}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1176, "y": 2635, "z": 0}, + "Map": "Trammel", + "Name": "Destard", + "Priority": 50, + "Area": [{"x1": 5120, "y1": 770, "x2": 5371, "y2": 1028}], + "GoLocation": {"x": 5243, "y": 1004, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 4722, "y": 3814, "z": 0}, + "Map": "Trammel", + "Name": "Hythloth", + "Priority": 50, + "Area": [{"x1": 5898, "y1": 2, "x2": 6136, "y2": 246}], + "GoLocation": {"x": 5905, "y": 22, "z": 44}, + "Music": "Dungeon9" + }, + { + "$type": "JailRegion", + "Map": "Trammel", + "Name": "Jail", + "Priority": 50, + "Area": [{"x1": 5271, "y1": 1159, "x2": 5312, "y2": 1192}], + "GoLocation": {"x": 5275, "y": 1163, "z": 0} + }, + { + "$type": "GreenAcresRegion", + "Map": "Trammel", + "Name": "Green Acres", + "Priority": 1, + "Area": [{"x1": 5376, "y1": 512, "x2": 6143, "y2": 1279}], + "GoLocation": {"x": 5445, "y": 1153, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 512, "y": 1559, "z": 0}, + "Map": "Trammel", + "Name": "Shame", + "Priority": 50, + "Area": [ + {"x1": 5377, "y1": 2, "x2": 5634, "y2": 262}, + {"x1": 5635, "y1": 2, "x2": 5895, "y2": 126} + ], + "GoLocation": {"x": 5395, "y": 126, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2042, "y": 226, "z": 0}, + "Map": "Trammel", + "Name": "Wrong", + "Priority": 50, + "Area": [{"x1": 5633, "y1": 511, "x2": 5886, "y2": 1021}], + "GoLocation": {"x": 5825, "y": 599, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Cave 1", + "Priority": 50, + "Area": [ + {"x1": 2373, "y1": 900, "x2": 2395, "y2": 928}, + {"x1": 2395, "y1": 903, "x2": 2409, "y2": 919}, + {"x1": 2373, "y1": 928, "x2": 2383, "y2": 937}, + {"x1": 2359, "y1": 927, "x2": 2373, "y2": 945} + ], + "GoLocation": {"x": 2367, "y": 942, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Cave 2", + "Priority": 50, + "Area": [{"x1": 1887, "y1": 354, "x2": 1920, "y2": 377}], + "GoLocation": {"x": 1903, "y": 365, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Cave 3", + "Priority": 50, + "Area": [{"x1": 1925, "y1": 307, "x2": 1943, "y2": 325}], + "GoLocation": {"x": 1934, "y": 316, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Cave 4", + "Priority": 50, + "Area": [{"x1": 2323, "y1": 809, "x2": 2368, "y2": 851}], + "GoLocation": {"x": 2345, "y": 830, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Britain Mine 1", + "Priority": 50, + "Area": [{"x1": 1436, "y1": 1215, "x2": 1465, "y2": 1252}], + "GoLocation": {"x": 1443, "y": 1228, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Britain Mine 2", + "Priority": 50, + "Area": [{"x1": 1611, "y1": 1175, "x2": 1662, "y2": 1204}], + "GoLocation": {"x": 1629, "y": 1189, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Minoc Cave 1", + "Priority": 50, + "Area": [{"x1": 2406, "y1": 168, "x2": 2428, "y2": 184}], + "GoLocation": {"x": 2426, "y": 177, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Minoc Cave 2", + "Priority": 50, + "Area": [{"x1": 2418, "y1": 81, "x2": 2442, "y2": 113}], + "GoLocation": {"x": 2440, "y": 94, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Minoc Cave 3", + "Priority": 50, + "Area": [{"x1": 2447, "y1": 39, "x2": 2489, "y2": 67}], + "GoLocation": {"x": 2471, "y": 64, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Minoc Mine", + "Priority": 50, + "Area": [ + {"x1": 2556, "y1": 501, "x2": 2562, "y2": 504}, + {"x1": 2556, "y1": 474, "x2": 2582, "y2": 501} + ], + "GoLocation": {"x": 2558, "y": 499, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Avatar Isle Cave", + "Priority": 50, + "Area": [{"x1": 4594, "y1": 3807, "x2": 4617, "y2": 3824}], + "GoLocation": {"x": 4605, "y": 3815, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Ice Isle Cave 1", + "Priority": 50, + "Area": [{"x1": 4018, "y1": 421, "x2": 4067, "y2": 470}], + "GoLocation": {"x": 4054, "y": 440, "z": 3} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Ice Isle Cave 2", + "Priority": 50, + "Area": [ + {"x1": 4002, "y1": 310, "x2": 4045, "y2": 351}, + {"x1": 4005, "y1": 298, "x2": 4027, "y2": 310} + ], + "GoLocation": {"x": 4030, "y": 325, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "North Territory Cave", + "Priority": 50, + "Area": [{"x1": 1973, "y1": 251, "x2": 1994, "y2": 274}], + "GoLocation": {"x": 1984, "y": 262, "z": 8} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Yew Cave", + "Priority": 50, + "Area": [{"x1": 766, "y1": 1683, "x2": 778, "y2": 1697}], + "GoLocation": {"x": 773, "y": 1684, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "North Territory Mine 1", + "Priority": 50, + "Area": [{"x1": 1713, "y1": 1055, "x2": 1753, "y2": 1080}], + "GoLocation": {"x": 1723, "y": 1065, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "North Territory Mine 2", + "Priority": 50, + "Area": [{"x1": 1604, "y1": 958, "x2": 1650, "y2": 1009}], + "GoLocation": {"x": 1638, "y": 974, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Mt Kendall", + "Priority": 50, + "Area": [ + {"x1": 2552, "y1": 448, "x2": 2623, "y2": 507}, + {"x1": 2547, "y1": 380, "x2": 2590, "y2": 448}, + {"x1": 2590, "y1": 413, "x2": 2623, "y2": 448} + ], + "GoLocation": {"x": 2593, "y": 477, "z": 60} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "Covetous Mine", + "Priority": 50, + "Area": [ + {"x1": 2429, "y1": 866, "x2": 2448, "y2": 911}, + {"x1": 2448, "y1": 879, "x2": 2475, "y2": 907}, + {"x1": 2456, "y1": 907, "x2": 2475, "y2": 941} + ], + "GoLocation": {"x": 2445, "y": 880, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 5426, "y": 3120, "z": 0}, + "Map": "Trammel", + "Name": "Terathan Keep", + "Priority": 50, + "Area": [ + {"x1": 5404, "y1": 3099, "x2": 5481, "y2": 3167}, + {"x1": 5120, "y1": 1530, "x2": 5374, "y2": 1788} + ], + "GoLocation": {"x": 5451, "y": 3143, "z": -60}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2922, "y": 3402, "z": 0}, + "Map": "Trammel", + "Name": "Fire", + "Priority": 50, + "Area": [{"x1": 5635, "y1": 1285, "x2": 5880, "y2": 1520}], + "GoLocation": {"x": 5760, "y": 2908, "z": 15}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1996, "y": 80, "z": 0}, + "Map": "Trammel", + "Name": "Ice", + "Priority": 50, + "Area": [ + {"x1": 5668, "y1": 130, "x2": 5888, "y2": 268}, + {"x1": 5800, "y1": 319, "x2": 5863, "y2": 384}, + {"x1": 5654, "y1": 300, "x2": 5708, "y2": 340} + ], + "GoLocation": {"x": 5210, "y": 2322, "z": 30}, + "Music": "Dungeon9" + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Delucia", + "Priority": 50, + "Area": [ + {"x1": 5123, "y1": 3942, "x2": 5315, "y2": 4064}, + {"x1": 5147, "y1": 4064, "x2": 5272, "y2": 4084}, + {"x1": 5235, "y1": 3930, "x2": 5315, "y2": 3942} + ], + "GoLocation": {"x": 5228, "y": 3978, "z": 37} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Delucia", "Map": "Trammel" }, + "Priority": 50, + "Area": [{"x1": 5194, "y1": 4053, "x2": 5204, "y2": 4073}] + }, + { + "$type": "TownRegion", + "Map": "Trammel", + "Name": "Papua", + "Priority": 50, + "Area": [ + {"x1": 5639, "y1": 3095, "x2": 5831, "y2": 3318}, + {"x1": 5831, "y1": 3237, "x2": 5851, "y2": 3267} + ], + "GoLocation": {"x": 5769, "y": 3176, "z": 0} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Trammel", + "Parent": { "Name": "Papua", "Map": "Trammel" }, + "Priority": 50, + "Area": [{"x1": 5757, "y1": 3150, "x2": 5781, "y2": 3174}] + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Wrong Entrance", + "Priority": 50, + "Area": [{"x1": 1939, "y1": 215, "x2": 2073, "y2": 352}], + "GoLocation": {"x": 2043, "y": 236, "z": 13}, + "Music": "Mountn_a" + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Covetous Entrance", + "Priority": 50, + "Area": [{"x1": 2433, "y1": 846, "x2": 2561, "y2": 974}], + "GoLocation": {"x": 2499, "y": 918, "z": 0}, + "Music": "Vesper" + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Despise Entrance", + "Priority": 50, + "Area": [{"x1": 1289, "y1": 1064, "x2": 1321, "y2": 1101}], + "GoLocation": {"x": 1298, "y": 1081, "z": 0} + }, + { + "$type": "NoHousingRegion", + "Map": "Trammel", + "Name": "Despise Passage", + "Priority": 50, + "Area": [ + {"x1": 1338, "y1": 1060, "x2": 1389, "y2": 1122}, + {"x1": 1354, "y1": 1122, "x2": 1396, "y2": 1243}, + {"x1": 1349, "y1": 1122, "x2": 1354, "y2": 1224} + ], + "GoLocation": {"x": 1380, "y": 1114, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1492, "y": 1641, "z": 0}, + "Map": "Trammel", + "Name": "Misc Dungeons", + "Priority": 50, + "Area": [{"x1": 5886, "y1": 1281, "x2": 6143, "y2": 1535}], + "GoLocation": {"x": 6032, "y": 1499, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1014, "y": 1434, "z": 0}, + "Map": "Trammel", + "Name": "Orc Cave", + "Priority": 50, + "Area": [ + {"x1": 5281, "y1": 1283, "x2": 5373, "y2": 1386}, + {"x1": 5267, "y1": 1955, "x2": 5364, "y2": 2046}, + {"x1": 5127, "y1": 1941, "x2": 5164, "y2": 2024} + ], + "GoLocation": {"x": 5137, "y": 2015, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "BaseRegion", + "RuneName": "A Cotton Field in Moonglow", + "Map": "Trammel", + "Name": "A Cotton Field in Moonglow", + "Priority": 50, + "Area": [{"x1": 4557, "y1": 1471, "x2": 4577, "y2": 1481}], + "GoLocation": {"x": 4567, "y": 1475, "z": 0} + }, + { + "$type": "BaseRegion", + "RuneName": "A Wheatfield in Skara Brae", + "Map": "Trammel", + "Name": "A Wheatfield in Skara Brae 1", + "Priority": 50, + "Area": [{"x1": 796, "y1": 2152, "x2": 832, "y2": 2176}], + "GoLocation": {"x": 813, "y": 2163, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "A Carrot Field in Skara Brae", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2251, "x2": 832, "y2": 2259}], + "GoLocation": {"x": 823, "y": 2254, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "An Onion Field in Skara Brae", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2261, "x2": 832, "y2": 2269}], + "GoLocation": {"x": 823, "y": 2264, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "A Cabbage Field in Skara Brae 1", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2271, "x2": 832, "y2": 2279}], + "GoLocation": {"x": 823, "y": 2264, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "A Cabbage Field in Skara Brae 2", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2281, "x2": 832, "y2": 2289}], + "GoLocation": {"x": 823, "y": 2274, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "A Wheatfield in Skara Brae 2", + "Priority": 50, + "Area": [{"x1": 835, "y1": 2344, "x2": 851, "y2": 2360}], + "GoLocation": {"x": 843, "y": 2352, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Trammel", + "Name": "A Cotton Field in Skara Brae", + "Priority": 50, + "Area": [{"x1": 816, "y1": 2344, "x2": 832, "y2": 2368}], + "GoLocation": {"x": 823, "y": 2355, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1450, "y": 1473, "z": -23}, + "Map": "Ilshenar", + "Name": "Twisted Weald", + "Priority": 50, + "Area": [{"x1": 2107, "y1": 1145, "x2": 2254, "y2": 1280}], + "GoLocation": {"x": 2189, "y": 1253, "z": 0}, + "Music": "DreadHornArea" + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Sheep Farm", + "Priority": 50, + "Area": [{"x1": 1298, "y1": 1307, "x2": 1336, "y2": 1344}] + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Valor", + "Priority": 37, + "Area": [{"x1": 512, "y1": 200, "x2": 544, "y2": 232}], + "GoLocation": {"x": 539, "y": 221, "z": -36} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Chaos Shrine", + "Priority": 50, + "Area": [{"x1": 1736, "y1": 224, "x2": 1760, "y2": 248}], + "GoLocation": {"x": 1748, "y": 236, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Sacrifice", + "Priority": 50, + "Area": [{"x1": 1160, "y1": 1280, "x2": 1192, "y2": 1296}], + "GoLocation": {"x": 1196, "y": 1290, "z": -25} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Honesty", + "Priority": 37, + "Area": [{"x1": 712, "y1": 1344, "x2": 736, "y2": 1376}], + "GoLocation": {"x": 725, "y": 1355, "z": -61} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Compassion", + "Priority": 50, + "Area": [{"x1": 1200, "y1": 448, "x2": 1232, "y2": 488}], + "GoLocation": {"x": 1223, "y": 475, "z": -16} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Spirituality", + "Priority": 50, + "Area": [{"x1": 1520, "y1": 1336, "x2": 1536, "y2": 1352}], + "GoLocation": {"x": 1528, "y": 1344, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Humility", + "Priority": 50, + "Area": [{"x1": 272, "y1": 1008, "x2": 296, "y2": 1024}], + "GoLocation": {"x": 284, "y": 1016, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of Honor", + "Priority": 50, + "Area": [{"x1": 736, "y1": 712, "x2": 760, "y2": 744}], + "GoLocation": {"x": 748, "y": 728, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Terort Skitas", + "Priority": 11, + "Area": [{"x1": 528, "y1": 376, "x2": 624, "y2": 464}], + "GoLocation": {"x": 568, "y": 430, "z": 41} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Gargoyle City", + "Priority": 50, + "Area": [ + {"x1": 824, "y1": 552, "x2": 856, "y2": 728}, + {"x1": 752, "y1": 576, "x2": 824, "y2": 704}, + {"x1": 856, "y1": 576, "x2": 904, "y2": 704}, + {"x1": 904, "y1": 624, "x2": 928, "y2": 656} + ], + "GoLocation": {"x": 840, "y": 571, "z": 0} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Ilshenar", + "Parent": { "Name": "Gargoyle City", "Map": "Ilshenar" }, + "Priority": 50, + "Area": [{"x1": 827, "y1": 694, "x2": 853, "y2": 727}] + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Pormir Harm", + "Priority": 50, + "Area": [{"x1": 736, "y1": 480, "x2": 768, "y2": 504}], + "GoLocation": {"x": 745, "y": 492, "z": -66}, + "Music": "Stones2" + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Pormir Felwis", + "Priority": 50, + "Area": [{"x1": 472, "y1": 336, "x2": 512, "y2": 368}], + "GoLocation": {"x": 472, "y": 352, "z": -78} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Cyclops Temple", + "Priority": 50, + "Area": [{"x1": 880, "y1": 1256, "x2": 936, "y2": 1368}], + "GoLocation": {"x": 901, "y": 1297, "z": -71} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Rat Fort", + "Priority": 50, + "Area": [{"x1": 616, "y1": 792, "x2": 672, "y2": 848}], + "GoLocation": {"x": 642, "y": 845, "z": -58} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Rat Fort Cellar", + "Priority": 50, + "Area": [{"x1": 160, "y1": 736, "x2": 192, "y2": 768}], + "GoLocation": {"x": 171, "y": 750, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance to Central Ilshenar 1", + "Priority": 50, + "Area": [{"x1": 1136, "y1": 584, "x2": 1152, "y2": 600}], + "GoLocation": {"x": 1141, "y": 594, "z": -80} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance to Central Ilshenar 2", + "Priority": 50, + "Area": [{"x1": 1232, "y1": 576, "x2": 1240, "y2": 592}], + "GoLocation": {"x": 1238, "y": 583, "z": -19} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Montor", + "Priority": 41, + "GuardsDisabled": true, + "Area": [ + {"x1": 1576, "y1": 272, "x2": 1720, "y2": 368}, + {"x1": 1616, "y1": 200, "x2": 1696, "y2": 272}, + {"x1": 1696, "y1": 144, "x2": 1736, "y2": 272}, + {"x1": 1736, "y1": 144, "x2": 1768, "y2": 208} + ], + "GoLocation": {"x": 1646, "y": 319, "z": 48} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Termir Flam", + "Priority": 40, + "Area": [ + {"x1": 1520, "y1": 368, "x2": 1752, "y2": 496}, + {"x1": 1520, "y1": 296, "x2": 1576, "y2": 368}, + {"x1": 1752, "y1": 440, "x2": 1776, "y2": 480} + ], + "GoLocation": {"x": 1636, "y": 432, "z": 0} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Ancient Citadel", + "Priority": 11, + "GuardsDisabled": true, + "Area": [{"x1": 1448, "y1": 496, "x2": 1552, "y2": 600}], + "GoLocation": {"x": 1516, "y": 542, "z": 85} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Alexandretta's Bowl", + "Priority": 41, + "GuardsDisabled": true, + "Area": [{"x1": 1272, "y1": 352, "x2": 1440, "y2": 592}], + "GoLocation": {"x": 1396, "y": 432, "z": -17} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance to Rock Dungeon", + "Priority": 2, + "Area": [{"x1": 1784, "y1": 560, "x2": 1792, "y2": 576}], + "GoLocation": {"x": 1784, "y": 568, "z": 100} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1788, "y": 571, "z": 0}, + "Map": "Ilshenar", + "Name": "Rock Dungeon", + "Priority": 2, + "Area": [ + {"x1": 2176, "y1": 288, "x2": 2200, "y2": 328}, + {"x1": 2088, "y1": 8, "x2": 2248, "y2": 184} + ], + "GoLocation": {"x": 2187, "y": 316, "z": -7} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Lenmir Anfinmotas", + "Priority": 5, + "GuardsDisabled": true, + "Area": [{"x1": 1560, "y1": 792, "x2": 1792, "y2": 912}], + "GoLocation": {"x": 1673, "y": 876, "z": -25} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance to Spider Cave", + "Priority": 2, + "Area": [{"x1": 1400, "y1": 896, "x2": 1440, "y2": 920}], + "GoLocation": {"x": 1418, "y": 915, "z": -19} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1420, "y": 910, "z": 0}, + "Map": "Ilshenar", + "Name": "Spider Cave", + "Priority": 50, + "Area": [ + {"x1": 1752, "y1": 952, "x2": 1864, "y2": 1000}, + {"x1": 1480, "y1": 864, "x2": 1528, "y2": 896} + ], + "GoLocation": {"x": 1785, "y": 991, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Twin Oaks", + "Priority": 48, + "Area": [{"x1": 1536, "y1": 1032, "x2": 1584, "y2": 1072}], + "GoLocation": {"x": 1560, "y": 1052, "z": 0} + }, + { + "$type": "BaseRegion", + "NoLogoutDelay": true, + "Map": "Ilshenar", + "Parent": { "Name": "Twin Oaks", "Map": "Ilshenar" }, + "Priority": 48, + "Area": [{"x1": 1546, "y1": 1037, "x2": 1567, "y2": 1057}] + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Reg Volon", + "Priority": 50, + "GuardsDisabled": true, + "Area": [{"x1": 1328, "y1": 1008, "x2": 1408, "y2": 1120}], + "GoLocation": {"x": 1363, "y": 1055, "z": -13} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Spectre Dungeon", + "Priority": 2, + "Area": [{"x1": 1360, "y1": 1030, "x2": 1366, "y2": 1034}], + "GoLocation": {"x": 1363, "y": 1033, "z": -8} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1362, "y": 1031, "z": 0}, + "Map": "Ilshenar", + "Name": "Spectre Dungeon", + "Priority": 2, + "Area": [{"x1": 1936, "y1": 1000, "x2": 2032, "y2": 1120}], + "GoLocation": {"x": 1983, "y": 1107, "z": -18} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Blood Dungeon", + "Priority": 2, + "Area": [{"x1": 1736, "y1": 1224, "x2": 1760, "y2": 1240}], + "GoLocation": {"x": 1747, "y": 1227, "z": -1} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1745, "y": 1236, "z": 0}, + "Map": "Ilshenar", + "Name": "Blood Dungeon", + "Priority": 2, + "Area": [{"x1": 2032, "y1": 808, "x2": 2200, "y2": 1064}], + "GoLocation": {"x": 2114, "y": 839, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Mushroom Cave", + "Priority": 2, + "Area": [{"x1": 1448, "y1": 1320, "x2": 1464, "y2": 1336}], + "GoLocation": {"x": 1460, "y": 1329, "z": -25} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Mushroom Cave", + "Priority": 11, + "Area": [{"x1": 1392, "y1": 1448, "x2": 1496, "y2": 1568}], + "GoLocation": {"x": 1476, "y": 1494, "z": -28} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Lake Shire", + "Priority": 48, + "GuardsDisabled": true, + "Area": [{"x1": 1144, "y1": 1072, "x2": 1264, "y2": 1200}], + "GoLocation": {"x": 1212, "y": 1144, "z": -25} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Rat Cave", + "Priority": 2, + "Area": [{"x1": 1024, "y1": 1152, "x2": 1032, "y2": 1160}], + "GoLocation": {"x": 1031, "y": 1154, "z": -24} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Rat Cave Territory", + "Priority": 2, + "Area": [{"x1": 1144, "y1": 1440, "x2": 1368, "y2": 1584}], + "GoLocation": {"x": 1345, "y": 1511, "z": -3} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Ratman Cave", + "Priority": 2, + "Area": [ + {"x1": 1264, "y1": 1448, "x2": 1360, "y2": 1576}, + {"x1": 1152, "y1": 1456, "x2": 1256, "y2": 1560} + ], + "GoLocation": {"x": 1348, "y": 1511, "z": -3} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Bet-Lem Reg", + "Priority": 48, + "GuardsDisabled": true, + "Area": [{"x1": 1232, "y1": 936, "x2": 1272, "y2": 984}], + "GoLocation": {"x": 1238, "y": 974, "z": -34} + }, + { + "$type": "TownRegion", + "Map": "Ilshenar", + "Name": "Mistas", + "Priority": 48, + "GuardsDisabled": true, + "Area": [ + {"x1": 744, "y1": 984, "x2": 912, "y2": 1152}, + {"x1": 744, "y1": 1152, "x2": 792, "y2": 1176} + ], + "GoLocation": {"x": 818, "y": 1073, "z": -30} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Ilshenar", + "Parent": { "Name": "Mistas", "Map": "Ilshenar" }, + "Priority": 48, + "Area": [{"x1": 752, "y1": 1131, "x2": 768, "y2": 1152}] + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Serpentine Passage", + "Priority": 2, + "Area": [{"x1": 800, "y1": 864, "x2": 816, "y2": 880}], + "GoLocation": {"x": 808, "y": 872, "z": 5} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Serpentine Passage", + "Priority": 11, + "Area": [{"x1": 368, "y1": 1488, "x2": 560, "y2": 1592}], + "GoLocation": {"x": 533, "y": 1526, "z": -25} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 668, "y": 928, "z": 0}, + "Map": "Ilshenar", + "Name": "Ankh Dungeon", + "Priority": 2, + "Area": [ + {"x1": 0, "y1": 1248, "x2": 176, "y2": 1592}, + {"x1": 568, "y1": 1152, "x2": 584, "y2": 1160} + ], + "GoLocation": {"x": 156, "y": 1484, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Kirin passage", + "Priority": 11, + "Area": [{"x1": 0, "y1": 800, "x2": 192, "y2": 1200}], + "GoLocation": {"x": 11, "y": 881, "z": -29} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Lizards Passage", + "Priority": 2, + "Area": [{"x1": 312, "y1": 1328, "x2": 320, "y2": 1336}], + "GoLocation": {"x": 314, "y": 1331, "z": -37} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 652, "y": 1301, "z": 0}, + "Map": "Ilshenar", + "Name": "Wisp Dungeon", + "Priority": 2, + "Area": [ + {"x1": 616, "y1": 1480, "x2": 704, "y2": 1576}, + {"x1": 704, "y1": 1544, "x2": 728, "y2": 1576}, + {"x1": 704, "y1": 1480, "x2": 744, "y2": 1520}, + {"x1": 816, "y1": 1448, "x2": 912, "y2": 1584}, + {"x1": 944, "y1": 1416, "x2": 1000, "y2": 1456}, + {"x1": 912, "y1": 1456, "x2": 1024, "y2": 1584}, + {"x1": 832, "y1": 1424, "x2": 880, "y2": 1448}, + {"x1": 744, "y1": 1504, "x2": 816, "y2": 1584}, + {"x1": 744, "y1": 1456, "x2": 800, "y2": 1504} + ], + "GoLocation": {"x": 628, "y": 1524, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Lizard Passage", + "Priority": 11, + "Area": [{"x1": 256, "y1": 1560, "x2": 336, "y2": 1592}], + "GoLocation": {"x": 285, "y": 1585, "z": -27} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 827, "y": 777, "z": 0}, + "Map": "Ilshenar", + "Name": "Exodus Dungeon", + "Priority": 50, + "Area": [{"x1": 1832, "y1": 16, "x2": 2080, "y2": 216}], + "GoLocation": {"x": 1966, "y": 117, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Lizard Man's Huts", + "Priority": 48, + "Area": [{"x1": 264, "y1": 1280, "x2": 320, "y2": 1376}], + "GoLocation": {"x": 298, "y": 1336, "z": -24} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Nox Tereg", + "Priority": 50, + "Area": [{"x1": 344, "y1": 1112, "x2": 488, "y2": 1240}], + "GoLocation": {"x": 416, "y": 1176, "z": 0} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 546, "y": 455, "z": 0}, + "Map": "Ilshenar", + "Name": "Sorcerer's Dungeon", + "Priority": 2, + "Area": [ + {"x1": 368, "y1": 0, "x2": 488, "y2": 120}, + {"x1": 200, "y1": 0, "x2": 368, "y2": 112}, + {"x1": 256, "y1": 120, "x2": 288, "y2": 152}, + {"x1": 48, "y1": 0, "x2": 184, "y2": 136}, + {"x1": 224, "y1": 112, "x2": 248, "y2": 144} + ], + "GoLocation": {"x": 429, "y": 108, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Entrance Ancient Lair", + "Priority": 50, + "Area": [{"x1": 936, "y1": 488, "x2": 944, "y2": 496}], + "GoLocation": {"x": 940, "y": 503, "z": -30} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 938, "y": 494, "z": 0}, + "Map": "Ilshenar", + "Name": "Ancient Lair", + "Priority": 2, + "Area": [{"x1": 24, "y1": 664, "x2": 136, "y2": 760}], + "GoLocation": {"x": 86, "y": 744, "z": -28} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp", + "Priority": 50, + "Area": [{"x1": 480, "y1": 520, "x2": 536, "y2": 568}], + "GoLocation": {"x": 504, "y": 536, "z": -60} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp 1", + "Priority": 50, + "Area": [{"x1": 1480, "y1": 600, "x2": 1536, "y2": 640}], + "GoLocation": {"x": 1508, "y": 620, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp 2", + "Priority": 48, + "Area": [{"x1": 1592, "y1": 528, "x2": 1632, "y2": 568}], + "GoLocation": {"x": 1612, "y": 548, "z": 0} + }, + { + "$type": "BaseRegion", + "NoLogoutDelay": true, + "Map": "Ilshenar", + "Parent": { "Name": "Gypsy Camp 2", "Map": "Ilshenar" }, + "Priority": 48, + "Area": [{"x1": 1617, "y1": 546, "x2": 1629, "y2": 554}] + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Juka Camp", + "Priority": 50, + "Area": [{"x1": 1192, "y1": 656, "x2": 1304, "y2": 792}], + "GoLocation": {"x": 1248, "y": 724, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp 3", + "Priority": 50, + "Area": [{"x1": 928, "y1": 408, "x2": 968, "y2": 448}], + "GoLocation": {"x": 948, "y": 428, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp 4", + "Priority": 50, + "Area": [{"x1": 840, "y1": 440, "x2": 880, "y2": 488}], + "GoLocation": {"x": 860, "y": 464, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp 5", + "Priority": 50, + "Area": [ + {"x1": 1216, "y1": 520, "x2": 1240, "y2": 568}, + {"x1": 1248, "y1": 568, "x2": 1264, "y2": 592} + ], + "GoLocation": {"x": 1228, "y": 544, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Gypsy Camp 6", + "Priority": 50, + "Area": [{"x1": 1376, "y1": 416, "x2": 1408, "y2": 448}], + "GoLocation": {"x": 1392, "y": 432, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Lord Blackthorn's Ilshenar Castle", + "Priority": 50, + "Area": [ + {"x1": 1048, "y1": 616, "x2": 1120, "y2": 688}, + {"x1": 1080, "y1": 688, "x2": 1104, "y2": 712} + ], + "GoLocation": {"x": 1084, "y": 652, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Shrine of the Virtues", + "Priority": 50, + "Area": [{"x1": 1072, "y1": 376, "x2": 1136, "y2": 432}], + "GoLocation": {"x": 1104, "y": 404, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Pass of Karnaugh", + "Priority": 50, + "Area": [ + {"x1": 848, "y1": 288, "x2": 968, "y2": 336}, + {"x1": 936, "y1": 232, "x2": 968, "y2": 288}, + {"x1": 968, "y1": 248, "x2": 1088, "y2": 304}, + {"x1": 1000, "y1": 304, "x2": 1024, "y2": 320}, + {"x1": 1048, "y1": 304, "x2": 1184, "y2": 360}, + {"x1": 1184, "y1": 336, "x2": 1248, "y2": 392} + ], + "GoLocation": {"x": 908, "y": 312, "z": 0} + }, + { + "$type": "BaseRegion", + "Map": "Ilshenar", + "Name": "Vinculum Inn", + "Priority": 50, + "Area": [{"x1": 658, "y1": 654, "x2": 671, "y2": 678}], + "GoLocation": {"x": 656, "y": 666, "z": -36} + }, + { + "$type": "BaseRegion", + "NoLogoutDelay": true, + "Map": "Ilshenar", + "Parent": { "Name": "Vinculum Inn", "Map": "Ilshenar" }, + "Priority": 50, + "Area": [{"x1": 658, "y1": 654, "x2": 671, "y2": 678}] + }, + { + "$type": "NoHousingRegion", + "Map": "Malas", + "Name": "Ilse of the Divide", + "Priority": 50, + "Area": [{"x1": 1532, "y1": 857, "x2": 1832, "y2": 1057}], + "GoLocation": {"x": 1731, "y": 981, "z": -79} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2068, "y": 1372, "z": -75}, + "Map": "Malas", + "Name": "Bedlam", + "Priority": 50, + "Area": [{"x1": 75, "y1": 1590, "x2": 210, "y2": 1760}], + "GoLocation": {"x": 117, "y": 1681, "z": 0}, + "Music": "GrizzleDungeon" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1349, "y": 769, "z": 14}, + "Map": "Malas", + "Name": "The Citadel", + "Priority": 50, + "Area": [{"x1": 60, "y1": 1855, "x2": 195, "y2": 1990}], + "GoLocation": {"x": 106, "y": 1884, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "TownRegion", + "Map": "Malas", + "Name": "Luna", + "Priority": 50, + "Area": [{"x1": 945, "y1": 490, "x2": 1036, "y2": 551}], + "GoLocation": {"x": 989, "y": 520, "z": -50}, + "Music": "Tavern04" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Malas", + "Parent": { "Name": "Luna", "Map": "Malas" }, + "Priority": 50, + "Area": [{"x1": 986, "y1": 509, "x2": 1004, "y2": 530}] + }, + { + "$type": "TownRegion", + "Map": "Malas", + "Name": "Umbra", + "Priority": 50, + "Area": [ + {"x1": 2042, "y1": 1265, "x2": 2090, "y2": 1278}, + {"x1": 1960, "y1": 1278, "x2": 2106, "y2": 1413}, + {"x1": 2004, "y1": 1413, "x2": 2040, "y2": 1419} + ], + "GoLocation": {"x": 2049, "y": 1344, "z": -85} + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Malas", + "Parent": { "Name": "Umbra", "Map": "Malas" }, + "Priority": 50, + "Area": [{"x1": 2031, "y1": 1311, "x2": 2046, "y2": 1325}] + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 1731, "y": 981, "z": -79}, + "Map": "Malas", + "Name": "Labyrinth", + "Priority": 50, + "Area": [{"x1": 256, "y1": 1795, "x2": 499, "y2": 2025}], + "GoLocation": {"x": 336, "y": 1970, "z": 0}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2357, "y": 1268, "z": 0}, + "Map": "Malas", + "Name": "Doom", + "Priority": 50, + "Area": [{"x1": 256, "y1": 0, "x2": 512, "y2": 304}], + "GoLocation": {"x": 2366, "y": 1268, "z": -85}, + "Music": "Dungeon9" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 2357, "y": 1268, "z": 0}, + "Map": "Malas", + "Name": "Doom Gauntlet", + "Priority": 50, + "Area": [{"x1": 256, "y1": 304, "x2": 512, "y2": 560}], + "GoLocation": {"x": 429, "y": 340, "z": -1}, + "Music": "Dungeon9" + }, + { + "$type": "BaseRegion", + "Map": "Malas", + "Name": "Orc Fortress", + "Priority": 50, + "Area": [{"x1": 1295, "y1": 1189, "x2": 1395, "y2": 1294}], + "GoLocation": {"x": 1340, "y": 1226, "z": -90} + }, + { + "$type": "NoHousingRegion", + "Map": "Malas", + "Name": "Crystal Cave Entrance", + "Priority": 50, + "Area": [{"x1": 1176, "y1": 509, "x2": 1219, "y2": 522}], + "GoLocation": {"x": 1195, "y": 515, "z": -90} + }, + { + "$type": "NoHousingRegion", + "Map": "Malas", + "Name": "Protected Island", + "Priority": 50, + "Area": [ + {"x1": 1976, "y1": 101, "x2": 2448, "y2": 345}, + {"x1": 2051, "y1": 345, "x2": 2356, "y2": 382}, + {"x1": 2026, "y1": 345, "x2": 2051, "y2": 370}, + {"x1": 2069, "y1": 382, "x2": 2144, "y2": 396}, + {"x1": 2254, "y1": 382, "x2": 2352, "y2": 420}, + {"x1": 2222, "y1": 382, "x2": 2254, "y2": 402} + ], + "GoLocation": {"x": 2104, "y": 380, "z": -90} + }, + { + "$type": "NoHousingRegion", + "Map": "Malas", + "Name": "Grand Arena", + "Priority": 50, + "Area": [{"x1": 919, "y1": 622, "x2": 962, "y2": 652}], + "GoLocation": {"x": 961, "y": 637, "z": -90} + }, + { + "$type": "BaseRegion", + "NoLogoutDelay": true, + "Map": "Malas", + "Name": "Hanse's Hostel", + "Priority": 50, + "Area": [ + {"x1": 1049, "y1": 1420, "x2": 1062, "y2": 1445}, + {"x1": 1062, "y1": 1420, "x2": 1065, "y2": 1431}, + {"x1": 1062, "y1": 1439, "x2": 1065, "y2": 1445} + ], + "GoLocation": {"x": 1063, "y": 1435, "z": -90} + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 259, "y": 783, "z": 0}, + "Map": "Malas", + "Name": "Yomotsu Mines", + "Priority": 50, + "Area": [{"x1": 0, "y1": 0, "x2": 129, "y2": 129}], + "GoLocation": {"x": 6, "y": 118, "z": 0}, + "Music": "TokunoDungeon" + }, + { + "$type": "DungeonRegion", + "Entrance": {"x": 977, "y": 223, "z": 0}, + "Map": "Malas", + "Name": "Fan Dancer's Dojo", + "Priority": 50, + "Area": [{"x1": 40, "y1": 320, "x2": 210, "y2": 720}], + "GoLocation": {"x": 71, "y": 337, "z": 0}, + "Music": "TokunoDungeon" + }, + { + "$type": "BaseRegion", + "Map": "Malas", + "Name": "Samurai start location", + "Priority": 50, + "Area": [{"x1": 320, "y1": 690, "x2": 435, "y2": 790}], + "Music": "Zento" + }, + { + "$type": "BaseRegion", + "Map": "Malas", + "Name": "Ninja start location", + "Priority": 50, + "Area": [{"x1": 360, "y1": 795, "x2": 440, "y2": 835}], + "Music": "Zento" + }, + { + "$type": "BaseRegion", + "Map": "Malas", + "Name": "Ninja cave", + "Priority": 50, + "Area": [{"x1": 360, "y1": 960, "x2": 460, "y2": 1175}] + }, + { + "$type": "GuardedRegion", + "Map": "Tokuno", + "Name": "Moongates", + "Priority": 50, + "Area": [ + {"x1": 1167, "y1": 996, "x2": 1171, "y2": 1000}, + {"x1": 799, "y1": 1201, "x2": 805, "y2": 1207}, + {"x1": 267, "y1": 625, "x2": 274, "y2": 632} + ] + }, + { + "$type": "TownRegion", + "Map": "Tokuno", + "Name": "Zento", + "Priority": 50, + "Area": [{"x1": 656, "y1": 1192, "x2": 816, "y2": 1320}], + "GoLocation": {"x": 736, "y": 1256, "z": 30}, + "Music": "Zento" + }, + { + "$type": "TownRegion", + "NoLogoutDelay": true, + "Map": "Tokuno", + "Parent": { "Name": "Zento", "Map": "Tokuno" }, + "Priority": 50, + "Area": [{"x1": 674, "y1": 1203, "x2": 699, "y2": 1219}] + }, + { + "$type": "NoHousingRegion", + "Map": "Tokuno", + "Name": "Fan Dancer's Dojo", + "Priority": 50, + "Area": [{"x1": 969, "y1": 194, "x2": 992, "y2": 224}], + "GoLocation": {"x": 977, "y": 223, "z": 23} + }, + { + "$type": "NoHousingRegion", + "Map": "Tokuno", + "Name": "Bushido Dojo", + "Priority": 50, + "Area": [{ "x1": 283, "y1": 361, "x2": 351, "y2": 463 }], + "GoLocation": { "x": 320, "y": 408, "z": 32 } + }, + { + "$type": "DungeonRegion", + "Name": "Abyss", + "Map": "TerMur", + "Priority": 50, + "Music": "StygianAbyss", + "Area": [ + { "x1": 301, "y1": 328, "x2": 1076, "y2": 802 }, + { "x1": 780, "y1": 32, "x2": 1076, "y2": 328 }, + { "x1": 436, "y1": 802, "x2": 740, "y2": 980 }, + { "x1": 754, "y1": 881, "x2": 862, "y2": 974 }, + { "x1": 756, "y1": 1049, "x2": 860, "y2": 1142 }, + { "x1": 756, "y1": 1214, "x2": 861, "y2": 1313 }, + { "x1": 0, "y1": 684, "x2": 134, "y2": 860 }, + { "x1": 0, "y1": 485, "x2": 134, "y2": 647 }, + { "x1": 0, "y1": 286, "x2": 137, "y2": 445 }, + { "x1": 254, "y1": 70, "x2": 500, "y2": 253 } + ], + "GoLocation": { "x": 946, "y": 72, "z": 72 } + }, + { + "$type": "BaseRegion", + "Name": "Abyssal Lair", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 931, "y1": 314, "x2": 1031, "y2": 454 }], + "GoLocation": { "x": 985, "y": 366, "z": -11 } + }, + { + "$type": "BaseRegion", + "Name": "Medusas Lair", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 100, + "Area": [ + { "x1": 764, "y1": 894, "x2": 833, "y2": 983 }, + { "x1": 816, "y1": 749, "x2": 829, "y2": 765 } + ], + "GoLocation": { "x": 771, "y": 928, "z": 0 } + }, + { + "$type": "BaseRegion", + "Name": "NPC Encampment", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 100, + "LogoutDelay": false, + "GuardsDisabled": true, + "Area": [{ "x1": 1087, "y1": 1127, "z1": -42, "x2": 1096, "y2": 1133, "z2": 128 }], + "GoLocation": { "x": 1090, "y": 1130, "z": -42 }, + "Entrance": { "x": 1090, "y": 1132 } + }, + { + "$type": "BaseRegion", + "Name": "Cavern of the Discarded", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [ + { "x1": 887, "y1": 475, "x2": 1000, "y2": 575 }, + { "x1": 926, "y1": 533, "x2": 971, "y2": 553 }, + { "x1": 928, "y1": 543, "x2": 970, "y2": 573 }, + { "x1": 929, "y1": 536, "x2": 970, "y2": 564 }, + { "x1": 936, "y1": 559, "x2": 972, "y2": 573 }, + { "x1": 938, "y1": 541, "x2": 958, "y2": 572 }, + { "x1": 961, "y1": 527, "x2": 983, "y2": 559 } + ], + "GoLocation": { "x": 912, "y": 501, "z": -12 } + }, + { + "$type": "BaseRegion", + "Name": "Chamber of Virtue", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 739, "y1": 465, "x2": 760, "y2": 486 }], + "GoLocation": { "x": 747, "y": 474, "z": -17 } + }, + { + "$type": "BaseRegion", + "Name": "Crimson Veins", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 931, "y1": 135, "x2": 996, "y2": 200 }], + "GoLocation": { "x": 974, "y": 164, "z": -11 } + }, + { + "$type": "BaseRegion", + "Name": "Enslaved Goblins", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [ + { "x1": 538, "y1": 806, "x2": 613, "y2": 856 }, + { "x1": 539, "y1": 790, "x2": 614, "y2": 840 }, + { "x1": 553, "y1": 791, "x2": 613, "y2": 841 }, + { "x1": 560, "y1": 796, "x2": 581, "y2": 817 }, + { "x1": 570, "y1": 773, "x2": 613, "y2": 857 }, + { "x1": 571, "y1": 788, "x2": 596, "y2": 813 } + ], + "GoLocation": { "x": 581, "y": 815, "z": -45 } + }, + { + "$type": "BaseRegion", + "Name": "Fairy Dragon Lair", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 849, "y1": 254, "x2": 916, "y2": 297 }], + "GoLocation": { "x": 888, "y": 277, "z": 3 } + }, + { + "$type": "BaseRegion", + "Name": "Fire Temple Ruins", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [ + { "x1": 474, "y1": 766, "x2": 563, "y2": 789 }, + { "x1": 485, "y1": 760, "x2": 553, "y2": 789 }, + { "x1": 495, "y1": 750, "x2": 560, "y2": 779 }, + { "x1": 506, "y1": 750, "x2": 572, "y2": 782 }, + { "x1": 520, "y1": 739, "x2": 584, "y2": 771 } + ], + "GoLocation": { "x": 519, "y": 765, "z": -92 } + }, + { + "$type": "BaseRegion", + "Name": "Fractured City", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [ + { "x1": 664, "y1": 439, "x2": 731, "y2": 527 }, + { "x1": 726, "y1": 505, "x2": 771, "y2": 527 }, + { "x1": 712, "y1": 402, "x2": 820, "y2": 460 }, + { "x1": 720, "y1": 504, "x2": 746, "y2": 527 }, + { "x1": 771, "y1": 460, "x2": 810, "y2": 518 }, + { "x1": 808, "y1": 480, "x2": 820, "y2": 512 } + ], + "GoLocation": { "x": 780, "y": 445, "z": -15 } + }, + { + "$type": "BaseRegion", + "Name": "Lands of the Lich", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [ + { "x1": 499, "y1": 575, "x2": 570, "y2": 695 }, + { "x1": 554, "y1": 600, "x2": 628, "y2": 642 } + ], + "GoLocation": { "x": 538, "y": 656, "z": 8 } + }, + { + "$type": "BaseRegion", + "Name": "Lava Caldera", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 553, "y1": 860, "x2": 639, "y2": 948 }], + "GoLocation": { "x": 587, "y": 895, "z": -73 } + }, + { + "$type": "BaseRegion", + "Name": "Passage of Tears", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 638, "y1": 530, "x2": 738, "y2": 635 }], + "GoLocation": { "x": 685, "y": 579, "z": -15 } + }, + { + "$type": "BaseRegion", + "Name": "Secret Garden", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 413, "y1": 678, "x2": 489, "y2": 749 }], + "GoLocation": { "x": 462, "y": 719, "z": 22 } + }, + { + "$type": "BaseRegion", + "Name": "Serpents Lair", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [ + { "x1": 681, "y1": 679, "x2": 849, "y2": 790 }, + { "x1": 703, "y1": 656, "x2": 850, "y2": 789 }, + { "x1": 785, "y1": 640, "x2": 850, "y2": 775 } + ], + "GoLocation": { "x": 711, "y": 720, "z": -11 } + }, + { + "$type": "BaseRegion", + "Name": "Silver Sapling", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 311, "y1": 582, "x2": 379, "y2": 650 }], + "GoLocation": { "x": 341, "y": 619, "z": 26 } + }, + { + "$type": "BaseRegion", + "Name": "Skeletal Dragon", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 643, "y1": 809, "x2": 698, "y2": 872 }], + "GoLocation": { "x": 675, "y": 828, "z": -109 } + }, + { + "$type": "BaseRegion", + "Name": "Sutek the Mage", + "Map": "TerMur", + "Parent": { "Name": "Abyss", "Map": "TerMur" }, + "Priority": 50, + "Area": [{ "x1": 901, "y1": 574, "x2": 954, "y2": 614 }], + "GoLocation": { "x": 924, "y": 595, "z": -14 } + }, + { + "$type": "DungeonRegion", + "Name": "Underworld", + "Map": "TerMur", + "Priority": 50, + "Music": "HumanLevel", + "Area": [{ "x1": 898, "y1": 808, "x2": 1280, "y2": 1231 }], + "GoLocation": { "x": 1128, "y": 1207, "z": -2 } + }, + { + "$type": "DungeonRegion", + "Name": "Atoll Bend", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 1027, "y1": 3311, "x2": 1224, "y2": 3514 }], + "GoLocation": { "x": 1118, "y": 3408, "z": -42 } + }, + { + "$type": "DungeonRegion", + "Name": "Chicken Chase", + "Map": "TerMur", + "Priority": 50, + "rune": "Chicken Chase", + "Area": [{ "x1": 448, "y1": 3352, "x2": 613, "y2": 3480 }], + "GoLocation": { "x": 560, "y": 3412, "z": 37 } + }, + { + "$type": "DungeonRegion", + "Name": "Fishermans Reach", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 547, "y1": 2835, "x2": 745, "y2": 3102 }], + "GoLocation": { "x": 631, "y": 3035, "z": 36 } + }, + { + "$type": "DungeonRegion", + "Name": "Gated Isle", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 603, "y1": 3836, "x2": 815, "y2": 4043 }], + "GoLocation": { "x": 703, "y": 3934, "z": -31 } + }, + { + "$type": "DungeonRegion", + "Name": "High Plain", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 748, "y1": 2843, "x2": 981, "y2": 3050 }], + "GoLocation": { "x": 863, "y": 2931, "z": 38 } + }, + { + "$type": "BaseRegion", + "Name": "Holy City", + "Map": "TerMur", + "Priority": 50, + "Music": "Holycity", + "Area": [{ "x1": 922, "y1": 3838, "x2": 1071, "y2": 3965 }], + "GoLocation": { "x": 997, "y": 3869, "z": -42 } + }, + { + "$type": "DungeonRegion", + "Name": "Kepetch Waste", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 356, "y1": 3143, "x2": 523, "y2": 3224 }], + "GoLocation": { "x": 447, "y": 3188, "z": 20 } + }, + { + "$type": "DungeonRegion", + "Name": "Lost Settlement", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 454, "y1": 3708, "x2": 648, "y2": 3957 }], + "GoLocation": { "x": 526, "y": 3822, "z": -44 } + }, + { + "$type": "GuardedRegion", + "Name": "Moongates", + "Map": "TerMur", + "Priority": 50, + "Area": [ + { "x1": 852, "y1": 3525, "x2": 867, "y2": 3540 }, + { "x1": 925, "y1": 3988, "x2": 940, "y2": 4003 } + ] + }, + { + "$type": "DungeonRegion", + "Name": "Northern Steppes", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 694, "y1": 3020, "x2": 954, "y2": 3148 }], + "GoLocation": { "x": 822, "y": 3063, "z": 61 } + }, + { + "$type": "DungeonRegion", + "Name": "Raptor Island", + "Map": "TerMur", + "Priority": 50, + "Area": [ + { "x1": 712, "y1": 3765, "x2": 879, "y2": 3829 }, + { "x1": 728, "y1": 3809, "x2": 839, "y2": 3923 }, + { "x1": 769, "y1": 3721, "x2": 972, "y2": 3768 }, + { "x1": 808, "y1": 3692, "x2": 930, "y2": 3732 } + ], + "GoLocation": { "x": 816, "y": 3778, "z": -42 } + }, + { + "$type": "TownRegion", + "Name": "Royal City", + "Map": "TerMur", + "Priority": 50, + "Music": "RoyalCity", + "Area": [{ "x1": 624, "y1": 3296, "x2": 927, "y2": 3583 }], + "GoLocation": { "x": 750, "y": 3440, "z": -20 } + }, + { + "$type": "BaseRegion", + "Name": "Queen's Palace", + "Map": "TerMur", + "Parent": { "Name": "Royal City", "Map": "TerMur" }, + "Music": "QueenPalace", + "Area": [{ "x1": 717, "y1": 3339, "x2": 785, "y2": 3412 }], + "GoLocation": { "x": 748, "y": 3357, "z": 55 } + }, + { + "$type": "BaseRegion", + "Name": "Royal City Inn", + "Map": "TerMur", + "Parent": { "Name": "Royal City", "Map": "TerMur" }, + "LogoutDelay": false, + "Area": [ + { "x1": 724, "y1": 3463, "x2": 761, "y2": 3483 }, + { "x1": 747, "y1": 3480, "x2": 760, "y2": 3493 } + ] + }, + { + "$type": "DungeonRegion", + "Name": "Royal Park", + "Map": "TerMur", + "Priority": 50, + "Area": [ + { "x1": 632, "y1": 3233, "x2": 808, "y2": 3268 }, + { "x1": 753, "y1": 3161, "x2": 990, "y2": 3292 } + ], + "GoLocation": { "x": 711, "y": 3255, "z": -42 } + }, + { + "$type": "DungeonRegion", + "Name": "Slith Valley", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 1028, "y1": 3263, "x2": 1221, "y2": 3404 }], + "GoLocation": { "x": 1078, "y": 3331, "z": -42 } + }, + { + "$type": "DungeonRegion", + "Name": "Spider Island", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 1063, "y1": 3695, "x2": 1181, "y2": 3779 }], + "GoLocation": { "x": 1115, "y": 3730, "z": -42 } + }, + { + "$type": "DungeonRegion", + "Name": "Stygian Dragon Lair", + "Map": "TerMur", + "Priority": 50, + "Music": "StygianDragon", + "Area": [{ "x1": 258, "y1": 90, "x2": 489, "y2": 245 }], + "GoLocation": { "x": 367, "y": 155, "z": 0 } + }, + { + "$type": "DungeonRegion", + "Name": "Talon Point", + "Map": "TerMur", + "Priority": 50, + "Area": [ + { "x1": 632, "y1": 3830, "x2": 730, "y2": 3885 }, + { "x1": 629, "y1": 3786, "x2": 705, "y2": 3826 }, + { "x1": 656, "y1": 3750, "x2": 707, "y2": 3791 } + ], + "GoLocation": { "x": 676, "y": 3831, "z": -39 } + }, + { + "$type": "DungeonRegion", + "Name": "Toxic Desert", + "Map": "TerMur", + "Priority": 50, + "rune": "Toxic Desert", + "Area": [{ "x1": 1010, "y1": 2929, "z1": 37, "x2": 1156, "y2": 3049, "z2": 128 }], + "GoLocation": { "x": 1047, "y": 2980, "z": 62 }, + "Entrance": { "x": 1051, "y": 3022 } + }, + { + "$type": "DungeonRegion", + "Name": "Void Island", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 314, "y1": 3473, "x2": 581, "y2": 3699 }], + "GoLocation": { "x": 440, "y": 3577, "z": 38 } + }, + { + "$type": "DungeonRegion", + "Name": "Volcano", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 298, "y1": 2895, "x2": 499, "y2": 3117 }], + "GoLocation": { "x": 407, "y": 3010, "z": -24 } + }, + { + "$type": "DungeonRegion", + "Name": "Walled Circus", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 291, "y1": 3207, "x2": 445, "y2": 3354 }], + "GoLocation": { "x": 370, "y": 3273, "z": 0 } + }, + { + "$type": "DungeonRegion", + "Name": "Waterfall Point", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 635, "y1": 2862, "x2": 731, "y2": 2926 }], + "GoLocation": { "x": 661, "y": 2894, "z": 39 } + }, + { + "$type": "BaseRegion", + "Name": "Shrine of Singularity", + "Map": "TerMur", + "Priority": 50, + "Music": "CodexShrine", + "Area": [{ "x1": 978, "y1": 3786, "x2": 1018, "y2": 3838 }], + "GoLocation": { "x": 995, "y": 3806, "z": 0 } + }, + { + "$type": "DungeonRegion", + "Name": "Great Ape Lair", + "Map": "TerMur", + "Priority": 100, + "Area": [{ "x1": 832, "y1": 1363, "z1": -25, "x2": 938, "y2": 1474, "z2": 128 }], + "GoLocation": { "x": 926, "y": 1464, "z": 0 } + }, + { + "$type": "DungeonRegion", + "Name": "Zipactriotl Lair", + "Map": "TerMur", + "Priority": 100, + "Area": [{ "x1": 831, "y1": 2239, "z1": -25, "x2": 959, "y2": 2367, "z2": 128 }], + "GoLocation": { "x": 896, "y": 2304, "z": -19 } + }, + { + "$type": "DungeonRegion", + "Name": "Myrmidex Queen Lair", + "Map": "TerMur", + "Priority": 100, + "Area": [{ "x1": 704, "y1": 2248, "z1": -25, "x2": 828, "y2": 2363, "z2": 128 }], + "GoLocation": { "x": 766, "y": 2306, "z": 0 } + }, + { + "$type": "DungeonRegion", + "Name": "Kotl City", + "Map": "TerMur", + "Priority": 50, + "Area": [{ "x1": 435, "y1": 2270, "z1": -25, "x2": 675, "y2": 2490, "z2": 128 }], + "GoLocation": { "x": 542, "y": 2473, "z": 0 } + } +] diff --git a/src/Prima.Server/Handlers/CharacterHandler.cs b/src/Prima.Server/Handlers/CharacterHandler.cs index 1a7ec55..2ef1597 100644 --- a/src/Prima.Server/Handlers/CharacterHandler.cs +++ b/src/Prima.Server/Handlers/CharacterHandler.cs @@ -9,6 +9,7 @@ using Prima.UOData.Data.EventData; using Prima.UOData.Entities; using Prima.UOData.Entities.Db; +using Prima.UOData.Events.Login; using Prima.UOData.Id; using Prima.UOData.Interfaces.Services; using Prima.UOData.Packets; @@ -59,6 +60,7 @@ public async Task OnPacketReceived(NetworkSession session, CharacterCreation pac playerMobile.Name = packet.Name; + _worldManagerService.AddWorldEntity(playerMobile); await _databaseService.InsertAsync(characterEntity); @@ -66,6 +68,8 @@ public async Task OnPacketReceived(NetworkSession session, CharacterCreation pac TriggerCharacterCreatedEvent(packet); await session.SendPacketAsync(new ClientVersionReq()); + + await PublishEvent(new LoginCompleteEvent(session.Id)); } private void TriggerCharacterCreatedEvent(CharacterCreation packet) @@ -102,11 +106,13 @@ public async Task OnPacketReceived(NetworkSession session, CharacterLogin packet // TODO: Check if character exists - session.SetProperty(character.MobileId, "mobileId"); - var mobile = _worldManagerService.GetEntityBySerial(character.MobileId); + session.SetProperty(mobile); + await session.SendPacketAsync(new ClientVersionReq()); + + await PublishEvent(new LoginCompleteEvent(session.Id)); } public async Task OnPacketReceived(NetworkSession session, CharacterDelete packet) diff --git a/src/Prima.Server/Handlers/LoginCompleteHandler.cs b/src/Prima.Server/Handlers/LoginCompleteHandler.cs new file mode 100644 index 0000000..9152e41 --- /dev/null +++ b/src/Prima.Server/Handlers/LoginCompleteHandler.cs @@ -0,0 +1,45 @@ +using Orion.Core.Server.Listeners.EventBus; +using Prima.Core.Server.Handlers.Base; +using Prima.Core.Server.Interfaces.Services; +using Prima.Network.Packets; +using Prima.Network.Types; +using Prima.UOData.Entities; +using Prima.UOData.Events.Login; +using Prima.UOData.Packets; +using Prima.UOData.Types; + +namespace Prima.Server.Handlers; + +public class LoginCompleteHandler : BasePacketListenerHandler, IEventBusListener +{ + public LoginCompleteHandler( + ILogger logger, INetworkService networkService, IServiceProvider serviceProvider + ) : base(logger, networkService, serviceProvider) + { + } + + + protected override void RegisterHandlers() + { + SubscribeEvent(this); + } + + public async Task HandleAsync(LoginCompleteEvent @event, CancellationToken cancellationToken = default) + { + var session = SessionService.GetSession(@event.SessionId); + var mobile = session.GetProperty(); + + // Login confirmation packet + await session.SendPacketAsync(new CharLocaleAndBody(mobile)); + // GeneralInformation packet + await session.SendPacketAsync(new SeasonalInformation(Season.Spring, true)); + await session.SendPacketAsync(new DrawGamePlayer(mobile)); + await session.SendPacketAsync(new CharacterDraw(mobile)); + + await session.SendPacketAsync(new GlobalLightLevel(0xFF)); + await session.SendPacketAsync(new PersonalLightLevel(mobile, 0xFF)); + await session.SendPacketAsync(new FeatureFlagsResponse(FeatureFlags.UOR | FeatureFlags.AOS)); + await session.SendPacketAsync(new CharacterWarMode(false)); + await session.SendPacketAsync(new LoginComplete()); + } +} diff --git a/src/Prima.UOData/Context/UOContext.cs b/src/Prima.UOData/Context/UOContext.cs index 8092946..a8fe161 100644 --- a/src/Prima.UOData/Context/UOContext.cs +++ b/src/Prima.UOData/Context/UOContext.cs @@ -1,7 +1,10 @@ using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using Prima.Core.Server.Data; using Prima.Core.Server.Data.Uo; using Prima.Core.Server.Types.Uo; using Prima.UOData.Data; +using Prima.UOData.Interfaces.Services; using Prima.UOData.Types; namespace Prima.UOData.Context; @@ -16,6 +19,9 @@ public static class UOContext public static ExpansionInfo ExpansionInfo { get; set; } + public static IWorldManagerService WorldManagerService => + PrimaServerContext.ServiceProvider.GetRequiredService(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasProtocolChanges(ProtocolChanges changes) => (ClientVersion.ProtocolChanges & changes) != 0; diff --git a/src/Prima.UOData/Data/Tiles/LandTile.cs b/src/Prima.UOData/Data/Tiles/LandTile.cs new file mode 100644 index 0000000..9d5fa2e --- /dev/null +++ b/src/Prima.UOData/Data/Tiles/LandTile.cs @@ -0,0 +1,34 @@ +using System.Runtime.InteropServices; + +namespace Prima.UOData.Data.Tiles; + +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public struct LandTile +{ + internal short m_ID; + internal sbyte m_Z; + + public int ID => m_ID; + + public int Z + { + get => m_Z; + set => m_Z = (sbyte)value; + } + + public int Height => 0; + + public bool Ignored => m_ID is 2 or 0x1DB or >= 0x1AE and <= 0x1B5; + + public LandTile(short id, sbyte z) + { + m_ID = id; + m_Z = z; + } + + public void Set(short id, sbyte z) + { + m_ID = id; + m_Z = z; + } +} diff --git a/src/Prima.UOData/Data/Tiles/TileMatrix.cs b/src/Prima.UOData/Data/Tiles/TileMatrix.cs new file mode 100644 index 0000000..b71d1ae --- /dev/null +++ b/src/Prima.UOData/Data/Tiles/TileMatrix.cs @@ -0,0 +1,467 @@ +using System.Runtime.CompilerServices; +using Prima.Core.Server.Data.Uo; +using Prima.UOData.Context; +using Prima.UOData.Data.Geometry; +using Prima.UOData.Mul; +using Serilog; + +namespace Prima.UOData.Data.Tiles; + +public class TileMatrix +{ + public const int SectorShift = 3; + + private static readonly ILogger logger = Log.ForContext(); + private static readonly List _instances = new(); + + private readonly StaticTile[][][][][] _staticTiles; + private readonly LandTile[][][] _landTiles; + private readonly LandTile[] _invalidLandBlock; + private readonly StaticTile[][][] _emptyStaticBlock; + private readonly UOPEntry[] _uopMapEntries; + + private readonly int _fileIndex; + + //private readonly Map _map; + private readonly int[][] _staticPatches; + private readonly int[][] _landPatches; + private readonly List _fileShare = new(); + + public TileMatrixPatch Patch { get; } + public int BlockWidth { get; } + public int BlockHeight { get; } + public FileStream MapStream { get; } + public FileStream IndexStream { get; } + public FileStream DataStream { get; } + public BinaryReader IndexReader { get; } + + public static bool Pre6000ClientSupport { get; private set; } + + public static void Configure() + { + // Set to true to support < 6.0.0 clients where map0.mul is both Felucca & Trammel + var isPre6000Trammel = UOContext.ClientVersion != null && UOContext.ClientVersion < ClientVersion.Version6000; + Pre6000ClientSupport = + isPre6000Trammel; //ServerConfiguration.GetSetting("maps.enablePre6000Trammel", isPre6000Trammel); + } + +// public TileMatrix(Map owner, int fileIndex, int mapID, int width, int height) + + public TileMatrix(int fileIndex, int mapID, int width, int height) + { + lock (_instances) + { + for (var i = 0; i < _instances.Count; ++i) + { + var tm = _instances[i]; + + if (tm._fileIndex == fileIndex) + { + lock (_fileShare) + { + lock (tm._fileShare) + { + tm._fileShare.Add(this); + _fileShare.Add(tm); + } + } + } + } + + _instances.Add(this); + } + + _fileIndex = fileIndex; + BlockWidth = width >> SectorShift; + BlockHeight = height >> SectorShift; + + // _map = owner; + + if (fileIndex != 0x7F) + { + var mapFileIndex = Pre6000ClientSupport && mapID == 1 ? 0 : fileIndex; + + var mapPath = UoFiles.FindDataFile($"map{mapFileIndex}.mul", false); + + if (mapPath != null) + { + MapStream = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.Read); + } + else + { + mapPath = UoFiles.FindDataFile($"map{mapFileIndex}LegacyMUL.uop", false); + + if (mapPath != null) + { + MapStream = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.Read); + + var uopEntries = UOPFiles.ReadUOPIndexes(MapStream, ".dat", 0x14000, 5); + + _uopMapEntries = GC.AllocateUninitializedArray(uopEntries.Count); + uopEntries.Values.CopyTo(_uopMapEntries, 0); + + ConvertToMapEntries(MapStream); + } + else + { + logger.Warning("{File} was not found.", $"map{mapFileIndex}.mul"); + } + } + + var indexPath = UoFiles.FindDataFile($"staidx{mapFileIndex}.mul", false); + + if (indexPath != null) + { + IndexStream = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read); + IndexReader = new BinaryReader(IndexStream); + } + else + { + logger.Warning("{File} was not found.", $"staidx{mapFileIndex}.mul"); + } + + var staticsPath = UoFiles.FindDataFile($"statics{mapFileIndex}.mul", false); + + if (staticsPath != null) + { + DataStream = new FileStream(staticsPath, FileMode.Open, FileAccess.Read, FileShare.Read); + } + else + { + logger.Warning("{File} was not found.", $"statics{fileIndex}.mul"); + } + } + + _emptyStaticBlock = new StaticTile[8][][]; + + for (var i = 0; i < 8; ++i) + { + _emptyStaticBlock[i] = new StaticTile[8][]; + + for (var j = 0; j < 8; ++j) + { + _emptyStaticBlock[i][j] = new StaticTile[0]; + } + } + + _invalidLandBlock = new LandTile[196]; + + _landTiles = new LandTile[BlockWidth][][]; + _staticTiles = new StaticTile[BlockWidth][][][][]; + _staticPatches = new int[BlockWidth][]; + _landPatches = new int[BlockWidth][]; + + Patch = new TileMatrixPatch(this, fileIndex); + } + + public StaticTile[][][] EmptyStaticBlock => _emptyStaticBlock; + + public void SetStaticBlock(int x, int y, StaticTile[][][] value) + { + if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight) + { + return; + } + + _staticTiles[x] ??= new StaticTile[BlockHeight][][][]; + _staticTiles[x][y] = value; + + _staticPatches[x] ??= new int[(BlockHeight + 31) >> 5]; + _staticPatches[x][y >> 5] |= 1 << (y & 0x1F); + } + + public StaticTile[][][] GetStaticBlock(int x, int y) + { + if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || DataStream == null || IndexStream == null) + { + return _emptyStaticBlock; + } + + _staticTiles[x] ??= new StaticTile[BlockHeight][][][]; + + var tiles = _staticTiles[x][y]; + + if (tiles == null) + { + for (var i = 0; tiles == null && i < _fileShare.Count; ++i) + { + var shared = _fileShare[i]; + + lock (shared) + { + if (x < shared.BlockWidth && y < shared.BlockHeight) + { + var theirTiles = shared._staticTiles[x]; + + if (theirTiles != null) + { + tiles = theirTiles[y]; + } + + if (tiles != null) + { + var theirBits = shared._staticPatches[x]; + + if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0) + { + tiles = null; + } + } + } + } + } + + tiles ??= ReadStaticBlock(x, y); + + _staticTiles[x][y] = tiles; + } + + return tiles; + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public Map.StaticTileEnumerable GetStaticTiles(int x, int y) => new(_map, new Point2D(x, y), includeMultis: false); + // + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public Map.StaticTileEnumerable GetStaticAndMultiTiles(int x, int y) => new(_map, new Point2D(x, y)); + // + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public Map.StaticTileEnumerable GetMultiTiles(int x, int y) => new(_map, new Point2D(x, y), false); + + public void SetLandBlock(int x, int y, LandTile[] value) + { + if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight) + { + return; + } + + _landTiles[x] ??= new LandTile[BlockHeight][]; + _landTiles[x][y] = value; + + _landPatches[x] ??= new int[(BlockHeight + 31) >> 5]; + _landPatches[x][y >> 5] |= 1 << (y & 0x1F); + } + + public LandTile[] GetLandBlock(int x, int y) + { + if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || MapStream == null) + { + return _invalidLandBlock; + } + + _landTiles[x] ??= new LandTile[BlockHeight][]; + + var tiles = _landTiles[x][y]; + + if (tiles != null) + { + return tiles; + } + + for (var i = 0; tiles == null && i < _fileShare.Count; ++i) + { + var shared = _fileShare[i]; + + if (x < shared.BlockWidth && y < shared.BlockHeight) + { + var theirTiles = shared._landTiles[x]; + + if (theirTiles != null) + { + tiles = theirTiles[y]; + } + + if (tiles != null) + { + var theirBits = shared._landPatches[x]; + + if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0) + { + tiles = null; + } + } + } + } + + tiles ??= ReadLandBlock(x, y); + + _landTiles[x][y] = tiles; + + return tiles; + } + + public LandTile GetLandTile(int x, int y) + { + var tiles = GetLandBlock(x >> SectorShift, y >> SectorShift); + + return tiles[((y & 0x7) << 3) + (x & 0x7)]; + } + + private TileList[][] m_Lists; + + private StaticTile[] m_TileBuffer = new StaticTile[128]; + + private unsafe StaticTile[][][] ReadStaticBlock(int x, int y) + { + try + { + IndexReader.BaseStream.Seek((x * BlockHeight + y) * 12, SeekOrigin.Begin); + + var lookup = IndexReader.ReadInt32(); + var length = IndexReader.ReadInt32(); + + if (lookup < 0 || length <= 0) + { + return _emptyStaticBlock; + } + + var count = length / 7; + + DataStream.Seek(lookup, SeekOrigin.Begin); + + if (m_TileBuffer.Length < count) + { + m_TileBuffer = new StaticTile[count]; + } + + var staTiles = m_TileBuffer; + + fixed (StaticTile* pTiles = staTiles) + { + _ = DataStream.Read(new Span(pTiles, length)); + + if (m_Lists == null) + { + m_Lists = new TileList[8][]; + + for (var i = 0; i < 8; ++i) + { + m_Lists[i] = new TileList[8]; + + for (var j = 0; j < 8; ++j) + { + m_Lists[i][j] = new TileList(); + } + } + } + + var lists = m_Lists; + + StaticTile* pCur = pTiles, pEnd = pTiles + count; + + while (pCur < pEnd) + { + lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add(pCur); + + pCur = pCur + 1; + } + + var tiles = new StaticTile[8][][]; + + for (var i = 0; i < 8; ++i) + { + tiles[i] = new StaticTile[8][]; + + for (var j = 0; j < 8; ++j) + { + tiles[i][j] = lists[i][j].ToArray(); + } + } + + return tiles; + } + } + catch (EndOfStreamException ex) + { + if (DateTime.Now >= m_NextStaticWarning) + { + // logger.Warning("Warning: Static EOS for {0} ({1}, {2})", _map, x, y); + m_NextStaticWarning = DateTime.Now + TimeSpan.FromMinutes(1.0); + } + + return _emptyStaticBlock; + } + } + + private DateTime m_NextStaticWarning; + private DateTime m_NextLandWarning; + + + private unsafe LandTile[] ReadLandBlock(int x, int y) + { + try + { + long offset = (x * BlockHeight + y) * 196 + 4; + + if (_uopMapEntries != null) + { + offset = FindOffset(offset); + } + + MapStream.Seek(offset, SeekOrigin.Begin); + + var tiles = new LandTile[64]; + + fixed (LandTile* pTiles = tiles) + { + _ = MapStream.Read(new Span(pTiles, 192)); + } + + return tiles; + } + catch (Exception ex) + { + if (DateTime.Now >= m_NextLandWarning) + { + // logger.Warning("Warning: Land EOS for {0} ({1}, {2})", _map, x, y); + m_NextLandWarning = DateTime.Now + TimeSpan.FromMinutes(1.0); + } + + return _invalidLandBlock; + } + } + + public long FindOffset(long offset) + { + var total = 0; + + for (var i = 0; i < _uopMapEntries.Length; ++i) + { + var entry = _uopMapEntries[i]; + var newTotal = total + entry.Size; + + if (offset < newTotal) + { + return entry.Offset + (offset - total); + } + + total = newTotal; + } + + return -1; + } + + private void ConvertToMapEntries(FileStream stream) + { + // Sorting by offset to make seeking faster + Array.Sort(_uopMapEntries, (a, b) => a.Offset.CompareTo(b.Offset)); + + var reader = new BinaryReader(stream); + + for (var i = 0; i < _uopMapEntries.Length; ++i) + { + var entry = _uopMapEntries[i]; + stream.Seek(entry.Offset, SeekOrigin.Begin); + _uopMapEntries[i].Extra = reader.ReadInt32(); // order + } + + Array.Sort(_uopMapEntries, (a, b) => a.Extra.CompareTo(b.Extra)); + } + + public void Dispose() + { + MapStream?.Close(); + DataStream?.Close(); + IndexReader?.Close(); + } +} diff --git a/src/Prima.UOData/Data/Tiles/TileMatrixPatch.cs b/src/Prima.UOData/Data/Tiles/TileMatrixPatch.cs new file mode 100644 index 0000000..16c6b17 --- /dev/null +++ b/src/Prima.UOData/Data/Tiles/TileMatrixPatch.cs @@ -0,0 +1,162 @@ +using Prima.Core.Server.Data.Uo; +using Prima.UOData.Context; +using Prima.UOData.Mul; +using Prima.UOData.Types; + +namespace Prima.UOData.Data.Tiles; + +public class TileMatrixPatch +{ + private StaticTile[] _tileBuffer = new StaticTile[128]; + + public static bool PatchLandEnabled { get; private set; } + public static bool PatchStaticsEnabled { get; private set; } + + public int LandBlocks { get; } + public int StaticBlocks { get; } + + public TileMatrixPatch(TileMatrix matrix, int index) + { + if (PatchLandEnabled) + { + var mapDataPath = UoFiles.FindDataFile($"mapdif{index}.mul", false); + var mapIndexPath = UoFiles.FindDataFile($"mapdifl{index}.mul", false); + + if (mapDataPath != null && mapIndexPath != null) + { + LandBlocks = PatchLand(matrix, mapDataPath, mapIndexPath); + } + } + + if (PatchStaticsEnabled) + { + var staDataPath = UoFiles.FindDataFile($"stadif{index}.mul", false); + var staIndexPath = UoFiles.FindDataFile($"stadifl{index}.mul", false); + var staLookupPath = UoFiles.FindDataFile($"stadifi{index}.mul", false); + + if (staDataPath != null && staIndexPath != null && staLookupPath != null) + { + StaticBlocks = PatchStatics(matrix, staDataPath, staIndexPath, staLookupPath); + } + } + } + + public static void Configure() + { + if (UOContext.ClientVersion != null) + { + PatchLandEnabled = + UOContext.ClientVersion < ClientVersion.Version6000; + + // TODO: Should this be enabled for 6.0 through <7.0.9? + PatchStaticsEnabled = UOContext.ClientVersion < ClientVersion.Version6000; + } + } + + private unsafe int PatchLand(TileMatrix matrix, string dataPath, string indexPath) + { + using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var fsIndex = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var indexReader = new BinaryReader(fsIndex); + + var count = (int)(indexReader.BaseStream.Length / 4); + + for (var i = 0; i < count; i++) + { + var blockID = indexReader.ReadInt32(); + var x = Math.DivRem(blockID, matrix.BlockHeight, out var y); + + fsData.Seek(4, SeekOrigin.Current); + + var tiles = new LandTile[64]; + fixed (LandTile* pTiles = tiles) + { + _ = fsData.Read(new Span(pTiles, 192)); + } + + matrix.SetLandBlock(x, y, tiles); + } + + return count; + } + + private unsafe int PatchStatics(TileMatrix matrix, string dataPath, string indexPath, string lookupPath) + { + using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var fsIndex = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var fsLookup = new FileStream(lookupPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var indexReader = new BinaryReader(fsIndex); + using var lookupReader = new BinaryReader(fsLookup); + + var count = (int)(indexReader.BaseStream.Length / 4); + + var lists = new TileList[8][]; + + for (var x = 0; x < 8; ++x) + { + lists[x] = new TileList[8]; + + for (var y = 0; y < 8; ++y) + { + lists[x][y] = new TileList(); + } + } + + for (var i = 0; i < count; ++i) + { + var blockID = indexReader.ReadInt32(); + var blockX = blockID / matrix.BlockHeight; + var blockY = blockID % matrix.BlockHeight; + + var offset = lookupReader.ReadInt32(); + var length = lookupReader.ReadInt32(); + lookupReader.ReadInt32(); // Extra + + if (offset < 0 || length <= 0) + { + matrix.SetStaticBlock(blockX, blockY, matrix.EmptyStaticBlock); + continue; + } + + fsData.Seek(offset, SeekOrigin.Begin); + + var tileCount = length / 7; + + if (_tileBuffer.Length < tileCount) + { + _tileBuffer = new StaticTile[tileCount]; + } + + var staTiles = _tileBuffer; + + fixed (StaticTile* pTiles = staTiles) + { + _ = fsData.Read(new Span(pTiles, length)); + + StaticTile* pCur = pTiles, pEnd = pTiles + tileCount; + + while (pCur < pEnd) + { + lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add(pCur); + pCur += 1; + } + + var tiles = new StaticTile[8][][]; + + for (var x = 0; x < 8; ++x) + { + tiles[x] = new StaticTile[8][]; + + for (var y = 0; y < 8; ++y) + { + tiles[x][y] = lists[x][y].ToArray(); + } + } + + matrix.SetStaticBlock(blockX, blockY, tiles); + } + } + + return count; + } +} diff --git a/src/Prima.UOData/Entities/ItemEntity.cs b/src/Prima.UOData/Entities/ItemEntity.cs index f88b3d3..d1f20d9 100644 --- a/src/Prima.UOData/Entities/ItemEntity.cs +++ b/src/Prima.UOData/Entities/ItemEntity.cs @@ -9,6 +9,8 @@ namespace Prima.UOData.Entities; [SerializableHeader(0x02)] public class ItemEntity : BaseWorldEntity { + public short ModelId { get; set; } + public string Name { get; set; } public int Hue { get; set; } diff --git a/src/Prima.UOData/Entities/MobileEntity.cs b/src/Prima.UOData/Entities/MobileEntity.cs index 1044878..9cd6913 100644 --- a/src/Prima.UOData/Entities/MobileEntity.cs +++ b/src/Prima.UOData/Entities/MobileEntity.cs @@ -1,5 +1,6 @@ using MessagePack; using Prima.Core.Server.Attributes; +using Prima.Core.Server.Data.Session; using Prima.Core.Server.Types; using Prima.UOData.Data.Geometry; using Prima.UOData.Entities.Base; @@ -11,22 +12,57 @@ namespace Prima.UOData.Entities; [SerializableHeader(0x01)] public class MobileEntity : BaseWorldEntity { + public Serial ModelId { get; set; } public string Name { get; set; } + public string Title { get; set; } + + public CharacterStatus StatusFlag { get; set; } = CharacterStatus.Normal; + + public NetworkSession? NetworkSession { get; set; } public bool IsPlayer { get; set; } + public bool IsRunning { get; set; } + public CommandPermissionType AccessLevel { get; set; } + public Notoriety Notoriety { get; set; } = Notoriety.Friend; + public int Hue { get; set; } public Point3D Position { get; set; } - public Direction Direction { get; set; } + public Dictionary Items { get; set; } = new(); + public MobileEntity() { } + public void MoveForward(int distance) + { + var x = Position.X; + var y = Position.Y; + + switch (Direction) + { + case Direction.North: + y -= distance; + break; + case Direction.East: + x += distance; + break; + case Direction.South: + y += distance; + break; + case Direction.West: + x -= distance; + break; + } + + Position = new Point3D(x, y, Position.Z); + } + public MobileEntity(Serial serial) { Id = serial; diff --git a/src/Prima.UOData/Events/Login/LoginCompleteEvent.cs b/src/Prima.UOData/Events/Login/LoginCompleteEvent.cs new file mode 100644 index 0000000..e420353 --- /dev/null +++ b/src/Prima.UOData/Events/Login/LoginCompleteEvent.cs @@ -0,0 +1,3 @@ +namespace Prima.UOData.Events.Login; + +public record LoginCompleteEvent(string SessionId); diff --git a/src/Prima.UOData/Extensions/SpanExtensions.cs b/src/Prima.UOData/Extensions/SpanExtensions.cs new file mode 100644 index 0000000..3dba314 --- /dev/null +++ b/src/Prima.UOData/Extensions/SpanExtensions.cs @@ -0,0 +1,12 @@ +using Orion.Foundations.Spans; +using Prima.UOData.Id; + +namespace Prima.UOData.Extensions; + +public static class SpanExtensions +{ + public static void Write(this SpanWriter writer, Serial serial) + { + writer.Write((uint)serial); + } +} diff --git a/src/Prima.UOData/Extensions/StreamWriterExtension.cs b/src/Prima.UOData/Extensions/StreamWriterExtension.cs index 99cbc96..94236b4 100644 --- a/src/Prima.UOData/Extensions/StreamWriterExtension.cs +++ b/src/Prima.UOData/Extensions/StreamWriterExtension.cs @@ -82,6 +82,7 @@ public static Direction ReadDirection(this BinaryReader reader) } + public static void Write(this BinaryWriter writer, IHaveSerial serial) { writer.Write(serial.Id.Value); diff --git a/src/Prima.UOData/Js/Items/JSItemObject.cs b/src/Prima.UOData/Js/Items/JSItemObject.cs index fb1a0b3..09d2080 100644 --- a/src/Prima.UOData/Js/Items/JSItemObject.cs +++ b/src/Prima.UOData/Js/Items/JSItemObject.cs @@ -1,3 +1,5 @@ +using Prima.UOData.Types; + namespace Prima.UOData.Js.Items; public class JSItemObject @@ -10,7 +12,7 @@ public class JSItemObject public double Weight { get; set; } - public string Layer { get; set; } + public Layer Layer { get; set; } public int Amount { get; set; } diff --git a/src/Prima.UOData/Mul/UoFiles.cs b/src/Prima.UOData/Mul/UoFiles.cs index 838c870..45e7505 100644 --- a/src/Prima.UOData/Mul/UoFiles.cs +++ b/src/Prima.UOData/Mul/UoFiles.cs @@ -52,6 +52,7 @@ public static void ReLoadDirectory() ScanForFiles(RootDir); } + public static void ScanForFiles(string path = "") { RootDir = path; @@ -87,10 +88,20 @@ public static void SetMulPath(string path, string key) public static string GetFilePath(string fileName) { - return MulPath.GetValueOrDefault(fileName.ToLower()); } + public static string? FindDataFile(string fileName, bool throwError = true) + { + var filePath = MulPath.GetValueOrDefault(fileName.ToLower()); + if (filePath == null && throwError) + { + throw new FileNotFoundException($"File {fileName} not found in {RootDir}"); + } + + return filePath; + } + /// /// Compares given MD5 hash with hash of given file /// diff --git a/src/Prima.UOData/Packets/CharLocaleAndBody.cs b/src/Prima.UOData/Packets/CharLocaleAndBody.cs new file mode 100644 index 0000000..55c63a6 --- /dev/null +++ b/src/Prima.UOData/Packets/CharLocaleAndBody.cs @@ -0,0 +1,56 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; +using Prima.UOData.Data.Geometry; +using Prima.UOData.Entities; +using Prima.UOData.Id; +using Prima.UOData.Types; + +namespace Prima.UOData.Packets; + +public class CharLocaleAndBody : BaseUoNetworkPacket +{ + public Serial MobileId { get; set; } + public short BodyType { get; set; } + public Point3D Position { get; set; } + public Direction Direction { get; set; } + + public Point2D MapSize { get; set; } + + + public CharLocaleAndBody(MobileEntity mobile) : this() + { + MobileId = mobile.Id; + BodyType = 0x190; + Position = mobile.Position; + Direction = mobile.Direction; + MapSize = new Point2D(7168, 4096); + } + + public CharLocaleAndBody() : base(0x1B, 37) + { + } + + public override Span Write() + { + using var packetWriter = new SpanWriter(stackalloc byte[36], true); + + packetWriter.Write((uint)MobileId); + packetWriter.Write((int)0); + packetWriter.Write((short)BodyType); + packetWriter.Write((short)Position.X); + packetWriter.Write((short)Position.Y); + packetWriter.Write((byte)0); + packetWriter.Write((short)Position.Z); + packetWriter.Write((byte)Direction); + packetWriter.Write((int)0); + packetWriter.Write((int)0); + packetWriter.Write((byte)0); + packetWriter.Write((short)MapSize.X); + packetWriter.Write((short)MapSize.Y); + packetWriter.Write((short)0); + packetWriter.Write((int)0); + + + return packetWriter.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/CharacterDraw.cs b/src/Prima.UOData/Packets/CharacterDraw.cs new file mode 100644 index 0000000..1d893e8 --- /dev/null +++ b/src/Prima.UOData/Packets/CharacterDraw.cs @@ -0,0 +1,104 @@ +using System.Runtime.CompilerServices; +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; +using Prima.UOData.Data.Geometry; +using Prima.UOData.Entities; +using Prima.UOData.Extensions; +using Prima.UOData.Id; +using Prima.UOData.Types; + +namespace Prima.UOData.Packets; + +public class CharacterDraw : BaseUoNetworkPacket +{ + public Serial Serial { get; set; } + + public Serial ModelId { get; set; } + + public Point3D Position { get; set; } + + public Direction Direction { get; set; } + + public short Hue { get; set; } + + public Notoriety Notoriety { get; set; } + + public CharacterStatus StatusFlag { get; set; } + + public int ItemCount { get; set; } = 0; + + public readonly Dictionary Items = new(); + + public CharacterDraw(MobileEntity mobile) : this() + { + Serial = mobile.Id; + ModelId = mobile.ModelId; + Position = mobile.Position; + Direction = mobile.Direction; + StatusFlag = mobile.StatusFlag; + Hue = (short)mobile.Hue; + Notoriety = mobile.Notoriety; + StatusFlag = 0x00; + ItemCount = mobile.Items.Count; + + foreach (var item in mobile.Items) + { + Items.Add(item.Key, item.Value); + } + } + + public CharacterDraw() : base(0x78, -1) + { + } + + public override Span Write() + { + using var writer = new SpanWriter(stackalloc byte[10], true); + + writer.Write(Serial); + writer.Write(ModelId); + writer.Write((short)Position.X); + writer.Write((short)Position.Y); + writer.Write((short)Position.Z); + writer.Write((byte)Direction); + writer.Write(Hue); + writer.Write((byte)StatusFlag); + writer.Write((byte)Notoriety); + + if (ItemCount == 0) + { + writer.Write((int)0); + writer.Write((byte)0); + } + else + { + writer.Write(ItemCount); + + foreach (var (layer, item) in Items) + { + writer.Write(item.Id); + + var modelId = item.ModelId & 0x7FFF; + var writeHue = item.Hue != 0; + + if (writeHue) + { + modelId |= 0x8000; + } + + writer.Write(modelId); + writer.Write((byte)layer); + + if (writeHue) + { + writer.Write((short)item.Hue); + } + } + + writer.Write(0); + } + + + return writer.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/CharacterWarMode.cs b/src/Prima.UOData/Packets/CharacterWarMode.cs new file mode 100644 index 0000000..81cee5b --- /dev/null +++ b/src/Prima.UOData/Packets/CharacterWarMode.cs @@ -0,0 +1,31 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; + +namespace Prima.UOData.Packets; + +public class CharacterWarMode : BaseUoNetworkPacket +{ + public bool IsWarMode { get; set; } + + public CharacterWarMode() : base(0x72, 5) + { + } + + public CharacterWarMode(bool isWarMode = false) : this() + { + IsWarMode = isWarMode; + } + + + public override Span Write() + { + using var packetWriter = new SpanWriter(stackalloc byte[Length - 4], true); + + packetWriter.Write(IsWarMode); + packetWriter.Write((byte)0); + packetWriter.Write((byte)0x32); + packetWriter.Write((byte)0); + + return packetWriter.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/DrawGamePlayer.cs b/src/Prima.UOData/Packets/DrawGamePlayer.cs new file mode 100644 index 0000000..04b5819 --- /dev/null +++ b/src/Prima.UOData/Packets/DrawGamePlayer.cs @@ -0,0 +1,54 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; +using Prima.UOData.Data.Geometry; +using Prima.UOData.Entities; +using Prima.UOData.Id; +using Prima.UOData.Types; + +namespace Prima.UOData.Packets; + +public class DrawGamePlayer : BaseUoNetworkPacket +{ + public Serial MobileId { get; set; } + + public short BodyType { get; set; } + + public int Hue { get; set; } + + public byte StatusFlag { get; set; } = 0x00; + + public Point3D Position { get; set; } + + public Direction Direction { get; set; } + + public DrawGamePlayer(MobileEntity mobile) : this() + { + MobileId = mobile.Id; + BodyType = 0x190; + Position = mobile.Position; + Direction = mobile.Direction; + Hue = mobile.Hue; + } + + public DrawGamePlayer() : base(0x20, 19) + { + } + + public override Span Write() + { + using var writer = new SpanWriter(stackalloc byte[Length - 1]); + + writer.Write((uint)MobileId); + writer.Write((short)BodyType); + writer.Write((byte)0); + writer.Write((short)Hue); + writer.Write((byte)StatusFlag); + writer.Write((short)Position.X); + writer.Write((short)Position.Y); + writer.Write((short)0); + writer.Write((byte)Direction); + writer.Write((short)Position.Z); + + return writer.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/GeneralInfoPacket.cs b/src/Prima.UOData/Packets/GeneralInfoPacket.cs new file mode 100644 index 0000000..294ae44 --- /dev/null +++ b/src/Prima.UOData/Packets/GeneralInfoPacket.cs @@ -0,0 +1,108 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; + +namespace Prima.UOData.Packets; + +/// +/// Represents a General Information packet (0xBF) used for various extended functionality in the UO protocol. +/// This packet serves as a container for numerous different sub-packets identified by a subcommand. +/// +/// +/// OpCode: 0xBF +/// The General Information packet has a variable length and can contain different data +/// based on the subcommand value. It's one of the most versatile packets in the UO protocol +/// and is used for features added after the initial protocol design. +/// +public class GeneralInfoPacket : BaseUoNetworkPacket +{ + /// + /// Gets or sets the subcommand that identifies the specific type of information being sent. + /// + public ushort Subcommand { get; set; } + + /// + /// Gets or sets the raw payload data for this packet. + /// The content varies based on the subcommand. + /// + public byte[] Payload { get; set; } = []; + + /// + /// Initializes a new instance of the GeneralInfoPacket class. + /// + public GeneralInfoPacket() : base(0xBF, -1) // Variable length packet + { + } + + /// + /// Initializes a new instance of the GeneralInfoPacket class with the specified subcommand and payload. + /// + /// The subcommand value that identifies the specific type of information. + /// The raw payload data for this packet. + public GeneralInfoPacket(ushort subcommand, byte[] payload) : this() + { + Subcommand = subcommand; + Payload = payload ?? Array.Empty(); + } + + /// + /// Reads the packet data from the provided packet reader. + /// + /// The packet reader containing the packet data. + public override void Read(SpanReader reader) + { + // Skip the first 3 bytes (already read: opcode and packet length) + // if this causes issues, you may need to not skip them depending on how your reader is implemented + + // Read the subcommand + Subcommand = reader.ReadUInt16(); + + // Read remaining bytes as payload + var remainingLength = reader.Remaining; + if (remainingLength > 0) + { + Payload = new byte[remainingLength]; + Payload = reader.ReadBytes(remainingLength); + } + else + { + Payload = []; + } + } + + /// + /// Writes the packet data to a byte span. + /// + /// A span containing the serialized packet data. + public override Span Write() + { + // Calculate the total packet length + // 5 = 1 (opcode) + 2 (length) + 2 (subcommand) + int packetLength = 5 + Payload.Length; + + // Create a writer with the calculated capacity + using var writer = new SpanWriter(stackalloc byte[packetLength], true); + + // Write packet length (big endian) + writer.Write((ushort)packetLength); + + // Write subcommand + writer.Write(Subcommand); + + // Write payload + if (Payload.Length > 0) + { + writer.Write(Payload); + } + + return writer.ToSpan().Span; + } + + /// + /// Returns a string representation of this packet for debugging purposes. + /// + /// A string containing the packet type, OpCode, subcommand, and payload length. + public override string ToString() + { + return base.ToString() + $" {{ Subcommand: 0x{Subcommand:X4}, PayloadLength: {Payload.Length} }}"; + } +} diff --git a/src/Prima.UOData/Packets/GeneralInfoSubPacket.cs b/src/Prima.UOData/Packets/GeneralInfoSubPacket.cs new file mode 100644 index 0000000..843825b --- /dev/null +++ b/src/Prima.UOData/Packets/GeneralInfoSubPacket.cs @@ -0,0 +1,35 @@ +using Orion.Foundations.Spans; + +namespace Prima.UOData.Packets; + +/// +/// Base class for specialized sub-packets of the 0xBF General Information packet. +/// +public abstract class GeneralInfoSubPacket +{ + /// + /// Gets the subcommand value that identifies this specific type of information. + /// + public abstract ushort Subcommand { get; } + + /// + /// Serializes the sub-packet data to a byte array. + /// + /// A byte array containing the serialized sub-packet data. + public abstract byte[] Serialize(); + + /// + /// Deserializes the sub-packet data from a packet reader. + /// + /// The packet reader containing the sub-packet data. + public abstract void Deserialize(SpanReader reader); + + /// + /// Creates a General Information packet containing this sub-packet's data. + /// + /// A GeneralInfoPacket instance ready to be sent. + public GeneralInfoPacket CreatePacket() + { + return new GeneralInfoPacket(Subcommand, Serialize()); + } +} diff --git a/src/Prima.UOData/Packets/GlobalLightLevel.cs b/src/Prima.UOData/Packets/GlobalLightLevel.cs new file mode 100644 index 0000000..24c7999 --- /dev/null +++ b/src/Prima.UOData/Packets/GlobalLightLevel.cs @@ -0,0 +1,26 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; + +namespace Prima.UOData.Packets; + +public class GlobalLightLevel : BaseUoNetworkPacket +{ + public int LightLevel { get; set; } + + public GlobalLightLevel() : base(0x4F, 2) + { + } + + + public GlobalLightLevel(int lightLevel) : this() + { + LightLevel = lightLevel; + } + + public override Span Write() + { + using var packetWriter = new SpanWriter(stackalloc byte[2], true); + packetWriter.Write((sbyte)LightLevel); + return packetWriter.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/LoginComplete.cs b/src/Prima.UOData/Packets/LoginComplete.cs new file mode 100644 index 0000000..a4cd63b --- /dev/null +++ b/src/Prima.UOData/Packets/LoginComplete.cs @@ -0,0 +1,10 @@ +using Prima.Network.Packets.Base; + +namespace Prima.UOData.Packets; + +public class LoginComplete : BaseUoNetworkPacket +{ + public LoginComplete() : base(0x55, 1) + { + } +} diff --git a/src/Prima.UOData/Packets/PersonalLightLevel.cs b/src/Prima.UOData/Packets/PersonalLightLevel.cs new file mode 100644 index 0000000..1c6c1f7 --- /dev/null +++ b/src/Prima.UOData/Packets/PersonalLightLevel.cs @@ -0,0 +1,39 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; +using Prima.UOData.Id; +using Prima.UOData.Interfaces.Entities; + +namespace Prima.UOData.Packets; + +public class PersonalLightLevel : BaseUoNetworkPacket +{ + public Serial Serial { get; set; } + + public int LightLevel { get; set; } + + + + public PersonalLightLevel() : base(0x4E, 6) + { + } + + public PersonalLightLevel(IHaveSerial serial, int lightLevel) : this() + { + Serial = serial.Id; + LightLevel = lightLevel; + } + + public PersonalLightLevel(Serial serial, int lightLevel) : this() + { + Serial = serial; + LightLevel = lightLevel; + } + + public override Span Write() + { + using var packetWriter = new SpanWriter(stackalloc byte[Length -1]); + packetWriter.Write((uint)Serial); + packetWriter.Write((sbyte)LightLevel); + return packetWriter.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/SeasonalInformation.cs b/src/Prima.UOData/Packets/SeasonalInformation.cs new file mode 100644 index 0000000..c27fa7e --- /dev/null +++ b/src/Prima.UOData/Packets/SeasonalInformation.cs @@ -0,0 +1,30 @@ +using Orion.Foundations.Spans; +using Prima.Network.Packets.Base; +using Prima.UOData.Types; + +namespace Prima.UOData.Packets; + +public class SeasonalInformation : BaseUoNetworkPacket +{ + public Season Season { get; set; } + + public bool PlaySound { get; set; } + + public SeasonalInformation(Season season, bool playSound) : this() + { + } + + public SeasonalInformation() : base(0xBC, 3) + { + } + + public override Span Write() + { + using var packetWriter = new SpanWriter(stackalloc byte[3]); + + packetWriter.Write((byte)Season); + packetWriter.Write((byte)(PlaySound ? 1 : 0)); + + return packetWriter.Span.ToArray(); + } +} diff --git a/src/Prima.UOData/Packets/SubCommands/ScreenSizePacket.cs b/src/Prima.UOData/Packets/SubCommands/ScreenSizePacket.cs new file mode 100644 index 0000000..ebacbdf --- /dev/null +++ b/src/Prima.UOData/Packets/SubCommands/ScreenSizePacket.cs @@ -0,0 +1,68 @@ +using Orion.Foundations.Spans; + +namespace Prima.UOData.Packets.SubCommands; + +/// +/// Represents a Screen Size sub-packet of the General Information packet. +/// This packet is used to inform the server about the client's screen dimensions. +/// +/// +/// Subcommand: 0x05 +/// +public class ScreenSizePacket : GeneralInfoSubPacket +{ + /// + /// Gets the subcommand value for Screen Size information. + /// + public override ushort Subcommand => 0x05; + + /// + /// Gets or sets the client screen width in pixels. + /// + public ushort Width { get; set; } + + /// + /// Gets or sets the client screen height in pixels. + /// + public ushort Height { get; set; } + + /// + /// Initializes a new instance of the ScreenSizePacket class. + /// + public ScreenSizePacket() + { + } + + /// + /// Initializes a new instance of the ScreenSizePacket class with the specified dimensions. + /// + /// The client screen width in pixels. + /// The client screen height in pixels. + public ScreenSizePacket(ushort width, ushort height) + { + Width = width; + Height = height; + } + + /// + /// Serializes the screen size data to a byte array. + /// + /// A byte array containing the serialized screen size data. + public override byte[] Serialize() + { + using var writer = new SpanWriter(stackalloc byte[4], true); + writer.Write(Width); + writer.Write(Height); + return writer.ToSpan().Span.ToArray(); + } + + /// + /// Deserializes the screen size data from a packet reader. + /// + /// The packet reader containing the screen size data. + public override void Deserialize(SpanReader reader) + { + Width = reader.ReadUInt16(); + Height = reader.ReadUInt16(); + } +} diff --git a/src/Prima.UOData/Prima.UOData.csproj b/src/Prima.UOData/Prima.UOData.csproj index 10a7524..eeb9940 100644 --- a/src/Prima.UOData/Prima.UOData.csproj +++ b/src/Prima.UOData/Prima.UOData.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/src/Prima.UOData/Serializers/Base/BaseEntitySerializer.cs b/src/Prima.UOData/Serializers/Base/BaseEntitySerializer.cs index ae451e4..f3b1d55 100644 --- a/src/Prima.UOData/Serializers/Base/BaseEntitySerializer.cs +++ b/src/Prima.UOData/Serializers/Base/BaseEntitySerializer.cs @@ -8,10 +8,8 @@ namespace Prima.UOData.Serializers.Base; public abstract class BaseEntitySerializer : IEntitySerializer where TEntity : class, ISerializableEntity { public Type EntityType => typeof(TEntity); - public byte Header { get; } - protected BaseEntitySerializer() { var attribute = typeof(TEntity).GetCustomAttribute(); diff --git a/src/Prima.UOData/Serializers/Binary/BinaryItemSerializer.cs b/src/Prima.UOData/Serializers/Binary/BinaryItemSerializer.cs index ba7afef..310e227 100644 --- a/src/Prima.UOData/Serializers/Binary/BinaryItemSerializer.cs +++ b/src/Prima.UOData/Serializers/Binary/BinaryItemSerializer.cs @@ -32,4 +32,5 @@ public override byte[] Serialize(ItemEntity entity) return buffer.ToArray(); } + } diff --git a/src/Prima.UOData/Serializers/Binary/BinaryMobileSerializer.cs b/src/Prima.UOData/Serializers/Binary/BinaryMobileSerializer.cs index acef406..66f3e53 100644 --- a/src/Prima.UOData/Serializers/Binary/BinaryMobileSerializer.cs +++ b/src/Prima.UOData/Serializers/Binary/BinaryMobileSerializer.cs @@ -1,6 +1,7 @@ using Prima.UOData.Entities; using Prima.UOData.Extensions; using Prima.UOData.Serializers.Base; +using Prima.UOData.Types; namespace Prima.UOData.Serializers.Binary; @@ -21,6 +22,17 @@ public override object Deserialize(byte[] data) Direction = stream.ReadDirection() }; + // Read items count + var itemsCount = stream.ReadInt32(); + + for (var i = 0; i < itemsCount; i++) + { + var itemId = stream.ReadSerial(); + var layer = (Layer)stream.ReadByte(); + var item = new ItemEntity(itemId); + mobile.Items.Add(layer, item); + } + return mobile; } @@ -35,6 +47,15 @@ public override byte[] Serialize(MobileEntity entity) stream.Write(entity.Hue); stream.Write(entity.Position); stream.Write(entity.Direction); + // Start to write items + stream.Write(entity.Items.Count); + + foreach (var item in entity.Items) + { + stream.Write((byte)item.Key); + stream.Write(item.Value.Id); + } + return buffer.ToArray(); } diff --git a/src/Prima.UOData/Types/CharacterStatus.cs b/src/Prima.UOData/Types/CharacterStatus.cs new file mode 100644 index 0000000..11cee9f --- /dev/null +++ b/src/Prima.UOData/Types/CharacterStatus.cs @@ -0,0 +1,13 @@ +namespace Prima.UOData.Types; + +public enum CharacterStatus : byte +{ + Normal = 0x00, + Unknown = 0x01, + CanAlterPaperdoll = 0x02, + Poisoned = 0x04, + GoldenHealth = 0x08, + Unknown0x10 = 0x10, + Unknown0x20 = 0x20, + WarMode = 0x40 +} diff --git a/src/Prima.UOData/Types/GeneralInformationPacketSubcommand.cs b/src/Prima.UOData/Types/GeneralInformationPacketSubcommand.cs new file mode 100644 index 0000000..7f887d4 --- /dev/null +++ b/src/Prima.UOData/Types/GeneralInformationPacketSubcommand.cs @@ -0,0 +1,37 @@ +namespace Prima.UOData.Types; + +public enum GeneralInformationPacketSubcommand : byte +{ + InitializeFastWalkPrevention = 0x01, + AddKeyToFastWalkStack = 0x02, + CloseGenericGump = 0x04, + ScreenSize = 0x05, + PartySystem = 0x06, + SetCursorHueSetMep = 0x08, + SetCursorHue = 0x09, + ClientLanguage = 0x0B, + ClosedStatusGump = 0x0C, + ThreeDClientAction = 0x0e, + ClientType = 0x0F, + RequestPopupMenu = 0x13, + DisplayPopupMenu = 0x14, + PopupEntrySelection = 0x15, + CloseUserInterfaceWindows = 0x16, + CodexOfWisdom = 0x17, + EnableMapDiff = 0x18, + ExtendedStats = 0x19, + ExtendedStats2 = 0x1A, + NewSpellbook = 0x1B, + SpellSelectedClientSide = 0x1C, + SendHouseRevisionState = 0x1D, + CustomHousing = 0x20, + AOS_AbilityIconConfirm = 0x21, + Damage = 0x22, + SEAbilityChange = 0x25, + MountSpeed = 0x26, + ServerChangeRaceRequest = 0x2A, + ChangeRaceResponse = 0x2A, + UseTargetedItem = 0x2C, + CastTargetedSpell = 0x2D, + UseTargetedSkill = 0x2E, +} diff --git a/src/Prima.UOData/Types/Layer.cs b/src/Prima.UOData/Types/Layer.cs new file mode 100644 index 0000000..88f8261 --- /dev/null +++ b/src/Prima.UOData/Types/Layer.cs @@ -0,0 +1,169 @@ +namespace Prima.UOData.Types; + +public enum Layer : byte +{ + /// + /// Invalid layer. + /// + Invalid = 0x00, + + /// + /// First valid layer. Equivalent to Layer.OneHanded. + /// + FirstValid = 0x01, + + /// + /// One handed weapon. + /// + OneHanded = 0x01, + + /// + /// Two handed weapon or shield. + /// + TwoHanded = 0x02, + + /// + /// Shoes. + /// + Shoes = 0x03, + + /// + /// Pants. + /// + Pants = 0x04, + + /// + /// Shirts. + /// + Shirt = 0x05, + + /// + /// Helmets, hats, and masks. + /// + Helm = 0x06, + + /// + /// Gloves. + /// + Gloves = 0x07, + + /// + /// Rings. + /// + Ring = 0x08, + + /// + /// Talismans. + /// + Talisman = 0x09, + + /// + /// Gorgets and necklaces. + /// + Neck = 0x0A, + + /// + /// Hair. + /// + Hair = 0x0B, + + /// + /// Half aprons. + /// + Waist = 0x0C, + + /// + /// Torso, inner layer. + /// + InnerTorso = 0x0D, + + /// + /// Bracelets. + /// + Bracelet = 0x0E, + + /// + /// Unused. + /// + Unused_xF = 0x0F, + + /// + /// Beards and mustaches. + /// + FacialHair = 0x10, + + /// + /// Torso, outer layer. + /// + MiddleTorso = 0x11, + + /// + /// Earings. + /// + Earrings = 0x12, + + /// + /// Arms and sleeves. + /// + Arms = 0x13, + + /// + /// Cloaks. + /// + Cloak = 0x14, + + /// + /// Backpacks. + /// + Backpack = 0x15, + + /// + /// Torso, outer layer. + /// + OuterTorso = 0x16, + + /// + /// Leggings, outer layer. + /// + OuterLegs = 0x17, + + /// + /// Leggings, inner layer. + /// + InnerLegs = 0x18, + + /// + /// Last valid non-internal layer. Equivalent to Layer.InnerLegs. + /// + LastUserValid = 0x18, + + /// + /// Mount item layer. + /// + Mount = 0x19, + + /// + /// Vendor 'buy pack' layer. + /// + ShopBuy = 0x1A, + + /// + /// Vendor 'resale pack' layer. + /// + ShopResale = 0x1B, + + /// + /// Vendor 'sell pack' layer. + /// + ShopSell = 0x1C, + + /// + /// Bank box layer. + /// + Bank = 0x1D, + + /// + /// Last valid layer. Equivalent to Layer.Bank. + /// + LastValid = 0x1D +} diff --git a/src/Prima.UOData/Types/MusicName.cs b/src/Prima.UOData/Types/MusicName.cs new file mode 100644 index 0000000..c2f8b6e --- /dev/null +++ b/src/Prima.UOData/Types/MusicName.cs @@ -0,0 +1,110 @@ +namespace Prima.UOData.Types; + +public enum MusicName +{ + Invalid = -1, + OldUlt01 = 0, + Create1, + DragFlit, + OldUlt02, + OldUlt03, + OldUlt04, + OldUlt05, + OldUlt06, + Stones2, + Britain1, + Britain2, + Bucsden, + Jhelom, + LBCastle, + Linelle, + Magincia, + Minoc, + Ocllo, + Samlethe, + Serpents, + Skarabra, + Trinsic, + Vesper, + Wind, + Yew, + Cave01, + Dungeon9, + Forest_a, + InTown01, + Jungle_a, + Mountn_a, + Plains_a, + Sailing, + Swamp_a, + Tavern01, + Tavern02, + Tavern03, + Tavern04, + Combat1, + Combat2, + Combat3, + Approach, + Death, + Victory, + BTCastle, + Nujelm, + Dungeon2, + Cove, + Moonglow, + Zento, + TokunoDungeon, + Taiko, + DreadHornArea, + ElfCity, + GrizzleDungeon, + MelisandesLair, + ParoxysmusLair, + GwennoConversation, + GoodEndGame, + GoodVsEvil, + GreatEarthSerpents, + Humanoids_U9, + MinocNegative, + Paws, + SelimsBar, + SerpentIsleCombat_U7, + ValoriaShips, + TheWanderer, + Castle, + Festival, + Honor, + Medieval, + BattleOnStones, + Docktown, + GargoyleQueen, + GenericCombat, + Holycity, + HumanLevel, + LoginLoop, + NorthernForestBattleonStones, + PrimevalLich, + QueenPalace, + RoyalCity, + SlasherVeil, + StygianAbyss, + StygianDragon, + Void, + CodexShrine, + AnvilStrikeInMinoc, + ASkaranLullaby, + BlackthornsMarch, + DupresNightInTrinsic, + FayaxionAndTheSix, + FlightOfTheNexus, + GalehavenJaunt, + JhelomToArms, + MidnightInYew, + MoonglowSonata, + NewMaginciaMarch, + NujelmWaltz, + SherrysSong, + StarlightInBritain, + TheVesperMist, + NoMusic = 0x1FFF +} diff --git a/src/Prima.UOData/Types/Notoriety.cs b/src/Prima.UOData/Types/Notoriety.cs new file mode 100644 index 0000000..855275c --- /dev/null +++ b/src/Prima.UOData/Types/Notoriety.cs @@ -0,0 +1,12 @@ +namespace Prima.UOData.Types; + +public enum Notoriety : byte +{ + Innocent = 0x01, + Friend = 0x02, + Animal = 0x03, + Criminal = 0x04, + Enemy = 0x05, + Murdered = 0x06, + Invulnerable = 0x07, +} diff --git a/src/Prima.UOData/Types/Season.cs b/src/Prima.UOData/Types/Season.cs new file mode 100644 index 0000000..d7cda70 --- /dev/null +++ b/src/Prima.UOData/Types/Season.cs @@ -0,0 +1,10 @@ +namespace Prima.UOData.Types; + +public enum Season : byte +{ + Spring = 0, + Summer = 1, + Fall = 2, + Winter = 3, + Desolation = 4, +}