Reorganizes Project (#41)
This commit is contained in:
parent
08bf44af9a
commit
3614a66aee
3499 changed files with 79 additions and 55 deletions
696
Projects/Scripts/Commands/Add.cs
Normal file
696
Projects/Scripts/Commands/Add.cs
Normal file
|
|
@ -0,0 +1,696 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Server.Items;
|
||||
using CPA = Server.CommandPropertyAttribute;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Add
|
||||
{
|
||||
private static Type m_EntityType = typeof(IEntity);
|
||||
|
||||
private static Type m_ConstructibleType = typeof(ConstructibleAttribute);
|
||||
|
||||
private static Type m_EnumType = typeof(Enum);
|
||||
|
||||
private static Type m_TypeType = typeof(Type);
|
||||
|
||||
private static Type m_ParsableType = typeof(ParsableAttribute);
|
||||
|
||||
private static Type[] m_ParseTypes = { typeof(string) };
|
||||
private static object[] m_ParseArgs = new object[1];
|
||||
|
||||
private static Type[] m_SignedNumerics =
|
||||
{
|
||||
typeof(long),
|
||||
typeof(int),
|
||||
typeof(short),
|
||||
typeof(sbyte)
|
||||
};
|
||||
|
||||
private static 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 = ScriptCompiler.FindTypeByName(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;
|
||||
|
||||
int totalParams = 0;
|
||||
|
||||
// Handle optional constructors
|
||||
ParameterInfo[] paramList = ctor.GetParameters();
|
||||
for (int j = 0; j < paramList.Length; j++)
|
||||
if (!paramList[j].HasDefaultValue)
|
||||
totalParams += 1;
|
||||
|
||||
|
||||
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 ScriptCompiler.FindTypeByName(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, Map map, 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, map, 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, map, 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, map, 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)
|
||||
{
|
||||
return 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)
|
||||
{
|
||||
return type.IsSubclassOf(m_EnumType);
|
||||
}
|
||||
|
||||
public static bool IsType(Type type)
|
||||
{
|
||||
return type == m_TypeType || type.IsSubclassOf(m_TypeType);
|
||||
}
|
||||
|
||||
public static bool IsParsable(Type type)
|
||||
{
|
||||
return 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 string[] m_Args;
|
||||
public int m_FixedZ;
|
||||
public bool m_Outline;
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Projects/Scripts/Commands/Attributes.cs
Normal file
34
Projects/Scripts/Commands/Attributes.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class UsageAttribute : Attribute
|
||||
{
|
||||
public UsageAttribute(string usage)
|
||||
{
|
||||
Usage = usage;
|
||||
}
|
||||
|
||||
public string Usage{ get; }
|
||||
}
|
||||
|
||||
public class DescriptionAttribute : Attribute
|
||||
{
|
||||
public DescriptionAttribute(string description)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public string Description{ get; }
|
||||
}
|
||||
|
||||
public class AliasesAttribute : Attribute
|
||||
{
|
||||
public AliasesAttribute(params string[] aliases)
|
||||
{
|
||||
Aliases = aliases;
|
||||
}
|
||||
|
||||
public string[] Aliases{ get; }
|
||||
}
|
||||
}
|
||||
418
Projects/Scripts/Commands/Batch.cs
Normal file
418
Projects/Scripts/Commands/Batch.cs
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Server.Commands.Generic;
|
||||
using Server.Gumps;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Batch : BaseCommand
|
||||
{
|
||||
public Batch()
|
||||
{
|
||||
Commands = new[] { "Batch" };
|
||||
ListOptimized = true;
|
||||
}
|
||||
|
||||
public BaseCommandImplementor Scope{ get; set; }
|
||||
|
||||
public string Condition{ get; set; } = "";
|
||||
|
||||
public List<BatchCommand> BatchCommands{ get; } = new List<BatchCommand>();
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
if (list.Count == 0)
|
||||
{
|
||||
LogFailure("Nothing was found to use this command on.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
BaseCommand[] commands = new BaseCommand[BatchCommands.Count];
|
||||
CommandEventArgs[] eventArgs = new CommandEventArgs[BatchCommands.Count];
|
||||
|
||||
for (int i = 0; i < BatchCommands.Count; ++i)
|
||||
{
|
||||
BatchCommand bc = BatchCommands[i];
|
||||
|
||||
bc.GetDetails(out string commandString, out string argString, out string[] args);
|
||||
|
||||
BaseCommand command = Scope.Commands[commandString];
|
||||
|
||||
commands[i] = command;
|
||||
eventArgs[i] = new CommandEventArgs(e.Mobile, commandString, argString, args);
|
||||
|
||||
if (command == null)
|
||||
{
|
||||
e.Mobile.SendMessage(
|
||||
"That is either an invalid command name or one that does not support this modifier: {0}.",
|
||||
commandString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Mobile.AccessLevel < command.AccessLevel)
|
||||
{
|
||||
e.Mobile.SendMessage("You do not have access to that command: {0}.", commandString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!command.ValidateArgs(Scope, eventArgs[i])) return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < commands.Length; ++i)
|
||||
{
|
||||
BaseCommand command = commands[i];
|
||||
BatchCommand bc = BatchCommands[i];
|
||||
|
||||
if (list.Count > 20)
|
||||
CommandLogging.Enabled = false;
|
||||
|
||||
List<object> usedList;
|
||||
|
||||
if (Utility.InsensitiveCompare(bc.Object, "Current") == 0)
|
||||
{
|
||||
usedList = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
Dictionary<Type, PropertyInfo[]> propertyChains = new Dictionary<Type, PropertyInfo[]>();
|
||||
|
||||
usedList = new List<object>(list.Count);
|
||||
|
||||
for (int j = 0; j < list.Count; ++j)
|
||||
{
|
||||
object obj = list[j];
|
||||
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
||||
Type type = obj.GetType();
|
||||
string failReason = "";
|
||||
|
||||
if (!propertyChains.TryGetValue(type, out PropertyInfo[] chain))
|
||||
propertyChains[type] = chain = Properties.GetPropertyInfoChain(e.Mobile, type, bc.Object,
|
||||
PropertyAccess.Read, ref failReason);
|
||||
|
||||
if (chain == null)
|
||||
continue;
|
||||
|
||||
PropertyInfo endProp = Properties.GetPropertyInfo(ref obj, chain, ref failReason);
|
||||
|
||||
if (endProp == null)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
obj = endProp.GetValue(obj, null);
|
||||
|
||||
if (obj != null)
|
||||
usedList.Add(obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command.ExecuteList(eventArgs[i], usedList);
|
||||
|
||||
if (list.Count > 20)
|
||||
CommandLogging.Enabled = true;
|
||||
|
||||
command.Flush(e.Mobile, list.Count > 20);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
e.Mobile.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Run(Mobile from)
|
||||
{
|
||||
if (Scope == null)
|
||||
{
|
||||
from.SendMessage("You must select the batch command scope.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Condition.Length > 0 && !Scope.SupportsConditionals)
|
||||
{
|
||||
from.SendMessage("This command scope does not support conditionals.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Condition.Length > 0 && !Utility.InsensitiveStartsWith(Condition, "where"))
|
||||
{
|
||||
from.SendMessage("The condition field must start with \"where\".");
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] args = CommandSystem.Split(Condition);
|
||||
|
||||
Scope.Process(from, this, args);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("Batch", AccessLevel.Counselor, Batch_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("Batch")]
|
||||
[Description("Allows multiple commands to be run at the same time.")]
|
||||
public static void Batch_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.SendGump(new BatchGump(e.Mobile, new Batch()));
|
||||
}
|
||||
}
|
||||
|
||||
public class BatchCommand
|
||||
{
|
||||
public BatchCommand(string command, string obj)
|
||||
{
|
||||
Command = command;
|
||||
Object = obj;
|
||||
}
|
||||
|
||||
public string Command{ get; set; }
|
||||
|
||||
public string Object{ get; set; }
|
||||
|
||||
public void GetDetails(out string command, out string argString, out string[] args)
|
||||
{
|
||||
int indexOf = Command.IndexOf(' ');
|
||||
|
||||
if (indexOf >= 0)
|
||||
{
|
||||
argString = Command.Substring(indexOf + 1);
|
||||
|
||||
command = Command.Substring(0, indexOf);
|
||||
args = CommandSystem.Split(argString);
|
||||
}
|
||||
else
|
||||
{
|
||||
argString = "";
|
||||
command = Command.ToLower();
|
||||
args = new string[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BatchGump : BaseGridGump
|
||||
{
|
||||
private Batch m_Batch;
|
||||
private Mobile m_From;
|
||||
|
||||
public BatchGump(Mobile from, Batch batch) : base(30, 30)
|
||||
{
|
||||
m_From = from;
|
||||
m_Batch = batch;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
AddNewPage();
|
||||
|
||||
/* Header */
|
||||
AddEntryHeader(20);
|
||||
AddEntryHtml(180, Center("Batch Commands"));
|
||||
AddEntryHeader(20);
|
||||
AddNewLine();
|
||||
|
||||
AddEntryHeader(9);
|
||||
AddEntryLabel(191, "Run Batch");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, GetButtonID(1, 0, 0), ArrowRightWidth, ArrowRightHeight);
|
||||
AddNewLine();
|
||||
|
||||
AddBlankLine();
|
||||
|
||||
/* Scope */
|
||||
AddEntryHeader(20);
|
||||
AddEntryHtml(180, Center("Scope"));
|
||||
AddEntryHeader(20);
|
||||
AddNewLine();
|
||||
|
||||
AddEntryHeader(9);
|
||||
AddEntryLabel(191, m_Batch.Scope == null ? "Select Scope" : m_Batch.Scope.Accessors[0]);
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, GetButtonID(1, 0, 1), ArrowRightWidth, ArrowRightHeight);
|
||||
AddNewLine();
|
||||
|
||||
AddBlankLine();
|
||||
|
||||
/* Condition */
|
||||
AddEntryHeader(20);
|
||||
AddEntryHtml(180, Center("Condition"));
|
||||
AddEntryHeader(20);
|
||||
AddNewLine();
|
||||
|
||||
AddEntryHeader(9);
|
||||
AddEntryText(202, 0, m_Batch.Condition);
|
||||
AddEntryHeader(9);
|
||||
AddNewLine();
|
||||
|
||||
AddBlankLine();
|
||||
|
||||
/* Commands */
|
||||
AddEntryHeader(20);
|
||||
AddEntryHtml(180, Center("Commands"));
|
||||
AddEntryHeader(20);
|
||||
|
||||
for (int i = 0; i < m_Batch.BatchCommands.Count; ++i)
|
||||
{
|
||||
BatchCommand bc = m_Batch.BatchCommands[i];
|
||||
|
||||
AddNewLine();
|
||||
|
||||
AddImageTiled(CurrentX, CurrentY, 9, 2, 0x24A8);
|
||||
AddImageTiled(CurrentX, CurrentY + 2, 2, EntryHeight + OffsetSize + EntryHeight - 4, 0x24A8);
|
||||
AddImageTiled(CurrentX, CurrentY + EntryHeight + OffsetSize + EntryHeight - 2, 9, 2, 0x24A8);
|
||||
AddImageTiled(CurrentX + 3, CurrentY + 3, 6, EntryHeight + EntryHeight - 4 - OffsetSize, HeaderGumpID);
|
||||
|
||||
IncreaseX(9);
|
||||
AddEntryText(202, 1 + i * 2, bc.Command);
|
||||
AddEntryHeader(9, 2);
|
||||
|
||||
AddNewLine();
|
||||
|
||||
IncreaseX(9);
|
||||
AddEntryText(202, 2 + i * 2, bc.Object);
|
||||
}
|
||||
|
||||
AddNewLine();
|
||||
|
||||
AddEntryHeader(9);
|
||||
AddEntryLabel(191, "Add New Command");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, GetButtonID(1, 0, 2), ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
FinishPage();
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
if (!SplitButtonID(info.ButtonID, 1, out int type, out int index))
|
||||
return;
|
||||
|
||||
TextRelay entry = info.GetTextEntry(0);
|
||||
|
||||
if (entry != null)
|
||||
m_Batch.Condition = entry.Text;
|
||||
|
||||
for (int i = m_Batch.BatchCommands.Count - 1; i >= 0; --i)
|
||||
{
|
||||
BatchCommand sc = m_Batch.BatchCommands[i];
|
||||
|
||||
entry = info.GetTextEntry(1 + i * 2);
|
||||
|
||||
if (entry != null)
|
||||
sc.Command = entry.Text;
|
||||
|
||||
entry = info.GetTextEntry(2 + i * 2);
|
||||
|
||||
if (entry != null)
|
||||
sc.Object = entry.Text;
|
||||
|
||||
if (sc.Command.Length == 0 && sc.Object.Length == 0)
|
||||
m_Batch.BatchCommands.RemoveAt(i);
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0: // main
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: // run
|
||||
{
|
||||
m_Batch.Run(m_From);
|
||||
break;
|
||||
}
|
||||
case 1: // set scope
|
||||
{
|
||||
m_From.SendGump(new BatchScopeGump(m_From, m_Batch));
|
||||
return;
|
||||
}
|
||||
case 2: // add command
|
||||
{
|
||||
m_Batch.BatchCommands.Add(new BatchCommand("", ""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_From.SendGump(new BatchGump(m_From, m_Batch));
|
||||
}
|
||||
}
|
||||
|
||||
public class BatchScopeGump : BaseGridGump
|
||||
{
|
||||
private Batch m_Batch;
|
||||
private Mobile m_From;
|
||||
|
||||
public BatchScopeGump(Mobile from, Batch batch) : base(30, 30)
|
||||
{
|
||||
m_From = from;
|
||||
m_Batch = batch;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
AddNewPage();
|
||||
|
||||
/* Header */
|
||||
AddEntryHeader(20);
|
||||
AddEntryHtml(140, Center("Change Scope"));
|
||||
AddEntryHeader(20);
|
||||
|
||||
/* Options */
|
||||
for (int i = 0; i < BaseCommandImplementor.Implementors.Count; ++i)
|
||||
{
|
||||
BaseCommandImplementor impl = BaseCommandImplementor.Implementors[i];
|
||||
|
||||
if (m_From.AccessLevel < impl.AccessLevel)
|
||||
continue;
|
||||
|
||||
AddNewLine();
|
||||
|
||||
AddEntryLabel(20 + OffsetSize + 140, impl.Accessors[0]);
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, GetButtonID(1, 0, i), ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
|
||||
FinishPage();
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
if (SplitButtonID(info.ButtonID, 1, out int type, out int index))
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (index < BaseCommandImplementor.Implementors.Count)
|
||||
{
|
||||
BaseCommandImplementor impl = BaseCommandImplementor.Implementors[index];
|
||||
|
||||
if (m_From.AccessLevel >= impl.AccessLevel)
|
||||
m_Batch.Scope = impl;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_From.SendGump(new BatchGump(m_From, m_Batch));
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Projects/Scripts/Commands/BoundingBoxPicker.cs
Normal file
63
Projects/Scripts/Commands/BoundingBoxPicker.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
using Server.Targeting;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public delegate void BoundingBoxCallback(Map map, Point3D start, Point3D end);
|
||||
|
||||
public static class BoundingBoxPicker
|
||||
{
|
||||
public static void Begin(Mobile from, BoundingBoxCallback callback)
|
||||
{
|
||||
from.SendMessage("Target the first location of the bounding box.");
|
||||
from.Target = new PickTarget(callback);
|
||||
}
|
||||
|
||||
private class PickTarget : Target
|
||||
{
|
||||
private BoundingBoxCallback m_Callback;
|
||||
private bool m_First;
|
||||
private Map m_Map;
|
||||
private Point3D m_Store;
|
||||
|
||||
public PickTarget(BoundingBoxCallback callback) : this(Point3D.Zero, true, null, callback)
|
||||
{
|
||||
}
|
||||
|
||||
public PickTarget(Point3D store, bool first, Map map, BoundingBoxCallback callback) : base(-1, true, TargetFlags.None)
|
||||
{
|
||||
m_Store = store;
|
||||
m_First = first;
|
||||
m_Map = map;
|
||||
m_Callback = callback;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!(targeted is IPoint3D p))
|
||||
return;
|
||||
|
||||
if (p is Item item)
|
||||
p = item.GetWorldTop();
|
||||
|
||||
if (m_First)
|
||||
{
|
||||
from.SendMessage("Target another location to complete the bounding box.");
|
||||
from.Target = new PickTarget(new Point3D(p), false, from.Map, m_Callback);
|
||||
}
|
||||
else if (from.Map != m_Map)
|
||||
{
|
||||
from.SendMessage("Both locations must reside on the same map.");
|
||||
}
|
||||
else if (m_Map != null && m_Map != Map.Internal && m_Callback != null)
|
||||
{
|
||||
Point3D start = m_Store;
|
||||
Point3D end = new Point3D(p);
|
||||
|
||||
Utility.FixPoints(ref start, ref end);
|
||||
|
||||
m_Callback(m_Map, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
Projects/Scripts/Commands/ConvertPlayers.cs
Normal file
86
Projects/Scripts/Commands/ConvertPlayers.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class ConvertPlayers
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("ConvertPlayers", AccessLevel.Administrator, Convert_OnCommand);
|
||||
}
|
||||
|
||||
public static void Convert_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.SendMessage(
|
||||
"Converting all players to PlayerMobile. You will be disconnected. Please Restart the server after the world has finished saving.");
|
||||
List<Mobile> mobs = new List<Mobile>(World.Mobiles.Values);
|
||||
int count = 0;
|
||||
|
||||
foreach (Mobile m in mobs)
|
||||
if (m.Player && !(m is PlayerMobile))
|
||||
{
|
||||
count++;
|
||||
m.NetState?.Dispose();
|
||||
|
||||
PlayerMobile pm = new PlayerMobile(m.Serial);
|
||||
pm.DefaultMobileInit();
|
||||
|
||||
List<Item> copy = new List<Item>(m.Items);
|
||||
for (int i = 0; i < copy.Count; i++)
|
||||
pm.AddItem(copy[i]);
|
||||
|
||||
CopyProps(pm, m);
|
||||
|
||||
for (int i = 0; i < m.Skills.Length; i++)
|
||||
{
|
||||
pm.Skills[i].Base = m.Skills[i].Base;
|
||||
pm.Skills[i].SetLockNoRelay(m.Skills[i].Lock);
|
||||
}
|
||||
|
||||
World.Mobiles[m.Serial] = pm;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
NetState.ProcessDisposedQueue();
|
||||
World.Save();
|
||||
|
||||
Console.WriteLine("{0} players have been converted to PlayerMobile. {1}.", count,
|
||||
Core.Service ? "The server is now restarting" : "Press any key to restart the server");
|
||||
|
||||
if (!Core.Service)
|
||||
Console.ReadKey(true);
|
||||
|
||||
Core.Kill(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("Couldn't find any Players to convert.");
|
||||
}
|
||||
}
|
||||
|
||||
private static PropertyInfo[] _mobProps =
|
||||
typeof(Mobile).GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(prop => prop.CanRead && prop.CanWrite).ToArray();
|
||||
|
||||
private static void CopyProps(Mobile to, Mobile from)
|
||||
{
|
||||
foreach (PropertyInfo prop in _mobProps)
|
||||
{
|
||||
try
|
||||
{
|
||||
prop.SetValue(to, prop.GetValue(from, null), null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1097
Projects/Scripts/Commands/Decorate.cs
Normal file
1097
Projects/Scripts/Commands/Decorate.cs
Normal file
File diff suppressed because it is too large
Load diff
1095
Projects/Scripts/Commands/DecorateMag.cs
Normal file
1095
Projects/Scripts/Commands/DecorateMag.cs
Normal file
File diff suppressed because it is too large
Load diff
2718
Projects/Scripts/Commands/Docs.cs
Normal file
2718
Projects/Scripts/Commands/Docs.cs
Normal file
File diff suppressed because it is too large
Load diff
26
Projects/Scripts/Commands/DragEffects.cs
Normal file
26
Projects/Scripts/Commands/DragEffects.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
namespace Server.Commands
|
||||
{
|
||||
public static class DragEffects
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("DragEffects", AccessLevel.Developer, DragEffects_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("DragEffects [enable=false]")]
|
||||
[Description("Enables or disables the item drag and drop effects.")]
|
||||
public static void DragEffects_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 0)
|
||||
{
|
||||
e.Mobile.SendMessage("Drag effects are currently {0}.", Mobile.DragEffects ? "enabled" : "disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
Mobile.DragEffects = e.GetBoolean(0);
|
||||
|
||||
e.Mobile.SendMessage("Drag effects have been {0}.", Mobile.DragEffects ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
Projects/Scripts/Commands/Dupe.cs
Normal file
135
Projects/Scripts/Commands/Dupe.cs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Dupe
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("Dupe", AccessLevel.GameMaster, Dupe_OnCommand);
|
||||
CommandSystem.Register("DupeInBag", AccessLevel.GameMaster, DupeInBag_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("Dupe [amount]")]
|
||||
[Description("Dupes a targeted item.")]
|
||||
private static void Dupe_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
int amount = 1;
|
||||
if (e.Length >= 1)
|
||||
amount = e.GetInt32(0);
|
||||
e.Mobile.Target = new DupeTarget(false, amount > 0 ? amount : 1);
|
||||
e.Mobile.SendMessage("What do you wish to dupe?");
|
||||
}
|
||||
|
||||
[Usage("DupeInBag <count>")]
|
||||
[Description("Dupes an item at it's current location (count) number of times.")]
|
||||
private static void DupeInBag_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
int amount = 1;
|
||||
if (e.Length >= 1)
|
||||
amount = e.GetInt32(0);
|
||||
|
||||
e.Mobile.Target = new DupeTarget(true, amount > 0 ? amount : 1);
|
||||
e.Mobile.SendMessage("What do you wish to dupe?");
|
||||
}
|
||||
|
||||
public static void CopyProperties(Item dest, Item src)
|
||||
{
|
||||
PropertyInfo[] props = src.GetType().GetProperties();
|
||||
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
try
|
||||
{
|
||||
if (props[i].CanRead && props[i].CanWrite) props[i].SetValue(dest, props[i].GetValue(src, null), null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Console.WriteLine( "Denied" );
|
||||
}
|
||||
}
|
||||
|
||||
private class DupeTarget : Target
|
||||
{
|
||||
private int m_Amount;
|
||||
private bool m_InBag;
|
||||
|
||||
public DupeTarget(bool inbag, int amount)
|
||||
: base(15, false, TargetFlags.None)
|
||||
{
|
||||
m_InBag = inbag;
|
||||
m_Amount = amount;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targ)
|
||||
{
|
||||
bool done = false;
|
||||
if (!(targ is Item))
|
||||
{
|
||||
from.SendMessage("You can only dupe items.");
|
||||
return;
|
||||
}
|
||||
|
||||
CommandLogging.WriteLine(from, "{0} {1} duping {2} (inBag={3}; amount={4})", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(targ), m_InBag, m_Amount);
|
||||
|
||||
Item copy = (Item)targ;
|
||||
Container pack = null;
|
||||
|
||||
if (m_InBag)
|
||||
{
|
||||
if (copy.Parent is Container cont)
|
||||
pack = cont;
|
||||
else if (copy.Parent is Mobile m)
|
||||
pack = m.Backpack;
|
||||
}
|
||||
else
|
||||
{
|
||||
pack = from.Backpack;
|
||||
}
|
||||
|
||||
Type t = copy.GetType();
|
||||
|
||||
//ConstructorInfo[] info = t.GetConstructors();
|
||||
|
||||
ConstructorInfo c = t.GetConstructor(Type.EmptyTypes);
|
||||
|
||||
if (c != null)
|
||||
try
|
||||
{
|
||||
from.SendMessage("Duping {0}...", m_Amount);
|
||||
for (int i = 0; i < m_Amount; i++)
|
||||
if (c.Invoke(null) is Item newItem)
|
||||
{
|
||||
CopyProperties(newItem, copy); //copy.Dupe( item, copy.Amount );
|
||||
copy.OnAfterDuped(newItem);
|
||||
newItem.Parent = null;
|
||||
|
||||
if (pack != null)
|
||||
pack.DropItem(newItem);
|
||||
else
|
||||
newItem.MoveToWorld(from.Location, from.Map);
|
||||
|
||||
newItem.InvalidateProperties();
|
||||
|
||||
CommandLogging.WriteLine(from, "{0} {1} duped {2} creating {3}", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(targ),
|
||||
CommandLogging.Format(newItem));
|
||||
}
|
||||
|
||||
from.SendMessage("Done");
|
||||
done = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
from.SendMessage("Error!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!done) from.SendMessage("Unable to dupe. Item must have a 0 parameter constructor.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Projects/Scripts/Commands/ExportWSC.cs
Normal file
79
Projects/Scripts/Commands/ExportWSC.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class ExportCommand
|
||||
{
|
||||
private const string ExportFile = @"C:\Uo\WorldForge\items.wsc";
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("ExportWSC", AccessLevel.Administrator, Export_OnCommand);
|
||||
}
|
||||
|
||||
public static void Export_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
StreamWriter w = new StreamWriter(ExportFile);
|
||||
List<Item> remove = new List<Item>();
|
||||
int count = 0;
|
||||
|
||||
e.Mobile.SendMessage("Exporting all static items to \"{0}\"...", ExportFile);
|
||||
e.Mobile.SendMessage("This will delete all static items in the world. Please make a backup.");
|
||||
|
||||
foreach (Item item in World.Items.Values)
|
||||
if ((item is Static || item is BaseFloor || item is BaseWall)
|
||||
&& item.RootParent == null)
|
||||
{
|
||||
w.WriteLine("SECTION WORLDITEM {0}", count);
|
||||
w.WriteLine("{");
|
||||
w.WriteLine("SERIAL {0}", item.Serial);
|
||||
w.WriteLine("NAME #");
|
||||
w.WriteLine("NAME2 #");
|
||||
w.WriteLine("ID {0}", item.ItemID);
|
||||
w.WriteLine("X {0}", item.X);
|
||||
w.WriteLine("Y {0}", item.Y);
|
||||
w.WriteLine("Z {0}", item.Z);
|
||||
w.WriteLine("COLOR {0}", item.Hue);
|
||||
w.WriteLine("CONT -1");
|
||||
w.WriteLine("TYPE 0");
|
||||
w.WriteLine("AMOUNT 1");
|
||||
w.WriteLine("WEIGHT 255");
|
||||
w.WriteLine("OWNER -1");
|
||||
w.WriteLine("SPAWN -1");
|
||||
w.WriteLine("VALUE 1");
|
||||
w.WriteLine("}");
|
||||
w.WriteLine("");
|
||||
|
||||
count++;
|
||||
remove.Add(item);
|
||||
w.Flush();
|
||||
}
|
||||
|
||||
w.Close();
|
||||
|
||||
foreach (Item item in remove)
|
||||
item.Delete();
|
||||
|
||||
e.Mobile.SendMessage("Export complete. Exported {0} statics.", count);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*SECTION WORLDITEM 1
|
||||
{
|
||||
SERIAL 1073741830
|
||||
NAME #
|
||||
NAME2 #
|
||||
ID 1709
|
||||
X 1439
|
||||
Y 1613
|
||||
Z 20
|
||||
CONT -1
|
||||
TYPE 12
|
||||
AMOUNT 1
|
||||
WEIGHT 25500
|
||||
OWNER -1
|
||||
SPAWN -1
|
||||
VALUE 1
|
||||
}*/
|
||||
402
Projects/Scripts/Commands/GenCategorization.cs
Normal file
402
Projects/Scripts/Commands/GenCategorization.cs
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Categorization
|
||||
{
|
||||
private static CategoryEntry m_RootItems, m_RootMobiles;
|
||||
|
||||
private static Type typeofItem = typeof(Item);
|
||||
private static Type typeofMobile = typeof(Mobile);
|
||||
private static 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 });
|
||||
|
||||
Export(root, "Data/objects.xml", "Objects");
|
||||
|
||||
e.Mobile.SendMessage("Categorization menu rebuilt.");
|
||||
}
|
||||
|
||||
public static void Export(CategoryEntry ce, string fileName, string title)
|
||||
{
|
||||
XmlTextWriter xml = new XmlTextWriter(fileName, Encoding.UTF8);
|
||||
|
||||
xml.Indentation = 1;
|
||||
xml.IndentChar = '\t';
|
||||
xml.Formatting = Formatting.Indented;
|
||||
|
||||
xml.WriteStartDocument(true);
|
||||
|
||||
RecurseExport(xml, ce);
|
||||
|
||||
xml.Flush();
|
||||
xml.Close();
|
||||
}
|
||||
|
||||
public static void RecurseExport(XmlTextWriter xml, CategoryEntry ce)
|
||||
{
|
||||
xml.WriteStartElement("category");
|
||||
|
||||
xml.WriteAttributeString("title", ce.Title);
|
||||
|
||||
List<CategoryEntry> subCats = new List<CategoryEntry>(ce.SubCategories);
|
||||
|
||||
subCats.Sort(new CategorySorter());
|
||||
|
||||
for (int i = 0; i < subCats.Count; ++i)
|
||||
RecurseExport(xml, subCats[i]);
|
||||
|
||||
ce.Matched.Sort(new CategoryTypeSorter());
|
||||
|
||||
for (int i = 0; i < ce.Matched.Count; ++i)
|
||||
{
|
||||
CategoryTypeEntry cte = ce.Matched[i];
|
||||
|
||||
xml.WriteStartElement("object");
|
||||
|
||||
xml.WriteAttributeString("type", cte.Type.ToString());
|
||||
|
||||
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;
|
||||
|
||||
xml.WriteAttributeString("gfx", XmlConvert.ToString(itemID));
|
||||
|
||||
int hue = item.Hue & 0x7FFF;
|
||||
|
||||
if ((hue & 0x4000) != 0)
|
||||
hue = 0;
|
||||
|
||||
if (hue != 0)
|
||||
xml.WriteAttributeString("hue", XmlConvert.ToString(hue));
|
||||
|
||||
item.Delete();
|
||||
}
|
||||
else if (cte.Object is Mobile mob)
|
||||
{
|
||||
int itemID = ShrinkTable.Lookup(mob, 1);
|
||||
|
||||
xml.WriteAttributeString("gfx", XmlConvert.ToString(itemID));
|
||||
|
||||
int hue = mob.Hue & 0x7FFF;
|
||||
|
||||
if ((hue & 0x4000) != 0)
|
||||
hue = 0;
|
||||
|
||||
if (hue != 0)
|
||||
xml.WriteAttributeString("hue", XmlConvert.ToString(hue));
|
||||
|
||||
mob.Delete();
|
||||
}
|
||||
|
||||
xml.WriteEndElement();
|
||||
}
|
||||
|
||||
xml.WriteEndElement();
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
|
||||
AddTypes(Core.Assembly, types);
|
||||
|
||||
for (int i = 0; i < ScriptCompiler.Assemblies.Length; ++i)
|
||||
AddTypes(ScriptCompiler.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)
|
||||
{
|
||||
int index = 0;
|
||||
CategoryEntry root = new CategoryEntry(null, lines, ref index);
|
||||
|
||||
Fill(root, types);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
return new CategoryEntry();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
|
||||
if (a == null)
|
||||
return 1;
|
||||
|
||||
return 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;
|
||||
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
|
||||
if (a == null)
|
||||
return 1;
|
||||
|
||||
return a.CompareTo(b);
|
||||
}
|
||||
}
|
||||
|
||||
public class CategoryTypeEntry
|
||||
{
|
||||
public CategoryTypeEntry(Type type)
|
||||
{
|
||||
Type = type;
|
||||
Object = Activator.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 ?? new CategoryEntry[0];
|
||||
Matches = new Type[0];
|
||||
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 = ScriptCompiler.FindTypeByName(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
1043
Projects/Scripts/Commands/GenTeleporter.cs
Normal file
1043
Projects/Scripts/Commands/GenTeleporter.cs
Normal file
File diff suppressed because it is too large
Load diff
137
Projects/Scripts/Commands/Generic/Commands/BaseCommand.cs
Normal file
137
Projects/Scripts/Commands/Generic/Commands/BaseCommand.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public enum ObjectTypes
|
||||
{
|
||||
Both,
|
||||
Items,
|
||||
Mobiles,
|
||||
All
|
||||
}
|
||||
|
||||
public abstract class BaseCommand
|
||||
{
|
||||
private List<MessageEntry> m_Responses = new List<MessageEntry>();
|
||||
private List<MessageEntry> m_Failures = new List<MessageEntry>();
|
||||
|
||||
public bool ListOptimized{ get; set; }
|
||||
|
||||
public string[] Commands{ get; set; }
|
||||
|
||||
public string Usage{ get; set; }
|
||||
|
||||
public string Description{ get; set; }
|
||||
|
||||
public AccessLevel AccessLevel{ get; set; }
|
||||
|
||||
public ObjectTypes ObjectTypes{ get; set; }
|
||||
|
||||
public CommandSupport Supports{ get; set; }
|
||||
|
||||
public static bool IsAccessible(Mobile from, object obj)
|
||||
{
|
||||
if (from.AccessLevel >= AccessLevel.Administrator || obj == null)
|
||||
return true;
|
||||
|
||||
Mobile mob = null;
|
||||
|
||||
if (obj is Mobile m)
|
||||
mob = m;
|
||||
else if (obj is Item item)
|
||||
mob = item.RootParent as Mobile;
|
||||
|
||||
return mob == null || mob == from || from.AccessLevel > mob.AccessLevel;
|
||||
}
|
||||
|
||||
public virtual void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
Execute(e, list[i]);
|
||||
}
|
||||
|
||||
public virtual void Execute(CommandEventArgs e, object obj)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool ValidateArgs(BaseCommandImplementor impl, CommandEventArgs e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddResponse(string message)
|
||||
{
|
||||
for (int i = 0; i < m_Responses.Count; ++i)
|
||||
{
|
||||
MessageEntry entry = m_Responses[i];
|
||||
|
||||
if (entry.m_Message == message)
|
||||
{
|
||||
++entry.m_Count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Responses.Count == 10)
|
||||
return;
|
||||
|
||||
m_Responses.Add(new MessageEntry(message));
|
||||
}
|
||||
|
||||
public void LogFailure(string message)
|
||||
{
|
||||
for (int i = 0; i < m_Failures.Count; ++i)
|
||||
{
|
||||
MessageEntry entry = m_Failures[i];
|
||||
|
||||
if (entry.m_Message == message)
|
||||
{
|
||||
++entry.m_Count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Failures.Count == 10)
|
||||
return;
|
||||
|
||||
m_Failures.Add(new MessageEntry(message));
|
||||
}
|
||||
|
||||
public void Flush(Mobile from, bool flushToLog)
|
||||
{
|
||||
if (m_Responses.Count > 0)
|
||||
for (int i = 0; i < m_Responses.Count; ++i)
|
||||
{
|
||||
MessageEntry entry = m_Responses[i];
|
||||
|
||||
from.SendMessage(entry.ToString());
|
||||
|
||||
if (flushToLog)
|
||||
CommandLogging.WriteLine(from, entry.ToString());
|
||||
}
|
||||
else
|
||||
for (int i = 0; i < m_Failures.Count; ++i)
|
||||
from.SendMessage(m_Failures[i].ToString());
|
||||
|
||||
m_Responses.Clear();
|
||||
m_Failures.Clear();
|
||||
}
|
||||
|
||||
private class MessageEntry
|
||||
{
|
||||
public int m_Count;
|
||||
public string m_Message;
|
||||
|
||||
public MessageEntry(string message)
|
||||
{
|
||||
m_Message = message;
|
||||
m_Count = 1;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return m_Count > 1 ? $"{m_Message} ({m_Count})" : m_Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1111
Projects/Scripts/Commands/Generic/Commands/Commands.cs
Normal file
1111
Projects/Scripts/Commands/Generic/Commands/Commands.cs
Normal file
File diff suppressed because it is too large
Load diff
203
Projects/Scripts/Commands/Generic/Commands/DesignInsert.cs
Normal file
203
Projects/Scripts/Commands/Generic/Commands/DesignInsert.cs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
using System.Collections.Generic;
|
||||
using Server.Gumps;
|
||||
using Server.Items;
|
||||
using Server.Multis;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class DesignInsertCommand : BaseCommand
|
||||
{
|
||||
public enum DesignInsertResult
|
||||
{
|
||||
Valid,
|
||||
InvalidItem,
|
||||
NotInHouse,
|
||||
OutsideHouseBounds
|
||||
}
|
||||
|
||||
public DesignInsertCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Single | CommandSupport.Area;
|
||||
Commands = new[] { "DesignInsert" };
|
||||
ObjectTypes = ObjectTypes.Items;
|
||||
Usage = "DesignInsert [allItems=false]";
|
||||
Description = "Inserts multiple targeted items into a customizable house's design.";
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
TargetCommands.Register(new DesignInsertCommand());
|
||||
}
|
||||
|
||||
public static DesignInsertResult ProcessInsert(Item item, bool staticsOnly, out HouseFoundation house)
|
||||
{
|
||||
house = null;
|
||||
|
||||
if (item == null || item is BaseMulti || item is HouseSign || staticsOnly && !(item is Static))
|
||||
return DesignInsertResult.InvalidItem;
|
||||
|
||||
house = BaseHouse.FindHouseAt(item) as HouseFoundation;
|
||||
|
||||
if (house == null)
|
||||
return DesignInsertResult.NotInHouse;
|
||||
|
||||
int x = item.X - house.X;
|
||||
int y = item.Y - house.Y;
|
||||
int z = item.Z - house.Z;
|
||||
|
||||
if (!TryInsertIntoState(house.CurrentState, item.ItemID, x, y, z))
|
||||
return DesignInsertResult.OutsideHouseBounds;
|
||||
|
||||
TryInsertIntoState(house.DesignState, item.ItemID, x, y, z);
|
||||
item.Delete();
|
||||
|
||||
return DesignInsertResult.Valid;
|
||||
}
|
||||
|
||||
private static bool TryInsertIntoState(DesignState state, int itemID, int x, int y, int z)
|
||||
{
|
||||
MultiComponentList mcl = state.Components;
|
||||
|
||||
if (x < mcl.Min.X || y < mcl.Min.Y || x > mcl.Max.X || y > mcl.Max.Y)
|
||||
return false;
|
||||
|
||||
mcl.Add(itemID, x, y, z);
|
||||
state.OnRevised();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Single targeting mode
|
||||
|
||||
public override void Execute(CommandEventArgs e, object obj)
|
||||
{
|
||||
Target t = new DesignInsertTarget(new List<HouseFoundation>(), e.Length < 1 || !e.GetBoolean(0));
|
||||
t.Invoke(e.Mobile, obj);
|
||||
}
|
||||
|
||||
private class DesignInsertTarget : Target
|
||||
{
|
||||
private List<HouseFoundation> m_Foundations;
|
||||
private bool m_StaticsOnly;
|
||||
|
||||
public DesignInsertTarget(List<HouseFoundation> foundations, bool staticsOnly)
|
||||
: base(-1, false, TargetFlags.None)
|
||||
{
|
||||
m_Foundations = foundations;
|
||||
m_StaticsOnly = staticsOnly;
|
||||
}
|
||||
|
||||
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
|
||||
{
|
||||
if (m_Foundations.Count != 0)
|
||||
{
|
||||
from.SendMessage("Your changes have been committed. Updating...");
|
||||
|
||||
foreach (HouseFoundation house in m_Foundations)
|
||||
house.Delta(ItemDelta.Update);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object obj)
|
||||
{
|
||||
DesignInsertResult result = ProcessInsert(obj as Item, m_StaticsOnly, out HouseFoundation house);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DesignInsertResult.Valid:
|
||||
{
|
||||
if (m_Foundations.Count == 0)
|
||||
from.SendMessage(
|
||||
"The item has been inserted into the house design. Press ESC when you are finished.");
|
||||
else
|
||||
from.SendMessage("The item has been inserted into the house design.");
|
||||
|
||||
if (!m_Foundations.Contains(house))
|
||||
m_Foundations.Add(house);
|
||||
|
||||
break;
|
||||
}
|
||||
case DesignInsertResult.InvalidItem:
|
||||
{
|
||||
from.SendMessage("That cannot be inserted. Try again.");
|
||||
break;
|
||||
}
|
||||
case DesignInsertResult.NotInHouse:
|
||||
case DesignInsertResult.OutsideHouseBounds:
|
||||
{
|
||||
from.SendMessage("That item is not inside a customizable house. Try again.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
from.Target = new DesignInsertTarget(m_Foundations, m_StaticsOnly);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Area targeting mode
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
from.SendGump(new WarningGump(1060637, 30720,
|
||||
$"You are about to insert {list.Count} objects. This cannot be undone without a full server revert.<br><br>Continue?",
|
||||
0xFFC000, 420, 280, okay => OnConfirmCallback(from, okay, list, e.Length < 1 || !e.GetBoolean(0))));
|
||||
AddResponse("Awaiting confirmation...");
|
||||
}
|
||||
|
||||
private void OnConfirmCallback(Mobile from, bool okay, List<object> list, bool staticsOnly)
|
||||
{
|
||||
bool flushToLog = false;
|
||||
|
||||
if (okay)
|
||||
{
|
||||
List<HouseFoundation> foundations = new List<HouseFoundation>();
|
||||
flushToLog = list.Count > 20;
|
||||
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
{
|
||||
DesignInsertResult result = ProcessInsert(list[i] as Item, staticsOnly, out HouseFoundation house);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DesignInsertResult.Valid:
|
||||
{
|
||||
AddResponse("The item has been inserted into the house design.");
|
||||
|
||||
if (!foundations.Contains(house))
|
||||
foundations.Add(house);
|
||||
|
||||
break;
|
||||
}
|
||||
case DesignInsertResult.InvalidItem:
|
||||
{
|
||||
LogFailure("That cannot be inserted.");
|
||||
break;
|
||||
}
|
||||
case DesignInsertResult.NotInHouse:
|
||||
case DesignInsertResult.OutsideHouseBounds:
|
||||
{
|
||||
LogFailure("That item is not inside a customizable house.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (HouseFoundation house in foundations)
|
||||
house.Delta(ItemDelta.Update);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddResponse("Command aborted.");
|
||||
}
|
||||
|
||||
Flush(from, flushToLog);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
568
Projects/Scripts/Commands/Generic/Commands/Interface.cs
Normal file
568
Projects/Scripts/Commands/Generic/Commands/Interface.cs
Normal file
|
|
@ -0,0 +1,568 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Server.Gumps;
|
||||
using Server.Network;
|
||||
using Server.Targets;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class InterfaceCommand : BaseCommand
|
||||
{
|
||||
public InterfaceCommand()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.Complex | CommandSupport.Simple;
|
||||
Commands = new[] { "Interface" };
|
||||
ObjectTypes = ObjectTypes.Both;
|
||||
Usage = "Interface [view <properties ...>]";
|
||||
Description = "Opens an interface to interact with matched objects. Generally used with condition arguments.";
|
||||
ListOptimized = true;
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
if (list.Count > 0)
|
||||
{
|
||||
List<string> columns = new List<string> { "Object" };
|
||||
|
||||
|
||||
if (e.Length > 0)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
if (Insensitive.Equals(e.GetString(0), "view"))
|
||||
++offset;
|
||||
|
||||
while (offset < e.Length)
|
||||
columns.Add(e.GetString(offset++));
|
||||
}
|
||||
|
||||
e.Mobile.SendGump(new InterfaceGump(e.Mobile, columns.ToArray(), list, 0, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddResponse("No matching objects found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InterfaceGump : BaseGridGump
|
||||
{
|
||||
private const int EntriesPerPage = 15;
|
||||
|
||||
private string[] m_Columns;
|
||||
private Mobile m_From;
|
||||
|
||||
private List<object> m_List;
|
||||
private int m_Page;
|
||||
|
||||
private object m_Select;
|
||||
|
||||
public InterfaceGump(Mobile from, string[] columns, List<object> list, int page, object select) : base(30, 30)
|
||||
{
|
||||
m_From = from;
|
||||
|
||||
m_Columns = columns;
|
||||
|
||||
m_List = list;
|
||||
m_Page = page;
|
||||
|
||||
m_Select = select;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
AddNewPage();
|
||||
|
||||
if (m_Page > 0)
|
||||
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
|
||||
else
|
||||
AddEntryHeader(20);
|
||||
|
||||
AddEntryHtml(40 + m_Columns.Length * 130 - 20 + (m_Columns.Length - 2) * OffsetSize, Center(
|
||||
$"Page {m_Page + 1} of {(m_List.Count + EntriesPerPage - 1) / EntriesPerPage}"));
|
||||
|
||||
if ((m_Page + 1) * EntriesPerPage < m_List.Count)
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
|
||||
else
|
||||
AddEntryHeader(20);
|
||||
|
||||
if (m_Columns.Length > 1)
|
||||
{
|
||||
AddNewLine();
|
||||
|
||||
for (int i = 0; i < m_Columns.Length; ++i)
|
||||
{
|
||||
if (i > 0 && m_List.Count > 0)
|
||||
{
|
||||
object obj = m_List[0];
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
string failReason = null;
|
||||
PropertyInfo[] chain = Properties.GetPropertyInfoChain(m_From, obj.GetType(), m_Columns[i],
|
||||
PropertyAccess.Read, ref failReason);
|
||||
|
||||
if (chain?.Length > 0)
|
||||
{
|
||||
m_Columns[i] = "";
|
||||
|
||||
for (int j = 0; j < chain.Length; ++j)
|
||||
{
|
||||
if (j > 0)
|
||||
m_Columns[i] += '.';
|
||||
|
||||
m_Columns[i] += chain[j].Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddEntryHtml(130 + (i == 0 ? 40 : 0), m_Columns[i]);
|
||||
}
|
||||
|
||||
AddEntryHeader(20);
|
||||
}
|
||||
|
||||
for (int i = m_Page * EntriesPerPage, line = 0; line < EntriesPerPage && i < m_List.Count; ++i, ++line)
|
||||
{
|
||||
AddNewLine();
|
||||
|
||||
object obj = m_List[i];
|
||||
bool isDeleted = false;
|
||||
|
||||
if (obj is Item item)
|
||||
{
|
||||
if (!(isDeleted = item.Deleted))
|
||||
AddEntryHtml(40 + 130, item.GetType().Name);
|
||||
}
|
||||
else if (obj is Mobile mob)
|
||||
{
|
||||
if (!(isDeleted = mob.Deleted))
|
||||
AddEntryHtml(40 + 130, mob.Name);
|
||||
}
|
||||
|
||||
if (isDeleted)
|
||||
{
|
||||
AddEntryHtml(40 + 130, "(deleted)");
|
||||
|
||||
for (int j = 1; j < m_Columns.Length; ++j)
|
||||
AddEntryHtml(130, "---");
|
||||
|
||||
AddEntryHeader(20);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 1; j < m_Columns.Length; ++j)
|
||||
{
|
||||
object src = obj;
|
||||
|
||||
string value;
|
||||
string failReason = "";
|
||||
|
||||
PropertyInfo[] chain = Properties.GetPropertyInfoChain(m_From, src.GetType(), m_Columns[j],
|
||||
PropertyAccess.Read, ref failReason);
|
||||
|
||||
if (chain == null || chain.Length == 0)
|
||||
{
|
||||
value = "---";
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyInfo p = Properties.GetPropertyInfo(ref src, chain, ref failReason);
|
||||
|
||||
if (p == null)
|
||||
value = "---";
|
||||
else
|
||||
value = PropertiesGump.ValueToString(src, p);
|
||||
}
|
||||
|
||||
AddEntryHtml(130, value);
|
||||
}
|
||||
|
||||
bool isSelected = m_Select != null && obj == m_Select;
|
||||
|
||||
AddEntryButton(20, isSelected ? 9762 : ArrowRightID1, isSelected ? 9763 : ArrowRightID2, 3 + i,
|
||||
ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
}
|
||||
|
||||
FinishPage();
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
switch (info.ButtonID)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if (m_Page > 0)
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page - 1, m_Select));
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if ((m_Page + 1) * EntriesPerPage < m_List.Count)
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page + 1, m_Select));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
int v = info.ButtonID - 3;
|
||||
|
||||
if (v >= 0 && v < m_List.Count)
|
||||
{
|
||||
object obj = m_List[v];
|
||||
|
||||
if (!BaseCommand.IsAccessible(m_From, obj))
|
||||
{
|
||||
m_From.SendLocalizedMessage(500447); // That is not accessible.
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Select));
|
||||
break;
|
||||
}
|
||||
|
||||
if (obj is Item item && !item.Deleted)
|
||||
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, item));
|
||||
else if (obj is Mobile mobile && !mobile.Deleted)
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, mobile));
|
||||
else
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Select));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InterfaceItemGump : BaseGridGump
|
||||
{
|
||||
private string[] m_Columns;
|
||||
private Mobile m_From;
|
||||
|
||||
private Item m_Item;
|
||||
|
||||
private List<object> m_List;
|
||||
private int m_Page;
|
||||
|
||||
public InterfaceItemGump(Mobile from, string[] columns, List<object> list, int page, Item item) : base(30, 30)
|
||||
{
|
||||
m_From = from;
|
||||
|
||||
m_Columns = columns;
|
||||
|
||||
m_List = list;
|
||||
m_Page = page;
|
||||
|
||||
m_Item = item;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
AddNewPage();
|
||||
|
||||
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
|
||||
AddEntryHtml(160, m_Item.GetType().Name);
|
||||
AddEntryHeader(20);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Properties");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Delete");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 3, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Go there");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 4, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Move to target");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 5, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Bring to pack");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 6, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
FinishPage();
|
||||
}
|
||||
|
||||
private void InvokeCommand(string ip)
|
||||
{
|
||||
CommandSystem.Handle(m_From, $"{CommandSystem.Prefix}{ip}");
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
if (m_Item.Deleted)
|
||||
{
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BaseCommand.IsAccessible(m_From, m_Item))
|
||||
{
|
||||
m_From.SendMessage("That is no longer accessible.");
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (info.ButtonID)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
break;
|
||||
}
|
||||
case 2: // Properties
|
||||
{
|
||||
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
m_From.SendGump(new PropertiesGump(m_From, m_Item));
|
||||
break;
|
||||
}
|
||||
case 3: // Delete
|
||||
{
|
||||
CommandLogging.WriteLine(m_From, "{0} {1} deleting {2}", m_From.AccessLevel,
|
||||
CommandLogging.Format(m_From), CommandLogging.Format(m_Item));
|
||||
m_Item.Delete();
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
break;
|
||||
}
|
||||
case 4: // Go there
|
||||
{
|
||||
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
InvokeCommand($"Go {m_Item.Serial.Value}");
|
||||
break;
|
||||
}
|
||||
case 5: // Move to target
|
||||
{
|
||||
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
m_From.Target = new MoveTarget(m_Item);
|
||||
break;
|
||||
}
|
||||
case 6: // Bring to pack
|
||||
{
|
||||
Mobile owner = m_Item.RootParent as Mobile;
|
||||
|
||||
if (owner?.Map != null && owner.Map != Map.Internal &&
|
||||
!BaseCommand.IsAccessible(m_From, owner) /* !m_From.CanSee( owner )*/)
|
||||
{
|
||||
m_From.SendMessage("You can not get what you can not see.");
|
||||
}
|
||||
else if (owner != null && (owner.Map == null || owner.Map == Map.Internal) && owner.Hidden &&
|
||||
owner.AccessLevel >= m_From.AccessLevel)
|
||||
{
|
||||
m_From.SendMessage("You can not get what you can not see.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
|
||||
m_From.AddToBackpack(m_Item);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InterfaceMobileGump : BaseGridGump
|
||||
{
|
||||
private string[] m_Columns;
|
||||
private Mobile m_From;
|
||||
|
||||
private List<object> m_List;
|
||||
|
||||
private Mobile m_Mobile;
|
||||
private int m_Page;
|
||||
|
||||
public InterfaceMobileGump(Mobile from, string[] columns, List<object> list, int page, Mobile mob)
|
||||
: base(30, 30)
|
||||
{
|
||||
m_From = from;
|
||||
|
||||
m_Columns = columns;
|
||||
|
||||
m_List = list;
|
||||
m_Page = page;
|
||||
|
||||
m_Mobile = mob;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
AddNewPage();
|
||||
|
||||
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
|
||||
AddEntryHtml(160, m_Mobile.Name);
|
||||
AddEntryHeader(20);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Properties");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
if (!m_Mobile.Player)
|
||||
{
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Delete");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 3, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
|
||||
if (m_Mobile != m_From)
|
||||
{
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Go to there");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 4, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Bring them here");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 5, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Move to target");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 6, ArrowRightWidth, ArrowRightHeight);
|
||||
|
||||
if (m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel)
|
||||
{
|
||||
AddNewLine();
|
||||
if (m_Mobile.Alive)
|
||||
{
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Kill");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 7, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Resurrect");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 8, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Mobile.NetState != null)
|
||||
{
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, "Client");
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 9, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
|
||||
FinishPage();
|
||||
}
|
||||
|
||||
private void InvokeCommand(string ip)
|
||||
{
|
||||
CommandSystem.Handle(m_From, $"{CommandSystem.Prefix}{ip}");
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
if (m_Mobile.Deleted)
|
||||
{
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BaseCommand.IsAccessible(m_From, m_Mobile))
|
||||
{
|
||||
m_From.SendMessage("That is no longer accessible.");
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (info.ButtonID)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
break;
|
||||
}
|
||||
case 2: // Properties
|
||||
{
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
m_From.SendGump(new PropertiesGump(m_From, m_Mobile));
|
||||
break;
|
||||
}
|
||||
case 3: // Delete
|
||||
{
|
||||
if (!m_Mobile.Player)
|
||||
{
|
||||
CommandLogging.WriteLine(m_From, "{0} {1} deleting {2}", m_From.AccessLevel,
|
||||
CommandLogging.Format(m_From), CommandLogging.Format(m_Mobile));
|
||||
m_Mobile.Delete();
|
||||
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 4: // Go there
|
||||
{
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
InvokeCommand($"Go {m_Mobile.Serial.Value}");
|
||||
break;
|
||||
}
|
||||
case 5: // Bring them here
|
||||
{
|
||||
if (m_From.Map == null || m_From.Map == Map.Internal)
|
||||
{
|
||||
m_From.SendMessage("You cannot bring that person here.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
m_Mobile.MoveToWorld(m_From.Location, m_From.Map);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 6: // Move to target
|
||||
{
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
m_From.Target = new MoveTarget(m_Mobile);
|
||||
break;
|
||||
}
|
||||
case 7: // Kill
|
||||
{
|
||||
if (m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel)
|
||||
m_Mobile.Kill();
|
||||
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
|
||||
break;
|
||||
}
|
||||
case 8: // Res
|
||||
{
|
||||
if (m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel)
|
||||
{
|
||||
m_Mobile.PlaySound(0x214);
|
||||
m_Mobile.FixedEffect(0x376A, 10, 16);
|
||||
|
||||
m_Mobile.Resurrect();
|
||||
}
|
||||
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
|
||||
break;
|
||||
}
|
||||
case 9: // Client
|
||||
{
|
||||
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
|
||||
|
||||
if (m_Mobile.NetState != null)
|
||||
m_From.SendGump(new ClientGump(m_From, m_Mobile.NetState));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Projects/Scripts/Commands/Generic/Extensions/BaseExtension.cs
Normal file
133
Projects/Scripts/Commands/Generic/Extensions/BaseExtension.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public delegate BaseExtension ExtensionConstructor();
|
||||
|
||||
public sealed class ExtensionInfo
|
||||
{
|
||||
public ExtensionInfo(int order, string name, int size, ExtensionConstructor constructor)
|
||||
{
|
||||
Name = name;
|
||||
Size = size;
|
||||
|
||||
Order = order;
|
||||
|
||||
Constructor = constructor;
|
||||
}
|
||||
|
||||
public static Dictionary<string, ExtensionInfo> Table{ get; } =
|
||||
new Dictionary<string, ExtensionInfo>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
public int Order{ get; }
|
||||
|
||||
public string Name{ get; }
|
||||
|
||||
public int Size{ get; }
|
||||
|
||||
public bool IsFixedSize => Size >= 0;
|
||||
|
||||
public ExtensionConstructor Constructor{ get; }
|
||||
|
||||
public static void Register(ExtensionInfo ext)
|
||||
{
|
||||
Table[ext.Name] = ext;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Extensions : List<BaseExtension>
|
||||
{
|
||||
public bool IsValid(object obj)
|
||||
{
|
||||
for (int i = 0; i < Count; ++i)
|
||||
if (!this[i].IsValid(obj))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Filter(List<object> list)
|
||||
{
|
||||
for (int i = 0; i < Count; ++i)
|
||||
this[i].Filter(list);
|
||||
}
|
||||
|
||||
public static Extensions Parse(Mobile from, ref string[] args)
|
||||
{
|
||||
Extensions parsed = new Extensions();
|
||||
|
||||
int size = args.Length;
|
||||
|
||||
Type baseType = null;
|
||||
|
||||
for (int i = args.Length - 1; i >= 0; --i)
|
||||
{
|
||||
if (!ExtensionInfo.Table.TryGetValue(args[i], out ExtensionInfo extInfo))
|
||||
continue;
|
||||
|
||||
if (extInfo.IsFixedSize && i != size - extInfo.Size - 1)
|
||||
throw new Exception("Invalid extended argument count.");
|
||||
|
||||
BaseExtension ext = extInfo.Constructor();
|
||||
|
||||
ext.Parse(from, args, i + 1, size - i - 1);
|
||||
|
||||
if (ext is WhereExtension extension)
|
||||
baseType = extension.Conditional.Type;
|
||||
|
||||
parsed.Add(ext);
|
||||
|
||||
size = i;
|
||||
}
|
||||
|
||||
parsed.Sort((a, b) => a.Order - b.Order);
|
||||
|
||||
AssemblyEmitter emitter = null;
|
||||
|
||||
foreach (BaseExtension update in parsed)
|
||||
update.Optimize(from, baseType, ref emitter);
|
||||
|
||||
if (size != args.Length)
|
||||
{
|
||||
string[] old = args;
|
||||
args = new string[size];
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
args[i] = old[i];
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BaseExtension
|
||||
{
|
||||
public abstract ExtensionInfo Info{ get; }
|
||||
|
||||
public string Name => Info.Name;
|
||||
|
||||
public int Size => Info.Size;
|
||||
|
||||
public bool IsFixedSize => Info.IsFixedSize;
|
||||
|
||||
public int Order => Info.Order;
|
||||
|
||||
public virtual void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Parse(Mobile from, string[] arguments, int offset, int size)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool IsValid(object obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Filter(List<object> list)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,549 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public interface IConditional
|
||||
{
|
||||
bool Verify(object obj);
|
||||
}
|
||||
|
||||
public interface ICondition
|
||||
{
|
||||
// Invoked during the constructor
|
||||
void Construct(TypeBuilder typeBuilder, ILGenerator il, int index);
|
||||
|
||||
// Target object will be loaded on the stack
|
||||
void Compile(MethodEmitter emitter);
|
||||
}
|
||||
|
||||
public sealed class TypeCondition : ICondition
|
||||
{
|
||||
public static TypeCondition Default = new TypeCondition();
|
||||
|
||||
void ICondition.Construct(TypeBuilder typeBuilder, ILGenerator il, int index)
|
||||
{
|
||||
}
|
||||
|
||||
void ICondition.Compile(MethodEmitter emitter)
|
||||
{
|
||||
// The object was safely cast to be the conditionals type
|
||||
// If it's null, then the type cast didn't work...
|
||||
|
||||
emitter.LoadNull();
|
||||
emitter.Compare(OpCodes.Ceq);
|
||||
emitter.LogicalNot();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PropertyValue
|
||||
{
|
||||
public PropertyValue(Type type, object value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public Type Type{ get; }
|
||||
|
||||
public object Value{ get; private set; }
|
||||
|
||||
public FieldInfo Field{ get; private set; }
|
||||
|
||||
public bool HasField => Field != null;
|
||||
|
||||
public void Load(MethodEmitter method)
|
||||
{
|
||||
if (Field != null)
|
||||
{
|
||||
method.LoadArgument(0);
|
||||
method.LoadField(Field);
|
||||
}
|
||||
else if (Value == null)
|
||||
{
|
||||
method.LoadNull(Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Value is int i)
|
||||
method.Load(i);
|
||||
else if (Value is long l)
|
||||
method.Load(l);
|
||||
else if (Value is float f)
|
||||
method.Load(f);
|
||||
else if (Value is double d)
|
||||
method.Load(d);
|
||||
else if (Value is char c)
|
||||
method.Load(c);
|
||||
else if (Value is bool b)
|
||||
method.Load(b);
|
||||
else if (Value is string s)
|
||||
method.Load(s);
|
||||
else if (Value is Enum e)
|
||||
method.Load(e);
|
||||
else
|
||||
throw new InvalidOperationException("Unrecognized comparison value.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Acquire(TypeBuilder typeBuilder, ILGenerator il, string fieldName)
|
||||
{
|
||||
if (!(Value is string toParse))
|
||||
return;
|
||||
|
||||
if (!Type.IsValueType && toParse == "null")
|
||||
{
|
||||
Value = null;
|
||||
}
|
||||
else if (Type == typeof(string))
|
||||
{
|
||||
if (toParse == @"@""null""")
|
||||
toParse = "null";
|
||||
|
||||
Value = toParse;
|
||||
}
|
||||
else if (Type.IsEnum)
|
||||
{
|
||||
Value = Enum.Parse(Type, toParse, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo parseMethod;
|
||||
object[] parseArgs;
|
||||
|
||||
MethodInfo parseNumber = Type.GetMethod(
|
||||
"Parse",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
null,
|
||||
new[] { typeof(string), typeof(NumberStyles) },
|
||||
null
|
||||
);
|
||||
|
||||
if (parseNumber != null)
|
||||
{
|
||||
NumberStyles style = NumberStyles.Integer;
|
||||
|
||||
if (Insensitive.StartsWith(toParse, "0x"))
|
||||
{
|
||||
style = NumberStyles.HexNumber;
|
||||
toParse = toParse.Substring(2);
|
||||
}
|
||||
|
||||
parseMethod = parseNumber;
|
||||
parseArgs = new object[] { toParse, style };
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo parseGeneral = Type.GetMethod(
|
||||
"Parse",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
null,
|
||||
new[] { typeof(string) },
|
||||
null
|
||||
);
|
||||
|
||||
parseMethod = parseGeneral;
|
||||
parseArgs = new object[] { toParse };
|
||||
}
|
||||
|
||||
if (parseMethod != null)
|
||||
{
|
||||
Value = parseMethod.Invoke(null, parseArgs);
|
||||
|
||||
if (!Type.IsPrimitive)
|
||||
{
|
||||
Field = typeBuilder.DefineField(
|
||||
fieldName,
|
||||
Type,
|
||||
FieldAttributes.Private | FieldAttributes.InitOnly
|
||||
);
|
||||
|
||||
// parseMethod.Invoke(null,
|
||||
// parseArgs.Length == 2 ? new object[] {toParse, (int) parseArgs[1]} : new object[] {toParse});
|
||||
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
il.Emit(OpCodes.Ldstr, toParse);
|
||||
|
||||
if (parseArgs.Length == 2) // dirty evil hack :-(
|
||||
il.Emit(OpCodes.Ldc_I4, (int)parseArgs[1]);
|
||||
|
||||
il.Emit(OpCodes.Call, parseMethod);
|
||||
il.Emit(OpCodes.Stfld, Field);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to convert string \"{Value}\" into type '{Type}'."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class PropertyCondition : ICondition
|
||||
{
|
||||
protected bool m_Not;
|
||||
protected Property m_Property;
|
||||
|
||||
public PropertyCondition(Property property, bool not)
|
||||
{
|
||||
m_Property = property;
|
||||
m_Not = not;
|
||||
}
|
||||
|
||||
public abstract void Construct(TypeBuilder typeBuilder, ILGenerator il, int index);
|
||||
|
||||
public abstract void Compile(MethodEmitter emitter);
|
||||
}
|
||||
|
||||
public enum StringOperator
|
||||
{
|
||||
Equal,
|
||||
NotEqual,
|
||||
|
||||
Contains,
|
||||
|
||||
StartsWith,
|
||||
EndsWith
|
||||
}
|
||||
|
||||
public sealed class StringCondition : PropertyCondition
|
||||
{
|
||||
private bool m_IgnoreCase;
|
||||
private StringOperator m_Operator;
|
||||
private PropertyValue m_Value;
|
||||
|
||||
public StringCondition(Property property, bool not, StringOperator op, object value, bool ignoreCase)
|
||||
: base(property, not)
|
||||
{
|
||||
m_Operator = op;
|
||||
m_Value = new PropertyValue(property.Type, value);
|
||||
|
||||
m_IgnoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
public override void Construct(TypeBuilder typeBuilder, ILGenerator il, int index)
|
||||
{
|
||||
m_Value.Acquire(typeBuilder, il, "v" + index);
|
||||
}
|
||||
|
||||
public override void Compile(MethodEmitter emitter)
|
||||
{
|
||||
bool inverse = false;
|
||||
|
||||
string methodName;
|
||||
|
||||
switch (m_Operator)
|
||||
{
|
||||
case StringOperator.Equal:
|
||||
methodName = "Equals";
|
||||
break;
|
||||
|
||||
case StringOperator.NotEqual:
|
||||
methodName = "Equals";
|
||||
inverse = true;
|
||||
break;
|
||||
|
||||
case StringOperator.Contains:
|
||||
methodName = "Contains";
|
||||
break;
|
||||
|
||||
case StringOperator.StartsWith:
|
||||
methodName = "StartsWith";
|
||||
break;
|
||||
|
||||
case StringOperator.EndsWith:
|
||||
methodName = "EndsWith";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid string comparison operator.");
|
||||
}
|
||||
|
||||
if (m_IgnoreCase || methodName == "Equals")
|
||||
{
|
||||
Type type = m_IgnoreCase ? typeof(Insensitive) : typeof(string);
|
||||
|
||||
emitter.BeginCall(
|
||||
type.GetMethod(
|
||||
methodName,
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
null,
|
||||
new[]
|
||||
{
|
||||
typeof(string),
|
||||
typeof(string)
|
||||
},
|
||||
null
|
||||
)
|
||||
);
|
||||
|
||||
emitter.Chain(m_Property);
|
||||
m_Value.Load(emitter);
|
||||
|
||||
emitter.FinishCall();
|
||||
}
|
||||
else
|
||||
{
|
||||
Label notNull = emitter.CreateLabel();
|
||||
Label moveOn = emitter.CreateLabel();
|
||||
|
||||
LocalBuilder temp = emitter.AcquireTemp(m_Property.Type);
|
||||
|
||||
emitter.Chain(m_Property);
|
||||
|
||||
emitter.StoreLocal(temp);
|
||||
emitter.LoadLocal(temp);
|
||||
|
||||
emitter.BranchIfTrue(notNull);
|
||||
|
||||
emitter.Load(false);
|
||||
emitter.Pop();
|
||||
emitter.Branch(moveOn);
|
||||
|
||||
emitter.MarkLabel(notNull);
|
||||
emitter.LoadLocal(temp);
|
||||
|
||||
emitter.BeginCall(
|
||||
typeof(string).GetMethod(
|
||||
methodName,
|
||||
BindingFlags.Public | BindingFlags.Instance,
|
||||
null,
|
||||
new[]
|
||||
{
|
||||
typeof(string)
|
||||
},
|
||||
null
|
||||
)
|
||||
);
|
||||
|
||||
m_Value.Load(emitter);
|
||||
|
||||
emitter.FinishCall();
|
||||
|
||||
emitter.MarkLabel(moveOn);
|
||||
}
|
||||
|
||||
if (m_Not != inverse)
|
||||
emitter.LogicalNot();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ComparisonOperator
|
||||
{
|
||||
Equal,
|
||||
NotEqual,
|
||||
Greater,
|
||||
GreaterEqual,
|
||||
Lesser,
|
||||
LesserEqual
|
||||
}
|
||||
|
||||
public sealed class ComparisonCondition : PropertyCondition
|
||||
{
|
||||
private ComparisonOperator m_Operator;
|
||||
private PropertyValue m_Value;
|
||||
|
||||
public ComparisonCondition(Property property, bool not, ComparisonOperator op, object value)
|
||||
: base(property, not)
|
||||
{
|
||||
m_Operator = op;
|
||||
m_Value = new PropertyValue(property.Type, value);
|
||||
}
|
||||
|
||||
public override void Construct(TypeBuilder typeBuilder, ILGenerator il, int index)
|
||||
{
|
||||
m_Value.Acquire(typeBuilder, il, "v" + index);
|
||||
}
|
||||
|
||||
public override void Compile(MethodEmitter emitter)
|
||||
{
|
||||
emitter.Chain(m_Property);
|
||||
|
||||
bool inverse = false;
|
||||
|
||||
bool couldCompare =
|
||||
emitter.CompareTo(1, delegate { m_Value.Load(emitter); });
|
||||
|
||||
if (couldCompare)
|
||||
{
|
||||
emitter.Load(0);
|
||||
|
||||
switch (m_Operator)
|
||||
{
|
||||
case ComparisonOperator.Equal:
|
||||
emitter.Compare(OpCodes.Ceq);
|
||||
break;
|
||||
|
||||
case ComparisonOperator.NotEqual:
|
||||
emitter.Compare(OpCodes.Ceq);
|
||||
inverse = true;
|
||||
break;
|
||||
|
||||
case ComparisonOperator.Greater:
|
||||
emitter.Compare(OpCodes.Cgt);
|
||||
break;
|
||||
|
||||
case ComparisonOperator.GreaterEqual:
|
||||
emitter.Compare(OpCodes.Clt);
|
||||
inverse = true;
|
||||
break;
|
||||
|
||||
case ComparisonOperator.Lesser:
|
||||
emitter.Compare(OpCodes.Clt);
|
||||
break;
|
||||
|
||||
case ComparisonOperator.LesserEqual:
|
||||
emitter.Compare(OpCodes.Cgt);
|
||||
inverse = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid comparison operator.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This type is -not- comparable
|
||||
// We can only support == and != operations
|
||||
|
||||
m_Value.Load(emitter);
|
||||
|
||||
switch (m_Operator)
|
||||
{
|
||||
case ComparisonOperator.Equal:
|
||||
emitter.Compare(OpCodes.Ceq);
|
||||
break;
|
||||
|
||||
case ComparisonOperator.NotEqual:
|
||||
emitter.Compare(OpCodes.Ceq);
|
||||
inverse = true;
|
||||
break;
|
||||
|
||||
case ComparisonOperator.Greater:
|
||||
case ComparisonOperator.GreaterEqual:
|
||||
case ComparisonOperator.Lesser:
|
||||
case ComparisonOperator.LesserEqual:
|
||||
throw new InvalidOperationException("Property does not support relational comparisons.");
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid operator.");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Not != inverse)
|
||||
emitter.LogicalNot();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConditionalCompiler
|
||||
{
|
||||
public static IConditional Compile(AssemblyEmitter assembly, Type objectType, ICondition[] conditions, int index)
|
||||
{
|
||||
TypeBuilder typeBuilder = assembly.DefineType(
|
||||
"__conditional" + index,
|
||||
TypeAttributes.Public,
|
||||
typeof(object)
|
||||
);
|
||||
|
||||
#region Constructor
|
||||
|
||||
{
|
||||
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.Public,
|
||||
CallingConventions.Standard,
|
||||
Type.EmptyTypes
|
||||
);
|
||||
|
||||
ILGenerator il = ctor.GetILGenerator();
|
||||
|
||||
// : base()
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
|
||||
|
||||
for (int i = 0; i < conditions.Length; ++i)
|
||||
conditions[i].Construct(typeBuilder, il, i);
|
||||
|
||||
// return;
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IComparer
|
||||
|
||||
typeBuilder.AddInterfaceImplementation(typeof(IConditional));
|
||||
|
||||
MethodBuilder compareMethod;
|
||||
|
||||
#region Compare
|
||||
|
||||
{
|
||||
MethodEmitter emitter = new MethodEmitter(typeBuilder);
|
||||
|
||||
emitter.Define(
|
||||
/* name */ "Verify",
|
||||
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
/* return */ typeof(bool),
|
||||
/* params */ new[] { typeof(object) });
|
||||
|
||||
LocalBuilder obj = emitter.CreateLocal(objectType);
|
||||
LocalBuilder eq = emitter.CreateLocal(typeof(bool));
|
||||
|
||||
emitter.LoadArgument(1);
|
||||
emitter.CastAs(objectType);
|
||||
emitter.StoreLocal(obj);
|
||||
|
||||
Label done = emitter.CreateLabel();
|
||||
|
||||
for (int i = 0; i < conditions.Length; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
emitter.LoadLocal(eq);
|
||||
|
||||
emitter.BranchIfFalse(done);
|
||||
}
|
||||
|
||||
emitter.LoadLocal(obj);
|
||||
|
||||
conditions[i].Compile(emitter);
|
||||
|
||||
emitter.StoreLocal(eq);
|
||||
}
|
||||
|
||||
emitter.MarkLabel(done);
|
||||
|
||||
emitter.LoadLocal(eq);
|
||||
|
||||
emitter.Return();
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
emitter.Method,
|
||||
typeof(IConditional).GetMethod(
|
||||
"Verify",
|
||||
new[]
|
||||
{
|
||||
typeof(object)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
compareMethod = emitter.Method;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
Type conditionalType = typeBuilder.CreateType();
|
||||
|
||||
return (IConditional)Activator.CreateInstance(conditionalType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public static class DistinctCompiler
|
||||
{
|
||||
public static IComparer<T> Compile<T>(AssemblyEmitter assembly, Type objectType, Property[] props)
|
||||
{
|
||||
TypeBuilder typeBuilder = assembly.DefineType(
|
||||
"__distinct",
|
||||
TypeAttributes.Public,
|
||||
typeof(object)
|
||||
);
|
||||
|
||||
#region Constructor
|
||||
|
||||
{
|
||||
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.Public,
|
||||
CallingConventions.Standard,
|
||||
Type.EmptyTypes
|
||||
);
|
||||
|
||||
ILGenerator il = ctor.GetILGenerator();
|
||||
|
||||
// : base()
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Call, typeof(T).GetConstructor(Type.EmptyTypes) ??
|
||||
throw new Exception($"Could not find empty constructor for type {typeof(T).FullName}"));
|
||||
|
||||
// return;
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IComparer
|
||||
|
||||
typeBuilder.AddInterfaceImplementation(typeof(IComparer<T>));
|
||||
|
||||
MethodBuilder compareMethod;
|
||||
|
||||
#region Compare
|
||||
|
||||
{
|
||||
MethodEmitter emitter = new MethodEmitter(typeBuilder);
|
||||
|
||||
emitter.Define(
|
||||
/* name */ "Compare",
|
||||
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
/* return */ typeof(int),
|
||||
/* params */ new[] { typeof(T), typeof(T) });
|
||||
|
||||
LocalBuilder a = emitter.CreateLocal(objectType);
|
||||
LocalBuilder b = emitter.CreateLocal(objectType);
|
||||
|
||||
LocalBuilder v = emitter.CreateLocal(typeof(int));
|
||||
|
||||
emitter.LoadArgument(1);
|
||||
emitter.CastAs(objectType);
|
||||
emitter.StoreLocal(a);
|
||||
|
||||
emitter.LoadArgument(2);
|
||||
emitter.CastAs(objectType);
|
||||
emitter.StoreLocal(b);
|
||||
|
||||
emitter.Load(0);
|
||||
emitter.StoreLocal(v);
|
||||
|
||||
Label end = emitter.CreateLabel();
|
||||
|
||||
for (int i = 0; i < props.Length; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
emitter.LoadLocal(v);
|
||||
emitter.BranchIfTrue(end); // if ( v != 0 ) return v;
|
||||
}
|
||||
|
||||
Property prop = props[i];
|
||||
|
||||
emitter.LoadLocal(a);
|
||||
emitter.Chain(prop);
|
||||
|
||||
bool couldCompare =
|
||||
emitter.CompareTo(1, delegate
|
||||
{
|
||||
emitter.LoadLocal(b);
|
||||
emitter.Chain(prop);
|
||||
});
|
||||
|
||||
if (!couldCompare)
|
||||
throw new InvalidOperationException("Property is not comparable.");
|
||||
|
||||
emitter.StoreLocal(v);
|
||||
}
|
||||
|
||||
emitter.MarkLabel(end);
|
||||
|
||||
emitter.LoadLocal(v);
|
||||
emitter.Return();
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
emitter.Method,
|
||||
typeof(IComparer<T>).GetMethod(
|
||||
"Compare",
|
||||
new[]
|
||||
{
|
||||
typeof(T),
|
||||
typeof(T)
|
||||
}
|
||||
) ?? throw new Exception($"No Compare method found for type {typeof(T).FullName}")
|
||||
);
|
||||
|
||||
compareMethod = emitter.Method;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEqualityComparer
|
||||
|
||||
typeBuilder.AddInterfaceImplementation(typeof(IEqualityComparer<T>));
|
||||
|
||||
#region Equals
|
||||
|
||||
{
|
||||
MethodEmitter emitter = new MethodEmitter(typeBuilder);
|
||||
|
||||
emitter.Define(
|
||||
/* name */ "Equals",
|
||||
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
/* return */ typeof(bool),
|
||||
/* params */ new[] { typeof(T), typeof(T) });
|
||||
|
||||
emitter.Generator.Emit(OpCodes.Ldarg_0);
|
||||
emitter.Generator.Emit(OpCodes.Ldarg_1);
|
||||
emitter.Generator.Emit(OpCodes.Ldarg_2);
|
||||
|
||||
emitter.Generator.Emit(OpCodes.Call, compareMethod);
|
||||
|
||||
emitter.Generator.Emit(OpCodes.Ldc_I4_0);
|
||||
|
||||
emitter.Generator.Emit(OpCodes.Ceq);
|
||||
|
||||
emitter.Generator.Emit(OpCodes.Ret);
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
emitter.Method,
|
||||
typeof(IEqualityComparer<T>).GetMethod(
|
||||
"Equals",
|
||||
new[]
|
||||
{
|
||||
typeof(T),
|
||||
typeof(T)
|
||||
}
|
||||
) ?? throw new Exception($"No Equals method found for type {typeof(T).FullName}")
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetHashCode
|
||||
|
||||
{
|
||||
MethodEmitter emitter = new MethodEmitter(typeBuilder);
|
||||
|
||||
emitter.Define(
|
||||
/* name */ "GetHashCode",
|
||||
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
/* return */ typeof(int),
|
||||
/* params */ new[] { typeof(T) });
|
||||
|
||||
LocalBuilder obj = emitter.CreateLocal(objectType);
|
||||
|
||||
emitter.LoadArgument(1);
|
||||
emitter.CastAs(objectType);
|
||||
emitter.StoreLocal(obj);
|
||||
|
||||
for (int i = 0; i < props.Length; ++i)
|
||||
{
|
||||
Property prop = props[i];
|
||||
|
||||
emitter.LoadLocal(obj);
|
||||
emitter.Chain(prop);
|
||||
|
||||
Type active = emitter.Active;
|
||||
|
||||
MethodInfo getHashCode = active.GetMethod("GetHashCode", Type.EmptyTypes);
|
||||
|
||||
if (getHashCode == null)
|
||||
getHashCode = typeof(T).GetMethod("GetHashCode", Type.EmptyTypes);
|
||||
|
||||
if (active != typeof(int))
|
||||
{
|
||||
if (!active.IsValueType)
|
||||
{
|
||||
LocalBuilder value = emitter.AcquireTemp(active);
|
||||
|
||||
Label valueNotNull = emitter.CreateLabel();
|
||||
Label done = emitter.CreateLabel();
|
||||
|
||||
emitter.StoreLocal(value);
|
||||
emitter.LoadLocal(value);
|
||||
|
||||
emitter.BranchIfTrue(valueNotNull);
|
||||
|
||||
emitter.Load(0);
|
||||
emitter.Pop(typeof(int));
|
||||
|
||||
emitter.Branch(done);
|
||||
|
||||
emitter.MarkLabel(valueNotNull);
|
||||
|
||||
emitter.LoadLocal(value);
|
||||
emitter.Call(getHashCode);
|
||||
|
||||
emitter.ReleaseTemp(value);
|
||||
|
||||
emitter.MarkLabel(done);
|
||||
}
|
||||
else
|
||||
{
|
||||
emitter.Call(getHashCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
emitter.Xor();
|
||||
}
|
||||
|
||||
emitter.Return();
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
emitter.Method,
|
||||
typeof(IEqualityComparer<T>).GetMethod(
|
||||
"GetHashCode",
|
||||
new[]
|
||||
{
|
||||
typeof(T)
|
||||
}
|
||||
) ?? throw new Exception($"No GetHashCode method found for type {typeof(T).FullName}")
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
Type comparerType = typeBuilder.CreateType();
|
||||
|
||||
return (IComparer<T>)Activator.CreateInstance(comparerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public sealed class OrderInfo
|
||||
{
|
||||
private int m_Order;
|
||||
|
||||
public OrderInfo(Property property, bool isAscending)
|
||||
{
|
||||
Property = property;
|
||||
|
||||
IsAscending = isAscending;
|
||||
}
|
||||
|
||||
public Property Property{ get; set; }
|
||||
|
||||
public bool IsAscending
|
||||
{
|
||||
get => m_Order > 0;
|
||||
set => m_Order = value ? +1 : -1;
|
||||
}
|
||||
|
||||
public bool IsDescending
|
||||
{
|
||||
get => m_Order < 0;
|
||||
set => m_Order = value ? -1 : +1;
|
||||
}
|
||||
|
||||
public int Sign
|
||||
{
|
||||
get => Math.Sign(m_Order);
|
||||
set
|
||||
{
|
||||
m_Order = Math.Sign(value);
|
||||
|
||||
if (m_Order == 0)
|
||||
throw new InvalidOperationException("Sign cannot be zero.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortCompiler
|
||||
{
|
||||
public static IComparer<T> Compile<T>(AssemblyEmitter assembly, Type objectType, OrderInfo[] orders)
|
||||
{
|
||||
TypeBuilder typeBuilder = assembly.DefineType(
|
||||
"__sort",
|
||||
TypeAttributes.Public,
|
||||
typeof(T)
|
||||
);
|
||||
|
||||
#region Constructor
|
||||
|
||||
{
|
||||
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.Public,
|
||||
CallingConventions.Standard,
|
||||
Type.EmptyTypes
|
||||
);
|
||||
|
||||
ILGenerator il = ctor.GetILGenerator();
|
||||
|
||||
// : base()
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Call, typeof(T).GetConstructor(Type.EmptyTypes) ??
|
||||
throw new Exception($"Could not find empty constructor for type {typeof(T).FullName}"));
|
||||
|
||||
// return;
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IComparer
|
||||
|
||||
typeBuilder.AddInterfaceImplementation(typeof(IComparer<T>));
|
||||
|
||||
#region Compare
|
||||
{
|
||||
MethodEmitter emitter = new MethodEmitter(typeBuilder);
|
||||
|
||||
emitter.Define(
|
||||
/* name */ "Compare",
|
||||
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
/* return */ typeof(int),
|
||||
/* params */ new[] { typeof(T), typeof(T) });
|
||||
|
||||
LocalBuilder a = emitter.CreateLocal(objectType);
|
||||
LocalBuilder b = emitter.CreateLocal(objectType);
|
||||
|
||||
LocalBuilder v = emitter.CreateLocal(typeof(int));
|
||||
|
||||
emitter.LoadArgument(1);
|
||||
emitter.CastAs(objectType);
|
||||
emitter.StoreLocal(a);
|
||||
|
||||
emitter.LoadArgument(2);
|
||||
emitter.CastAs(objectType);
|
||||
emitter.StoreLocal(b);
|
||||
|
||||
emitter.Load(0);
|
||||
emitter.StoreLocal(v);
|
||||
|
||||
Label end = emitter.CreateLabel();
|
||||
|
||||
for (int i = 0; i < orders.Length; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
emitter.LoadLocal(v);
|
||||
emitter.BranchIfTrue(end); // if ( v != 0 ) return v;
|
||||
}
|
||||
|
||||
OrderInfo orderInfo = orders[i];
|
||||
|
||||
Property prop = orderInfo.Property;
|
||||
int sign = orderInfo.Sign;
|
||||
|
||||
emitter.LoadLocal(a);
|
||||
emitter.Chain(prop);
|
||||
|
||||
bool couldCompare =
|
||||
emitter.CompareTo(sign, delegate
|
||||
{
|
||||
emitter.LoadLocal(b);
|
||||
emitter.Chain(prop);
|
||||
});
|
||||
|
||||
if (!couldCompare)
|
||||
throw new InvalidOperationException("Property is not comparable.");
|
||||
|
||||
emitter.StoreLocal(v);
|
||||
}
|
||||
|
||||
emitter.MarkLabel(end);
|
||||
|
||||
emitter.LoadLocal(v);
|
||||
emitter.Return();
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
emitter.Method,
|
||||
typeof(IComparer<T>).GetMethod(
|
||||
"Compare",
|
||||
new[]
|
||||
{
|
||||
typeof(T),
|
||||
typeof(T)
|
||||
}
|
||||
) ?? throw new Exception($"No Compare method found for type {typeof(T).FullName}")
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
Type comparerType = typeBuilder.CreateType();
|
||||
return (IComparer<T>)Activator.CreateInstance(comparerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public sealed class DistinctExtension : BaseExtension
|
||||
{
|
||||
public static ExtensionInfo ExtInfo =
|
||||
new ExtensionInfo(30, "Distinct", -1, () => new DistinctExtension());
|
||||
|
||||
private IComparer<object> m_Comparer;
|
||||
|
||||
private List<Property> m_Properties;
|
||||
|
||||
public DistinctExtension()
|
||||
{
|
||||
m_Properties = new List<Property>();
|
||||
}
|
||||
|
||||
public override ExtensionInfo Info => ExtInfo;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
ExtensionInfo.Register(ExtInfo);
|
||||
}
|
||||
|
||||
public override void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
|
||||
{
|
||||
if (baseType == null)
|
||||
throw new Exception("Distinct extension may only be used in combination with an object conditional.");
|
||||
|
||||
foreach (Property prop in m_Properties)
|
||||
{
|
||||
prop.BindTo(baseType, PropertyAccess.Read);
|
||||
prop.CheckAccess(from);
|
||||
}
|
||||
|
||||
if (assembly == null)
|
||||
assembly = new AssemblyEmitter("__dynamic");
|
||||
|
||||
m_Comparer = DistinctCompiler.Compile<object>(assembly, baseType, m_Properties.ToArray());
|
||||
}
|
||||
|
||||
public override void Parse(Mobile from, string[] arguments, int offset, int size)
|
||||
{
|
||||
if (size < 1)
|
||||
throw new Exception("Invalid distinction syntax.");
|
||||
|
||||
int end = offset + size;
|
||||
|
||||
while (offset < end)
|
||||
{
|
||||
string binding = arguments[offset++];
|
||||
|
||||
m_Properties.Add(new Property(binding));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Filter(List<object> list)
|
||||
{
|
||||
if (m_Comparer == null)
|
||||
throw new InvalidOperationException("The extension must first be optimized.");
|
||||
|
||||
List<object> copy = new List<object>(list);
|
||||
|
||||
copy.Sort(m_Comparer);
|
||||
|
||||
list.Clear();
|
||||
|
||||
object last = null;
|
||||
|
||||
for (int i = 0; i < copy.Count; ++i)
|
||||
{
|
||||
object obj = copy[i];
|
||||
|
||||
if (last == null || m_Comparer.Compare(obj, last) != 0)
|
||||
{
|
||||
list.Add(obj);
|
||||
last = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public sealed class LimitExtension : BaseExtension
|
||||
{
|
||||
public static ExtensionInfo ExtInfo = new ExtensionInfo(80, "Limit", 1, () => new LimitExtension());
|
||||
|
||||
public override ExtensionInfo Info => ExtInfo;
|
||||
|
||||
public int Limit{ get; private set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
ExtensionInfo.Register(ExtInfo);
|
||||
}
|
||||
|
||||
public override void Parse(Mobile from, string[] arguments, int offset, int size)
|
||||
{
|
||||
Limit = Utility.ToInt32(arguments[offset]);
|
||||
|
||||
if (Limit < 0)
|
||||
throw new Exception("Limit cannot be less than zero.");
|
||||
}
|
||||
|
||||
public override void Filter(List<object> list)
|
||||
{
|
||||
if (list.Count > Limit)
|
||||
list.RemoveRange(Limit, list.Count - Limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Projects/Scripts/Commands/Generic/Extensions/SortExtension.cs
Normal file
103
Projects/Scripts/Commands/Generic/Extensions/SortExtension.cs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public sealed class SortExtension : BaseExtension
|
||||
{
|
||||
public static ExtensionInfo ExtInfo = new ExtensionInfo(40, "Order", -1, () => new SortExtension());
|
||||
|
||||
private IComparer<object> m_Comparer;
|
||||
|
||||
private List<OrderInfo> m_Orders;
|
||||
|
||||
public SortExtension()
|
||||
{
|
||||
m_Orders = new List<OrderInfo>();
|
||||
}
|
||||
|
||||
public override ExtensionInfo Info => ExtInfo;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
ExtensionInfo.Register(ExtInfo);
|
||||
}
|
||||
|
||||
public override void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
|
||||
{
|
||||
if (baseType == null)
|
||||
throw new Exception("The ordering extension may only be used in combination with an object conditional.");
|
||||
|
||||
foreach (OrderInfo order in m_Orders)
|
||||
{
|
||||
order.Property.BindTo(baseType, PropertyAccess.Read);
|
||||
order.Property.CheckAccess(from);
|
||||
}
|
||||
|
||||
if (assembly == null)
|
||||
assembly = new AssemblyEmitter("__dynamic");
|
||||
|
||||
m_Comparer = SortCompiler.Compile<object>(assembly, baseType, m_Orders.ToArray());
|
||||
}
|
||||
|
||||
public override void Parse(Mobile from, string[] arguments, int offset, int size)
|
||||
{
|
||||
if (size < 1)
|
||||
throw new Exception("Invalid ordering syntax.");
|
||||
|
||||
if (Insensitive.Equals(arguments[offset], "by"))
|
||||
{
|
||||
++offset;
|
||||
--size;
|
||||
|
||||
if (size < 1)
|
||||
throw new Exception("Invalid ordering syntax.");
|
||||
}
|
||||
|
||||
int end = offset + size;
|
||||
|
||||
while (offset < end)
|
||||
{
|
||||
string binding = arguments[offset++];
|
||||
|
||||
bool isAscending = true;
|
||||
|
||||
if (offset < end)
|
||||
{
|
||||
string next = arguments[offset];
|
||||
|
||||
switch (next.ToLower())
|
||||
{
|
||||
case "+":
|
||||
case "up":
|
||||
case "asc":
|
||||
case "ascending":
|
||||
isAscending = true;
|
||||
++offset;
|
||||
break;
|
||||
|
||||
case "-":
|
||||
case "down":
|
||||
case "desc":
|
||||
case "descending":
|
||||
isAscending = false;
|
||||
++offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Property property = new Property(binding);
|
||||
|
||||
m_Orders.Add(new OrderInfo(property, isAscending));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Filter(List<object> list)
|
||||
{
|
||||
if (m_Comparer == null)
|
||||
throw new InvalidOperationException("The extension must first be optimized.");
|
||||
|
||||
list.Sort(m_Comparer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public sealed class WhereExtension : BaseExtension
|
||||
{
|
||||
public static ExtensionInfo ExtInfo = new ExtensionInfo(20, "Where", -1, () => new WhereExtension());
|
||||
|
||||
public override ExtensionInfo Info => ExtInfo;
|
||||
|
||||
public ObjectConditional Conditional{ get; private set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
ExtensionInfo.Register(ExtInfo);
|
||||
}
|
||||
|
||||
public override void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
|
||||
{
|
||||
if (baseType == null)
|
||||
throw new InvalidOperationException("Insanity.");
|
||||
|
||||
Conditional.Compile(ref assembly);
|
||||
}
|
||||
|
||||
public override void Parse(Mobile from, string[] arguments, int offset, int size)
|
||||
{
|
||||
if (size < 1)
|
||||
throw new Exception("Invalid condition syntax.");
|
||||
|
||||
Conditional = ObjectConditional.ParseDirect(from, arguments, offset, size);
|
||||
}
|
||||
|
||||
public override bool IsValid(object obj)
|
||||
{
|
||||
return Conditional.CheckCondition(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class AreaCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public AreaCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Area", "Group" };
|
||||
SupportRequirement = CommandSupport.Area;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Area <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all appropriate objects in a targeted area. Optional condition arguments can further restrict the set of objects.";
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public static AreaCommandImplementor Instance{ get; private set; }
|
||||
|
||||
public override void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
BoundingBoxPicker.Begin(from, (map, start, end) => OnTarget(from, map, start, end, command, args));
|
||||
}
|
||||
|
||||
public void OnTarget(Mobile from, Map map, Point3D start, Point3D end, BaseCommand command, string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
Rectangle2D rect = new Rectangle2D(start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1);
|
||||
|
||||
Extensions ext = Extensions.Parse(from, ref args);
|
||||
|
||||
if (!CheckObjectTypes(from, command, ext, out bool items, out bool mobiles))
|
||||
return;
|
||||
|
||||
if (!(items || mobiles))
|
||||
return;
|
||||
|
||||
IPooledEnumerable<IEntity> eable = map.GetObjectsInBounds(rect, items, mobiles);
|
||||
|
||||
List<object> objs = eable.Where(obj => !mobiles || !(obj is Mobile) || BaseCommand.IsAccessible(from, obj))
|
||||
.Where(obj => ext.IsValid(obj)).Cast<object>().ToList();
|
||||
|
||||
eable.Free();
|
||||
ext.Filter(objs);
|
||||
|
||||
RunCommand(from, objs, command, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
from.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
[Flags]
|
||||
public enum CommandSupport
|
||||
{
|
||||
Single = 0x0001,
|
||||
Global = 0x0002,
|
||||
Online = 0x0004,
|
||||
Multi = 0x0008,
|
||||
Area = 0x0010,
|
||||
Self = 0x0020,
|
||||
Region = 0x0040,
|
||||
Contained = 0x0080,
|
||||
IPAddress = 0x0100,
|
||||
|
||||
All = Single | Global | Online | Multi | Area | Self | Region | Contained | IPAddress,
|
||||
AllMobiles = All & ~Contained,
|
||||
AllNPCs = All & ~(IPAddress | Online | Self | Contained),
|
||||
AllItems = All & ~(IPAddress | Online | Self | Region),
|
||||
|
||||
Simple = Single | Multi,
|
||||
Complex = Global | Online | Area | Region | Contained | IPAddress
|
||||
}
|
||||
|
||||
public abstract class BaseCommandImplementor
|
||||
{
|
||||
private static List<BaseCommandImplementor> m_Implementors;
|
||||
|
||||
public BaseCommandImplementor()
|
||||
{
|
||||
Commands = new Dictionary<string, BaseCommand>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool SupportsConditionals{ get; set; }
|
||||
|
||||
public string[] Accessors{ get; set; }
|
||||
|
||||
public string Usage{ get; set; }
|
||||
|
||||
public string Description{ get; set; }
|
||||
|
||||
public AccessLevel AccessLevel{ get; set; }
|
||||
|
||||
public CommandSupport SupportRequirement{ get; set; }
|
||||
|
||||
public Dictionary<string, BaseCommand> Commands{ get; }
|
||||
|
||||
public static List<BaseCommandImplementor> Implementors
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Implementors == null)
|
||||
{
|
||||
m_Implementors = new List<BaseCommandImplementor>();
|
||||
RegisterImplementors();
|
||||
}
|
||||
|
||||
return m_Implementors;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterImplementors()
|
||||
{
|
||||
Register(new RegionCommandImplementor());
|
||||
Register(new GlobalCommandImplementor());
|
||||
Register(new OnlineCommandImplementor());
|
||||
Register(new SingleCommandImplementor());
|
||||
Register(new SerialCommandImplementor());
|
||||
Register(new MultiCommandImplementor());
|
||||
Register(new AreaCommandImplementor());
|
||||
Register(new SelfCommandImplementor());
|
||||
Register(new ContainedCommandImplementor());
|
||||
Register(new IPAddressCommandImplementor());
|
||||
|
||||
Register(new RangeCommandImplementor());
|
||||
Register(new ScreenCommandImplementor());
|
||||
Register(new FacetCommandImplementor());
|
||||
}
|
||||
|
||||
public virtual void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
|
||||
{
|
||||
obj = null;
|
||||
}
|
||||
|
||||
public virtual void Register(BaseCommand command)
|
||||
{
|
||||
for (int i = 0; i < command.Commands.Length; ++i)
|
||||
Commands[command.Commands[i]] = command;
|
||||
}
|
||||
|
||||
public bool CheckObjectTypes(Mobile from, BaseCommand command, Extensions ext, out bool items, out bool mobiles)
|
||||
{
|
||||
items = mobiles = false;
|
||||
|
||||
ObjectConditional cond = ObjectConditional.Empty;
|
||||
|
||||
foreach (BaseExtension check in ext)
|
||||
if (check is WhereExtension extension)
|
||||
{
|
||||
cond = extension.Conditional;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bool condIsItem = cond.IsItem;
|
||||
bool condIsMobile = cond.IsMobile;
|
||||
|
||||
switch (command.ObjectTypes)
|
||||
{
|
||||
case ObjectTypes.All:
|
||||
case ObjectTypes.Both:
|
||||
{
|
||||
if (condIsItem)
|
||||
items = true;
|
||||
|
||||
if (condIsMobile)
|
||||
mobiles = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Items:
|
||||
{
|
||||
if (condIsItem)
|
||||
{
|
||||
items = true;
|
||||
}
|
||||
else if (condIsMobile)
|
||||
{
|
||||
from.SendMessage("You may not use a mobile type condition for this command.");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Mobiles:
|
||||
{
|
||||
if (condIsMobile)
|
||||
{
|
||||
mobiles = true;
|
||||
}
|
||||
else if (condIsItem)
|
||||
{
|
||||
from.SendMessage("You may not use an item type condition for this command.");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RunCommand(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
object obj = null;
|
||||
|
||||
Compile(from, command, ref args, ref obj);
|
||||
|
||||
RunCommand(from, obj, command, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
from.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public string GenerateArgString(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
return "";
|
||||
|
||||
// NOTE: this does not preserve the case where quotation marks are used on a single word
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append(' ');
|
||||
|
||||
if (args[i].IndexOf(' ') >= 0)
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(args[i]);
|
||||
sb.Append('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void RunCommand(Mobile from, object obj, BaseCommand command, string[] args)
|
||||
{
|
||||
// try
|
||||
// {
|
||||
CommandEventArgs e = new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args);
|
||||
|
||||
if (!command.ValidateArgs(this, e))
|
||||
return;
|
||||
|
||||
bool flushToLog = false;
|
||||
|
||||
if (obj is List<object> list)
|
||||
{
|
||||
if (list.Count > 20)
|
||||
CommandLogging.Enabled = false;
|
||||
else if (list.Count == 0)
|
||||
command.LogFailure("Nothing was found to use this command on.");
|
||||
|
||||
command.ExecuteList(e, list);
|
||||
|
||||
if (list.Count > 20)
|
||||
{
|
||||
flushToLog = true;
|
||||
CommandLogging.Enabled = true;
|
||||
}
|
||||
}
|
||||
else if (obj != null)
|
||||
{
|
||||
if (command.ListOptimized)
|
||||
command.ExecuteList(e, new List<object>{ obj });
|
||||
else
|
||||
command.Execute(e, obj);
|
||||
}
|
||||
|
||||
command.Flush(from, flushToLog);
|
||||
// }
|
||||
// catch ( Exception ex )
|
||||
// {
|
||||
// from.SendMessage( ex.Message );
|
||||
// }
|
||||
}
|
||||
|
||||
public virtual void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
RunCommand(from, command, args);
|
||||
}
|
||||
|
||||
public virtual void Execute(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length >= 1)
|
||||
{
|
||||
if (!Commands.TryGetValue(e.GetString(0), out BaseCommand command))
|
||||
{
|
||||
e.Mobile.SendMessage(
|
||||
"That is either an invalid command name or one that does not support this modifier.");
|
||||
}
|
||||
else if (e.Mobile.AccessLevel < command.AccessLevel)
|
||||
{
|
||||
e.Mobile.SendMessage("You do not have access to that command.");
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] oldArgs = e.Arguments;
|
||||
string[] args = new string[oldArgs.Length - 1];
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
args[i] = oldArgs[i + 1];
|
||||
|
||||
Process(e.Mobile, command, args);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("You must supply a command name.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
if (Accessors == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < Accessors.Length; ++i)
|
||||
CommandSystem.Register(Accessors[i], AccessLevel, Execute);
|
||||
}
|
||||
|
||||
public static void Register(BaseCommandImplementor impl)
|
||||
{
|
||||
m_Implementors.Add(impl);
|
||||
impl.Register();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class ContainedCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public ContainedCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Contained" };
|
||||
SupportRequirement = CommandSupport.Contained;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Contained <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all child items in a targeted container. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
if (command.ValidateArgs(this, new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args)))
|
||||
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
|
||||
(m, targeted) => OnTarget(m, targeted, command, args));
|
||||
}
|
||||
|
||||
public void OnTarget(Mobile from, object targeted, BaseCommand command, string[] args)
|
||||
{
|
||||
if (!BaseCommand.IsAccessible(from, targeted))
|
||||
{
|
||||
from.SendLocalizedMessage(500447); // That is not accessible.
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.ObjectTypes == ObjectTypes.Mobiles)
|
||||
return; // sanity check
|
||||
|
||||
if (!(targeted is Container cont))
|
||||
{
|
||||
from.SendMessage("That is not a container.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Extensions ext = Extensions.Parse(from, ref args);
|
||||
|
||||
if (!CheckObjectTypes(from, command, ext, out bool items, out bool _))
|
||||
return;
|
||||
|
||||
if (!items)
|
||||
{
|
||||
from.SendMessage("This command only works on items.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<object> list = new List<object>();
|
||||
|
||||
foreach (Item item in cont.FindItemsByType<Item>())
|
||||
{
|
||||
if (ext.IsValid(item))
|
||||
list.Add(item);
|
||||
}
|
||||
|
||||
ext.Filter(list);
|
||||
|
||||
RunCommand(from, list, command, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
from.SendMessage(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class FacetCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public FacetCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Facet" };
|
||||
SupportRequirement = CommandSupport.Area;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Facet <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all appropriate objects within your facet's map bounds. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
AreaCommandImplementor impl = AreaCommandImplementor.Instance;
|
||||
|
||||
if (impl == null)
|
||||
return;
|
||||
|
||||
Map map = from.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
return;
|
||||
|
||||
impl.OnTarget(from, map, Point3D.Zero, new Point3D(map.Width - 1, map.Height - 1, 0), command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class GlobalCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public GlobalCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Global" };
|
||||
SupportRequirement = CommandSupport.Global;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.Administrator;
|
||||
Usage = "Global <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all appropriate objects in the world. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
Extensions ext = Extensions.Parse(from, ref args);
|
||||
|
||||
if (!CheckObjectTypes(from, command, ext, out bool items, out bool mobiles))
|
||||
return;
|
||||
|
||||
List<object> list = new List<object>();
|
||||
|
||||
if (items)
|
||||
foreach (Item item in World.Items.Values)
|
||||
if (ext.IsValid(item))
|
||||
list.Add(item);
|
||||
|
||||
if (mobiles)
|
||||
foreach (Mobile mob in World.Mobiles.Values)
|
||||
if (ext.IsValid(mob))
|
||||
list.Add(mob);
|
||||
|
||||
ext.Filter(list);
|
||||
|
||||
obj = list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
from.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class IPAddressCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public IPAddressCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "IPAddress" };
|
||||
SupportRequirement = CommandSupport.IPAddress;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.Administrator;
|
||||
Usage = "IPAddress <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on one mobile from each IP address that is logged in. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
Extensions ext = Extensions.Parse(from, ref args);
|
||||
|
||||
if (!CheckObjectTypes(from, command, ext, out bool _, out bool mobiles))
|
||||
return;
|
||||
|
||||
if (!mobiles) // sanity check
|
||||
{
|
||||
command.LogFailure("This command does not support items.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<object> list = new List<object>();
|
||||
List<IPAddress> addresses = new List<IPAddress>();
|
||||
|
||||
List<NetState> states = NetState.Instances;
|
||||
|
||||
for (int i = 0; i < states.Count; ++i)
|
||||
{
|
||||
NetState ns = states[i];
|
||||
Mobile mob = ns.Mobile;
|
||||
|
||||
if (mob != null && !addresses.Contains(ns.Address) && ext.IsValid(mob))
|
||||
{
|
||||
list.Add(mob);
|
||||
addresses.Add(ns.Address);
|
||||
}
|
||||
}
|
||||
|
||||
ext.Filter(list);
|
||||
|
||||
obj = list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
from.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class MultiCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public MultiCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Multi", "m" };
|
||||
SupportRequirement = CommandSupport.Multi;
|
||||
AccessLevel = AccessLevel.Counselor;
|
||||
Usage = "Multi <command>";
|
||||
Description = "Invokes the command on multiple targeted objects.";
|
||||
}
|
||||
|
||||
public override void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
if (command.ValidateArgs(this, new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args)))
|
||||
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
|
||||
(m, targeted) => OnTarget(m, targeted, command, args));
|
||||
}
|
||||
|
||||
public void OnTarget(Mobile from, object targeted, BaseCommand command, string[] args)
|
||||
{
|
||||
if (!BaseCommand.IsAccessible(from, targeted))
|
||||
{
|
||||
from.SendLocalizedMessage(500447); // That is not accessible.
|
||||
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
|
||||
(m, t) => OnTarget(m, t, command, args));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command.ObjectTypes)
|
||||
{
|
||||
case ObjectTypes.Both:
|
||||
{
|
||||
if (!(targeted is Item || targeted is Mobile))
|
||||
{
|
||||
from.SendMessage("This command does not work on that.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Items:
|
||||
{
|
||||
if (!(targeted is Item))
|
||||
{
|
||||
from.SendMessage("This command only works on items.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Mobiles:
|
||||
{
|
||||
if (!(targeted is Mobile))
|
||||
{
|
||||
from.SendMessage("This command only works on mobiles.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RunCommand(from, targeted, command, args);
|
||||
|
||||
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
|
||||
(m, t) => OnTarget(m, t, command, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public sealed class ObjectConditional
|
||||
{
|
||||
private static readonly Type typeofItem = typeof(Item);
|
||||
private static readonly Type typeofMobile = typeof(Mobile);
|
||||
|
||||
public static readonly ObjectConditional Empty = new ObjectConditional(null, null);
|
||||
|
||||
private IConditional[] m_Conditionals;
|
||||
|
||||
private ICondition[][] m_Conditions;
|
||||
|
||||
public ObjectConditional(Type objectType, ICondition[][] conditions)
|
||||
{
|
||||
Type = objectType;
|
||||
m_Conditions = conditions;
|
||||
}
|
||||
|
||||
public Type Type{ get; }
|
||||
|
||||
public bool IsItem => Type == null || Type == typeofItem || Type.IsSubclassOf(typeofItem);
|
||||
|
||||
public bool IsMobile => Type == null || Type == typeofMobile || Type.IsSubclassOf(typeofMobile);
|
||||
|
||||
public bool HasCompiled => m_Conditionals != null;
|
||||
|
||||
public void Compile(ref AssemblyEmitter emitter)
|
||||
{
|
||||
if (emitter == null)
|
||||
emitter = new AssemblyEmitter("__dynamic");
|
||||
|
||||
m_Conditionals = new IConditional[m_Conditions.Length];
|
||||
|
||||
for (int i = 0; i < m_Conditionals.Length; ++i)
|
||||
m_Conditionals[i] = ConditionalCompiler.Compile(emitter, Type, m_Conditions[i], i);
|
||||
}
|
||||
|
||||
public bool CheckCondition(object obj)
|
||||
{
|
||||
if (Type == null)
|
||||
return true; // null type means no condition
|
||||
|
||||
if (!HasCompiled)
|
||||
{
|
||||
AssemblyEmitter emitter = null;
|
||||
|
||||
Compile(ref emitter);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_Conditionals.Length; ++i)
|
||||
if (m_Conditionals[i].Verify(obj))
|
||||
return true;
|
||||
|
||||
return false; // all conditions false
|
||||
}
|
||||
|
||||
public static ObjectConditional Parse(Mobile from, ref string[] args)
|
||||
{
|
||||
string[] conditionArgs = null;
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
if (Insensitive.Equals(args[i], "where"))
|
||||
{
|
||||
string[] origArgs = args;
|
||||
|
||||
args = new string[i];
|
||||
|
||||
for (int j = 0; j < args.Length; ++j)
|
||||
args[j] = origArgs[j];
|
||||
|
||||
conditionArgs = new string[origArgs.Length - i - 1];
|
||||
|
||||
for (int j = 0; j < conditionArgs.Length; ++j)
|
||||
conditionArgs[j] = origArgs[i + j + 1];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return ParseDirect(from, conditionArgs, 0, conditionArgs?.Length ?? 0);
|
||||
}
|
||||
|
||||
public static ObjectConditional ParseDirect(Mobile from, string[] args, int offset, int size)
|
||||
{
|
||||
if (args == null || size == 0)
|
||||
return Empty;
|
||||
|
||||
int index = 0;
|
||||
|
||||
Type objectType = ScriptCompiler.FindTypeByName(args[offset + index], true);
|
||||
|
||||
if (objectType == null)
|
||||
throw new Exception($"No type with that name ({args[offset + index]}) was found.");
|
||||
|
||||
++index;
|
||||
|
||||
List<ICondition[]> conditions = new List<ICondition[]>();
|
||||
List<ICondition> current = new List<ICondition>();
|
||||
|
||||
current.Add(TypeCondition.Default);
|
||||
|
||||
while (index < size)
|
||||
{
|
||||
string cur = args[offset + index];
|
||||
|
||||
bool inverse = false;
|
||||
|
||||
if (Insensitive.Equals(cur, "not") || cur == "!")
|
||||
{
|
||||
inverse = true;
|
||||
++index;
|
||||
|
||||
if (index >= size)
|
||||
throw new Exception("Improperly formatted object conditional.");
|
||||
}
|
||||
else if (Insensitive.Equals(cur, "or") || cur == "||")
|
||||
{
|
||||
if (current.Count > 1)
|
||||
{
|
||||
conditions.Add(current.ToArray());
|
||||
|
||||
current.Clear();
|
||||
current.Add(TypeCondition.Default);
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
string binding = args[offset + index];
|
||||
index++;
|
||||
|
||||
if (index >= size)
|
||||
throw new Exception("Improperly formatted object conditional.");
|
||||
|
||||
string oper = args[offset + index];
|
||||
index++;
|
||||
|
||||
if (index >= size)
|
||||
throw new Exception("Improperly formatted object conditional.");
|
||||
|
||||
string val = args[offset + index];
|
||||
index++;
|
||||
|
||||
Property prop = new Property(binding);
|
||||
|
||||
prop.BindTo(objectType, PropertyAccess.Read);
|
||||
prop.CheckAccess(from);
|
||||
|
||||
ICondition condition = null;
|
||||
|
||||
switch (oper)
|
||||
{
|
||||
#region Equality
|
||||
|
||||
case "=":
|
||||
case "==":
|
||||
case "is":
|
||||
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.Equal, val);
|
||||
break;
|
||||
|
||||
case "!=":
|
||||
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.NotEqual, val);
|
||||
break;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Relational
|
||||
|
||||
case ">":
|
||||
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.Greater, val);
|
||||
break;
|
||||
|
||||
case "<":
|
||||
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.Lesser, val);
|
||||
break;
|
||||
|
||||
case ">=":
|
||||
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.GreaterEqual, val);
|
||||
break;
|
||||
|
||||
case "<=":
|
||||
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.LesserEqual, val);
|
||||
break;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Strings
|
||||
|
||||
case "==~":
|
||||
case "~==":
|
||||
case "=~":
|
||||
case "~=":
|
||||
case "is~":
|
||||
case "~is":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.Equal, val, true);
|
||||
break;
|
||||
|
||||
case "!=~":
|
||||
case "~!=":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.NotEqual, val, true);
|
||||
break;
|
||||
|
||||
case "starts":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.StartsWith, val, false);
|
||||
break;
|
||||
|
||||
case "starts~":
|
||||
case "~starts":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.StartsWith, val, true);
|
||||
break;
|
||||
|
||||
case "ends":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.EndsWith, val, false);
|
||||
break;
|
||||
|
||||
case "ends~":
|
||||
case "~ends":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.EndsWith, val, true);
|
||||
break;
|
||||
|
||||
case "contains":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.Contains, val, false);
|
||||
break;
|
||||
|
||||
case "contains~":
|
||||
case "~contains":
|
||||
condition = new StringCondition(prop, inverse, StringOperator.Contains, val, true);
|
||||
break;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
if (condition == null)
|
||||
throw new InvalidOperationException($"Unrecognized operator (\"{oper}\").");
|
||||
|
||||
current.Add(condition);
|
||||
}
|
||||
|
||||
conditions.Add(current.ToArray());
|
||||
|
||||
return new ObjectConditional(objectType, conditions.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class OnlineCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public OnlineCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Online" };
|
||||
SupportRequirement = CommandSupport.Online;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Online <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all mobiles that are currently logged in. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
Extensions ext = Extensions.Parse(from, ref args);
|
||||
|
||||
if (!CheckObjectTypes(from, command, ext, out bool _, out bool mobiles))
|
||||
return;
|
||||
|
||||
if (!mobiles) // sanity check
|
||||
{
|
||||
command.LogFailure("This command does not support items.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<object> list = new List<object>();
|
||||
|
||||
List<NetState> states = NetState.Instances;
|
||||
|
||||
for (int i = 0; i < states.Count; ++i)
|
||||
{
|
||||
NetState ns = states[i];
|
||||
Mobile mob = ns.Mobile;
|
||||
|
||||
if (mob == null)
|
||||
continue;
|
||||
|
||||
if (!BaseCommand.IsAccessible(from, mob))
|
||||
continue;
|
||||
|
||||
if (ext.IsValid(mob))
|
||||
list.Add(mob);
|
||||
}
|
||||
|
||||
ext.Filter(list);
|
||||
|
||||
obj = list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
from.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class RangeCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public RangeCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Range" };
|
||||
SupportRequirement = CommandSupport.Area;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Range <range> <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all appropriate objects within a specified range of you. Optional condition arguments can further restrict the set of objects.";
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public static RangeCommandImplementor Instance{ get; private set; }
|
||||
|
||||
public override void Execute(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length >= 2)
|
||||
{
|
||||
int range = e.GetInt32(0);
|
||||
|
||||
if (range < 0)
|
||||
{
|
||||
e.Mobile.SendMessage("The range must not be negative.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Commands.TryGetValue(e.GetString(1), out BaseCommand command);
|
||||
|
||||
if (command == null)
|
||||
{
|
||||
e.Mobile.SendMessage(
|
||||
"That is either an invalid command name or one that does not support this modifier.");
|
||||
}
|
||||
else if (e.Mobile.AccessLevel < command.AccessLevel)
|
||||
{
|
||||
e.Mobile.SendMessage("You do not have access to that command.");
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] oldArgs = e.Arguments;
|
||||
string[] args = new string[oldArgs.Length - 2];
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
args[i] = oldArgs[i + 2];
|
||||
|
||||
Process(range, e.Mobile, command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("You must supply a range and a command name.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Process(int range, Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
AreaCommandImplementor impl = AreaCommandImplementor.Instance;
|
||||
|
||||
if (impl == null)
|
||||
return;
|
||||
|
||||
Map map = from.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
return;
|
||||
|
||||
Point3D start = new Point3D(from.X - range, from.Y - range, from.Z);
|
||||
Point3D end = new Point3D(from.X + range, from.Y + range, from.Z);
|
||||
|
||||
impl.OnTarget(from, map, start, end, command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class RegionCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public RegionCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Region" };
|
||||
SupportRequirement = CommandSupport.Region;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Region <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all appropriate mobiles in your current region. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
Extensions ext = Extensions.Parse(from, ref args);
|
||||
|
||||
if (!CheckObjectTypes(from, command, ext, out bool _, out bool mobiles))
|
||||
return;
|
||||
|
||||
Region reg = from.Region;
|
||||
|
||||
List<object> list = new List<object>();
|
||||
|
||||
if (mobiles)
|
||||
{
|
||||
foreach (Mobile mob in reg.GetMobiles())
|
||||
{
|
||||
if (!BaseCommand.IsAccessible(from, mob))
|
||||
continue;
|
||||
|
||||
if (ext.IsValid(mob))
|
||||
list.Add(mob);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
command.LogFailure("This command does not support items.");
|
||||
return;
|
||||
}
|
||||
|
||||
ext.Filter(list);
|
||||
|
||||
obj = list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
from.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class ScreenCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public ScreenCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Screen" };
|
||||
SupportRequirement = CommandSupport.Area;
|
||||
SupportsConditionals = true;
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Usage = "Screen <command> [condition]";
|
||||
Description =
|
||||
"Invokes the command on all appropriate objects in your screen. Optional condition arguments can further restrict the set of objects.";
|
||||
}
|
||||
|
||||
public override void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
RangeCommandImplementor impl = RangeCommandImplementor.Instance;
|
||||
|
||||
impl?.Process(18, from, command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class SelfCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public SelfCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Self" };
|
||||
SupportRequirement = CommandSupport.Self;
|
||||
AccessLevel = AccessLevel.Counselor;
|
||||
Usage = "Self <command>";
|
||||
Description = "Invokes the command on the commanding player.";
|
||||
}
|
||||
|
||||
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
|
||||
{
|
||||
if (command.ObjectTypes == ObjectTypes.Items)
|
||||
return; // sanity check
|
||||
|
||||
obj = from;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class SerialCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public SerialCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Serial" };
|
||||
SupportRequirement = CommandSupport.Single;
|
||||
AccessLevel = AccessLevel.Counselor;
|
||||
Usage = "Serial <serial> <command>";
|
||||
Description = "Invokes the command on a single object by serial.";
|
||||
}
|
||||
|
||||
public override void Execute(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length >= 2)
|
||||
{
|
||||
Serial serial = e.GetUInt32(0);
|
||||
|
||||
object obj = null;
|
||||
|
||||
if (serial.IsItem)
|
||||
obj = World.FindItem(serial);
|
||||
else if (serial.IsMobile)
|
||||
obj = World.FindMobile(serial);
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
e.Mobile.SendMessage("That is not a valid serial.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Commands.TryGetValue(e.GetString(1), out BaseCommand command);
|
||||
|
||||
if (command == null)
|
||||
{
|
||||
e.Mobile.SendMessage(
|
||||
"That is either an invalid command name or one that does not support this modifier.");
|
||||
}
|
||||
else if (e.Mobile.AccessLevel < command.AccessLevel)
|
||||
{
|
||||
e.Mobile.SendMessage("You do not have access to that command.");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (command.ObjectTypes)
|
||||
{
|
||||
// case ObjectTypes.Both:
|
||||
// {
|
||||
// if (!(obj is Item) && !(obj is Mobile))
|
||||
// {
|
||||
// e.Mobile.SendMessage("This command does not work on that.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
case ObjectTypes.Items:
|
||||
{
|
||||
if (!(obj is Item))
|
||||
{
|
||||
e.Mobile.SendMessage("This command only works on items.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Mobiles:
|
||||
{
|
||||
if (!(obj is Mobile))
|
||||
{
|
||||
e.Mobile.SendMessage("This command only works on mobiles.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string[] oldArgs = e.Arguments;
|
||||
string[] args = new string[oldArgs.Length - 2];
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
args[i] = oldArgs[i + 2];
|
||||
|
||||
RunCommand(e.Mobile, obj, command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("You must supply an object serial and a command name.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands.Generic
|
||||
{
|
||||
public class SingleCommandImplementor : BaseCommandImplementor
|
||||
{
|
||||
public SingleCommandImplementor()
|
||||
{
|
||||
Accessors = new[] { "Single" };
|
||||
SupportRequirement = CommandSupport.Single;
|
||||
AccessLevel = AccessLevel.Counselor;
|
||||
Usage = "Single <command>";
|
||||
Description =
|
||||
"Invokes the command on a single targeted object. This is the same as just invoking the command directly.";
|
||||
}
|
||||
|
||||
public override void Register(BaseCommand command)
|
||||
{
|
||||
base.Register(command);
|
||||
|
||||
for (int i = 0; i < command.Commands.Length; ++i)
|
||||
CommandSystem.Register(command.Commands[i], command.AccessLevel, Redirect);
|
||||
}
|
||||
|
||||
public void Redirect(CommandEventArgs e)
|
||||
{
|
||||
Commands.TryGetValue(e.Command, out BaseCommand command);
|
||||
|
||||
if (command == null)
|
||||
e.Mobile.SendMessage("That is either an invalid command name or one that does not support this modifier.");
|
||||
else if (e.Mobile.AccessLevel < command.AccessLevel)
|
||||
e.Mobile.SendMessage("You do not have access to that command.");
|
||||
else if (command.ValidateArgs(this, e))
|
||||
Process(e.Mobile, command, e.Arguments);
|
||||
}
|
||||
|
||||
public override void Process(Mobile from, BaseCommand command, string[] args)
|
||||
{
|
||||
if (command.ValidateArgs(this, new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args)))
|
||||
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
|
||||
(m, targeted) => OnTarget(m, targeted, command, args));
|
||||
}
|
||||
|
||||
public void OnTarget(Mobile from, object targeted, BaseCommand command, string[] args)
|
||||
{
|
||||
if (!BaseCommand.IsAccessible(from, targeted))
|
||||
{
|
||||
from.SendLocalizedMessage(500447); // That is not accessible.
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command.ObjectTypes)
|
||||
{
|
||||
case ObjectTypes.Both:
|
||||
{
|
||||
if (!(targeted is Item) && !(targeted is Mobile))
|
||||
{
|
||||
from.SendMessage("This command does not work on that.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Items:
|
||||
{
|
||||
if (!(targeted is Item))
|
||||
{
|
||||
from.SendMessage("This command only works on items.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectTypes.Mobiles:
|
||||
{
|
||||
if (!(targeted is Mobile))
|
||||
{
|
||||
from.SendMessage("This command only works on mobiles.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RunCommand(from, targeted, command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
962
Projects/Scripts/Commands/Handlers.cs
Normal file
962
Projects/Scripts/Commands/Handlers.cs
Normal file
|
|
@ -0,0 +1,962 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Server.Commands.Generic;
|
||||
using Server.Engines.Help;
|
||||
using Server.Gumps;
|
||||
using Server.Items;
|
||||
using Server.Menus.ItemLists;
|
||||
using Server.Menus.Questions;
|
||||
using Server.Misc;
|
||||
using Server.Mobiles;
|
||||
using Server.Multis;
|
||||
using Server.Network;
|
||||
using Server.Spells;
|
||||
using Server.Targeting;
|
||||
using Server.Targets;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class CommandHandlers
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Prefix = "[";
|
||||
|
||||
Register("Go", AccessLevel.Counselor, Go_OnCommand);
|
||||
|
||||
Register("DropHolding", AccessLevel.Counselor, DropHolding_OnCommand);
|
||||
|
||||
Register("GetFollowers", AccessLevel.GameMaster, GetFollowers_OnCommand);
|
||||
|
||||
Register("ClearFacet", AccessLevel.Administrator, ClearFacet_OnCommand);
|
||||
|
||||
Register("Where", AccessLevel.Counselor, Where_OnCommand);
|
||||
|
||||
Register("AutoPageNotify", AccessLevel.Counselor, APN_OnCommand);
|
||||
Register("APN", AccessLevel.Counselor, APN_OnCommand);
|
||||
|
||||
Register("Animate", AccessLevel.GameMaster, Animate_OnCommand);
|
||||
|
||||
Register("Cast", AccessLevel.Counselor, Cast_OnCommand);
|
||||
|
||||
Register("Stuck", AccessLevel.Counselor, Stuck_OnCommand);
|
||||
|
||||
Register("Help", AccessLevel.Player, Help_OnCommand);
|
||||
|
||||
Register("Save", AccessLevel.Administrator, Save_OnCommand);
|
||||
Register("BackgroundSave", AccessLevel.Administrator, BackgroundSave_OnCommand);
|
||||
Register("BGSave", AccessLevel.Administrator, BackgroundSave_OnCommand);
|
||||
Register("SaveBG", AccessLevel.Administrator, BackgroundSave_OnCommand);
|
||||
|
||||
Register("Move", AccessLevel.GameMaster, Move_OnCommand);
|
||||
Register("Client", AccessLevel.Counselor, Client_OnCommand);
|
||||
|
||||
Register("SMsg", AccessLevel.Counselor, StaffMessage_OnCommand);
|
||||
Register("SM", AccessLevel.Counselor, StaffMessage_OnCommand);
|
||||
Register("S", AccessLevel.Counselor, StaffMessage_OnCommand);
|
||||
|
||||
Register("BCast", AccessLevel.GameMaster, BroadcastMessage_OnCommand);
|
||||
Register("BC", AccessLevel.GameMaster, BroadcastMessage_OnCommand);
|
||||
Register("B", AccessLevel.GameMaster, BroadcastMessage_OnCommand);
|
||||
|
||||
Register("Bank", AccessLevel.GameMaster, Bank_OnCommand);
|
||||
|
||||
Register("Echo", AccessLevel.Counselor, Echo_OnCommand);
|
||||
|
||||
Register("Sound", AccessLevel.GameMaster, Sound_OnCommand);
|
||||
|
||||
Register("ViewEquip", AccessLevel.GameMaster, ViewEquip_OnCommand);
|
||||
|
||||
Register("Light", AccessLevel.Counselor, Light_OnCommand);
|
||||
Register("Stats", AccessLevel.Counselor, Stats_OnCommand);
|
||||
|
||||
Register("SpeedBoost", AccessLevel.Counselor, SpeedBoost_OnCommand);
|
||||
}
|
||||
|
||||
public static void Register(string command, AccessLevel access, CommandEventHandler handler)
|
||||
{
|
||||
CommandSystem.Register(command, access, handler);
|
||||
}
|
||||
|
||||
[Usage("SpeedBoost [true|false]")]
|
||||
[Description("Enables a speed boost for the invoker. Disable with parameters.")]
|
||||
private static void SpeedBoost_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
|
||||
if (e.Length <= 1)
|
||||
{
|
||||
if (e.Length == 1 && !e.GetBoolean(0))
|
||||
{
|
||||
from.Send(SpeedControl.Disable);
|
||||
from.SendMessage("Speed boost has been disabled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Send(SpeedControl.MountSpeed);
|
||||
from.SendMessage("Speed boost has been enabled.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("Format: SpeedBoost [true|false]");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("Where")]
|
||||
[Description("Tells the commanding player his coordinates, region, and facet.")]
|
||||
public static void Where_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
Map map = from.Map;
|
||||
|
||||
from.SendMessage("You are at {0} {1} {2} in {3}.", from.X, from.Y, from.Z, map);
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
Region reg = from.Region;
|
||||
|
||||
if (!reg.IsDefault)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(reg);
|
||||
reg = reg.Parent;
|
||||
|
||||
while (reg != null)
|
||||
{
|
||||
builder.Append(" <- " + reg);
|
||||
reg = reg.Parent;
|
||||
}
|
||||
|
||||
from.SendMessage("Your region is {0}.", builder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("DropHolding")]
|
||||
[Description(
|
||||
"Drops the item, if any, that a targeted player is holding. The item is placed into their backpack, or if that's full, at their feet.")]
|
||||
public static void DropHolding_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.BeginTarget(-1, false, TargetFlags.None, DropHolding_OnTarget);
|
||||
e.Mobile.SendMessage("Target the player to drop what they are holding.");
|
||||
}
|
||||
|
||||
public static void DropHolding_OnTarget(Mobile from, object obj)
|
||||
{
|
||||
if (obj is Mobile targ && targ.Player)
|
||||
{
|
||||
Item held = targ.Holding;
|
||||
|
||||
if (held == null)
|
||||
{
|
||||
from.SendMessage("They are not holding anything.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (from.AccessLevel == AccessLevel.Counselor)
|
||||
{
|
||||
PageEntry pe = PageQueue.GetEntry(targ);
|
||||
|
||||
if (pe?.Handler == from)
|
||||
from.SendMessage("You may only use this command if you are handling their help page.");
|
||||
else
|
||||
from.SendMessage("You may only use this command on someone who has paged you.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (targ.AddToBackpack(held))
|
||||
from.SendMessage("The item they were holding has been placed into their backpack.");
|
||||
else
|
||||
from.SendMessage("The item they were holding has been placed at their feet.");
|
||||
|
||||
held.ClearBounce();
|
||||
|
||||
targ.Holding = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.BeginTarget(-1, false, TargetFlags.None, DropHolding_OnTarget);
|
||||
from.SendMessage("That is not a player. Try again.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteList_Callback(Mobile from, bool okay, List<IEntity> list)
|
||||
{
|
||||
if (okay)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} deleting {2} object{3}", from.AccessLevel,
|
||||
CommandLogging.Format(from), list.Count, list.Count == 1 ? "" : "s");
|
||||
|
||||
NetState.Pause();
|
||||
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
list[i].Delete();
|
||||
|
||||
NetState.Resume();
|
||||
|
||||
from.SendMessage("You have deleted {0} object{1}.", list.Count, list.Count == 1 ? "" : "s");
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("You have chosen not to delete those objects.");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("ClearFacet")]
|
||||
[Description("Deletes all items and mobiles in your facet. Players and their inventory will not be deleted.")]
|
||||
public static void ClearFacet_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
Map map = from.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
from.SendMessage("You may not run that command here.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<IEntity> list = new List<IEntity>();
|
||||
|
||||
foreach (Item item in World.Items.Values)
|
||||
if (item.Map == map && item.Parent == null)
|
||||
list.Add(item);
|
||||
|
||||
foreach (Mobile m in World.Mobiles.Values)
|
||||
if (m.Map == map && !m.Player)
|
||||
list.Add(m);
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} starting facet clear of {2} ({3} object{4})",
|
||||
from.AccessLevel, CommandLogging.Format(from), map, list.Count, list.Count == 1 ? "" : "s");
|
||||
|
||||
from.SendGump(
|
||||
new WarningGump(1060635, 30720,
|
||||
$"You are about to delete {list.Count} object{(list.Count == 1 ? "" : "s")} from this facet. Do you really wish to continue?",
|
||||
0xFFC000, 360, 260, okay => DeleteList_Callback(from, okay, list)));
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("There were no objects found to delete.");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("GetFollowers")]
|
||||
[Description("Teleports all pets of a targeted player to your location.")]
|
||||
public static void GetFollowers_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.BeginTarget(-1, false, TargetFlags.None, GetFollowers_OnTarget);
|
||||
e.Mobile.SendMessage("Target a player to get their pets.");
|
||||
}
|
||||
|
||||
public static void GetFollowers_OnTarget(Mobile from, object obj)
|
||||
{
|
||||
if (obj is PlayerMobile pm)
|
||||
{
|
||||
List<Mobile> pets = pm.AllFollowers;
|
||||
|
||||
if (pets.Count > 0)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} getting all followers of {2}", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(pm));
|
||||
|
||||
from.SendMessage("That player has {0} pet{1}.", pets.Count, pets.Count != 1 ? "s" : "");
|
||||
|
||||
for (int i = 0; i < pets.Count; ++i)
|
||||
{
|
||||
Mobile pet = pets[i];
|
||||
|
||||
if (pet is IMount mount)
|
||||
mount.Rider = null; // make sure it's dismounted
|
||||
|
||||
pet.MoveToWorld(from.Location, from.Map);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("There were no pets found for that player.");
|
||||
}
|
||||
}
|
||||
else if (obj is Mobile master && master.Player)
|
||||
{
|
||||
List<BaseCreature> pets = new List<BaseCreature>();
|
||||
|
||||
foreach (Mobile m in World.Mobiles.Values)
|
||||
if (m is BaseCreature bc)
|
||||
if (bc.Controlled && bc.ControlMaster == master || bc.Summoned && bc.SummonMaster == master)
|
||||
pets.Add(bc);
|
||||
|
||||
if (pets.Count > 0)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} getting all followers of {2}", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(master));
|
||||
|
||||
from.SendMessage("That player has {0} pet{1}.", pets.Count, pets.Count != 1 ? "s" : "");
|
||||
|
||||
for (int i = 0; i < pets.Count; ++i)
|
||||
{
|
||||
Mobile pet = pets[i];
|
||||
|
||||
if (pet is IMount mount)
|
||||
mount.Rider = null; // make sure it's dismounted
|
||||
|
||||
pet.MoveToWorld(from.Location, from.Map);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("There were no pets found for that player.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.BeginTarget(-1, false, TargetFlags.None, GetFollowers_OnTarget);
|
||||
from.SendMessage("That is not a player. Try again.");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("ViewEquip")]
|
||||
[Description("Lists equipment of a targeted mobile. From the list you can move, delete, or open props.")]
|
||||
public static void ViewEquip_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.Target = new ViewEqTarget();
|
||||
}
|
||||
|
||||
[Usage("Sound <index> [toAll=true]")]
|
||||
[Description(
|
||||
"Plays a sound to players within 12 tiles of you. The (toAll) argument specifies to everyone, or just those who can see you.")]
|
||||
public static void Sound_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 1)
|
||||
PlaySound(e.Mobile, e.GetInt32(0), true);
|
||||
else if (e.Length == 2)
|
||||
PlaySound(e.Mobile, e.GetInt32(0), e.GetBoolean(1));
|
||||
else
|
||||
e.Mobile.SendMessage("Format: Sound <index> [toAll]");
|
||||
}
|
||||
|
||||
private static void PlaySound(Mobile m, int index, bool toAll)
|
||||
{
|
||||
Map map = m.Map;
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
CommandLogging.WriteLine(m, "{0} {1} playing sound {2} (toAll={3})", m.AccessLevel, CommandLogging.Format(m),
|
||||
index, toAll);
|
||||
|
||||
Packet p = new PlaySound(index, m.Location);
|
||||
|
||||
p.Acquire();
|
||||
|
||||
foreach (NetState state in m.GetClientsInRange(12))
|
||||
if (toAll || state.Mobile.CanSee(m))
|
||||
state.Send(p);
|
||||
|
||||
p.Release();
|
||||
}
|
||||
|
||||
[Usage("Echo <text>")]
|
||||
[Description("Relays (text) as a system message.")]
|
||||
public static void Echo_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
string toEcho = e.ArgString.Trim();
|
||||
|
||||
if (toEcho.Length > 0)
|
||||
e.Mobile.SendMessage(toEcho);
|
||||
else
|
||||
e.Mobile.SendMessage("Format: Echo \"<text>\"");
|
||||
}
|
||||
|
||||
[Usage("Bank")]
|
||||
[Description("Opens the bank box of a given target.")]
|
||||
public static void Bank_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.Target = new BankTarget();
|
||||
}
|
||||
|
||||
[Usage("Client")]
|
||||
[Description("Opens the client gump menu for a given player.")]
|
||||
private static void Client_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.Target = new ClientTarget();
|
||||
}
|
||||
|
||||
[Usage("Move")]
|
||||
[Description("Repositions a targeted item or mobile.")]
|
||||
private static void Move_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.Target = new PickMoveTarget();
|
||||
}
|
||||
|
||||
[Usage("Save")]
|
||||
[Description("Saves the world.")]
|
||||
private static void Save_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
AutoSave.Save();
|
||||
}
|
||||
|
||||
[Usage("BackgroundSave")]
|
||||
[Aliases("BGSave", "SaveBG")]
|
||||
[Description("Saves the world, writing to the disk in the background")]
|
||||
private static void BackgroundSave_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
AutoSave.Save(true);
|
||||
}
|
||||
|
||||
private static bool FixMap(ref Map map, ref Point3D loc, Item item)
|
||||
{
|
||||
return map != null && map != Map.Internal || item.RootParent is Mobile m && FixMap(ref map, ref loc, m);
|
||||
}
|
||||
|
||||
private static bool FixMap(ref Map map, ref Point3D loc, Mobile m)
|
||||
{
|
||||
bool validMap = map != null && map != Map.Internal;
|
||||
|
||||
if (!validMap)
|
||||
{
|
||||
map = m.LogoutMap;
|
||||
loc = m.LogoutLocation;
|
||||
}
|
||||
|
||||
return validMap;
|
||||
}
|
||||
|
||||
[Usage("Go [name | serial | (x y [z]) | (deg min (N | S) deg min (E | W))]")]
|
||||
[Description(
|
||||
"With no arguments, this command brings up the go menu. With one argument, (name), you are moved to that regions \"go location.\" Or, if a numerical value is specified for one argument, (serial), you are moved to that object. Two or three arguments, (x y [z]), will move your character to that location. When six arguments are specified, (deg min (N | S) deg min (E | W)), your character will go to an approximate of those sextant coordinates.")]
|
||||
private static void Go_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
|
||||
if (e.Length == 0)
|
||||
{
|
||||
GoGump.DisplayTo(from);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Length == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
uint ser = e.GetUInt32(0);
|
||||
|
||||
IEntity ent = World.FindEntity(ser);
|
||||
|
||||
if (ent is Item item)
|
||||
{
|
||||
Map map = item.Map;
|
||||
Point3D loc = item.GetWorldLocation();
|
||||
|
||||
Mobile owner = item.RootParent as Mobile;
|
||||
|
||||
if (owner?.Map != null && owner.Map != Map.Internal &&
|
||||
!BaseCommand.IsAccessible(from, owner) /* !from.CanSee( owner )*/)
|
||||
{
|
||||
from.SendMessage("You can not go to what you can not see.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner != null && (owner.Map == null || owner.Map == Map.Internal) && owner.Hidden &&
|
||||
owner.AccessLevel >= from.AccessLevel)
|
||||
{
|
||||
from.SendMessage("You can not go to what you can not see.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FixMap(ref map, ref loc, item))
|
||||
{
|
||||
from.SendMessage("That is an internal item and you cannot go to it.");
|
||||
return;
|
||||
}
|
||||
|
||||
from.MoveToWorld(loc, map);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent is Mobile m)
|
||||
{
|
||||
Map map = m.Map;
|
||||
Point3D loc = m.Location;
|
||||
|
||||
Mobile owner = m;
|
||||
|
||||
if (owner.Map != null && owner.Map != Map.Internal &&
|
||||
!BaseCommand.IsAccessible(from, owner) /* !from.CanSee( owner )*/)
|
||||
{
|
||||
from.SendMessage("You can not go to what you can not see.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((owner.Map == null || owner.Map == Map.Internal) && owner.Hidden &&
|
||||
owner.AccessLevel >= from.AccessLevel)
|
||||
{
|
||||
from.SendMessage("You can not go to what you can not see.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FixMap(ref map, ref loc, m))
|
||||
{
|
||||
from.SendMessage("That is an internal mobile and you cannot go to it.");
|
||||
return;
|
||||
}
|
||||
|
||||
from.MoveToWorld(loc, map);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = e.GetString(0);
|
||||
Map map;
|
||||
|
||||
for (int i = 0; i < Map.AllMaps.Count; ++i)
|
||||
{
|
||||
map = Map.AllMaps[i];
|
||||
|
||||
if (map.MapIndex == 0x7F || map.MapIndex == 0xFF)
|
||||
continue;
|
||||
|
||||
if (Insensitive.Equals(name, map.Name))
|
||||
{
|
||||
from.Map = map;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, Region> list = from.Map.Regions;
|
||||
|
||||
foreach (KeyValuePair<string, Region> kvp in list)
|
||||
{
|
||||
Region r = kvp.Value;
|
||||
|
||||
if (Insensitive.Equals(r.Name, name))
|
||||
{
|
||||
from.Location = new Point3D(r.GoLocation);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Map.AllMaps.Count; ++i)
|
||||
{
|
||||
map = Map.AllMaps[i];
|
||||
|
||||
if (map.MapIndex == 0x7F || map.MapIndex == 0xFF || from.Map == map)
|
||||
continue;
|
||||
|
||||
foreach (Region r in map.Regions.Values)
|
||||
if (Insensitive.Equals(r.Name, name))
|
||||
{
|
||||
from.MoveToWorld(r.GoLocation, map);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ser != 0)
|
||||
from.SendMessage("No object with that serial was found.");
|
||||
else
|
||||
from.SendMessage("No region with that name was found.");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
from.SendMessage("Region name not found");
|
||||
}
|
||||
else if (e.Length == 2 || e.Length == 3)
|
||||
{
|
||||
Map map = from.Map;
|
||||
|
||||
if (map != null)
|
||||
try
|
||||
{
|
||||
/*
|
||||
* This to avoid being teleported to (0,0) if trying to teleport
|
||||
* to a region with spaces in its name.
|
||||
*/
|
||||
int x = int.Parse(e.GetString(0));
|
||||
int y = int.Parse(e.GetString(1));
|
||||
int z = e.Length == 3 ? int.Parse(e.GetString(2)) : map.GetAverageZ(x, y);
|
||||
|
||||
from.Location = new Point3D(x, y, z);
|
||||
}
|
||||
catch
|
||||
{
|
||||
from.SendMessage("Region name not found.");
|
||||
}
|
||||
}
|
||||
else if (e.Length == 6)
|
||||
{
|
||||
Map map = from.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
Point3D p = Sextant.ReverseLookup(map, e.GetInt32(3), e.GetInt32(0), e.GetInt32(4), e.GetInt32(1),
|
||||
Insensitive.Equals(e.GetString(5), "E"), Insensitive.Equals(e.GetString(2), "S"));
|
||||
|
||||
if (p != Point3D.Zero)
|
||||
from.Location = p;
|
||||
else
|
||||
from.SendMessage("Sextant reverse lookup failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("Format: Go [name | serial | (x y [z]) | (deg min (N | S) deg min (E | W)]");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("Help")]
|
||||
[Description("Lists all available commands.")]
|
||||
public static void Help_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile m = e.Mobile;
|
||||
|
||||
List<CommandEntry> list = new List<CommandEntry>();
|
||||
|
||||
foreach (CommandEntry entry in CommandSystem.Entries.Values)
|
||||
if (m.AccessLevel >= entry.AccessLevel)
|
||||
list.Add(entry);
|
||||
|
||||
list.Sort();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (list.Count > 0)
|
||||
sb.Append(list[0].Command);
|
||||
|
||||
for (int i = 1; i < list.Count; ++i)
|
||||
{
|
||||
string v = list[i].Command;
|
||||
|
||||
if (sb.Length + 1 + v.Length >= 256)
|
||||
{
|
||||
m.SendAsciiMessage(0x482, sb.ToString());
|
||||
sb = new StringBuilder();
|
||||
sb.Append(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
m.SendAsciiMessage(0x482, sb.ToString());
|
||||
}
|
||||
|
||||
[Usage("SMsg <text>")]
|
||||
[Aliases("S", "SM")]
|
||||
[Description("Broadcasts a message to all online staff.")]
|
||||
public static void StaffMessage_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
BroadcastMessage(AccessLevel.Counselor, e.Mobile.SpeechHue, $"[{e.Mobile.Name}] {e.ArgString}");
|
||||
}
|
||||
|
||||
[Usage("BCast <text>")]
|
||||
[Aliases("B", "BC")]
|
||||
[Description("Broadcasts a message to everyone online.")]
|
||||
public static void BroadcastMessage_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
BroadcastMessage(AccessLevel.Player, 0x482, $"Staff message from {e.Mobile.Name}:");
|
||||
BroadcastMessage(AccessLevel.Player, 0x482, e.ArgString);
|
||||
}
|
||||
|
||||
public static void BroadcastMessage(AccessLevel ac, int hue, string message)
|
||||
{
|
||||
foreach (NetState state in NetState.Instances)
|
||||
{
|
||||
Mobile m = state.Mobile;
|
||||
|
||||
if (m?.AccessLevel >= ac)
|
||||
m.SendMessage(hue, message);
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("AutoPageNotify")]
|
||||
[Aliases("APN")]
|
||||
[Description("Toggles your auto-page-notify status.")]
|
||||
public static void APN_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile m = e.Mobile;
|
||||
|
||||
m.AutoPageNotify = !m.AutoPageNotify;
|
||||
|
||||
m.SendMessage("Your auto-page-notify has been turned {0}.", m.AutoPageNotify ? "on" : "off");
|
||||
}
|
||||
|
||||
[Usage("Animate <action> <frameCount> <repeatCount> <forward> <repeat> <delay>")]
|
||||
[Description("Makes your character do a specified animation.")]
|
||||
public static void Animate_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 6)
|
||||
e.Mobile.Animate(e.GetInt32(0), e.GetInt32(1), e.GetInt32(2), e.GetBoolean(3), e.GetBoolean(4),
|
||||
e.GetInt32(5));
|
||||
else
|
||||
e.Mobile.SendMessage("Format: Animate <action> <frameCount> <repeatCount> <forward> <repeat> <delay>");
|
||||
}
|
||||
|
||||
[Usage("Cast <name>")]
|
||||
[Description("Casts a spell by name.")]
|
||||
public static void Cast_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 1)
|
||||
{
|
||||
if (!DesignContext.Check(e.Mobile))
|
||||
return; // They are customizing
|
||||
|
||||
Spell spell = SpellRegistry.NewSpell(e.GetString(0), e.Mobile, null);
|
||||
|
||||
if (spell != null)
|
||||
spell.Cast();
|
||||
else
|
||||
e.Mobile.SendMessage("That spell was not found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("Format: Cast <name>");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("Stuck")]
|
||||
[Description("Opens a menu of towns, used for teleporting stuck mobiles.")]
|
||||
public static void Stuck_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.Target = new StuckMenuTarget();
|
||||
}
|
||||
|
||||
[Usage("Light <level>")]
|
||||
[Description("Set your local lightlevel.")]
|
||||
public static void Light_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.LightLevel = e.GetInt32(0);
|
||||
}
|
||||
|
||||
[Usage("Stats")]
|
||||
[Description("View some stats about the server.")]
|
||||
public static void Stats_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.SendMessage("Open Connections: {0}", NetState.Instances.Count);
|
||||
e.Mobile.SendMessage("Mobiles: {0}", World.Mobiles.Count);
|
||||
e.Mobile.SendMessage("Items: {0}", World.Items.Count);
|
||||
}
|
||||
|
||||
private class ViewEqTarget : Target
|
||||
{
|
||||
public ViewEqTarget() : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (!BaseCommand.IsAccessible(from, targeted))
|
||||
{
|
||||
from.SendLocalizedMessage(500447); // That is not accessible.
|
||||
return;
|
||||
}
|
||||
|
||||
if (targeted is Mobile mobile)
|
||||
from.SendMenu(new EquipMenu(from, mobile, GetEquip(mobile)));
|
||||
}
|
||||
|
||||
private static ItemListEntry[] GetEquip(Mobile m)
|
||||
{
|
||||
ItemListEntry[] entries = new ItemListEntry[m.Items.Count];
|
||||
|
||||
for (int i = 0; i < m.Items.Count; ++i)
|
||||
{
|
||||
Item item = m.Items[i];
|
||||
|
||||
entries[i] = new ItemListEntry($"{item.Layer}: {item.GetType().Name}", item.ItemID, item.Hue);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private class EquipMenu : ItemListMenu
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public EquipMenu(Mobile from, Mobile m, ItemListEntry[] entries) : base("Equipment", entries)
|
||||
{
|
||||
m_Mobile = m;
|
||||
|
||||
CommandLogging.WriteLine(from, "{0} {1} viewing equipment of {2}", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(m));
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState state, int index)
|
||||
{
|
||||
if (index >= 0 && index < m_Mobile.Items.Count)
|
||||
{
|
||||
Item item = m_Mobile.Items[index];
|
||||
|
||||
state.Mobile.SendMenu(new EquipDetailsMenu(m_Mobile, item));
|
||||
}
|
||||
}
|
||||
|
||||
private class EquipDetailsMenu : QuestionMenu
|
||||
{
|
||||
private Item m_Item;
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public EquipDetailsMenu(Mobile m, Item item) : base($"{item.Layer}: {item.GetType().Name}",
|
||||
new[] { "Move", "Delete", "Props" })
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Item = item;
|
||||
}
|
||||
|
||||
public override void OnCancel(NetState state)
|
||||
{
|
||||
state.Mobile.SendMenu(new EquipMenu(state.Mobile, m_Mobile, GetEquip(m_Mobile)));
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState state, int index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
CommandLogging.WriteLine(state.Mobile, "{0} {1} moving equipment item {2} of {3}",
|
||||
state.Mobile.AccessLevel, CommandLogging.Format(state.Mobile), CommandLogging.Format(m_Item),
|
||||
CommandLogging.Format(m_Mobile));
|
||||
state.Mobile.Target = new MoveTarget(m_Item);
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
CommandLogging.WriteLine(state.Mobile, "{0} {1} deleting equipment item {2} of {3}",
|
||||
state.Mobile.AccessLevel, CommandLogging.Format(state.Mobile), CommandLogging.Format(m_Item),
|
||||
CommandLogging.Format(m_Mobile));
|
||||
m_Item.Delete();
|
||||
}
|
||||
else if (index == 2)
|
||||
{
|
||||
CommandLogging.WriteLine(state.Mobile,
|
||||
"{0} {1} opening properties for equipment item {2} of {3}", state.Mobile.AccessLevel,
|
||||
CommandLogging.Format(state.Mobile), CommandLogging.Format(m_Item),
|
||||
CommandLogging.Format(m_Mobile));
|
||||
state.Mobile.SendGump(new PropertiesGump(state.Mobile, m_Item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BankTarget : Target
|
||||
{
|
||||
public BankTarget() : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile m)
|
||||
{
|
||||
BankBox box = m.Player ? m.BankBox : m.FindBankNoCreate();
|
||||
|
||||
if (box != null)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} opening bank box of {2}", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(m));
|
||||
|
||||
if (from == m)
|
||||
box.Open();
|
||||
else
|
||||
box.DisplayTo(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("They have no bank box.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DismountTarget : Target
|
||||
{
|
||||
public DismountTarget() : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile targ)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} dismounting {2}", from.AccessLevel, CommandLogging.Format(from),
|
||||
CommandLogging.Format(targ));
|
||||
|
||||
for (int i = 0; i < targ.Items.Count; ++i)
|
||||
{
|
||||
Item item = targ.Items[i];
|
||||
|
||||
if (item is IMountItem mountItem)
|
||||
{
|
||||
IMount mount = mountItem.Mount;
|
||||
|
||||
if (mount != null)
|
||||
mount.Rider = null;
|
||||
|
||||
if (targ.Items.IndexOf(item) == -1)
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < targ.Items.Count; ++i)
|
||||
{
|
||||
Item item = targ.Items[i];
|
||||
|
||||
if (item.Layer == Layer.Mount)
|
||||
{
|
||||
item.Delete();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ClientTarget : Target
|
||||
{
|
||||
public ClientTarget() : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile targ && targ.NetState != null)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} opening client menu of {2}", from.AccessLevel,
|
||||
CommandLogging.Format(from), CommandLogging.Format(targ));
|
||||
from.SendGump(new ClientGump(from, targ.NetState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StuckMenuTarget : Target
|
||||
{
|
||||
public StuckMenuTarget() : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile mobile)
|
||||
{
|
||||
if (mobile.AccessLevel >= from.AccessLevel && mobile != from)
|
||||
from.SendMessage("You can't do that to someone with higher Accesslevel than you!");
|
||||
else
|
||||
from.SendGump(new StuckMenu(from, mobile, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
402
Projects/Scripts/Commands/HelpInfo.cs
Normal file
402
Projects/Scripts/Commands/HelpInfo.cs
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Server.Commands.Generic;
|
||||
using Server.Gumps;
|
||||
using Server.Network;
|
||||
using CommandInfo = Server.Commands.Docs.DocCommandEntry;
|
||||
using CommandInfoSorter = Server.Commands.Docs.CommandEntrySorter;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class HelpInfo
|
||||
{
|
||||
public static Dictionary<string, CommandInfo> HelpInfos{ get; } = new Dictionary<string, CommandInfo>();
|
||||
|
||||
public static List<CommandInfo> SortedHelpInfo{ get; private set; } = new List<CommandInfo>();
|
||||
|
||||
[CallPriority(100)]
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("HelpInfo", AccessLevel.Player, HelpInfo_OnCommand);
|
||||
|
||||
FillTable();
|
||||
}
|
||||
|
||||
[Usage("HelpInfo [<command>]")]
|
||||
[Description(
|
||||
"Gives information on a specified command, or when no argument specified, displays a gump containing all commands")]
|
||||
private static void HelpInfo_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length > 0)
|
||||
{
|
||||
string arg = e.GetString(0).ToLower();
|
||||
if (HelpInfos.TryGetValue(arg, out CommandInfo c))
|
||||
{
|
||||
Mobile m = e.Mobile;
|
||||
|
||||
if (m.AccessLevel >= c.AccessLevel)
|
||||
m.SendGump(new CommandInfoGump(c));
|
||||
else
|
||||
m.SendMessage("You don't have access to that command.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage($"Command '{arg}' not found!");
|
||||
}
|
||||
|
||||
e.Mobile.SendGump(new CommandListGump(0, e.Mobile, null));
|
||||
}
|
||||
|
||||
public static void FillTable()
|
||||
{
|
||||
List<CommandEntry> commands = new List<CommandEntry>(CommandSystem.Entries.Values);
|
||||
List<CommandInfo> list = new List<CommandInfo>();
|
||||
|
||||
commands.Sort();
|
||||
commands.Reverse();
|
||||
Docs.Clean(commands);
|
||||
|
||||
for (int i = 0; i < commands.Count; ++i)
|
||||
{
|
||||
CommandEntry e = commands[i];
|
||||
|
||||
MethodInfo mi = e.Handler.Method;
|
||||
|
||||
object[] attrs = mi.GetCustomAttributes(typeof(UsageAttribute), false);
|
||||
|
||||
if (attrs.Length == 0)
|
||||
continue;
|
||||
|
||||
UsageAttribute usage = attrs[0] as UsageAttribute;
|
||||
|
||||
attrs = mi.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attrs.Length == 0)
|
||||
continue;
|
||||
|
||||
if (usage == null || !(attrs[0] is DescriptionAttribute desc))
|
||||
continue;
|
||||
|
||||
attrs = mi.GetCustomAttributes(typeof(AliasesAttribute), false);
|
||||
|
||||
AliasesAttribute aliases = attrs.Length == 0 ? null : attrs[0] as AliasesAttribute;
|
||||
|
||||
string descString = desc.Description.Replace("<", "(").Replace(">", ")");
|
||||
|
||||
if (aliases == null)
|
||||
{
|
||||
list.Add(new CommandInfo(e.AccessLevel, e.Command, null, usage.Usage, descString));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(new CommandInfo(e.AccessLevel, e.Command, aliases.Aliases, usage.Usage, descString));
|
||||
|
||||
for (int j = 0; j < aliases.Aliases.Length; j++)
|
||||
{
|
||||
string[] newAliases = new string[aliases.Aliases.Length];
|
||||
|
||||
aliases.Aliases.CopyTo(newAliases, 0);
|
||||
|
||||
newAliases[j] = e.Command;
|
||||
|
||||
list.Add(new CommandInfo(e.AccessLevel, aliases.Aliases[j], newAliases, usage.Usage, descString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < TargetCommands.AllCommands.Count; ++i)
|
||||
{
|
||||
BaseCommand command = TargetCommands.AllCommands[i];
|
||||
|
||||
string usage = command.Usage;
|
||||
string desc = command.Description;
|
||||
|
||||
if (usage == null || desc == null)
|
||||
continue;
|
||||
|
||||
string[] cmds = command.Commands;
|
||||
string cmd = cmds[0];
|
||||
string[] aliases = new string[cmds.Length - 1];
|
||||
|
||||
for (int j = 0; j < aliases.Length; ++j)
|
||||
aliases[j] = cmds[j + 1];
|
||||
|
||||
desc = desc.Replace("<", "(").Replace(">", ")");
|
||||
|
||||
if (command.Supports != CommandSupport.Single)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(50 + desc.Length);
|
||||
|
||||
sb.Append("Modifiers: ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Global) != 0)
|
||||
sb.Append("<i>Global</i>, ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Online) != 0)
|
||||
sb.Append("<i>Online</i>, ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Region) != 0)
|
||||
sb.Append("<i>Region</i>, ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Contained) != 0)
|
||||
sb.Append("<i>Contained</i>, ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Multi) != 0)
|
||||
sb.Append("<i>Multi</i>, ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Area) != 0)
|
||||
sb.Append("<i>Area</i>, ");
|
||||
|
||||
if ((command.Supports & CommandSupport.Self) != 0)
|
||||
sb.Append("<i>Self</i>, ");
|
||||
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
sb.Append("<br>");
|
||||
sb.Append(desc);
|
||||
|
||||
desc = sb.ToString();
|
||||
}
|
||||
|
||||
list.Add(new CommandInfo(command.AccessLevel, cmd, aliases, usage, desc));
|
||||
|
||||
for (int j = 0; j < aliases.Length; j++)
|
||||
{
|
||||
string[] newAliases = new string[aliases.Length];
|
||||
|
||||
aliases.CopyTo(newAliases, 0);
|
||||
|
||||
newAliases[j] = cmd;
|
||||
|
||||
list.Add(new CommandInfo(command.AccessLevel, aliases[j], newAliases, usage, desc));
|
||||
}
|
||||
}
|
||||
|
||||
List<BaseCommandImplementor> commandImpls = BaseCommandImplementor.Implementors;
|
||||
|
||||
for (int i = 0; i < commandImpls.Count; ++i)
|
||||
{
|
||||
BaseCommandImplementor command = commandImpls[i];
|
||||
|
||||
string usage = command.Usage;
|
||||
string desc = command.Description;
|
||||
|
||||
if (usage == null || desc == null)
|
||||
continue;
|
||||
|
||||
string[] cmds = command.Accessors;
|
||||
string cmd = cmds[0];
|
||||
string[] aliases = new string[cmds.Length - 1];
|
||||
|
||||
for (int j = 0; j < aliases.Length; ++j)
|
||||
aliases[j] = cmds[j + 1];
|
||||
|
||||
desc = desc.Replace("<", ")").Replace(">", ")");
|
||||
|
||||
list.Add(new CommandInfo(command.AccessLevel, cmd, aliases, usage, desc));
|
||||
|
||||
for (int j = 0; j < aliases.Length; j++)
|
||||
{
|
||||
string[] newAliases = new string[aliases.Length];
|
||||
|
||||
aliases.CopyTo(newAliases, 0);
|
||||
|
||||
newAliases[j] = cmd;
|
||||
|
||||
list.Add(new CommandInfo(command.AccessLevel, aliases[j], newAliases, usage, desc));
|
||||
}
|
||||
}
|
||||
|
||||
list.Sort(new CommandInfoSorter());
|
||||
|
||||
SortedHelpInfo = list;
|
||||
|
||||
foreach (CommandInfo c in SortedHelpInfo)
|
||||
if (!HelpInfos.ContainsKey(c.Name.ToLower()))
|
||||
HelpInfos.Add(c.Name.ToLower(), c);
|
||||
}
|
||||
|
||||
public class CommandListGump : BaseGridGump
|
||||
{
|
||||
private const int EntriesPerPage = 15;
|
||||
private List<CommandInfo> m_List;
|
||||
|
||||
private int m_Page;
|
||||
|
||||
public CommandListGump(int page, Mobile from, List<CommandInfo> list)
|
||||
: base(30, 30)
|
||||
{
|
||||
m_Page = page;
|
||||
|
||||
if (list == null)
|
||||
{
|
||||
m_List = new List<CommandInfo>();
|
||||
|
||||
foreach (CommandInfo c in SortedHelpInfo)
|
||||
if (from.AccessLevel >= c.AccessLevel)
|
||||
m_List.Add(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_List = list;
|
||||
}
|
||||
|
||||
AddNewPage();
|
||||
|
||||
if (m_Page > 0)
|
||||
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
|
||||
else
|
||||
AddEntryHeader(20);
|
||||
|
||||
AddEntryHtml(160, Center(
|
||||
$"Page {m_Page + 1} of {(m_List.Count + EntriesPerPage - 1) / EntriesPerPage}"));
|
||||
|
||||
if ((m_Page + 1) * EntriesPerPage < m_List.Count)
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
|
||||
else
|
||||
AddEntryHeader(20);
|
||||
|
||||
int last = (int)AccessLevel.Player - 1;
|
||||
|
||||
for (int i = m_Page * EntriesPerPage, line = 0; line < EntriesPerPage && i < m_List.Count; ++i, ++line)
|
||||
{
|
||||
CommandInfo c = m_List[i];
|
||||
if (from.AccessLevel >= c.AccessLevel)
|
||||
{
|
||||
if ((int)c.AccessLevel != last)
|
||||
{
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, Color(c.AccessLevel.ToString(), 0xFF0000));
|
||||
AddEntryHeader(20);
|
||||
line++;
|
||||
}
|
||||
|
||||
last = (int)c.AccessLevel;
|
||||
|
||||
AddNewLine();
|
||||
AddEntryHtml(20 + OffsetSize + 160, c.Name);
|
||||
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 3 + i, ArrowRightWidth, ArrowRightHeight);
|
||||
}
|
||||
}
|
||||
|
||||
FinishPage();
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
Mobile m = sender.Mobile;
|
||||
switch (info.ButtonID)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
m.CloseGump<CommandInfoGump>();
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (m_Page > 0)
|
||||
m.SendGump(new CommandListGump(m_Page - 1, m, m_List));
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if ((m_Page + 1) * EntriesPerPage < SortedHelpInfo.Count)
|
||||
m.SendGump(new CommandListGump(m_Page + 1, m, m_List));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
int v = info.ButtonID - 3;
|
||||
|
||||
if (v >= 0 && v < m_List.Count)
|
||||
{
|
||||
CommandInfo c = m_List[v];
|
||||
|
||||
if (m.AccessLevel >= c.AccessLevel)
|
||||
{
|
||||
m.SendGump(new CommandInfoGump(c));
|
||||
m.SendGump(new CommandListGump(m_Page, m, m_List));
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendMessage("You no longer have access to that command.");
|
||||
m.SendGump(new CommandListGump(m_Page, m, null));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CommandInfoGump : Gump
|
||||
{
|
||||
public CommandInfoGump(CommandInfo info, int width = 320, int height = 200)
|
||||
: base(300, 50)
|
||||
{
|
||||
AddPage(0);
|
||||
|
||||
AddBackground(0, 0, width, height, 5054);
|
||||
|
||||
//AddImageTiled( 10, 10, width - 20, 20, 2624 );
|
||||
//AddAlphaRegion( 10, 10, width - 20, 20 );
|
||||
//AddHtmlLocalized( 10, 10, width - 20, 20, header, headerColor, false, false );
|
||||
AddHtml(10, 10, width - 20, 20, Color(Center(info.Name), 0xFF0000));
|
||||
|
||||
//AddImageTiled( 10, 40, width - 20, height - 80, 2624 );
|
||||
//AddAlphaRegion( 10, 40, width - 20, height - 80 );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("Usage: ");
|
||||
sb.Append(info.Usage.Replace("<", "(").Replace(">", ")"));
|
||||
sb.Append("<BR>");
|
||||
|
||||
string[] aliases = info.Aliases;
|
||||
|
||||
if (aliases?.Length > 0)
|
||||
{
|
||||
sb.Append($"Alias{(aliases.Length == 1 ? "" : "es")}: ");
|
||||
|
||||
for (int i = 0; i < aliases.Length; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
sb.Append(", ");
|
||||
|
||||
sb.Append(aliases[i]);
|
||||
}
|
||||
|
||||
sb.Append("<BR>");
|
||||
}
|
||||
|
||||
sb.Append("AccessLevel: ");
|
||||
sb.Append(info.AccessLevel.ToString());
|
||||
sb.Append("<BR>");
|
||||
sb.Append("<BR>");
|
||||
|
||||
sb.Append(info.Description);
|
||||
|
||||
AddHtml(10, 40, width - 20, height - 80, sb.ToString(), false, true);
|
||||
|
||||
//AddImageTiled( 10, height - 30, width - 20, 20, 2624 );
|
||||
//AddAlphaRegion( 10, height - 30, width - 20, 20 );
|
||||
}
|
||||
|
||||
public string Color(string text, int color)
|
||||
{
|
||||
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
|
||||
}
|
||||
|
||||
public string Center(string text)
|
||||
{
|
||||
return $"<CENTER>{text}</CENTER>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
Projects/Scripts/Commands/Logging.cs
Normal file
141
Projects/Scripts/Commands/Logging.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Server.Accounting;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class CommandLogging
|
||||
{
|
||||
private static char[] m_NotSafe = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };
|
||||
public static bool Enabled{ get; set; } = true;
|
||||
|
||||
public static StreamWriter Output{ get; private set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.Command += EventSink_Command;
|
||||
|
||||
if (!Directory.Exists("Logs"))
|
||||
Directory.CreateDirectory("Logs");
|
||||
|
||||
string directory = "Logs/Commands";
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
try
|
||||
{
|
||||
Output = new StreamWriter(Path.Combine(directory, $"{DateTime.UtcNow.ToLongDateString()}.log"), true);
|
||||
|
||||
Output.AutoFlush = true;
|
||||
|
||||
Output.WriteLine("##############################");
|
||||
Output.WriteLine("Log started on {0}", DateTime.UtcNow);
|
||||
Output.WriteLine();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static object Format(object o)
|
||||
{
|
||||
if (o is Mobile m)
|
||||
{
|
||||
if (m.Account == null)
|
||||
return $"{m} (no account)";
|
||||
|
||||
return $"{m} ('{m.Account.Username}')";
|
||||
}
|
||||
|
||||
if (o is Item item) return $"0x{item.Serial.Value:X} ({item.GetType().Name})";
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
public static void WriteLine(Mobile from, string format, params object[] args)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
WriteLine(from, string.Format(format, args));
|
||||
}
|
||||
|
||||
public static void WriteLine(Mobile from, string text)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Output.WriteLine("{0}: {1}: {2}", DateTime.UtcNow, from.NetState, text);
|
||||
|
||||
string path = Core.BaseDirectory;
|
||||
|
||||
string name = !(from.Account is Account acct) ? from.Name : acct.Username;
|
||||
|
||||
AppendPath(ref path, "Logs");
|
||||
AppendPath(ref path, "Commands");
|
||||
AppendPath(ref path, from.AccessLevel.ToString());
|
||||
path = Path.Combine(path, $"{name}.log");
|
||||
|
||||
using (StreamWriter sw = new StreamWriter(path, true))
|
||||
{
|
||||
sw.WriteLine("{0}: {1}: {2}", DateTime.UtcNow, from.NetState, text);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static void AppendPath(ref string path, string toAppend)
|
||||
{
|
||||
path = Path.Combine(path, toAppend);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public static string Safe(string ip)
|
||||
{
|
||||
if (ip == null)
|
||||
return "null";
|
||||
|
||||
ip = ip.Trim();
|
||||
|
||||
if (ip.Length == 0)
|
||||
return "empty";
|
||||
|
||||
bool isSafe = true;
|
||||
|
||||
for (int i = 0; isSafe && i < m_NotSafe.Length; ++i)
|
||||
isSafe = ip.IndexOf(m_NotSafe[i]) == -1;
|
||||
|
||||
if (isSafe)
|
||||
return ip;
|
||||
|
||||
StringBuilder sb = new StringBuilder(ip);
|
||||
|
||||
for (int i = 0; i < m_NotSafe.Length; ++i)
|
||||
sb.Replace(m_NotSafe[i], '_');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static void EventSink_Command(CommandEventArgs e)
|
||||
{
|
||||
WriteLine(e.Mobile, "{0} {1} used command '{2} {3}'", e.Mobile.AccessLevel, Format(e.Mobile), e.Command,
|
||||
e.ArgString);
|
||||
}
|
||||
|
||||
public static void LogChangeProperty(Mobile from, object o, string name, string value)
|
||||
{
|
||||
WriteLine(from, "{0} {1} set property '{2}' of {3} to '{4}'", from.AccessLevel, Format(from), name, Format(o),
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
369
Projects/Scripts/Commands/Profiling.cs
Normal file
369
Projects/Scripts/Commands/Profiling.cs
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Server.Diagnostics;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Profiling
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("DumpTimers", AccessLevel.Administrator, DumpTimers_OnCommand);
|
||||
CommandSystem.Register("CountObjects", AccessLevel.Administrator, CountObjects_OnCommand);
|
||||
CommandSystem.Register("ProfileWorld", AccessLevel.Administrator, ProfileWorld_OnCommand);
|
||||
CommandSystem.Register("TraceInternal", AccessLevel.Administrator, TraceInternal_OnCommand);
|
||||
CommandSystem.Register("TraceExpanded", AccessLevel.Administrator, TraceExpanded_OnCommand);
|
||||
CommandSystem.Register("WriteProfiles", AccessLevel.Administrator, WriteProfiles_OnCommand);
|
||||
CommandSystem.Register("SetProfiles", AccessLevel.Administrator, SetProfiles_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("WriteProfiles")]
|
||||
[Description("Generates a log files containing performance diagnostic information.")]
|
||||
public static void WriteProfiles_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter("profiles.log", true))
|
||||
{
|
||||
sw.WriteLine("# Dump on {0:f}", DateTime.UtcNow);
|
||||
sw.WriteLine("# Core profiling for " + Core.ProfileTime);
|
||||
|
||||
sw.WriteLine("# Packet send");
|
||||
BaseProfile.WriteAll(sw, PacketSendProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Packet receive");
|
||||
BaseProfile.WriteAll(sw, PacketReceiveProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Timer");
|
||||
BaseProfile.WriteAll(sw, TimerProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Gump response");
|
||||
BaseProfile.WriteAll(sw, GumpProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Target response");
|
||||
BaseProfile.WriteAll(sw, TargetProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("SetProfiles [true | false]")]
|
||||
[Description("Enables, disables, or toggles the state of core packet and timer profiling.")]
|
||||
public static void SetProfiles_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 1)
|
||||
Core.Profiling = e.GetBoolean(0);
|
||||
else
|
||||
Core.Profiling = !Core.Profiling;
|
||||
|
||||
e.Mobile.SendMessage("Profiling has been {0}.", Core.Profiling ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
[Usage("DumpTimers")]
|
||||
[Description("Generates a log file of all currently executing timers. Used for tracing timer leaks.")]
|
||||
public static void DumpTimers_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter("timerdump.log", true))
|
||||
{
|
||||
Timer.DumpInfo(sw);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("CountObjects")]
|
||||
[Description("Generates a log file detailing all item and mobile types in the world.")]
|
||||
public static void CountObjects_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
using (StreamWriter op = new StreamWriter("objects.log"))
|
||||
{
|
||||
Dictionary<Type, int> table = new Dictionary<Type, int>();
|
||||
|
||||
foreach (Item item in World.Items.Values)
|
||||
{
|
||||
Type type = item.GetType();
|
||||
|
||||
table[type] = (table.TryGetValue(type, out int value) ? value : 0) + 1;
|
||||
}
|
||||
|
||||
List<KeyValuePair<Type, int>> items = table.ToList();
|
||||
table.Clear();
|
||||
|
||||
foreach (Mobile m in World.Mobiles.Values)
|
||||
{
|
||||
Type type = m.GetType();
|
||||
|
||||
table[type] = (table.TryGetValue(type, out int value) ? value : 0) + 1;
|
||||
}
|
||||
|
||||
List<KeyValuePair<Type, int>> mobiles = table.ToList();
|
||||
|
||||
items.Sort(new CountSorter());
|
||||
mobiles.Sort(new CountSorter());
|
||||
|
||||
op.WriteLine("# Object count table generated on {0}", DateTime.UtcNow);
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
|
||||
op.WriteLine("# Items:");
|
||||
|
||||
items.ForEach(kvp =>
|
||||
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / World.Items.Count, kvp.Key));
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
|
||||
op.WriteLine("#Mobiles:");
|
||||
|
||||
mobiles.ForEach(kvp =>
|
||||
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / World.Mobiles.Count, kvp.Key));
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Object table has been generated. See the file : <runuo root>/objects.log");
|
||||
}
|
||||
|
||||
[Usage("TraceExpanded")]
|
||||
[Description("Generates a log file describing all items using expanded memory.")]
|
||||
public static void TraceExpanded_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Dictionary<Type, int[]> typeTable = new Dictionary<Type, int[]>();
|
||||
|
||||
foreach (Item item in World.Items.Values)
|
||||
{
|
||||
ExpandFlag flags = item.GetExpandFlags();
|
||||
|
||||
if ((flags & ~(ExpandFlag.TempFlag | ExpandFlag.SaveFlag)) == 0)
|
||||
continue;
|
||||
|
||||
Type itemType = item.GetType();
|
||||
|
||||
do
|
||||
{
|
||||
if (!typeTable.TryGetValue(itemType, out int[] countTable))
|
||||
typeTable[itemType] = countTable = new int[9];
|
||||
|
||||
if ((flags & ExpandFlag.Name) != 0)
|
||||
++countTable[0];
|
||||
|
||||
if ((flags & ExpandFlag.Items) != 0)
|
||||
++countTable[1];
|
||||
|
||||
if ((flags & ExpandFlag.Bounce) != 0)
|
||||
++countTable[2];
|
||||
|
||||
if ((flags & ExpandFlag.Holder) != 0)
|
||||
++countTable[3];
|
||||
|
||||
if ((flags & ExpandFlag.Blessed) != 0)
|
||||
++countTable[4];
|
||||
|
||||
/*if ( ( flags & ExpandFlag.TempFlag ) != 0 )
|
||||
++countTable[5];
|
||||
|
||||
if ( ( flags & ExpandFlag.SaveFlag ) != 0 )
|
||||
++countTable[6];*/
|
||||
|
||||
if ((flags & ExpandFlag.Weight) != 0)
|
||||
++countTable[7];
|
||||
|
||||
if ((flags & ExpandFlag.Spawner) != 0)
|
||||
++countTable[8];
|
||||
|
||||
itemType = itemType.BaseType;
|
||||
} while (itemType != typeof(object));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamWriter op = new StreamWriter("expandedItems.log", true))
|
||||
{
|
||||
string[] names =
|
||||
{
|
||||
"Name",
|
||||
"Items",
|
||||
"Bounce",
|
||||
"Holder",
|
||||
"Blessed",
|
||||
"TempFlag",
|
||||
"SaveFlag",
|
||||
"Weight",
|
||||
"Spawner"
|
||||
};
|
||||
|
||||
List<KeyValuePair<Type, int[]>> list = typeTable.ToList();
|
||||
|
||||
list.Sort(new CountsSorter());
|
||||
|
||||
foreach (KeyValuePair<Type, int[]> kvp in list)
|
||||
{
|
||||
int[] countTable = kvp.Value;
|
||||
|
||||
op.WriteLine("# {0}", kvp.Key.FullName);
|
||||
|
||||
for (int i = 0; i < countTable.Length; ++i)
|
||||
if (countTable[i] > 0)
|
||||
op.WriteLine("{0}\t{1:N0}", names[i], countTable[i]);
|
||||
|
||||
op.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("TraceInternal")]
|
||||
[Description("Generates a log file describing all items in the 'internal' map.")]
|
||||
public static void TraceInternal_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
int totalCount = 0;
|
||||
Dictionary<Type, int[]> table = new Dictionary<Type, int[]>();
|
||||
|
||||
foreach (Item item in World.Items.Values)
|
||||
{
|
||||
if (item.Parent != null || item.Map != Map.Internal)
|
||||
continue;
|
||||
|
||||
++totalCount;
|
||||
|
||||
Type type = item.GetType();
|
||||
|
||||
if (table.TryGetValue(type, out int[] parms))
|
||||
{
|
||||
parms[0]++;
|
||||
parms[1] += item.Amount;
|
||||
} else
|
||||
table[type] = new[] { 1, item.Amount };
|
||||
}
|
||||
|
||||
using (StreamWriter op = new StreamWriter("internal.log"))
|
||||
{
|
||||
op.WriteLine("# {0} items found", totalCount);
|
||||
op.WriteLine("# {0} different types", table.Count);
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
op.WriteLine("Type\t\tCount\t\tAmount\t\tAvg. Amount");
|
||||
|
||||
foreach (KeyValuePair<Type, int[]> de in table)
|
||||
{
|
||||
int[] parms = de.Value;
|
||||
|
||||
op.WriteLine("{0}\t\t{1}\t\t{2}\t\t{3:F2}", de.Key.Name, parms[0], parms[1], (double)parms[1] / parms[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("ProfileWorld")]
|
||||
[Description("Prints the amount of data serialized for every object type in your world file.")]
|
||||
public static void ProfileWorld_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
ProfileWorld("items", "worldprofile_items.log");
|
||||
ProfileWorld("mobiles", "worldprofile_mobiles.log");
|
||||
}
|
||||
|
||||
public static void ProfileWorld(string type, string opFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
|
||||
using (BinaryReader bin = new BinaryReader(new FileStream(string.Format("Saves/{0}/{0}.tdb", type),
|
||||
FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
int count = bin.ReadInt32();
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
types.Add(ScriptCompiler.FindTypeByFullName(bin.ReadString()));
|
||||
}
|
||||
|
||||
long total = 0;
|
||||
|
||||
Dictionary<Type, int> table = new Dictionary<Type, int>();
|
||||
|
||||
using (BinaryReader bin = new BinaryReader(new FileStream(string.Format("Saves/{0}/{0}.idx", type),
|
||||
FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
int count = bin.ReadInt32();
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int typeID = bin.ReadInt32();
|
||||
int serial = bin.ReadInt32();
|
||||
long pos = bin.ReadInt64();
|
||||
int length = bin.ReadInt32();
|
||||
Type objType = types[typeID];
|
||||
|
||||
while (objType != null && objType != typeof(object))
|
||||
{
|
||||
table[objType] = length + (table.TryGetValue(objType, out int value) ? value : 0);
|
||||
objType = objType.BaseType;
|
||||
total += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<KeyValuePair<Type, int>> list = table.ToList();
|
||||
|
||||
list.Sort(new CountSorter());
|
||||
|
||||
using (StreamWriter op = new StreamWriter(opFile))
|
||||
{
|
||||
op.WriteLine("# Profile of world {0}", type);
|
||||
op.WriteLine("# Generated on {0}", DateTime.UtcNow);
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
|
||||
list.ForEach(kvp =>
|
||||
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / total, kvp.Key));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private class CountSorter : IComparer<KeyValuePair<Type, int>>
|
||||
{
|
||||
public int Compare(KeyValuePair<Type, int> x, KeyValuePair<Type, int> y)
|
||||
{
|
||||
int aCount = x.Value;
|
||||
int bCount = y.Value;
|
||||
|
||||
int v = -aCount.CompareTo(bCount);
|
||||
|
||||
return v != 0 ? v : x.Key.FullName.CompareTo(y.Key.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private class CountsSorter : IComparer<KeyValuePair<Type, int[]>>
|
||||
{
|
||||
public int Compare(KeyValuePair<Type, int[]> x, KeyValuePair<Type, int[]> y)
|
||||
{
|
||||
int aCount = x.Value.Aggregate(0, (t, val) => t + val);
|
||||
int bCount = y.Value.Aggregate(0, (t, val) => t + val);
|
||||
|
||||
int v = -aCount.CompareTo(bCount);
|
||||
|
||||
return v != 0 ? v : x.Key.FullName.CompareTo(y.Key.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
786
Projects/Scripts/Commands/Properties.cs
Normal file
786
Projects/Scripts/Commands/Properties.cs
Normal file
|
|
@ -0,0 +1,786 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using Server.Commands;
|
||||
using Server.Commands.Generic;
|
||||
using Server.Gumps;
|
||||
using Server.Targeting;
|
||||
using CPA = Server.CommandPropertyAttribute;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
[Flags]
|
||||
public enum PropertyAccess
|
||||
{
|
||||
Read = 0x01,
|
||||
Write = 0x02,
|
||||
ReadWrite = Read | Write
|
||||
}
|
||||
|
||||
public static class Properties
|
||||
{
|
||||
private static Type typeofCPA = typeof(CPA);
|
||||
|
||||
private static Type typeofSerial = typeof(Serial);
|
||||
|
||||
private static Type typeofType = typeof(Type);
|
||||
|
||||
private static Type typeofChar = typeof(char);
|
||||
|
||||
private static Type typeofString = typeof(string);
|
||||
|
||||
private static Type typeofText = typeof(TextDefinition);
|
||||
|
||||
private static Type typeofTimeSpan = typeof(TimeSpan);
|
||||
private static Type typeofParsable = typeof(ParsableAttribute);
|
||||
|
||||
private static Type[] m_ParseTypes = { typeof(string) };
|
||||
private static object[] m_ParseParams = new object[1];
|
||||
|
||||
private static Type[] m_NumericTypes =
|
||||
{
|
||||
typeof(byte), typeof(sbyte),
|
||||
typeof(short), typeof(ushort),
|
||||
typeof(int), typeof(uint),
|
||||
typeof(long), typeof(ulong)
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("Props", AccessLevel.Counselor, Props_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("Props [serial]")]
|
||||
[Description("Opens a menu where you can view and edit all properties of a targeted (or specified) object.")]
|
||||
private static void Props_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 1)
|
||||
{
|
||||
IEntity ent = World.FindEntity(e.GetUInt32(0));
|
||||
|
||||
if (ent == null)
|
||||
e.Mobile.SendMessage("No object with that serial was found.");
|
||||
else if (!BaseCommand.IsAccessible(e.Mobile, ent))
|
||||
e.Mobile.SendLocalizedMessage(500447); // That is not accessible.
|
||||
else
|
||||
e.Mobile.SendGump(new PropertiesGump(e.Mobile, ent));
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.Target = new PropsTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CIEqual(string l, string r)
|
||||
{
|
||||
return Insensitive.Equals(l, r);
|
||||
}
|
||||
|
||||
public static CPA GetCPA(PropertyInfo p)
|
||||
{
|
||||
object[] attrs = p.GetCustomAttributes(typeofCPA, false);
|
||||
|
||||
if (attrs.Length == 0)
|
||||
return null;
|
||||
|
||||
return attrs[0] as CPA;
|
||||
}
|
||||
|
||||
public static PropertyInfo[] GetPropertyInfoChain(Mobile from, Type type, string propertyString,
|
||||
PropertyAccess endAccess, ref string failReason)
|
||||
{
|
||||
string[] split = propertyString.Split('.');
|
||||
|
||||
if (split.Length == 0)
|
||||
return null;
|
||||
|
||||
PropertyInfo[] info = new PropertyInfo[split.Length];
|
||||
|
||||
for (int i = 0; i < info.Length; ++i)
|
||||
{
|
||||
string propertyName = split[i];
|
||||
|
||||
if (CIEqual(propertyName, "current"))
|
||||
continue;
|
||||
|
||||
PropertyInfo[] props = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
bool isFinal = i == info.Length - 1;
|
||||
|
||||
PropertyAccess access = endAccess;
|
||||
|
||||
if (!isFinal)
|
||||
access |= PropertyAccess.Read;
|
||||
|
||||
for (int j = 0; j < props.Length; ++j)
|
||||
{
|
||||
PropertyInfo p = props[j];
|
||||
|
||||
if (CIEqual(p.Name, propertyName))
|
||||
{
|
||||
CPA attr = GetCPA(p);
|
||||
|
||||
if (attr == null)
|
||||
{
|
||||
failReason = $"Property '{propertyName}' not found.";
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((access & PropertyAccess.Read) != 0 && from.AccessLevel < attr.ReadLevel)
|
||||
{
|
||||
failReason =
|
||||
$"You must be at least {Mobile.GetAccessLevelName(attr.ReadLevel)} to get the property '{propertyName}'.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((access & PropertyAccess.Write) != 0 && from.AccessLevel < attr.WriteLevel)
|
||||
{
|
||||
failReason =
|
||||
$"You must be at least {Mobile.GetAccessLevelName(attr.WriteLevel)} to set the property '{propertyName}'.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((access & PropertyAccess.Read) != 0 && !p.CanRead)
|
||||
{
|
||||
failReason = $"Property '{propertyName}' is write only.";
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((access & PropertyAccess.Write) != 0 && (!p.CanWrite || attr.ReadOnly) && isFinal)
|
||||
{
|
||||
failReason = $"Property '{propertyName}' is read only.";
|
||||
return null;
|
||||
}
|
||||
|
||||
info[i] = p;
|
||||
type = p.PropertyType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info[i] == null)
|
||||
{
|
||||
failReason = $"Property '{propertyName}' not found.";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static PropertyInfo GetPropertyInfo(Mobile from, ref object obj, string propertyName, PropertyAccess access,
|
||||
ref string failReason)
|
||||
{
|
||||
PropertyInfo[] chain = GetPropertyInfoChain(from, obj.GetType(), propertyName, access, ref failReason);
|
||||
|
||||
return chain == null ? null : GetPropertyInfo(ref obj, chain, ref failReason);
|
||||
}
|
||||
|
||||
public static PropertyInfo GetPropertyInfo(ref object obj, PropertyInfo[] chain, ref string failReason)
|
||||
{
|
||||
if (chain == null || chain.Length == 0)
|
||||
{
|
||||
failReason = "Property chain is empty.";
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < chain.Length - 1; ++i)
|
||||
{
|
||||
if (chain[i] == null)
|
||||
continue;
|
||||
|
||||
obj = chain[i].GetValue(obj, null);
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
failReason = $"Property '{chain[i]}' is null.";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return chain[chain.Length - 1];
|
||||
}
|
||||
|
||||
public static string GetValue(Mobile from, object o, string name)
|
||||
{
|
||||
string failReason = "";
|
||||
|
||||
PropertyInfo[] chain = GetPropertyInfoChain(from, o.GetType(), name, PropertyAccess.Read, ref failReason);
|
||||
|
||||
if (chain == null || chain.Length == 0)
|
||||
return failReason;
|
||||
|
||||
PropertyInfo p = GetPropertyInfo(ref o, chain, ref failReason);
|
||||
|
||||
return p == null ? failReason : InternalGetValue(o, p, chain);
|
||||
}
|
||||
|
||||
public static string IncreaseValue(Mobile from, object o, string[] args)
|
||||
{
|
||||
// Type type = o.GetType();
|
||||
|
||||
object[] realObjs = new object[args.Length / 2];
|
||||
PropertyInfo[] realProps = new PropertyInfo[args.Length / 2];
|
||||
int[] realValues = new int[args.Length / 2];
|
||||
|
||||
bool positive = false;
|
||||
bool negative = false;
|
||||
|
||||
for (int i = 0; i < realProps.Length; ++i)
|
||||
{
|
||||
string name = args[i * 2];
|
||||
|
||||
try
|
||||
{
|
||||
string valueString = args[1 + i * 2];
|
||||
|
||||
if (valueString.StartsWith("0x"))
|
||||
realValues[i] = Convert.ToInt32(valueString.Substring(2), 16);
|
||||
else
|
||||
realValues[i] = Convert.ToInt32(valueString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Offset value could not be parsed.";
|
||||
}
|
||||
|
||||
if (realValues[i] > 0)
|
||||
positive = true;
|
||||
else if (realValues[i] < 0)
|
||||
negative = true;
|
||||
else
|
||||
return "Zero is not a valid value to offset.";
|
||||
|
||||
string failReason = null;
|
||||
realObjs[i] = o;
|
||||
realProps[i] = GetPropertyInfo(from, ref realObjs[i], name, PropertyAccess.ReadWrite, ref failReason);
|
||||
|
||||
if (failReason != null)
|
||||
return failReason;
|
||||
|
||||
if (realProps[i] == null)
|
||||
return "Property not found.";
|
||||
}
|
||||
|
||||
for (int i = 0; i < realProps.Length; ++i)
|
||||
{
|
||||
object obj = realProps[i].GetValue(realObjs[i], null);
|
||||
|
||||
if (!(obj is IConvertible))
|
||||
return "Property is not IConvertable.";
|
||||
|
||||
try
|
||||
{
|
||||
long v = (long)Convert.ChangeType(obj, TypeCode.Int64);
|
||||
v += realValues[i];
|
||||
|
||||
realProps[i].SetValue(realObjs[i], Convert.ChangeType(v, realProps[i].PropertyType), null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Value could not be converted";
|
||||
}
|
||||
}
|
||||
|
||||
if (realProps.Length == 1)
|
||||
{
|
||||
if (positive)
|
||||
return "The property has been increased.";
|
||||
|
||||
return "The property has been decreased.";
|
||||
}
|
||||
|
||||
if (positive && negative)
|
||||
return "The properties have been changed.";
|
||||
|
||||
if (positive)
|
||||
return "The properties have been increased.";
|
||||
|
||||
return "The properties have been decreased.";
|
||||
}
|
||||
|
||||
private static string InternalGetValue(object o, PropertyInfo p, PropertyInfo[] chain = null)
|
||||
{
|
||||
Type type = p.PropertyType;
|
||||
|
||||
object value = p.GetValue(o, null);
|
||||
string toString;
|
||||
|
||||
if (value == null)
|
||||
toString = "null";
|
||||
else if (IsNumeric(type))
|
||||
toString = $"{value} (0x{value:X})";
|
||||
else if (IsChar(type))
|
||||
toString = $"'{value}' ({(int)value} [0x{(int)value:X}])";
|
||||
else if (IsString(type))
|
||||
toString = (string)value == "null" ? @"@""null""" : $"\"{value}\"";
|
||||
else if (IsText(type))
|
||||
toString = ((TextDefinition)value).Format(false);
|
||||
else
|
||||
toString = value.ToString();
|
||||
|
||||
if (chain == null)
|
||||
return $"{p.Name} = {toString}";
|
||||
|
||||
string[] concat = new string[chain.Length * 2 + 1];
|
||||
|
||||
for (int i = 0; i < chain.Length; ++i)
|
||||
{
|
||||
concat[i * 2 + 0] = chain[i].Name;
|
||||
concat[i * 2 + 1] = i < chain.Length - 1 ? "." : " = ";
|
||||
}
|
||||
|
||||
concat[concat.Length - 1] = toString;
|
||||
|
||||
return string.Concat(concat);
|
||||
}
|
||||
|
||||
public static string SetValue(Mobile from, object o, string name, string value)
|
||||
{
|
||||
object logObject = o;
|
||||
|
||||
string failReason = "";
|
||||
PropertyInfo p = GetPropertyInfo(from, ref o, name, PropertyAccess.Write, ref failReason);
|
||||
|
||||
return p == null ? failReason : InternalSetValue(from, logObject, o, p, name, value, true);
|
||||
}
|
||||
|
||||
private static bool IsSerial(Type t)
|
||||
{
|
||||
return t == typeofSerial;
|
||||
}
|
||||
|
||||
private static bool IsType(Type t)
|
||||
{
|
||||
return t == typeofType;
|
||||
}
|
||||
|
||||
private static bool IsChar(Type t)
|
||||
{
|
||||
return t == typeofChar;
|
||||
}
|
||||
|
||||
private static bool IsString(Type t)
|
||||
{
|
||||
return t == typeofString;
|
||||
}
|
||||
|
||||
private static bool IsText(Type t)
|
||||
{
|
||||
return t == typeofText;
|
||||
}
|
||||
|
||||
private static bool IsEnum(Type t)
|
||||
{
|
||||
return t.IsEnum;
|
||||
}
|
||||
|
||||
private static bool IsParsable(Type t)
|
||||
{
|
||||
return t == typeofTimeSpan || t.IsDefined(typeofParsable, false);
|
||||
}
|
||||
|
||||
private static object Parse(object o, Type t, string value)
|
||||
{
|
||||
MethodInfo method = t.GetMethod("Parse", m_ParseTypes);
|
||||
|
||||
m_ParseParams[0] = value;
|
||||
|
||||
return method?.Invoke(o, m_ParseParams);
|
||||
}
|
||||
|
||||
private static bool IsNumeric(Type t)
|
||||
{
|
||||
return Array.IndexOf(m_NumericTypes, t) >= 0;
|
||||
}
|
||||
|
||||
public static string ConstructFromString(Type type, object obj, string value, ref object constructed)
|
||||
{
|
||||
object toSet;
|
||||
bool isSerial = IsSerial(type);
|
||||
|
||||
if (isSerial) // mutate into int32
|
||||
type = m_NumericTypes[4];
|
||||
|
||||
if (value == "(-null-)" && !type.IsValueType)
|
||||
value = null;
|
||||
|
||||
if (IsEnum(type))
|
||||
try
|
||||
{
|
||||
toSet = Enum.Parse(type, value, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "That is not a valid enumeration member.";
|
||||
}
|
||||
else if (IsType(type))
|
||||
try
|
||||
{
|
||||
toSet = ScriptCompiler.FindTypeByName(value);
|
||||
|
||||
if (toSet == null)
|
||||
return "No type with that name was found.";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "No type with that name was found.";
|
||||
}
|
||||
else if (IsParsable(type))
|
||||
try
|
||||
{
|
||||
toSet = Parse(obj, type, value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "That is not properly formatted.";
|
||||
}
|
||||
else if (value == null)
|
||||
toSet = null;
|
||||
else if (value.StartsWith("0x") && IsNumeric(type))
|
||||
try
|
||||
{
|
||||
toSet = Convert.ChangeType(Convert.ToUInt64(value.Substring(2), 16), type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "That is not properly formatted.";
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
toSet = Convert.ChangeType(value, type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "That is not properly formatted.";
|
||||
}
|
||||
|
||||
if (isSerial) // mutate back
|
||||
toSet = (Serial)(toSet ?? Serial.MinusOne);
|
||||
|
||||
constructed = toSet;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string SetDirect(Mobile from, object logObject, object obj, PropertyInfo prop, string givenName,
|
||||
object toSet, bool shouldLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (toSet is AccessLevel newLevel)
|
||||
{
|
||||
AccessLevel reqLevel = AccessLevel.Administrator;
|
||||
|
||||
if (newLevel == AccessLevel.Administrator)
|
||||
reqLevel = AccessLevel.Developer;
|
||||
else if (newLevel >= AccessLevel.Developer)
|
||||
reqLevel = AccessLevel.Owner;
|
||||
|
||||
if (from.AccessLevel < reqLevel)
|
||||
return "You do not have access to that level.";
|
||||
}
|
||||
|
||||
if (shouldLog)
|
||||
CommandLogging.LogChangeProperty(from, logObject, givenName,
|
||||
toSet == null ? "(-null-)" : toSet.ToString());
|
||||
|
||||
prop.SetValue(obj, toSet, null);
|
||||
return "Property has been set.";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "An exception was caught, the property may not be set.";
|
||||
}
|
||||
}
|
||||
|
||||
public static string SetDirect(object obj, PropertyInfo prop, object toSet)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (toSet is AccessLevel) return "You do not have access to that level.";
|
||||
|
||||
prop.SetValue(obj, toSet, null);
|
||||
return "Property has been set.";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "An exception was caught, the property may not be set.";
|
||||
}
|
||||
}
|
||||
|
||||
public static string InternalSetValue(Mobile from, object logobj, object o, PropertyInfo p, string pname,
|
||||
string value, bool shouldLog)
|
||||
{
|
||||
object toSet = null;
|
||||
string result = ConstructFromString(p.PropertyType, o, value, ref toSet);
|
||||
|
||||
return result ?? SetDirect(from, logobj, o, p, pname, toSet, shouldLog);
|
||||
}
|
||||
|
||||
public static string InternalSetValue(object o, PropertyInfo p, string value)
|
||||
{
|
||||
object toSet = null;
|
||||
string result = ConstructFromString(p.PropertyType, o, value, ref toSet);
|
||||
|
||||
return result ?? SetDirect(o, p, toSet);
|
||||
}
|
||||
|
||||
private class PropsTarget : Target
|
||||
{
|
||||
public PropsTarget() : base(-1, true, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (!BaseCommand.IsAccessible(from, o))
|
||||
from.SendLocalizedMessage(500447); // That is not accessible.
|
||||
else
|
||||
from.SendGump(new PropertiesGump(from, o));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public abstract class PropertyException : ApplicationException
|
||||
{
|
||||
protected Property m_Property;
|
||||
|
||||
public PropertyException(Property property, string message)
|
||||
: base(message)
|
||||
{
|
||||
m_Property = property;
|
||||
}
|
||||
|
||||
public Property Property => m_Property;
|
||||
}
|
||||
|
||||
public abstract class BindingException : PropertyException
|
||||
{
|
||||
public BindingException(Property property, string message)
|
||||
: base(property, message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NotYetBoundException : BindingException
|
||||
{
|
||||
public NotYetBoundException(Property property)
|
||||
: base(property, "Property has not yet been bound.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AlreadyBoundException : BindingException
|
||||
{
|
||||
public AlreadyBoundException(Property property)
|
||||
: base(property, "Property has already been bound.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UnknownPropertyException : BindingException
|
||||
{
|
||||
public UnknownPropertyException(Property property, string current)
|
||||
: base(property, $"Property '{current}' not found.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReadOnlyException : BindingException
|
||||
{
|
||||
public ReadOnlyException(Property property)
|
||||
: base(property, "Property is read-only.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WriteOnlyException : BindingException
|
||||
{
|
||||
public WriteOnlyException(Property property)
|
||||
: base(property, "Property is write-only.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AccessException : PropertyException
|
||||
{
|
||||
public AccessException(Property property, string message)
|
||||
: base(property, message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InternalAccessException : AccessException
|
||||
{
|
||||
public InternalAccessException(Property property)
|
||||
: base(property, "Property is internal.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ClearanceException : AccessException
|
||||
{
|
||||
public ClearanceException(Property property, AccessLevel playerAccess, AccessLevel neededAccess, string accessType)
|
||||
: base(property,
|
||||
$"You must be at least {Mobile.GetAccessLevelName(neededAccess)} to {accessType} this property.")
|
||||
{
|
||||
}
|
||||
|
||||
public AccessLevel PlayerAccess{ get; set; }
|
||||
public AccessLevel NeededAccess{ get; set; }
|
||||
}
|
||||
|
||||
public sealed class ReadAccessException : ClearanceException
|
||||
{
|
||||
public ReadAccessException(Property property, AccessLevel playerAccess, AccessLevel neededAccess)
|
||||
: base(property, playerAccess, neededAccess, "read")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WriteAccessException : ClearanceException
|
||||
{
|
||||
public WriteAccessException(Property property, AccessLevel playerAccess, AccessLevel neededAccess)
|
||||
: base(property, playerAccess, neededAccess, "write")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Property
|
||||
{
|
||||
private PropertyInfo[] m_Chain;
|
||||
|
||||
public Property(string binding)
|
||||
{
|
||||
Binding = binding;
|
||||
}
|
||||
|
||||
public Property(PropertyInfo[] chain)
|
||||
{
|
||||
m_Chain = chain;
|
||||
}
|
||||
|
||||
public string Binding{ get; }
|
||||
|
||||
public bool IsBound => m_Chain != null;
|
||||
|
||||
public PropertyAccess Access{ get; private set; }
|
||||
|
||||
public PropertyInfo[] Chain
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsBound)
|
||||
throw new NotYetBoundException(this);
|
||||
|
||||
return m_Chain;
|
||||
}
|
||||
}
|
||||
|
||||
public Type Type
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsBound)
|
||||
throw new NotYetBoundException(this);
|
||||
|
||||
return m_Chain[m_Chain.Length - 1].PropertyType;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckAccess(Mobile from)
|
||||
{
|
||||
if (!IsBound)
|
||||
throw new NotYetBoundException(this);
|
||||
|
||||
for (int i = 0; i < m_Chain.Length; ++i)
|
||||
{
|
||||
PropertyInfo prop = m_Chain[i];
|
||||
|
||||
bool isFinal = i == m_Chain.Length - 1;
|
||||
|
||||
PropertyAccess access = Access;
|
||||
|
||||
if (!isFinal)
|
||||
access |= PropertyAccess.Read;
|
||||
|
||||
CPA security = Properties.GetCPA(prop);
|
||||
|
||||
if (security == null)
|
||||
throw new InternalAccessException(this);
|
||||
|
||||
if ((access & PropertyAccess.Read) != 0 && from.AccessLevel < security.ReadLevel)
|
||||
throw new ReadAccessException(this, from.AccessLevel, security.ReadLevel);
|
||||
|
||||
if ((access & PropertyAccess.Write) != 0 && (from.AccessLevel < security.WriteLevel || security.ReadOnly))
|
||||
throw new WriteAccessException(this, from.AccessLevel, security.ReadLevel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void BindTo(Type objectType, PropertyAccess desiredAccess)
|
||||
{
|
||||
if (IsBound)
|
||||
throw new AlreadyBoundException(this);
|
||||
|
||||
string[] split = Binding.Split('.');
|
||||
|
||||
PropertyInfo[] chain = new PropertyInfo[split.Length];
|
||||
|
||||
for (int i = 0; i < split.Length; ++i)
|
||||
{
|
||||
bool isFinal = i == chain.Length - 1;
|
||||
|
||||
chain[i] = objectType.GetProperty(split[i],
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
|
||||
|
||||
if (chain[i] == null)
|
||||
throw new UnknownPropertyException(this, split[i]);
|
||||
|
||||
objectType = chain[i].PropertyType;
|
||||
|
||||
PropertyAccess access = desiredAccess;
|
||||
|
||||
if (!isFinal)
|
||||
access |= PropertyAccess.Read;
|
||||
|
||||
if ((access & PropertyAccess.Read) != 0 && !chain[i].CanRead)
|
||||
throw new WriteOnlyException(this);
|
||||
|
||||
if ((access & PropertyAccess.Write) != 0 && !chain[i].CanWrite)
|
||||
throw new ReadOnlyException(this);
|
||||
}
|
||||
|
||||
Access = desiredAccess;
|
||||
m_Chain = chain;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (!IsBound)
|
||||
return Binding;
|
||||
|
||||
string[] toJoin = new string[m_Chain.Length];
|
||||
|
||||
for (int i = 0; i < toJoin.Length; ++i)
|
||||
toJoin[i] = m_Chain[i].Name;
|
||||
|
||||
return string.Join(".", toJoin);
|
||||
}
|
||||
|
||||
public static Property Parse(Type type, string binding, PropertyAccess access)
|
||||
{
|
||||
Property prop = new Property(binding);
|
||||
|
||||
prop.BindTo(type, access);
|
||||
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Projects/Scripts/Commands/ShardTime.cs
Normal file
19
Projects/Scripts/Commands/ShardTime.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class ShardTime
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("Time", AccessLevel.Player, Time_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("Time")]
|
||||
[Description("Returns the server's local time.")]
|
||||
private static void Time_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.SendMessage(DateTime.UtcNow.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
148
Projects/Scripts/Commands/SignParser.cs
Normal file
148
Projects/Scripts/Commands/SignParser.cs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class SignParser
|
||||
{
|
||||
private static Queue<Item> m_ToDelete = new Queue<Item>();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("SignGen", AccessLevel.Administrator, SignGen_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("SignGen")]
|
||||
[Description("Generates world/shop signs on all facets.")]
|
||||
public static void SignGen_OnCommand(CommandEventArgs c)
|
||||
{
|
||||
Parse(c.Mobile);
|
||||
}
|
||||
|
||||
public static void Parse(Mobile from)
|
||||
{
|
||||
string cfg = Path.Combine(Core.BaseDirectory, "Data/signs.cfg");
|
||||
|
||||
if (File.Exists(cfg))
|
||||
{
|
||||
List<SignEntry> list = new List<SignEntry>();
|
||||
from.SendMessage("Generating signs, please wait.");
|
||||
|
||||
using (StreamReader ip = new StreamReader(cfg))
|
||||
{
|
||||
string line;
|
||||
|
||||
while ((line = ip.ReadLine()) != null)
|
||||
{
|
||||
string[] split = line.Split(' ');
|
||||
|
||||
SignEntry e = new SignEntry(
|
||||
line.Substring(split[0].Length + 1 + split[1].Length + 1 + split[2].Length + 1 +
|
||||
split[3].Length + 1 + split[4].Length + 1),
|
||||
new Point3D(Utility.ToInt32(split[2]), Utility.ToInt32(split[3]), Utility.ToInt32(split[4])),
|
||||
Utility.ToInt32(split[1]), Utility.ToInt32(split[0]));
|
||||
|
||||
list.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
Map[] brit = { Map.Felucca, Map.Trammel };
|
||||
Map[] fel = { Map.Felucca };
|
||||
Map[] tram = { Map.Trammel };
|
||||
Map[] ilsh = { Map.Ilshenar };
|
||||
Map[] malas = { Map.Malas };
|
||||
Map[] tokuno = { Map.Tokuno };
|
||||
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
{
|
||||
SignEntry e = list[i];
|
||||
Map[] maps = null;
|
||||
|
||||
switch (e.m_Map)
|
||||
{
|
||||
case 0:
|
||||
maps = brit;
|
||||
break; // Trammel and Felucca
|
||||
case 1:
|
||||
maps = fel;
|
||||
break; // Felucca
|
||||
case 2:
|
||||
maps = tram;
|
||||
break; // Trammel
|
||||
case 3:
|
||||
maps = ilsh;
|
||||
break; // Ilshenar
|
||||
case 4:
|
||||
maps = malas;
|
||||
break; // Malas
|
||||
case 5:
|
||||
maps = tokuno;
|
||||
break; // Tokuno Islands
|
||||
}
|
||||
|
||||
for (int j = 0; maps?.Length >= j; ++j)
|
||||
Add_Static(e.m_ItemID, e.m_Location, maps[j], e.m_Text);
|
||||
}
|
||||
|
||||
from.SendMessage("Sign generating complete.");
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("{0} not found!", cfg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Add_Static(int itemID, Point3D location, Map map, string name)
|
||||
{
|
||||
IPooledEnumerable<Item> eable = map.GetItemsInRange(location, 0);
|
||||
|
||||
foreach (Item item in eable)
|
||||
if (item is Sign && item.Z == location.Z && item.ItemID == itemID)
|
||||
m_ToDelete.Enqueue(item);
|
||||
|
||||
eable.Free();
|
||||
|
||||
while (m_ToDelete.Count > 0)
|
||||
m_ToDelete.Dequeue().Delete();
|
||||
|
||||
Item sign;
|
||||
|
||||
if (name.StartsWith("#"))
|
||||
{
|
||||
sign = new LocalizedSign(itemID, Utility.ToInt32(name.Substring(1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
sign = new Sign(itemID);
|
||||
sign.Name = name;
|
||||
}
|
||||
|
||||
if (map == Map.Malas)
|
||||
{
|
||||
if (location.X >= 965 && location.Y >= 502 && location.X <= 1012 && location.Y <= 537)
|
||||
sign.Hue = 0x47E;
|
||||
else if (location.X >= 1960 && location.Y >= 1278 && location.X < 2106 && location.Y < 1413)
|
||||
sign.Hue = 0x44E;
|
||||
}
|
||||
|
||||
sign.MoveToWorld(location, map);
|
||||
}
|
||||
|
||||
private class SignEntry
|
||||
{
|
||||
public int m_ItemID;
|
||||
public Point3D m_Location;
|
||||
public int m_Map;
|
||||
public string m_Text;
|
||||
|
||||
public SignEntry(string text, Point3D pt, int itemID, int mapLoc)
|
||||
{
|
||||
m_Text = text;
|
||||
m_Location = pt;
|
||||
m_ItemID = itemID;
|
||||
m_Map = mapLoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
Projects/Scripts/Commands/Skills.cs
Normal file
129
Projects/Scripts/Commands/Skills.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class SkillsCommand
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("SetSkill", AccessLevel.GameMaster, SetSkill_OnCommand);
|
||||
CommandSystem.Register("GetSkill", AccessLevel.GameMaster, GetSkill_OnCommand);
|
||||
CommandSystem.Register("SetAllSkills", AccessLevel.GameMaster, SetAllSkills_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("SetSkill <name> <value>")]
|
||||
[Description("Sets a skill value by name of a targeted mobile.")]
|
||||
public static void SetSkill_OnCommand(CommandEventArgs arg)
|
||||
{
|
||||
if (arg.Length != 2)
|
||||
{
|
||||
arg.Mobile.SendMessage("SetSkill <skill name> <value>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Enum.TryParse(arg.GetString(0), true, out SkillName skill))
|
||||
arg.Mobile.Target = new SkillTarget(skill, arg.GetDouble(1));
|
||||
else
|
||||
arg.Mobile.SendLocalizedMessage(1005631); // You have specified an invalid skill to set.
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("SetAllSkills <name> <value>")]
|
||||
[Description("Sets all skill values of a targeted mobile.")]
|
||||
public static void SetAllSkills_OnCommand(CommandEventArgs arg)
|
||||
{
|
||||
if (arg.Length != 1)
|
||||
arg.Mobile.SendMessage("SetAllSkills <value>");
|
||||
else
|
||||
arg.Mobile.Target = new AllSkillsTarget(arg.GetDouble(0));
|
||||
}
|
||||
|
||||
[Usage("GetSkill <name>")]
|
||||
[Description("Gets a skill value by name of a targeted mobile.")]
|
||||
public static void GetSkill_OnCommand(CommandEventArgs arg)
|
||||
{
|
||||
if (arg.Length != 1)
|
||||
{
|
||||
arg.Mobile.SendMessage("GetSkill <skill name>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Enum.TryParse(arg.GetString(0), true, out SkillName skill))
|
||||
arg.Mobile.Target = new SkillTarget(skill);
|
||||
else
|
||||
arg.Mobile.SendMessage("You have specified an invalid skill to get.");
|
||||
}
|
||||
}
|
||||
|
||||
public class AllSkillsTarget : Target
|
||||
{
|
||||
private double m_Value;
|
||||
|
||||
public AllSkillsTarget(double value) : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
m_Value = value;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile targ)
|
||||
{
|
||||
Server.Skills skills = targ.Skills;
|
||||
|
||||
for (int i = 0; i < skills.Length; ++i)
|
||||
skills[i].Base = m_Value;
|
||||
|
||||
CommandLogging.LogChangeProperty(from, targ, "EverySkill.Base", m_Value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("That does not have skills!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SkillTarget : Target
|
||||
{
|
||||
private bool m_Set;
|
||||
private SkillName m_Skill;
|
||||
private double m_Value;
|
||||
|
||||
public SkillTarget(SkillName skill, double value) : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
m_Set = true;
|
||||
m_Skill = skill;
|
||||
m_Value = value;
|
||||
}
|
||||
|
||||
public SkillTarget(SkillName skill) : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
m_Set = false;
|
||||
m_Skill = skill;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (targeted is Mobile targ)
|
||||
{
|
||||
Skill skill = targ.Skills[m_Skill];
|
||||
|
||||
if (skill == null)
|
||||
return;
|
||||
|
||||
if (m_Set)
|
||||
{
|
||||
skill.Base = m_Value;
|
||||
CommandLogging.LogChangeProperty(from, targ, $"{m_Skill}.Base", m_Value.ToString());
|
||||
}
|
||||
|
||||
from.SendMessage("{0} : {1} (Base: {2})", m_Skill, skill.Value, skill.Base);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("That does not have skills!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Projects/Scripts/Commands/SkillsMenu.cs
Normal file
38
Projects/Scripts/Commands/SkillsMenu.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using Server.Gumps;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Skills
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
Register();
|
||||
}
|
||||
|
||||
public static void Register()
|
||||
{
|
||||
CommandSystem.Register("Skills", AccessLevel.Counselor, Skills_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("Skills")]
|
||||
[Description("Opens a menu where you can view or edit skills of a targeted mobile.")]
|
||||
private static void Skills_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
e.Mobile.Target = new SkillsTarget();
|
||||
}
|
||||
|
||||
private class SkillsTarget : Target
|
||||
{
|
||||
public SkillsTarget() : base(-1, true, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (o is Mobile mobile)
|
||||
from.SendGump(new SkillsGump(from, mobile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
586
Projects/Scripts/Commands/Statics.cs
Normal file
586
Projects/Scripts/Commands/Statics.cs
Normal file
|
|
@ -0,0 +1,586 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Server.Commands;
|
||||
using Server.Gumps;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class Statics
|
||||
{
|
||||
private const string BaseFreezeWarning = "{0} " +
|
||||
"Those items <u>will be removed from the world</u> and placed into the server data files. " +
|
||||
"Other players <u>will not see the changes</u> unless you distribute your data files to them.<br><br>" +
|
||||
"This operation may not complete unless the server and client are using different data files. " +
|
||||
"If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files. " +
|
||||
"Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.<br><br>" +
|
||||
"The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible. " +
|
||||
"It is strongly recommended that you make backup of the data files mentioned above. " +
|
||||
"Do you wish to proceed?";
|
||||
|
||||
private const string BaseUnfreezeWarning = "{0} " +
|
||||
"Those items <u>will be removed from the static files</u> and exchanged with unmovable dynamic items. " +
|
||||
"Other players <u>will not see the changes</u> unless you distribute your data files to them.<br><br>" +
|
||||
"This operation may not complete unless the server and client are using different data files. " +
|
||||
"If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files. " +
|
||||
"Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.<br><br>" +
|
||||
"The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible. " +
|
||||
"It is strongly recommended that you make backup of the data files mentioned above. " +
|
||||
"Do you wish to proceed?";
|
||||
|
||||
private static Point3D NullP3D = new Point3D(int.MinValue, int.MinValue, int.MinValue);
|
||||
|
||||
private static byte[] m_Buffer;
|
||||
|
||||
private static StaticTile[] m_TileBuffer = new StaticTile[128];
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("Freeze", AccessLevel.Administrator, Freeze_OnCommand);
|
||||
CommandSystem.Register("FreezeMap", AccessLevel.Administrator, FreezeMap_OnCommand);
|
||||
CommandSystem.Register("FreezeWorld", AccessLevel.Administrator, FreezeWorld_OnCommand);
|
||||
|
||||
CommandSystem.Register("Unfreeze", AccessLevel.Administrator, Unfreeze_OnCommand);
|
||||
CommandSystem.Register("UnfreezeMap", AccessLevel.Administrator, UnfreezeMap_OnCommand);
|
||||
CommandSystem.Register("UnfreezeWorld", AccessLevel.Administrator, UnfreezeWorld_OnCommand);
|
||||
}
|
||||
|
||||
public delegate void FreezeCallback( Mobile from, bool okay, StateInfo si );
|
||||
|
||||
[Usage("Freeze")]
|
||||
[Description("Makes a targeted area of dynamic items static.")]
|
||||
public static void Freeze_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
BoundingBoxPicker.Begin(from, (map, start, end) => FreezeBox_Callback(from, map, start, end));
|
||||
}
|
||||
|
||||
[Usage("FreezeMap")]
|
||||
[Description("Makes every dynamic item in your map static.")]
|
||||
public static void FreezeMap_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
Map map = from.Map;
|
||||
|
||||
if (map != null && map != Map.Internal)
|
||||
SendWarning(from, "You are about to freeze <u>all items in {0}</u>.", BaseFreezeWarning, map, NullP3D,
|
||||
NullP3D, FreezeWarning_Callback);
|
||||
}
|
||||
|
||||
[Usage("FreezeWorld")]
|
||||
[Description("Makes every dynamic item on all maps static.")]
|
||||
public static void FreezeWorld_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
SendWarning(e.Mobile, "You are about to freeze <u>every item on every map</u>.", BaseFreezeWarning, null,
|
||||
NullP3D, NullP3D, FreezeWarning_Callback);
|
||||
}
|
||||
|
||||
public static void SendWarning(Mobile m, string header, string baseWarning, Map map, Point3D start, Point3D end,
|
||||
FreezeCallback callback)
|
||||
{
|
||||
m.SendGump(new WarningGump(1060635, 30720, string.Format(baseWarning, string.Format(header, map)), 0xFFC000, 420,
|
||||
400, okay => callback(m, okay, new StateInfo(map, start, end))));
|
||||
}
|
||||
|
||||
private static void FreezeBox_Callback(Mobile from, Map map, Point3D start, Point3D end)
|
||||
{
|
||||
SendWarning(from, "You are about to freeze a section of items.", BaseFreezeWarning, map, start, end,
|
||||
FreezeWarning_Callback);
|
||||
}
|
||||
|
||||
private static void FreezeWarning_Callback(Mobile from, bool okay, StateInfo si)
|
||||
{
|
||||
if (!okay)
|
||||
return;
|
||||
|
||||
Freeze(from, si.m_Map, si.m_Start, si.m_End);
|
||||
}
|
||||
|
||||
public static void Freeze(Mobile from, Map targetMap, Point3D start3d, Point3D end3d)
|
||||
{
|
||||
Dictionary<Map, Dictionary<Point2D, DeltaState>> mapTable = new Dictionary<Map, Dictionary<Point2D, DeltaState>>();
|
||||
|
||||
if (start3d == NullP3D && end3d == NullP3D)
|
||||
{
|
||||
if (targetMap == null)
|
||||
CommandLogging.WriteLine(from, "{0} {1} invoking freeze for every item in every map", from.AccessLevel,
|
||||
CommandLogging.Format(from));
|
||||
else
|
||||
CommandLogging.WriteLine(from, "{0} {1} invoking freeze for every item in {0}", from.AccessLevel,
|
||||
CommandLogging.Format(from), targetMap);
|
||||
|
||||
foreach (Item item in World.Items.Values)
|
||||
{
|
||||
if (targetMap != null && item.Map != targetMap)
|
||||
continue;
|
||||
|
||||
if (item.Parent != null)
|
||||
continue;
|
||||
|
||||
if (item is Static || item is BaseFloor || item is BaseWall)
|
||||
{
|
||||
Map itemMap = item.Map;
|
||||
|
||||
if (itemMap == null || itemMap == Map.Internal)
|
||||
continue;
|
||||
|
||||
if (!mapTable.TryGetValue(itemMap, out Dictionary<Point2D, DeltaState> table))
|
||||
mapTable[itemMap] = table = new Dictionary<Point2D, DeltaState>();
|
||||
|
||||
Point2D p = new Point2D(item.X >> 3, item.Y >> 3);
|
||||
|
||||
if (!table.TryGetValue(p, out DeltaState state))
|
||||
table[p] = state = new DeltaState(p);
|
||||
|
||||
state.m_List.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (targetMap != null)
|
||||
{
|
||||
Point2D start = targetMap.Bound(new Point2D(start3d)), end = targetMap.Bound(new Point2D(end3d));
|
||||
|
||||
CommandLogging.WriteLine(from, "{0} {1} invoking freeze from {2} to {3} in {4}", from.AccessLevel,
|
||||
CommandLogging.Format(from), start, end, targetMap);
|
||||
|
||||
IPooledEnumerable<Item> eable =
|
||||
targetMap.GetItemsInBounds(new Rectangle2D(start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1));
|
||||
|
||||
foreach (Item item in eable)
|
||||
if (item is Static || item is BaseFloor || item is BaseWall)
|
||||
{
|
||||
Map itemMap = item.Map;
|
||||
|
||||
if (itemMap == null || itemMap == Map.Internal)
|
||||
continue;
|
||||
|
||||
if (!mapTable.TryGetValue(itemMap, out Dictionary<Point2D, DeltaState> table))
|
||||
mapTable[itemMap] = table = new Dictionary<Point2D, DeltaState>();
|
||||
|
||||
Point2D p = new Point2D(item.X >> 3, item.Y >> 3);
|
||||
|
||||
if (!table.TryGetValue(p, out DeltaState state))
|
||||
table[p] = state = new DeltaState(p);
|
||||
|
||||
state.m_List.Add(item);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
if (mapTable.Count == 0)
|
||||
{
|
||||
from.SendGump(new NoticeGump(1060637, 30720,
|
||||
"No freezable items were found. Only the following item types are frozen:<br> - Static<br> - BaseFloor<br> - BaseWall",
|
||||
0xFFC000, 320, 240));
|
||||
return;
|
||||
}
|
||||
|
||||
bool badDataFile = false;
|
||||
|
||||
int totalFrozen = 0;
|
||||
|
||||
foreach (KeyValuePair<Map, Dictionary<Point2D, DeltaState>> de in mapTable)
|
||||
{
|
||||
Map map = de.Key;
|
||||
Dictionary<Point2D, DeltaState> table = de.Value;
|
||||
|
||||
TileMatrix matrix = map.Tiles;
|
||||
|
||||
using (FileStream idxStream = OpenWrite(matrix.IndexStream))
|
||||
{
|
||||
using (FileStream mulStream = OpenWrite(matrix.DataStream))
|
||||
{
|
||||
if (idxStream == null || mulStream == null)
|
||||
{
|
||||
badDataFile = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
BinaryReader idxReader = new BinaryReader(idxStream);
|
||||
|
||||
BinaryWriter idxWriter = new BinaryWriter(idxStream);
|
||||
BinaryWriter mulWriter = new BinaryWriter(mulStream);
|
||||
|
||||
foreach (DeltaState state in table.Values)
|
||||
{
|
||||
StaticTile[] oldTiles = ReadStaticBlock(idxReader, mulStream, state.m_X, state.m_Y,
|
||||
matrix.BlockWidth, matrix.BlockHeight, out int oldTileCount);
|
||||
|
||||
if (oldTileCount < 0)
|
||||
continue;
|
||||
|
||||
int newTileCount = 0;
|
||||
StaticTile[] newTiles = new StaticTile[state.m_List.Count];
|
||||
|
||||
for (int i = 0; i < state.m_List.Count; ++i)
|
||||
{
|
||||
Item item = state.m_List[i];
|
||||
|
||||
int xOffset = item.X - state.m_X * 8;
|
||||
int yOffset = item.Y - state.m_Y * 8;
|
||||
|
||||
if (xOffset < 0 || xOffset >= 8 || yOffset < 0 || yOffset >= 8)
|
||||
continue;
|
||||
|
||||
StaticTile newTile = new StaticTile((ushort)item.ItemID, (byte)xOffset, (byte)yOffset,
|
||||
(sbyte)item.Z, (short)item.Hue);
|
||||
|
||||
newTiles[newTileCount++] = newTile;
|
||||
|
||||
item.Delete();
|
||||
|
||||
++totalFrozen;
|
||||
}
|
||||
|
||||
int mulPos = -1;
|
||||
int length = -1;
|
||||
int extra = 0;
|
||||
|
||||
if (oldTileCount + newTileCount > 0)
|
||||
{
|
||||
mulWriter.Seek(0, SeekOrigin.End);
|
||||
|
||||
mulPos = (int)mulWriter.BaseStream.Position;
|
||||
length = (oldTileCount + newTileCount) * 7;
|
||||
extra = 1;
|
||||
|
||||
for (int i = 0; i < oldTileCount; ++i)
|
||||
{
|
||||
StaticTile toWrite = oldTiles[i];
|
||||
|
||||
mulWriter.Write((ushort)toWrite.ID);
|
||||
mulWriter.Write((byte)toWrite.X);
|
||||
mulWriter.Write((byte)toWrite.Y);
|
||||
mulWriter.Write((sbyte)toWrite.Z);
|
||||
mulWriter.Write((short)toWrite.Hue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < newTileCount; ++i)
|
||||
{
|
||||
StaticTile toWrite = newTiles[i];
|
||||
|
||||
mulWriter.Write((ushort)toWrite.ID);
|
||||
mulWriter.Write((byte)toWrite.X);
|
||||
mulWriter.Write((byte)toWrite.Y);
|
||||
mulWriter.Write((sbyte)toWrite.Z);
|
||||
mulWriter.Write((short)toWrite.Hue);
|
||||
}
|
||||
|
||||
mulWriter.Flush();
|
||||
}
|
||||
|
||||
int idxPos = (state.m_X * matrix.BlockHeight + state.m_Y) * 12;
|
||||
|
||||
idxWriter.Seek(idxPos, SeekOrigin.Begin);
|
||||
idxWriter.Write(mulPos);
|
||||
idxWriter.Write(length);
|
||||
idxWriter.Write(extra);
|
||||
|
||||
idxWriter.Flush();
|
||||
|
||||
matrix.SetStaticBlock(state.m_X, state.m_Y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalFrozen == 0 && badDataFile)
|
||||
from.SendGump(new NoticeGump(1060637, 30720,
|
||||
"Output data files could not be opened and the freeze operation has been aborted.<br><br>This probably means your server and client are using the same data files. Instructions on how to resolve this can be found in the first warning window.",
|
||||
0xFFC000, 320, 240));
|
||||
else
|
||||
from.SendGump(new NoticeGump(1060637, 30720,
|
||||
$"Freeze operation completed successfully.<br><br>{totalFrozen} item{(totalFrozen != 1 ? "s were" : " was")} frozen.<br><br>You must restart your client and update it's data files to see the changes.",
|
||||
0xFFC000, 320, 240));
|
||||
}
|
||||
|
||||
[Usage("Unfreeze")]
|
||||
[Description("Makes a targeted area of static items dynamic.")]
|
||||
public static void Unfreeze_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Mobile from = e.Mobile;
|
||||
BoundingBoxPicker.Begin(from, (map, start, end) => UnfreezeBox_Callback(from, map, start, end));
|
||||
}
|
||||
|
||||
[Usage("UnfreezeMap")]
|
||||
[Description("Makes every static item in your map dynamic.")]
|
||||
public static void UnfreezeMap_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
Map map = e.Mobile.Map;
|
||||
|
||||
if (map != null && map != Map.Internal)
|
||||
SendWarning(e.Mobile, "You are about to unfreeze <u>all items in {0}</u>.", BaseUnfreezeWarning, map,
|
||||
NullP3D, NullP3D, UnfreezeWarning_Callback);
|
||||
}
|
||||
|
||||
[Usage("UnfreezeWorld")]
|
||||
[Description("Makes every static item on all maps dynamic.")]
|
||||
public static void UnfreezeWorld_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
SendWarning(e.Mobile, "You are about to unfreeze <u>every item on every map</u>.", BaseUnfreezeWarning, null,
|
||||
NullP3D, NullP3D, UnfreezeWarning_Callback);
|
||||
}
|
||||
|
||||
private static void UnfreezeBox_Callback(Mobile from, Map map, Point3D start, Point3D end)
|
||||
{
|
||||
SendWarning(from, "You are about to unfreeze a section of items.", BaseUnfreezeWarning, map, start, end,
|
||||
UnfreezeWarning_Callback);
|
||||
}
|
||||
|
||||
private static void UnfreezeWarning_Callback(Mobile from, bool okay, StateInfo si)
|
||||
{
|
||||
if (!okay)
|
||||
return;
|
||||
|
||||
Unfreeze(from, si.m_Map, si.m_Start, si.m_End);
|
||||
}
|
||||
|
||||
private static void DoUnfreeze(Map map, Point2D start, Point2D end, ref bool badDataFile, ref int totalUnfrozen)
|
||||
{
|
||||
start = map.Bound(start);
|
||||
end = map.Bound(end);
|
||||
|
||||
int xStartBlock = start.X >> 3;
|
||||
int yStartBlock = start.Y >> 3;
|
||||
int xEndBlock = end.X >> 3;
|
||||
int yEndBlock = end.Y >> 3;
|
||||
|
||||
int xTileStart = start.X, yTileStart = start.Y;
|
||||
int xTileWidth = end.X - start.X + 1, yTileHeight = end.Y - start.Y + 1;
|
||||
|
||||
TileMatrix matrix = map.Tiles;
|
||||
|
||||
using (FileStream idxStream = OpenWrite(matrix.IndexStream))
|
||||
{
|
||||
using (FileStream mulStream = OpenWrite(matrix.DataStream))
|
||||
{
|
||||
if (idxStream == null || mulStream == null)
|
||||
{
|
||||
badDataFile = true;
|
||||
return;
|
||||
}
|
||||
|
||||
BinaryReader idxReader = new BinaryReader(idxStream);
|
||||
|
||||
BinaryWriter idxWriter = new BinaryWriter(idxStream);
|
||||
BinaryWriter mulWriter = new BinaryWriter(mulStream);
|
||||
|
||||
for (int x = xStartBlock; x <= xEndBlock; ++x)
|
||||
for (int y = yStartBlock; y <= yEndBlock; ++y)
|
||||
{
|
||||
StaticTile[] oldTiles = ReadStaticBlock(idxReader, mulStream, x, y, matrix.BlockWidth,
|
||||
matrix.BlockHeight, out int oldTileCount);
|
||||
|
||||
if (oldTileCount < 0)
|
||||
continue;
|
||||
|
||||
int newTileCount = 0;
|
||||
StaticTile[] newTiles = new StaticTile[oldTileCount];
|
||||
|
||||
int baseX = (x << 3) - xTileStart, baseY = (y << 3) - yTileStart;
|
||||
|
||||
for (int i = 0; i < oldTileCount; ++i)
|
||||
{
|
||||
StaticTile oldTile = oldTiles[i];
|
||||
|
||||
int px = baseX + oldTile.X;
|
||||
int py = baseY + oldTile.Y;
|
||||
|
||||
if (px < 0 || px >= xTileWidth || py < 0 || py >= yTileHeight)
|
||||
{
|
||||
newTiles[newTileCount++] = oldTile;
|
||||
}
|
||||
else
|
||||
{
|
||||
++totalUnfrozen;
|
||||
|
||||
Item item = new Static(oldTile.ID);
|
||||
|
||||
item.Hue = oldTile.Hue;
|
||||
|
||||
item.MoveToWorld(new Point3D(px + xTileStart, py + yTileStart, oldTile.Z), map);
|
||||
}
|
||||
}
|
||||
|
||||
int mulPos = -1;
|
||||
int length = -1;
|
||||
int extra = 0;
|
||||
|
||||
if (newTileCount > 0)
|
||||
{
|
||||
mulWriter.Seek(0, SeekOrigin.End);
|
||||
|
||||
mulPos = (int)mulWriter.BaseStream.Position;
|
||||
length = newTileCount * 7;
|
||||
extra = 1;
|
||||
|
||||
for (int i = 0; i < newTileCount; ++i)
|
||||
{
|
||||
StaticTile toWrite = newTiles[i];
|
||||
|
||||
mulWriter.Write((ushort)toWrite.ID);
|
||||
mulWriter.Write((byte)toWrite.X);
|
||||
mulWriter.Write((byte)toWrite.Y);
|
||||
mulWriter.Write((sbyte)toWrite.Z);
|
||||
mulWriter.Write((short)toWrite.Hue);
|
||||
}
|
||||
|
||||
mulWriter.Flush();
|
||||
}
|
||||
|
||||
int idxPos = (x * matrix.BlockHeight + y) * 12;
|
||||
|
||||
idxWriter.Seek(idxPos, SeekOrigin.Begin);
|
||||
idxWriter.Write(mulPos);
|
||||
idxWriter.Write(length);
|
||||
idxWriter.Write(extra);
|
||||
|
||||
idxWriter.Flush();
|
||||
|
||||
matrix.SetStaticBlock(x, y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DoUnfreeze(Map map, ref bool badDataFile, ref int totalUnfrozen)
|
||||
{
|
||||
DoUnfreeze(map, Point2D.Zero, new Point2D(map.Width - 1, map.Height - 1), ref badDataFile, ref totalUnfrozen);
|
||||
}
|
||||
|
||||
public static void Unfreeze(Mobile from, Map map, Point3D start, Point3D end)
|
||||
{
|
||||
int totalUnfrozen = 0;
|
||||
bool badDataFile = false;
|
||||
|
||||
if (map == null)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} invoking unfreeze for every item in every map", from.AccessLevel,
|
||||
CommandLogging.Format(from));
|
||||
|
||||
DoUnfreeze(Map.Felucca, ref badDataFile, ref totalUnfrozen);
|
||||
DoUnfreeze(Map.Trammel, ref badDataFile, ref totalUnfrozen);
|
||||
DoUnfreeze(Map.Ilshenar, ref badDataFile, ref totalUnfrozen);
|
||||
DoUnfreeze(Map.Malas, ref badDataFile, ref totalUnfrozen);
|
||||
DoUnfreeze(Map.Tokuno, ref badDataFile, ref totalUnfrozen);
|
||||
}
|
||||
else if (start == NullP3D && end == NullP3D)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} invoking unfreeze for every item in {2}", from.AccessLevel,
|
||||
CommandLogging.Format(from), map);
|
||||
|
||||
DoUnfreeze(map, ref badDataFile, ref totalUnfrozen);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} invoking unfreeze from {2} to {3} in {4}", from.AccessLevel,
|
||||
CommandLogging.Format(from), new Point2D(start), new Point2D(end), map);
|
||||
|
||||
DoUnfreeze(map, new Point2D(start), new Point2D(end), ref badDataFile, ref totalUnfrozen);
|
||||
}
|
||||
|
||||
if (totalUnfrozen == 0 && badDataFile)
|
||||
from.SendGump(new NoticeGump(1060637, 30720,
|
||||
"Output data files could not be opened and the unfreeze operation has been aborted.<br><br>This probably means your server and client are using the same data files. Instructions on how to resolve this can be found in the first warning window.",
|
||||
0xFFC000, 320, 240));
|
||||
else
|
||||
from.SendGump(new NoticeGump(1060637, 30720,
|
||||
$"Unfreeze operation completed successfully.<br><br>{totalUnfrozen} item{(totalUnfrozen != 1 ? "s were" : " was")} unfrozen.<br><br>You must restart your client and update it's data files to see the changes.",
|
||||
0xFFC000, 320, 240));
|
||||
}
|
||||
|
||||
private static FileStream OpenWrite(FileStream orig)
|
||||
{
|
||||
if (orig == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return new FileStream(orig.Name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static StaticTile[] ReadStaticBlock(BinaryReader idxReader, FileStream mulStream, int x, int y, int width,
|
||||
int height, out int count)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (x < 0 || x >= width || y < 0 || y >= height)
|
||||
{
|
||||
count = -1;
|
||||
return m_TileBuffer;
|
||||
}
|
||||
|
||||
idxReader.BaseStream.Seek((x * height + y) * 12, SeekOrigin.Begin);
|
||||
|
||||
int lookup = idxReader.ReadInt32();
|
||||
int length = idxReader.ReadInt32();
|
||||
|
||||
if (lookup < 0 || length <= 0)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = length / 7;
|
||||
|
||||
mulStream.Seek(lookup, SeekOrigin.Begin);
|
||||
|
||||
if (m_TileBuffer.Length < count)
|
||||
m_TileBuffer = new StaticTile[count];
|
||||
|
||||
StaticTile[] staTiles = m_TileBuffer;
|
||||
|
||||
if (m_Buffer == null || length > m_Buffer.Length)
|
||||
m_Buffer = new byte[length];
|
||||
|
||||
mulStream.Read(m_Buffer, 0, length);
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
staTiles[i].Set((ushort)(m_Buffer[index++] | (m_Buffer[index++] << 8)),
|
||||
m_Buffer[index++], m_Buffer[index++], (sbyte)m_Buffer[index++],
|
||||
(short)(m_Buffer[index++] | (m_Buffer[index++] << 8)));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
count = -1;
|
||||
}
|
||||
|
||||
return m_TileBuffer;
|
||||
}
|
||||
|
||||
private class DeltaState
|
||||
{
|
||||
public List<Item> m_List;
|
||||
public int m_X, m_Y;
|
||||
|
||||
public DeltaState(Point2D p)
|
||||
{
|
||||
m_X = p.X;
|
||||
m_Y = p.Y;
|
||||
m_List = new List<Item>();
|
||||
}
|
||||
}
|
||||
|
||||
public class StateInfo
|
||||
{
|
||||
public Map m_Map;
|
||||
public Point3D m_Start, m_End;
|
||||
|
||||
public StateInfo(Map map, Point3D start, Point3D end)
|
||||
{
|
||||
m_Map = map;
|
||||
m_Start = start;
|
||||
m_End = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
Projects/Scripts/Commands/VisibilityList.cs
Normal file
141
Projects/Scripts/Commands/VisibilityList.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using System.Collections.Generic;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class VisibilityList
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.Login += OnLogin;
|
||||
|
||||
CommandSystem.Register("Vis", AccessLevel.Counselor, Vis_OnCommand);
|
||||
CommandSystem.Register("VisList", AccessLevel.Counselor, VisList_OnCommand);
|
||||
CommandSystem.Register("VisClear", AccessLevel.Counselor, VisClear_OnCommand);
|
||||
}
|
||||
|
||||
public static void OnLogin(LoginEventArgs e)
|
||||
{
|
||||
if (e.Mobile is PlayerMobile pm) pm.VisibilityList.Clear();
|
||||
}
|
||||
|
||||
[Usage("Vis")]
|
||||
[Description(
|
||||
"Adds or removes a targeted player from your visibility list. Anyone on your visibility list will be able to see you at all times, even when you're hidden.")]
|
||||
public static void Vis_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Mobile is PlayerMobile)
|
||||
{
|
||||
e.Mobile.Target = new VisTarget();
|
||||
e.Mobile.SendMessage("Select person to add or remove from your visibility list.");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("VisList")]
|
||||
[Description("Shows the names of everyone in your visibility list.")]
|
||||
public static void VisList_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Mobile is PlayerMobile pm)
|
||||
{
|
||||
List<Mobile> list = pm.VisibilityList;
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
pm.SendMessage("You are visible to {0} mobile{1}:", list.Count, list.Count == 1 ? "" : "s");
|
||||
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
pm.SendMessage("#{0}: {1}", i + 1, list[i].Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
pm.SendMessage("Your visibility list is empty.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("VisClear")]
|
||||
[Description("Removes everyone from your visibility list.")]
|
||||
public static void VisClear_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Mobile is PlayerMobile pm)
|
||||
{
|
||||
List<Mobile> list = new List<Mobile>(pm.VisibilityList);
|
||||
|
||||
pm.VisibilityList.Clear();
|
||||
pm.SendMessage("Your visibility list has been cleared.");
|
||||
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
{
|
||||
Mobile m = list[i];
|
||||
|
||||
if (!m.CanSee(pm) && Utility.InUpdateRange(m, pm))
|
||||
m.Send(pm.RemovePacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class VisTarget : Target
|
||||
{
|
||||
public VisTarget() : base(-1, false, TargetFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object targeted)
|
||||
{
|
||||
if (from is PlayerMobile pm && targeted is Mobile targ)
|
||||
{
|
||||
if (targ.AccessLevel <= pm.AccessLevel)
|
||||
{
|
||||
List<Mobile> list = pm.VisibilityList;
|
||||
|
||||
if (list.Contains(targ))
|
||||
{
|
||||
list.Remove(targ);
|
||||
pm.SendMessage("{0} has been removed from your visibility list.", targ.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(targ);
|
||||
pm.SendMessage("{0} has been added to your visibility list.", targ.Name);
|
||||
}
|
||||
|
||||
if (Utility.InUpdateRange(targ, from))
|
||||
{
|
||||
NetState ns = targ.NetState;
|
||||
|
||||
if (ns != null)
|
||||
{
|
||||
if (targ.CanSee(pm))
|
||||
{
|
||||
ns.Send(MobileIncoming.Create(ns, targ, pm));
|
||||
|
||||
if (ObjectPropertyList.Enabled)
|
||||
{
|
||||
ns.Send(pm.OPLPacket);
|
||||
|
||||
foreach (Item item in pm.Items)
|
||||
ns.Send(item.OPLPacket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ns.Send(pm.RemovePacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pm.SendMessage("They can already see you!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage("Add only mobiles to your visibility list.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Projects/Scripts/Commands/Wipe.cs
Normal file
94
Projects/Scripts/Commands/Wipe.cs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
using Server.Multis;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public class Wipe
|
||||
{
|
||||
[Flags]
|
||||
public enum WipeType
|
||||
{
|
||||
Items = 0x01,
|
||||
Mobiles = 0x02,
|
||||
Multis = 0x04,
|
||||
All = Items | Mobiles | Multis
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandSystem.Register("Wipe", AccessLevel.GameMaster, WipeAll_OnCommand);
|
||||
CommandSystem.Register("WipeItems", AccessLevel.GameMaster, WipeItems_OnCommand);
|
||||
CommandSystem.Register("WipeNPCs", AccessLevel.GameMaster, WipeNPCs_OnCommand);
|
||||
CommandSystem.Register("WipeMultis", AccessLevel.GameMaster, WipeMultis_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("Wipe")]
|
||||
[Description("Wipes all items and npcs in a targeted bounding box.")]
|
||||
private static void WipeAll_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
BeginWipe(e.Mobile, WipeType.Items | WipeType.Mobiles);
|
||||
}
|
||||
|
||||
[Usage("WipeItems")]
|
||||
[Description("Wipes all items in a targeted bounding box.")]
|
||||
private static void WipeItems_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
BeginWipe(e.Mobile, WipeType.Items);
|
||||
}
|
||||
|
||||
[Usage("WipeNPCs")]
|
||||
[Description("Wipes all npcs in a targeted bounding box.")]
|
||||
private static void WipeNPCs_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
BeginWipe(e.Mobile, WipeType.Mobiles);
|
||||
}
|
||||
|
||||
[Usage("WipeMultis")]
|
||||
[Description("Wipes all multis in a targeted bounding box.")]
|
||||
private static void WipeMultis_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
BeginWipe(e.Mobile, WipeType.Multis);
|
||||
}
|
||||
|
||||
public static void BeginWipe(Mobile from, WipeType type)
|
||||
{
|
||||
BoundingBoxPicker.Begin(from, (map, start, end) => DoWipe(from, map, start, end, type));
|
||||
}
|
||||
|
||||
public static void DoWipe(Mobile from, Map map, Point3D start, Point3D end, WipeType type)
|
||||
{
|
||||
CommandLogging.WriteLine(from, "{0} {1} wiping from {2} to {3} in {5} ({4})", from.AccessLevel,
|
||||
CommandLogging.Format(from), start, end, type, map);
|
||||
|
||||
bool mobiles = (type & WipeType.Mobiles) != 0;
|
||||
bool multis = (type & WipeType.Multis) != 0;
|
||||
bool items = (type & WipeType.Items) != 0;
|
||||
|
||||
List<IEntity> toDelete = new List<IEntity>();
|
||||
|
||||
Rectangle2D rect = new Rectangle2D(start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1);
|
||||
|
||||
IPooledEnumerable<IEntity> eable;
|
||||
|
||||
if (!items && !multis || !mobiles)
|
||||
return;
|
||||
|
||||
eable = map.GetObjectsInBounds(rect);
|
||||
|
||||
foreach (IEntity obj in eable)
|
||||
if (items && obj is Item && !(obj is BaseMulti || obj is HouseSign))
|
||||
toDelete.Add(obj);
|
||||
else if (multis && obj is BaseMulti)
|
||||
toDelete.Add(obj);
|
||||
else if (obj is Mobile mobile && !mobile.Player)
|
||||
toDelete.Add(mobile);
|
||||
|
||||
eable.Free();
|
||||
|
||||
for (int i = 0; i < toDelete.Count; ++i)
|
||||
toDelete[i].Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue