Updates Data to JSON (#159)

This commit is contained in:
Kamron Batman 2020-06-19 13:53:18 -07:00 • committed by GitHub
parent d2f7e08de4
commit 1246a135f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 18005 additions and 9152 deletions

View file

@ -0,0 +1,680 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Server.Items;
using CPA = Server.CommandPropertyAttribute;
namespace Server.Commands
{
public class Add
{
private static readonly Type m_EntityType = typeof(IEntity);
private static readonly Type m_ConstructibleType = typeof(ConstructibleAttribute);
private static readonly Type m_EnumType = typeof(Enum);
private static readonly Type m_TypeType = typeof(Type);
private static readonly Type m_ParsableType = typeof(ParsableAttribute);
private static readonly Type[] m_ParseTypes = { typeof(string) };
private static readonly object[] m_ParseArgs = new object[1];
private static readonly Type[] m_SignedNumerics =
{
typeof(long),
typeof(int),
typeof(short),
typeof(sbyte)
};
private static readonly Type[] m_UnsignedNumerics =
{
typeof(ulong),
typeof(uint),
typeof(ushort),
typeof(byte)
};
public static void Initialize()
{
CommandSystem.Register("Tile", AccessLevel.GameMaster, Tile_OnCommand);
CommandSystem.Register("TileRXYZ", AccessLevel.GameMaster, TileRXYZ_OnCommand);
CommandSystem.Register("TileXYZ", AccessLevel.GameMaster, TileXYZ_OnCommand);
CommandSystem.Register("TileZ", AccessLevel.GameMaster, TileZ_OnCommand);
CommandSystem.Register("TileAvg", AccessLevel.GameMaster, TileAvg_OnCommand);
CommandSystem.Register("Outline", AccessLevel.GameMaster, Outline_OnCommand);
CommandSystem.Register("OutlineRXYZ", AccessLevel.GameMaster, OutlineRXYZ_OnCommand);
CommandSystem.Register("OutlineXYZ", AccessLevel.GameMaster, OutlineXYZ_OnCommand);
CommandSystem.Register("OutlineZ", AccessLevel.GameMaster, OutlineZ_OnCommand);
CommandSystem.Register("OutlineAvg", AccessLevel.GameMaster, OutlineAvg_OnCommand);
}
public static void Invoke(Mobile from, Point3D start, Point3D end, string[] args, List<Container> packs = null,
bool outline = false, bool mapAvg = false)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0} {1} building ", from.AccessLevel, CommandLogging.Format(from));
if (start == end)
sb.AppendFormat("at {0} in {1}", start, from.Map);
else
sb.AppendFormat("from {0} to {1} in {2}", start, end, from.Map);
sb.Append(":");
for (int i = 0; i < args.Length; ++i)
sb.AppendFormat(" \"{0}\"", args[i]);
CommandLogging.WriteLine(from, sb.ToString());
string name = args[0];
FixArgs(ref args);
string[,] props = null;
for (int i = 0; i < args.Length; ++i)
if (Insensitive.Equals(args[i], "set"))
{
int remains = args.Length - i - 1;
if (remains >= 2)
{
props = new string[remains / 2, 2];
remains /= 2;
for (int j = 0; j < remains; ++j)
{
props[j, 0] = args[i + j * 2 + 1];
props[j, 1] = args[i + j * 2 + 2];
}
FixSetString(ref args, i);
}
break;
}
Type type = AssemblyHandler.FindFirstTypeForName(name);
if (!IsEntity(type))
{
from.SendMessage("No type with that name was found.");
return;
}
DateTime time = DateTime.UtcNow;
int built = BuildObjects(from, type, start, end, args, props, packs, outline, mapAvg);
if (built > 0)
from.SendMessage("{0} object{1} generated in {2:F1} seconds.", built, built != 1 ? "s" : "",
(DateTime.UtcNow - time).TotalSeconds);
else
SendUsage(type, from);
}
public static void FixSetString(ref string[] args, int index)
{
string[] old = args;
args = new string[index];
Array.Copy(old, 0, args, 0, index);
}
public static void FixArgs(ref string[] args)
{
string[] old = args;
args = new string[args.Length - 1];
Array.Copy(old, 1, args, 0, args.Length);
}
public static int BuildObjects(Mobile from, Type type, Point3D start, Point3D end, string[] args, string[,] props,
List<Container> packs, bool outline = false, bool mapAvg = false)
{
Utility.FixPoints(ref start, ref end);
PropertyInfo[] realProps = null;
if (props != null)
{
realProps = new PropertyInfo[props.GetLength(0)];
PropertyInfo[] allProps =
type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < realProps.Length; ++i)
{
PropertyInfo thisProp = null;
string propName = props[i, 0];
for (int j = 0; thisProp == null && j < allProps.Length; ++j)
if (Insensitive.Equals(propName, allProps[j].Name))
thisProp = allProps[j];
if (thisProp == null)
{
from.SendMessage("Property not found: {0}", propName);
}
else
{
CPA attr = Properties.GetCPA(thisProp);
if (attr == null)
from.SendMessage("Property ({0}) not found.", propName);
else if (from.AccessLevel < attr.WriteLevel)
from.SendMessage("Setting this property ({0}) requires at least {1} access level.", propName,
Mobile.GetAccessLevelName(attr.WriteLevel));
else if (!thisProp.CanWrite || attr.ReadOnly)
from.SendMessage("Property ({0}) is read only.", propName);
else
realProps[i] = thisProp;
}
}
}
ConstructorInfo[] ctors = type.GetConstructors();
for (int i = 0; i < ctors.Length; ++i)
{
ConstructorInfo ctor = ctors[i];
if (!IsConstructible(ctor, from.AccessLevel))
continue;
// Handle optional constructors
ParameterInfo[] paramList = ctor.GetParameters();
int totalParams = paramList.Count(t => !t.HasDefaultValue);
if (args.Length >= totalParams && args.Length <= paramList.Length)
{
object[] paramValues = ParseValues(paramList, args);
if (paramValues == null)
continue;
int built = Build(from, start, end, ctor, paramValues, props, realProps, packs, outline, mapAvg);
if (built > 0)
return built;
}
}
return 0;
}
public static object[] ParseValues(ParameterInfo[] paramList, string[] args)
{
object[] values = new object[paramList.Length];
for (int i = 0, a = 0; i < paramList.Length; i++)
{
ParameterInfo param = paramList[i];
object value = ParseValue(param.ParameterType, a < args.Length ? args[a++] : null);
if (value != null)
values[i] = value;
else if (param.HasDefaultValue)
values[i] = Type.Missing;
else
return null;
}
return values;
}
public static object ParseValue(Type type, string value)
{
try
{
if (IsEnum(type)) return Enum.Parse(type, value, true);
if (IsType(type)) return AssemblyHandler.FindFirstTypeForName(value);
if (IsParsable(type)) return ParseParsable(type, value);
object obj = value;
if (value?.StartsWith("0x") == true)
{
if (IsSignedNumeric(type))
obj = Convert.ToInt64(value.Substring(2), 16);
else if (IsUnsignedNumeric(type))
obj = Convert.ToUInt64(value.Substring(2), 16);
else
obj = Convert.ToInt32(value.Substring(2), 16);
}
if (obj == null && !type.IsValueType)
return null;
return Convert.ChangeType(obj, type);
}
catch
{
return null;
}
}
public static IEntity Build(Mobile from, ConstructorInfo ctor, object[] values, string[,] props,
PropertyInfo[] realProps, ref bool sendError)
{
object built = ctor.Invoke(values);
if (built != null && realProps != null)
{
bool hadError = false;
for (int i = 0; i < realProps.Length; ++i)
{
if (realProps[i] == null)
continue;
string result =
Properties.InternalSetValue(from, built, built, realProps[i], props[i, 1], props[i, 1], false);
if (result != "Property has been set.")
{
if (sendError)
from.SendMessage(result);
hadError = true;
}
}
if (hadError)
sendError = false;
}
return (IEntity)built;
}
public static int Build(Mobile from, Point3D start, Point3D end, ConstructorInfo ctor, object[] values,
string[,] props, PropertyInfo[] realProps, List<Container> packs, bool outline = false, bool mapAvg = false)
{
try
{
Map map = from.Map;
int width = end.X - start.X + 1;
int height = end.Y - start.Y + 1;
if (outline && (width < 3 || height < 3))
outline = false;
int objectCount;
if (packs != null)
objectCount = packs.Count;
else if (outline)
objectCount = (width + height - 2) * 2;
else
objectCount = width * height;
if (objectCount >= 20)
from.SendMessage("Constructing {0} objects, please wait.", objectCount);
bool sendError = true;
StringBuilder sb = new StringBuilder();
sb.Append("Serials: ");
if (packs != null)
{
for (int i = 0; i < packs.Count; ++i)
{
IEntity built = Build(from, ctor, values, props, realProps, ref sendError);
sb.AppendFormat("0x{0:X}; ", built.Serial.Value);
if (built is Item item)
packs[i].DropItem(item);
else if (built is Mobile m)
m.MoveToWorld(new Point3D(start.X, start.Y, start.Z), map);
}
}
else
{
int z = start.Z;
for (int x = start.X; x <= end.X; ++x)
for (int y = start.Y; y <= end.Y; ++y)
{
if (outline && x != start.X && x != end.X && y != start.Y && y != end.Y)
continue;
if (mapAvg)
z = map.GetAverageZ(x, y);
IEntity built = Build(from, ctor, values, props, realProps, ref sendError);
sb.AppendFormat("0x{0:X}; ", built.Serial.Value);
if (built is Item item)
item.MoveToWorld(new Point3D(x, y, z), map);
else if (built is Mobile m)
m.MoveToWorld(new Point3D(x, y, z), map);
}
}
CommandLogging.WriteLine(from, sb.ToString());
return objectCount;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return 0;
}
}
public static void SendUsage(Type type, Mobile from)
{
ConstructorInfo[] ctors = type.GetConstructors();
bool foundCtor = false;
for (int i = 0; i < ctors.Length; ++i)
{
ConstructorInfo ctor = ctors[i];
if (!IsConstructible(ctor, from.AccessLevel))
continue;
if (!foundCtor)
{
foundCtor = true;
from.SendMessage("Usage:");
}
SendCtor(type, ctor, from);
}
if (!foundCtor)
from.SendMessage("That type is not marked constructible.");
}
public static void SendCtor(Type type, ConstructorInfo ctor, Mobile from)
{
ParameterInfo[] paramList = ctor.GetParameters();
StringBuilder sb = new StringBuilder();
sb.Append(type.Name);
for (int i = 0; i < paramList.Length; ++i)
{
if (i != 0)
sb.Append(',');
sb.Append(' ');
sb.Append(paramList[i].ParameterType.Name);
sb.Append(' ');
sb.Append(paramList[i].Name);
}
from.SendMessage(sb.ToString());
}
private static void TileBox_Callback(Mobile from, Point3D start, Point3D end, TileState ts)
{
bool mapAvg = false;
switch (ts.m_ZType)
{
case TileZType.Fixed:
{
start.Z = end.Z = ts.m_FixedZ;
break;
}
case TileZType.MapAverage:
{
mapAvg = true;
break;
}
}
Invoke(from, start, end, ts.m_Args, null, ts.m_Outline, mapAvg);
}
private static void Internal_OnCommand(CommandEventArgs e, bool outline)
{
Mobile from = e.Mobile;
if (e.Length >= 1)
BoundingBoxPicker.Begin(from, (map, start, end) =>
TileBox_Callback(from, start, end, new TileState(TileZType.Start, 0, e.Arguments, outline)));
else
from.SendMessage("Format: {0} <type> [params] [set {{<propertyName> <value> ...}}]",
outline ? "Outline" : "Tile");
}
private static void InternalRXYZ_OnCommand(CommandEventArgs e, bool outline)
{
if (e.Length >= 6)
{
Point3D p = new Point3D(e.Mobile.X + e.GetInt32(0), e.Mobile.Y + e.GetInt32(1), e.Mobile.Z + e.GetInt32(4));
Point3D p2 = new Point3D(p.X + e.GetInt32(2) - 1, p.Y + e.GetInt32(3) - 1, p.Z);
string[] subArgs = new string[e.Length - 5];
for (int i = 0; i < subArgs.Length; ++i)
subArgs[i] = e.Arguments[i + 5];
Invoke(e.Mobile, p, p2, subArgs, null, outline);
}
else
{
e.Mobile.SendMessage(
"Format: {0}RXYZ <x> <y> <w> <h> <z> <type> [params] [set {{<propertyName> <value> ...}}]",
outline ? "Outline" : "Tile");
}
}
private static void InternalXYZ_OnCommand(CommandEventArgs e, bool outline)
{
if (e.Length >= 6)
{
Point3D p = new Point3D(e.GetInt32(0), e.GetInt32(1), e.GetInt32(4));
Point3D p2 = new Point3D(p.X + e.GetInt32(2) - 1, p.Y + e.GetInt32(3) - 1, e.GetInt32(4));
string[] subArgs = new string[e.Length - 5];
for (int i = 0; i < subArgs.Length; ++i)
subArgs[i] = e.Arguments[i + 5];
Invoke(e.Mobile, p, p2, subArgs, null, outline);
}
else
{
e.Mobile.SendMessage(
"Format: {0}XYZ <x> <y> <w> <h> <z> <type> [params] [set {{<propertyName> <value> ...}}]",
outline ? "Outline" : "Tile");
}
}
private static void InternalZ_OnCommand(CommandEventArgs e, bool outline)
{
Mobile from = e.Mobile;
if (e.Length >= 2)
{
string[] subArgs = new string[e.Length - 1];
for (int i = 0; i < subArgs.Length; ++i)
subArgs[i] = e.Arguments[i + 1];
BoundingBoxPicker.Begin(from, (map, start, end) =>
TileBox_Callback(from, start, end, new TileState(TileZType.Fixed, e.GetInt32(0), subArgs, outline)));
}
else
{
from.SendMessage("Format: {0}Z <z> <type> [params] [set {{<propertyName> <value> ...}}]",
outline ? "Outline" : "Tile");
}
}
private static void InternalAvg_OnCommand(CommandEventArgs e, bool outline)
{
Mobile from = e.Mobile;
if (e.Length >= 1)
BoundingBoxPicker.Begin(from, (map, start, end) =>
TileBox_Callback(from, start, end, new TileState(TileZType.MapAverage, 0, e.Arguments, outline)));
else
from.SendMessage("Format: {0}Avg <type> [params] [set {{<propertyName> <value> ...}}]",
outline ? "Outline" : "Tile");
}
[Usage("Tile <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name into a targeted bounding box. Optional constructor parameters. Optional set property list.")]
public static void Tile_OnCommand(CommandEventArgs e)
{
Internal_OnCommand(e, false);
}
[Usage("TileRXYZ <x> <y> <w> <h> <z> <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name into a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list.")]
public static void TileRXYZ_OnCommand(CommandEventArgs e)
{
InternalRXYZ_OnCommand(e, false);
}
[Usage("TileXYZ <x> <y> <w> <h> <z> <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name into a given bounding box. Optional constructor parameters. Optional set property list.")]
public static void TileXYZ_OnCommand(CommandEventArgs e)
{
InternalXYZ_OnCommand(e, false);
}
[Usage("TileZ <z> <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name into a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list.")]
public static void TileZ_OnCommand(CommandEventArgs e)
{
InternalZ_OnCommand(e, false);
}
[Usage("TileAvg <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name into a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list.")]
public static void TileAvg_OnCommand(CommandEventArgs e)
{
InternalAvg_OnCommand(e, false);
}
[Usage("Outline <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name around a targeted bounding box. Optional constructor parameters. Optional set property list.")]
public static void Outline_OnCommand(CommandEventArgs e)
{
Internal_OnCommand(e, true);
}
[Usage("OutlineRXYZ <x> <y> <w> <h> <z> <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name around a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list.")]
public static void OutlineRXYZ_OnCommand(CommandEventArgs e)
{
InternalRXYZ_OnCommand(e, true);
}
[Usage("OutlineXYZ <x> <y> <w> <h> <z> <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name around a given bounding box. Optional constructor parameters. Optional set property list.")]
public static void OutlineXYZ_OnCommand(CommandEventArgs e)
{
InternalXYZ_OnCommand(e, true);
}
[Usage("OutlineZ <z> <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name around a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list.")]
public static void OutlineZ_OnCommand(CommandEventArgs e)
{
InternalZ_OnCommand(e, true);
}
[Usage("OutlineAvg <name> [params] [set {<propertyName> <value> ...}]")]
[Description(
"Tiles an item or npc by name around a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list.")]
public static void OutlineAvg_OnCommand(CommandEventArgs e)
{
InternalAvg_OnCommand(e, true);
}
public static bool IsEntity(Type t) => m_EntityType.IsAssignableFrom(t);
public static bool IsConstructible(ConstructorInfo ctor, AccessLevel accessLevel)
{
object[] attrs = ctor.GetCustomAttributes(m_ConstructibleType, false);
return attrs.Length != 0 && accessLevel >= ((ConstructibleAttribute)attrs[0]).AccessLevel;
}
public static bool IsEnum(Type type) => type.IsSubclassOf(m_EnumType);
public static bool IsType(Type type) => type == m_TypeType || type.IsSubclassOf(m_TypeType);
public static bool IsParsable(Type type) => type.IsDefined(m_ParsableType, false);
public static object ParseParsable(Type type, string value)
{
MethodInfo method = type.GetMethod("Parse", m_ParseTypes);
m_ParseArgs[0] = value;
return method?.Invoke(null, m_ParseArgs);
}
public static bool IsSignedNumeric(Type type)
{
for (int i = 0; i < m_SignedNumerics.Length; ++i)
if (type == m_SignedNumerics[i])
return true;
return false;
}
public static bool IsUnsignedNumeric(Type type)
{
for (int i = 0; i < m_UnsignedNumerics.Length; ++i)
if (type == m_UnsignedNumerics[i])
return true;
return false;
}
private enum TileZType
{
Start,
Fixed,
MapAverage
}
private class TileState
{
public readonly string[] m_Args;
public readonly int m_FixedZ;
public readonly bool m_Outline;
public readonly TileZType m_ZType;
public TileState(TileZType zType, int fixedZ, string[] args, bool outline)
{
m_ZType = zType;
m_FixedZ = fixedZ;
m_Args = args;
m_Outline = outline;
}
}
}
}

View file

@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Server.Network;
using Server.Targeting;
namespace Server.Gumps
{
public class AddGump : Gump
{
private static readonly Type typeofItem = typeof(Item);
private static readonly Type typeofMobile = typeof(Mobile);
private readonly int m_Page;
private readonly Type[] m_SearchResults;
private readonly string m_SearchString;
public AddGump(Mobile from, string searchString, int page, Type[] searchResults, bool explicitSearch) : base(50, 50)
{
m_SearchString = searchString;
m_SearchResults = searchResults;
m_Page = page;
from.CloseGump<AddGump>();
AddPage(0);
AddBackground(0, 0, 420, 280, 5054);
AddImageTiled(10, 10, 400, 20, 2624);
AddAlphaRegion(10, 10, 400, 20);
AddImageTiled(41, 11, 184, 18, 0xBBC);
AddImageTiled(42, 12, 182, 16, 2624);
AddAlphaRegion(42, 12, 182, 16);
AddButton(10, 9, 4011, 4013, 1);
AddTextEntry(44, 10, 180, 20, 0x480, 0, searchString);
AddHtmlLocalized(230, 10, 100, 20, 3010005, 0x7FFF);
AddImageTiled(10, 40, 400, 200, 2624);
AddAlphaRegion(10, 40, 400, 200);
if (searchResults.Length > 0)
for (int i = page * 10; i < (page + 1) * 10 && i < searchResults.Length; ++i)
{
int index = i % 10;
AddLabel(44, 39 + index * 20, 0x480, searchResults[i].Name);
AddButton(10, 39 + index * 20, 4023, 4025, 4 + i);
}
else
AddLabel(15, 44, 0x480, explicitSearch ? "Nothing matched your search terms." : "No results to display.");
AddImageTiled(10, 250, 400, 20, 2624);
AddAlphaRegion(10, 250, 400, 20);
if (m_Page > 0)
AddButton(10, 249, 4014, 4016, 2);
else
AddImage(10, 249, 4014);
AddHtmlLocalized(44, 250, 170, 20, 1061028, m_Page > 0 ? 0x7FFF : 0x5EF7); // Previous page
if ((m_Page + 1) * 10 < searchResults.Length)
AddButton(210, 249, 4005, 4007, 3);
else
AddImage(210, 249, 4005);
AddHtmlLocalized(244, 250, 170, 20, 1061027, (m_Page + 1) * 10 < searchResults.Length ? 0x7FFF : 0x5EF7); // Next page
}
public static void Initialize()
{
CommandSystem.Register("AddMenu", AccessLevel.GameMaster, AddMenu_OnCommand);
}
[Usage("AddMenu [searchString]")]
[Description(
"Opens an add menu, with an optional initial search string. This menu allows you to search for Items or Mobiles and add them interactively.")]
private static void AddMenu_OnCommand(CommandEventArgs e)
{
string val = e.ArgString.Trim();
Type[] types;
bool explicitSearch = false;
if (val.Length == 0)
{
types = Type.EmptyTypes;
}
else if (val.Length < 3)
{
e.Mobile.SendMessage("Invalid search string.");
types = Type.EmptyTypes;
}
else
{
types = Match(val).ToArray();
explicitSearch = true;
}
e.Mobile.SendGump(new AddGump(e.Mobile, val, 0, types, explicitSearch));
}
private static void Match(string match, Type[] types, List<Type> results)
{
if (match.Length == 0)
return;
match = match.ToLower();
for (int i = 0; i < types.Length; ++i)
{
Type t = types[i];
if ((typeofMobile.IsAssignableFrom(t) || typeofItem.IsAssignableFrom(t)) &&
t.Name.ToLower().IndexOf(match) >= 0 && !results.Contains(t))
{
ConstructorInfo[] ctors = t.GetConstructors();
for (int j = 0; j < ctors.Length; ++j)
if (ctors[j].GetParameters().Length == 0 &&
ctors[j].IsDefined(typeof(ConstructibleAttribute), false))
{
results.Add(t);
break;
}
}
}
}
public static List<Type> Match(string match)
{
List<Type> results = new List<Type>();
Type[] types;
Assembly[] asms = AssemblyHandler.Assemblies;
for (int i = 0; i < asms.Length; ++i)
{
types = AssemblyHandler.GetTypeCache(asms[i]).Types.ToArray();
Match(match, types, results);
}
types = AssemblyHandler.GetTypeCache(Core.Assembly).Types.ToArray();
Match(match, types, results);
results.Sort(new TypeNameComparer());
return results;
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
switch (info.ButtonID)
{
case 1: // Search
{
TextRelay te = info.GetTextEntry(0);
string match = te?.Text.Trim() ?? "";
if (match.Length < 3)
{
from.SendMessage("Invalid search string.");
from.SendGump(new AddGump(from, match, m_Page, m_SearchResults, false));
}
else
{
from.SendGump(new AddGump(from, match, 0, Match(match).ToArray(), true));
}
break;
}
case 2: // Previous page
{
if (m_Page > 0)
from.SendGump(new AddGump(from, m_SearchString, m_Page - 1, m_SearchResults, true));
break;
}
case 3: // Next page
{
if ((m_Page + 1) * 10 < m_SearchResults.Length)
from.SendGump(new AddGump(from, m_SearchString, m_Page + 1, m_SearchResults, true));
break;
}
default:
{
int index = info.ButtonID - 4;
if (index >= 0 && index < m_SearchResults.Length)
{
from.SendMessage("Where do you wish to place this object? <ESC> to cancel.");
from.Target = new InternalTarget(m_SearchResults[index], m_SearchResults, m_SearchString, m_Page);
}
break;
}
}
}
private class TypeNameComparer : IComparer<Type>
{
public int Compare(Type x, Type y) => x.Name.CompareTo(y.Name);
}
public class InternalTarget : Target
{
private readonly int m_Page;
private readonly Type[] m_SearchResults;
private readonly string m_SearchString;
private readonly Type m_Type;
public InternalTarget(Type type, Type[] searchResults, string searchString, int page) : base(-1, true,
TargetFlags.None)
{
m_Type = type;
m_SearchResults = searchResults;
m_SearchString = searchString;
m_Page = page;
}
protected override void OnTarget(Mobile from, object o)
{
if (o is IPoint3D p)
{
if (p is Item item)
p = item.GetWorldTop();
else if (p is Mobile m)
p = m.Location;
Commands.Add.Invoke(from, new Point3D(p), new Point3D(p), new[] { m_Type.Name });
from.Target = new InternalTarget(m_Type, m_SearchResults, m_SearchString, m_Page);
}
}
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
{
if (cancelType == TargetCancelType.Canceled)
from.SendGump(new AddGump(from, m_SearchString, m_Page, m_SearchResults, true));
}
}
}
}

View file

@ -0,0 +1,28 @@
using Server.Gumps;
namespace Server.Commands
{
public class CAGCategory : CAGNode
{
private static CAGCategory m_Root;
public CAGCategory(string title, CAGCategory parent = null)
{
Title = title;
Parent = parent;
}
public override string Title { get; }
public CAGNode[] Nodes { get; set; }
public CAGCategory Parent { get; }
public static CAGCategory Root => m_Root ??= CAGLoader.Load();
public override void OnClick(Mobile from, int page)
{
from.SendGump(new CategorizedAddGump(from, this));
}
}
}

View file

@ -0,0 +1,68 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json.Serialization;
using Server.Json;
namespace Server.Commands
{
public static class CAGLoader
{
public static CAGCategory Load()
{
var root = new CAGCategory("Add Menu");
var path = Path.Combine(Core.BaseDirectory, "Data/objects.json");
List<CAGJson> list = JsonConfig.Deserialize<List<CAGJson>>(path);
// Not an optimized solution
foreach (var cag in list)
{
var parent = root;
// Navigate through the dot notation categories until we find the last one
var categories = cag.Category.Split(".");
for (int i = 0; i < categories.Length; i++)
{
var category = categories[i];
var oldParent = parent;
for (int j = 0; j < parent.Nodes.Length; j++)
{
var node = parent.Nodes[i];
if (category == node.Title && node is CAGCategory cat)
{
parent = cat;
break;
}
}
if (parent == oldParent)
parent = new CAGCategory(category, parent);
}
// Set the objects associated with the child most node
parent.Nodes = new CAGNode[cag.Objects.Length];
for (int i = 0; i < cag.Objects.Length; i++)
{
var obj = cag.Objects[i];
obj.Parent = parent;
parent.Nodes[i] = obj;
}
}
return root;
}
}
public class CAGJson
{
public CAGJson()
{
}
[JsonPropertyName("category")]
public string Category { get; set; }
[JsonPropertyName("objects")]
public CAGObject[] Objects { get; set; }
}
}

View file

@ -0,0 +1,8 @@
namespace Server.Commands
{
public abstract class CAGNode
{
public abstract string Title { get; }
public abstract void OnClick(Mobile from, int page);
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.Text.Json.Serialization;
using Server.Gumps;
namespace Server.Commands
{
public class CAGObject : CAGNode
{
public CAGObject()
{
}
[JsonPropertyName("type")]
public Type Type { get; set; }
[JsonPropertyName("gfx")]
public int ItemID { get; set; }
[JsonPropertyName("hue")]
public int? Hue { get; set; }
public CAGCategory Parent { get; set; }
public override string Title => Type == null ? "bad type" : Type.Name;
public override void OnClick(Mobile from, int page)
{
if (Type == null)
{
from.SendMessage("That is an invalid type name.");
}
else
{
CommandSystem.Handle(from, $"{CommandSystem.Prefix}Add {Type.Name}");
from.SendGump(new CategorizedAddGump(from, Parent, page));
}
}
}
}

View file

@ -0,0 +1,391 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Server.Items;
using Server.Json;
using Server.Utilities;
namespace Server.Commands
{
public static class Categorization
{
private static CategoryEntry m_RootItems, m_RootMobiles;
private static readonly Type typeofItem = typeof(Item);
private static readonly Type typeofMobile = typeof(Mobile);
private static readonly Type typeofConstructible = typeof(ConstructibleAttribute);
public static CategoryEntry Items
{
get
{
if (m_RootItems == null)
Load();
return m_RootItems;
}
}
public static CategoryEntry Mobiles
{
get
{
if (m_RootMobiles == null)
Load();
return m_RootMobiles;
}
}
public static void Initialize()
{
CommandSystem.Register("RebuildCategorization", AccessLevel.Administrator, RebuildCategorization_OnCommand);
}
[Usage("RebuildCategorization")]
[Description("Rebuilds the categorization data file used by the Add command.")]
public static void RebuildCategorization_OnCommand(CommandEventArgs e)
{
CategoryEntry root = new CategoryEntry(null, "Add Menu", new[] { Items, Mobiles });
List<CategoryEntry> ceList = new List<CategoryEntry>();
ceList.AddRange(root.SubCategories);
Export(ceList, "Data/objects.json");
e.Mobile.SendMessage("Categorization menu rebuilt.");
}
public static void Export(List<CategoryEntry> ceList, string fileName)
{
List<CAGJson> list = new List<CAGJson>();
foreach (var ce in ceList)
RecurseExport(list, ce, null);
JsonConfig.Serialize(fileName, list);
}
public static void RecurseExport(List<CAGJson> list, CategoryEntry ce, string category)
{
category = string.IsNullOrWhiteSpace(category) ? ce.Title : $"{category}{ce.Title}";
if (ce.Matched.Count > 0)
list.Add(new CAGJson
{
Category = category,
Objects = ce.Matched.Select(cte =>
{
if (cte.Object is Item item)
{
int itemID = item.ItemID;
if (item is BaseAddon addon && addon.Components.Count == 1)
itemID = addon.Components[0].ItemID;
if (itemID > TileData.MaxItemValue)
itemID = 1;
int? hue = item.Hue & 0x7FFF;
if ((hue & 0x4000) != 0)
hue = 0;
return new CAGObject
{
Type = cte.Type,
ItemID = itemID,
Hue = hue == 0 ? null : hue
};
}
if (cte.Object is Mobile m)
{
int itemID = ShrinkTable.Lookup(m, 1);
int? hue = m.Hue & 0x7FFF;
if ((hue & 0x4000) != 0)
hue = 0;
return new CAGObject
{
Type = cte.Type,
ItemID = itemID,
Hue = hue == 0 ? null : hue
};
}
throw new InvalidCastException($"Categorization Type Entry: {cte.Type.Name} is not a valid type.");
}).ToArray()
});
List<CategoryEntry> subCats = new List<CategoryEntry>(ce.SubCategories);
subCats.Sort(new CategorySorter());
for (int i = 0; i < subCats.Count; i++)
{
var subCat = subCats[i];
RecurseExport(list, subCat, category);
}
}
public static void Load()
{
List<Type> types = new List<Type>();
AddTypes(Core.Assembly, types);
for (int i = 0; i < AssemblyHandler.Assemblies.Length; ++i)
AddTypes(AssemblyHandler.Assemblies[i], types);
m_RootItems = Load(types, "Data/items.cfg");
m_RootMobiles = Load(types, "Data/mobiles.cfg");
}
private static CategoryEntry Load(List<Type> types, string config)
{
CategoryLine[] lines = CategoryLine.Load(config);
if (lines.Length <= 0) return new CategoryEntry();
int index = 0;
CategoryEntry root = new CategoryEntry(null, lines, ref index);
Fill(root, types);
return root;
}
private static bool IsConstructible(Type type)
{
if (!type.IsSubclassOf(typeofItem) && !type.IsSubclassOf(typeofMobile))
return false;
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
return ctor?.IsDefined(typeofConstructible, false) == true;
}
private static void AddTypes(Assembly asm, List<Type> types)
{
Type[] allTypes = asm.GetTypes();
for (int i = 0; i < allTypes.Length; ++i)
{
Type type = allTypes[i];
if (type.IsAbstract)
continue;
if (IsConstructible(type))
types.Add(type);
}
}
private static void Fill(CategoryEntry root, List<Type> list)
{
for (int i = 0; i < list.Count; ++i)
{
Type type = list[i];
CategoryEntry match = GetDeepestMatch(root, type);
if (match == null)
continue;
try
{
match.Matched.Add(new CategoryTypeEntry(type));
}
catch
{
// ignored
}
}
}
private static CategoryEntry GetDeepestMatch(CategoryEntry root, Type type)
{
if (!root.IsMatch(type))
return null;
for (int i = 0; i < root.SubCategories.Length; ++i)
{
CategoryEntry check = GetDeepestMatch(root.SubCategories[i], type);
if (check != null)
return check;
}
return root;
}
}
public class CategorySorter : IComparer<CategoryEntry>
{
public int Compare(CategoryEntry x, CategoryEntry y)
{
string a = x?.Title;
string b = y?.Title;
return a switch
{
null when b == null => 0,
null => 1,
_ => a.CompareTo(b)
};
}
}
public class CategoryTypeSorter : IComparer<CategoryTypeEntry>
{
public int Compare(CategoryTypeEntry x, CategoryTypeEntry y)
{
string a = x?.Type.Name;
string b = y?.Type.Name;
return a switch
{
null when b == null => 0,
null => 1,
_ => a.CompareTo(b)
};
}
}
public class CategoryTypeEntry
{
public CategoryTypeEntry(Type type)
{
Type = type;
Object = ActivatorUtil.CreateInstance(type);
}
public Type Type { get; }
public object Object { get; }
}
public class CategoryEntry
{
public CategoryEntry(CategoryEntry parent = null, string title = "(empty)", CategoryEntry[] subCats = null)
{
Parent = parent;
Title = title;
SubCategories = subCats ?? Array.Empty<CategoryEntry>();
Matches = Array.Empty<Type>();
Matched = new List<CategoryTypeEntry>();
}
public CategoryEntry(CategoryEntry parent, CategoryLine[] lines, ref int index)
{
Parent = parent;
string text = lines[index].Text;
int start = text.IndexOf('(');
if (start < 0)
throw new FormatException($"Input string not correctly formatted ('{text}')");
Title = text.Substring(0, start).Trim();
int end = text.IndexOf(')', ++start);
if (end < start)
throw new FormatException($"Input string not correctly formatted ('{text}')");
text = text.Substring(start, end - start);
string[] split = text.Split(';');
List<Type> list = new List<Type>();
for (int i = 0; i < split.Length; ++i)
{
Type type = AssemblyHandler.FindFirstTypeForName(split[i].Trim());
if (type == null)
Console.WriteLine("Match type not found ('{0}')", split[i].Trim());
else
list.Add(type);
}
Matches = list.ToArray();
list.Clear();
int ourIndentation = lines[index].Indentation;
++index;
List<CategoryEntry> entryList = new List<CategoryEntry>();
while (index < lines.Length && lines[index].Indentation > ourIndentation)
entryList.Add(new CategoryEntry(this, lines, ref index));
SubCategories = entryList.ToArray();
entryList.Clear();
Matched = new List<CategoryTypeEntry>();
}
public string Title { get; }
public Type[] Matches { get; }
public CategoryEntry Parent { get; }
public CategoryEntry[] SubCategories { get; }
public List<CategoryTypeEntry> Matched { get; }
public bool IsMatch(Type type)
{
bool isMatch = false;
for (int i = 0; !isMatch && i < Matches.Length; ++i)
isMatch = type == Matches[i] || type.IsSubclassOf(Matches[i]);
return isMatch;
}
}
public class CategoryLine
{
public CategoryLine(string input)
{
int index;
for (index = 0; index < input.Length; ++index)
if (char.IsLetter(input, index))
break;
if (index >= input.Length)
throw new FormatException($"Input string not correctly formatted ('{input}')");
Indentation = index;
Text = input.Substring(index);
}
public int Indentation { get; }
public string Text { get; }
public static CategoryLine[] Load(string path)
{
List<CategoryLine> list = new List<CategoryLine>();
if (File.Exists(path))
{
using StreamReader ip = new StreamReader(path);
string line;
while ((line = ip.ReadLine()) != null)
list.Add(new CategoryLine(line));
}
return list.ToArray();
}
}
}

View file

@ -0,0 +1,252 @@
using System;
using Server.Commands;
using Server.Network;
namespace Server.Gumps
{
public class CategorizedAddGump : Gump
{
public static bool OldStyle = PropsConfig.OldStyle;
public static readonly int EntryHeight = 24; // PropsConfig.EntryHeight;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int BorderSize = PropsConfig.BorderSize;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX,
SetOffsetY = PropsConfig.SetOffsetY + (EntryHeight - 20) / 2 / 2;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int PrevWidth = PropsConfig.PrevWidth;
public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX,
PrevOffsetY = PropsConfig.PrevOffsetY + (EntryHeight - 20) / 2 / 2;
public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1;
public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2;
public static readonly int NextWidth = PropsConfig.NextWidth;
public static readonly int NextOffsetX = PropsConfig.NextOffsetX,
NextOffsetY = PropsConfig.NextOffsetY + (EntryHeight - 20) / 2 / 2;
public static readonly int NextButtonID1 = PropsConfig.NextButtonID1;
public static readonly int NextButtonID2 = PropsConfig.NextButtonID2;
private static readonly bool PrevLabel = false;
private static readonly bool NextLabel = false;
private static readonly int PrevLabelOffsetX = PrevWidth + 1;
private static readonly int PrevLabelOffsetY = 0;
private static readonly int NextLabelOffsetX = -29;
private static readonly int NextLabelOffsetY = 0;
private static readonly int EntryWidth = 180;
private static readonly int EntryCount = 15;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int TotalHeight = OffsetSize + (EntryHeight + OffsetSize) * (EntryCount + 1);
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize;
private readonly CAGCategory m_Category;
private readonly Mobile m_Owner;
private int m_Page;
public CategorizedAddGump(Mobile owner) : this(owner, CAGCategory.Root)
{
}
public CategorizedAddGump(Mobile owner, CAGCategory category, int page = 0) : base(GumpOffsetX, GumpOffsetY)
{
owner.CloseGump<WhoGump>();
m_Owner = owner;
m_Category = category;
Initialize(page);
}
public void Initialize(int page)
{
m_Page = page;
CAGNode[] nodes = m_Category.Nodes;
int count = nodes.Length - page * EntryCount;
if (count < 0)
count = 0;
else if (count > EntryCount)
count = EntryCount;
int totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1);
AddPage(0);
AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight,
OffsetGumpID);
int x = BorderSize + OffsetSize;
int y = BorderSize + OffsetSize;
if (OldStyle)
AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID);
else
AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID);
if (m_Category.Parent != null)
{
AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1);
if (PrevLabel)
AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous");
}
x += PrevWidth + OffsetSize;
int emptyWidth = TotalWidth - PrevWidth * 2 - NextWidth - OffsetSize * 5 -
(OldStyle ? SetWidth + OffsetSize : 0);
if (!OldStyle)
AddImageTiled(x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight,
EntryGumpID);
AddHtml(x + TextOffsetX, y + (EntryHeight - 20) / 2, emptyWidth - TextOffsetX, EntryHeight,
$"<center>{m_Category.Title}</center>");
x += emptyWidth + OffsetSize;
if (OldStyle)
AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID);
else
AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID);
if (page > 0)
{
AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 2);
if (PrevLabel)
AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous");
}
x += PrevWidth + OffsetSize;
if (!OldStyle)
AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID);
if ((page + 1) * EntryCount < nodes.Length)
{
AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 3, GumpButtonType.Reply, 1);
if (NextLabel)
AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next");
}
for (int i = 0, index = page * EntryCount; i < EntryCount && index < nodes.Length; ++i, ++index)
{
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
CAGNode node = nodes[index];
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y + (EntryHeight - 20) / 2, EntryWidth - TextOffsetX, EntryHeight, TextHue,
node.Title);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 4);
if (node is CAGObject obj)
{
int itemID = obj.ItemID;
Rectangle2D bounds = ItemBounds.Table[itemID];
if (itemID != 1 && bounds.Height < EntryHeight * 2)
{
if (bounds.Height < EntryHeight)
AddItem(x - OffsetSize - 22 - i % 2 * 44 - bounds.Width / 2 - bounds.X,
y + EntryHeight / 2 - bounds.Height / 2 - bounds.Y, itemID);
else
AddItem(x - OffsetSize - 22 - i % 2 * 44 - bounds.Width / 2 - bounds.X,
y + EntryHeight - 1 - bounds.Height - bounds.Y, itemID);
}
}
}
}
public override void OnResponse(NetState state, RelayInfo info)
{
Mobile from = m_Owner;
switch (info.ButtonID)
{
case 0: // Closed
{
return;
}
case 1: // Up
{
if (m_Category.Parent != null)
{
int index = Array.IndexOf(m_Category.Parent.Nodes, m_Category) / EntryCount;
if (index < 0)
index = 0;
from.SendGump(new CategorizedAddGump(from, m_Category.Parent, index));
}
break;
}
case 2: // Previous
{
if (m_Page > 0)
from.SendGump(new CategorizedAddGump(from, m_Category, m_Page - 1));
break;
}
case 3: // Next
{
if ((m_Page + 1) * EntryCount < m_Category.Nodes.Length)
from.SendGump(new CategorizedAddGump(from, m_Category, m_Page + 1));
break;
}
default:
{
int index = m_Page * EntryCount + (info.ButtonID - 4);
if (index >= 0 && index < m_Category.Nodes.Length)
m_Category.Nodes[index].OnClick(from, m_Page);
break;
}
}
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Server.Items;
namespace Server.Commands
{
public struct TeleporterDefinition
{
[JsonPropertyName("src")]
public WorldLocation Source { get; set; }
[JsonPropertyName("dst")]
public WorldLocation Destination { get; set; }
[JsonPropertyName("back")]
public bool Back { get; set; }
public override string ToString() => $"{{{Source},{Destination},{Back}}}";
public bool Equals(TeleporterDefinition other) =>
Source.Equals(other.Source) && Destination.Equals(other.Destination) && Back == other.Back;
public override bool Equals(object obj) => obj is TeleporterDefinition other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Source, Destination, Back);
}
public static class GenTeleporter
{
private const int SuccessHue = 72, WarningHue = 53, ErrorHue = 33;
private static readonly string TeleporterJsonDataPath = Path.Combine("Data", "teleporters.json");
private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{
AllowTrailingCommas = true,
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip
};
public static void Initialize()
{
CommandSystem.Register("TelGen", AccessLevel.Administrator, GenTeleporter_OnCommand);
CommandSystem.Register("TelGenDelete", AccessLevel.Administrator, TelGenDelete_OnCommand);
}
[Usage("TelGenDelete")]
[Description("Destroys world/dungeon teleporters for all facets.")]
public static void TelGenDelete_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
from.SendMessage("Removing teleporters, please wait.");
int count = 0;
void ProcessDeletion(TeleporterDefinition x)
{
count += TeleportersCreator.DeleteTeleporters(x.Source);
if (x.Back) count += TeleportersCreator.DeleteTeleporters(x.Destination);
}
if (!ProcessTeleporterData(from, ProcessDeletion))
{
if (count > 0)
from.SendMessage(WarningHue, $"Partial Completion, {count} Teleporters Removed.");
return;
}
from.SendMessage(WarningHue, $"{count} Teleporters Removed.");
}
[Usage("TelGen")]
[Description("Generates world/dungeon teleporters for all facets.")]
public static void GenTeleporter_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
from.SendMessage("Generating teleporters, please wait.");
TeleportersCreator c = new TeleportersCreator();
if (!ProcessTeleporterData(from, c.CreateTeleporter))
{
if (c.DelCount > 0)
from.SendMessage(WarningHue, $"Partial Completion: {c.DelCount} Teleporters Removed.");
if (c.Count > 0)
from.SendMessage(WarningHue, $"Partial Completion: {c.Count} Teleporters Added.");
return;
}
from.SendMessage(SuccessHue, "Teleporter generating complete.");
from.SendMessage(WarningHue, $"{c.DelCount} Teleporters Removed.");
from.SendMessage(SuccessHue, $"{c.Count} Teleporters Added.");
}
private static bool ProcessTeleporterData(Mobile m, Action<TeleporterDefinition> processor)
{
try
{
string json;
using (StreamReader reader = new StreamReader(TeleporterJsonDataPath))
json = reader.ReadToEnd();
var teleporters = JsonSerializer.Deserialize<List<TeleporterDefinition>>(json, JsonOptions);
for (int i = 0; i < teleporters.Count; i++)
processor(teleporters[i]);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
m.SendMessage(ErrorHue, $"Failed to load/process data file '{TeleporterJsonDataPath}'");
return false;
}
return true;
}
private class TeleportersCreator
{
public int Count { get; private set; }
public int DelCount { get; private set; }
private static bool IsWithinZ(int delta) => delta >= -12 && delta <= 12;
public static int DeleteTeleporters(WorldLocation worldLocation)
{
IPooledEnumerable<Teleporter> eable = worldLocation.Map.GetItemsInRange<Teleporter>(worldLocation, 0);
var items = eable
.Where(x => !(x is KeywordTeleporter || x is SkillTeleporter) && IsWithinZ(x.Z - worldLocation.Z));
int count = 0;
foreach (var item in items)
{
count++;
item.Delete();
}
eable.Free();
return count;
}
public void CreateTeleporter(TeleporterDefinition telDef)
{
DelCount += DeleteTeleporters(telDef.Source);
Count++;
new Teleporter(telDef.Destination, telDef.Destination.Map).MoveToWorld(telDef.Source, telDef.Source.Map);
if (!telDef.Back) return;
DelCount += DeleteTeleporters(telDef.Destination);
Count++;
new Teleporter(telDef.Source, telDef.Source.Map).MoveToWorld(telDef.Destination, telDef.Destination.Map);
}
}
}
}