Updates Data to JSON (#159)
This commit is contained in:
parent
d2f7e08de4
commit
1246a135f7
61 changed files with 18005 additions and 9152 deletions
680
Projects/UOContent/Commands/Object Creation/Add.cs
Normal file
680
Projects/UOContent/Commands/Object Creation/Add.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
248
Projects/UOContent/Commands/Object Creation/AddGump.cs
Normal file
248
Projects/UOContent/Commands/Object Creation/AddGump.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Projects/UOContent/Commands/Object Creation/CAGCategory.cs
Normal file
28
Projects/UOContent/Commands/Object Creation/CAGCategory.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Projects/UOContent/Commands/Object Creation/CAGLoader.cs
Normal file
68
Projects/UOContent/Commands/Object Creation/CAGLoader.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
8
Projects/UOContent/Commands/Object Creation/CAGNode.cs
Normal file
8
Projects/UOContent/Commands/Object Creation/CAGNode.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
40
Projects/UOContent/Commands/Object Creation/CAGObject.cs
Normal file
40
Projects/UOContent/Commands/Object Creation/CAGObject.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
391
Projects/UOContent/Commands/Object Creation/Categorization.cs
Normal file
391
Projects/UOContent/Commands/Object Creation/Categorization.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1103
Projects/UOContent/Commands/Object Creation/Decorate.cs
Normal file
1103
Projects/UOContent/Commands/Object Creation/Decorate.cs
Normal file
File diff suppressed because it is too large
Load diff
1092
Projects/UOContent/Commands/Object Creation/DecorateMag.cs
Normal file
1092
Projects/UOContent/Commands/Object Creation/DecorateMag.cs
Normal file
File diff suppressed because it is too large
Load diff
152
Projects/UOContent/Commands/Object Creation/GenTeleporter.cs
Normal file
152
Projects/UOContent/Commands/Object Creation/GenTeleporter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue