Compare commits

...
Sign in to create a new pull request.

8 commits

Author SHA1 Message Date
Kamron Batman
3882fbe599
use var 2024-02-12 19:14:16 -08:00
Kamron Batman
8f20ea34c4
Revert change 2024-02-12 19:12:41 -08:00
Kamron Batman
a4040ae7bf
Paring down more 2024-02-12 19:11:02 -08:00
Voxpire
2c334d1a1f
XmlSpawner housekeeping. 2024-02-12 18:15:13 -08:00
Voxpire
a823299a7c
XmlSpawner housekeeping. 2024-02-12 18:15:12 -08:00
Voxpire
c60c3b62ad
BaseXmlSpawner housekeeping. 2024-02-12 18:15:12 -08:00
Kamron Batman
d984df1c16
Fixes SendMessages 2024-02-12 18:15:12 -08:00
Kamron Batman
13c3e52639
Adds xmlspawner 2024-02-12 18:15:04 -08:00
24 changed files with 22898 additions and 4 deletions

View file

@ -255,7 +255,7 @@ public class GenericEntityPersistence<T> : Persistence, IGenericEntityPersistenc
try try
{ {
using var op = new StreamWriter("world-save-errors.log", true); using var op = new StreamWriter("world-save-errors.log", true);
op.WriteLine("{0}\t{1}", DateTime.UtcNow, message); op.WriteLine("{0}\t{1}", Core.Now, message);
op.WriteLine(new StackTrace(2).ToString()); op.WriteLine(new StackTrace(2).ToString());
op.WriteLine(); op.WriteLine();
} }

View file

@ -51,7 +51,7 @@ public interface IGenericReader
{ {
long.MinValue => DateTime.MinValue, long.MinValue => DateTime.MinValue,
long.MaxValue => DateTime.MaxValue, long.MaxValue => DateTime.MaxValue,
var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc) var delta => new DateTime(delta + Core.Now.Ticks, DateTimeKind.Utc)
}; };
} }
decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() }); decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() });

View file

@ -70,7 +70,7 @@ public interface IGenericWriter
} }
// Technically supports negative deltas for times in the past // Technically supports negative deltas for times in the past
Write(value.Ticks - DateTime.UtcNow.Ticks); Write(value.Ticks - Core.Now.Ticks);
} }
void Write(IPAddress value) void Write(IPAddress value)
{ {

View file

@ -119,7 +119,7 @@ public static class EntityPersistence
return map; return map;
} }
var now = DateTime.UtcNow; var now = Core.Now;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
using System;
using System.IO;
namespace Server.Diagnostics;
// TODO: Replace this with serilog
public class ExceptionLogging
{
public static string LogDirectory { get; set; }
private static StreamWriter _Output;
public static StreamWriter Output
{
get
{
if (_Output == null)
{
_Output = new StreamWriter(Path.Combine(LogDirectory, $"{Core.Now.ToLongDateString()}.log"), true)
{
AutoFlush = true
};
_Output.WriteLine("##############################");
_Output.WriteLine("Exception log started on {0}", Core.Now);
_Output.WriteLine();
}
return _Output;
}
}
static ExceptionLogging()
{
var directory = Path.Combine(Core.BaseDirectory, "Logs/Exceptions");
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
LogDirectory = directory;
}
public static void LogException(Exception e)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Caught Exception:");
Utility.PopColor();
Utility.PushColor(ConsoleColor.DarkRed);
Console.WriteLine(e);
Utility.PopColor();
Output.WriteLine("Exception Caught: {0}", Core.Now);
Output.WriteLine(e);
Output.WriteLine();
}
public static void LogException(Exception e, string arg)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Caught Exception: {0}", arg);
Utility.PopColor();
Utility.PushColor(ConsoleColor.DarkRed);
Console.WriteLine(e);
Utility.PopColor();
Output.WriteLine("Exception Caught: {0}", Core.Now);
Output.WriteLine(e);
Output.WriteLine();
}
}

View file

@ -0,0 +1,134 @@
using System;
using Server.Targeting;
namespace Server.Items;
public partial class ItemFlags
{
private const int StealableFlag = 0x00200000;
private const int TakenFlag = 0x00100000;
public static void SetStealable(Item target, bool value)
{
target?.SetSavedFlag(StealableFlag, value);
}
public static bool GetStealable(Item target) => target != null && target.GetSavedFlag(StealableFlag);
public static void SetTaken(Item target, bool value)
{
target?.SetSavedFlag(TakenFlag, value);
}
public static bool GetTaken(Item target) => target != null && target.GetSavedFlag(TakenFlag);
[Usage("Flag flagfield")]
[Description("Gets the state of the specified SavedFlag on any item")]
public static void GetFlag_OnCommand(CommandEventArgs e)
{
var flag=0;
var error = false;
if (e.Arguments.Length > 0)
{
if (e.Arguments[0].StartsWith("0x"))
{
try{flag = Convert.ToInt32(e.Arguments[0].Substring(2), 16); } catch { error = true;}
} else
{
try{flag = int.Parse(e.Arguments[0]); } catch { error = true;}
}
}
if (!error)
{
e.Mobile.Target = new GetFlagTarget(e,flag);
} else
{
try{
e.Mobile.SendMessage(33,"Flag: Bad flagfield argument");
} catch {}
}
}
private class GetFlagTarget : Target
{
private CommandEventArgs m_e;
private int m_flag;
public GetFlagTarget(CommandEventArgs e, int flag) : base (30, false, TargetFlags.None)
{
m_e = e;
m_flag = flag;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (targeted is Item item)
{
var state = item.GetSavedFlag(m_flag);
from.SendMessage($"Flag (0x{m_flag:X}) = {state}");
} else
{
from.SendMessage("Must target an Item");
}
}
}
[Usage("Stealable [true/false]")]
[Description("Sets/gets the stealable flag on any item")]
public static void SetStealable_OnCommand(CommandEventArgs e)
{
var state = false;
var error = false;
if (e.Arguments.Length > 0)
{
try
{
state = bool.Parse(e.Arguments[0]);
}
catch
{
error = true;
}
}
if (!error)
{
e.Mobile.Target = new SetStealableTarget(e, state);
}
}
private class SetStealableTarget : Target
{
private CommandEventArgs m_e;
private bool m_state;
private bool set;
public SetStealableTarget(CommandEventArgs e, bool state) : base (30, false, TargetFlags.None)
{
m_e = e;
m_state = state;
if (e.Arguments.Length > 0)
{
set = true;
}
}
protected override void OnTarget(Mobile from, object targeted)
{
if (targeted is Item item)
{
if (set)
{
SetStealable(item, m_state);
}
var state = GetStealable(item);
from.SendMessage($"Stealable = {state}");
} else
{
from.SendMessage("Must target an Item");
}
}
}
}

View file

@ -0,0 +1,283 @@
using Server.Commands.Generic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Server.Engines.Spawners;
/*
** Sno's distro spawner importer/exporter
**
** [exportspawner filename - Saves distro spawners to XML to 'Saves/Spawners/filename'
**
** [importspawner filename - Restores distro spawners from 'Saves/Spawners/filename'. Note, this command does not check for
** duplication, so if you run it more than once you will end up with multiple spawners.
**
** These spawns can also be imported back in as XmlSpawners by using the '[xmlimportspawners Saves/Spawners/filename' command.
*/
namespace Server.Mobiles;
public class SpawnerExporter
{
public static void Initialize()
{
TargetCommands.Register(new ExportSpawnerCommand());
CommandSystem.Register("ImportSpawners", AccessLevel.Administrator, ImportSpawners_OnCommand);
}
public class ExportSpawnerCommand : BaseCommand
{
public ExportSpawnerCommand()
{
AccessLevel = AccessLevel.Administrator;
Supports = CommandSupport.Area | CommandSupport.Region | CommandSupport.Global | CommandSupport.Multi | CommandSupport.Single;
Commands = new[] { "ExportSpawner" };
ObjectTypes = ObjectTypes.Items;
Usage = "ExportSpawner <filename>";
Description = "Exports all Spawner objects to the specified filename.";
ListOptimized = true;
}
public override void ExecuteList(CommandEventArgs e, List<object> list)
{
var filename = e.GetString(0);
var spawners = new ArrayList();
for (var i = 0; i < list.Count; ++i)
{
if (list[i] is Spawner)
{
var spawner = (Spawner)list[i];
if (!spawner.Deleted && spawner.Map != Map.Internal && spawner.Parent == null)
{
spawners.Add(spawner);
}
}
}
AddResponse($"{spawners.Count.ToString()} spawners exported to Saves/Spawners/{filename}.");
ExportSpawners(spawners, filename);
}
public override bool ValidateArgs(BaseCommandImplementor impl, CommandEventArgs e)
{
if (e.Arguments.Length >= 1)
{
return true;
}
e.Mobile.SendMessage($"Usage: {Usage}");
return false;
}
private void ExportSpawners(ArrayList spawners, string filename)
{
if (spawners.Count == 0)
{
return;
}
if (!Directory.Exists("Saves/Spawners"))
{
Directory.CreateDirectory("Saves/Spawners");
}
var filePath = Path.Combine("Saves/Spawners", filename);
using (var op = new StreamWriter(filePath))
{
var xml = new XmlTextWriter(op)
{
Formatting = Formatting.Indented,
IndentChar = '\t',
Indentation = 1
};
xml.WriteStartDocument(true);
xml.WriteStartElement("spawners");
xml.WriteAttributeString("count", spawners.Count.ToString());
foreach (Spawner spawner in spawners)
{
ExportSpawner(spawner, xml);
}
xml.WriteEndElement();
xml.Close();
}
}
private void ExportSpawner(Spawner spawner, XmlWriter xml)
{
xml.WriteStartElement("spawner");
xml.WriteStartElement("count");
xml.WriteString(spawner.Count.ToString());
xml.WriteEndElement();
xml.WriteStartElement("group");
xml.WriteString(spawner.Group.ToString());
xml.WriteEndElement();
xml.WriteStartElement("homerange");
xml.WriteString(spawner.HomeRange.ToString());
xml.WriteEndElement();
xml.WriteStartElement("walkingrange");
xml.WriteString(spawner.WalkingRange.ToString());
xml.WriteEndElement();
xml.WriteStartElement("maxdelay");
xml.WriteString(spawner.MaxDelay.ToString());
xml.WriteEndElement();
xml.WriteStartElement("mindelay");
xml.WriteString(spawner.MinDelay.ToString());
xml.WriteEndElement();
xml.WriteStartElement("team");
xml.WriteString(spawner.Team.ToString());
xml.WriteEndElement();
xml.WriteStartElement("creaturesname");
foreach (var entry in spawner.Entries)
{
xml.WriteStartElement("creaturename");
xml.WriteString(entry.SpawnedName);
xml.WriteEndElement();
}
xml.WriteEndElement();
// Item properties
xml.WriteStartElement("name");
xml.WriteString(spawner.Name);
xml.WriteEndElement();
xml.WriteStartElement("location");
xml.WriteString(spawner.Location.ToString());
xml.WriteEndElement();
xml.WriteStartElement("map");
xml.WriteString(spawner.Map.ToString());
xml.WriteEndElement();
xml.WriteEndElement();
}
}
[Usage("ImportSpawners")]
[Description("Recreates Spawner items from the specified file.")]
public static void ImportSpawners_OnCommand(CommandEventArgs e)
{
if (e.Arguments.Length >= 1)
{
var filename = e.GetString(0);
var filePath = Path.Combine("Saves/Spawners", filename);
if (File.Exists(filePath))
{
var doc = new XmlDocument();
doc.Load(filePath);
var root = doc["spawners"];
int successes = 0, failures = 0;
foreach (XmlElement spawner in root.GetElementsByTagName("spawner"))
{
try
{
ImportSpawner(spawner);
successes++;
}
catch (Exception ex)
{
failures++;
Diagnostics.ExceptionLogging.LogException(ex);
}
}
e.Mobile.SendMessage($"{successes} spawners loaded successfully from {filePath}, {failures} failures.");
}
else
{
e.Mobile.SendMessage($"File {filePath} does not exist.");
}
}
else
{
e.Mobile.SendMessage("Usage: [ImportSpawners <filename>");
}
}
private static string GetText(XmlNode node, string defaultValue)
{
if (node == null)
{
return defaultValue;
}
return node.InnerText;
}
private static void ImportSpawner(XmlNode node)
{
var count = int.Parse(GetText(node["count"], "1"));
var homeRange = int.Parse(GetText(node["homerange"], "4"));
var walkingRange = int.Parse(GetText(node["walkingrange"], "-1"));
var team = int.Parse(GetText(node["team"], "0"));
var group = bool.Parse(GetText(node["group"], "False"));
var maxDelay = TimeSpan.Parse(GetText(node["maxdelay"], "10:00"));
var minDelay = TimeSpan.Parse(GetText(node["mindelay"], "05:00"));
var creaturesName = LoadCreaturesName(node["creaturesname"]);
var name = GetText(node["name"], "Spawner");
var location = Point3D.Parse(GetText(node["location"], "Error"));
var map = Map.Parse(GetText(node["map"], "Error"));
var spawner = new Spawner(count, minDelay, maxDelay, team, homeRange, creaturesName.ToArray());
if (walkingRange >= 0)
{
spawner.WalkingRange = walkingRange;
}
spawner.Name = name;
spawner.MoveToWorld(location, map);
if (spawner.Map == Map.Internal)
{
spawner.Delete();
throw new Exception("Spawner created on Internal map.");
}
spawner.Respawn();
}
private static IEnumerable<string> LoadCreaturesName(XmlElement node)
{
var names = new List<string>();
if (node != null)
{
foreach (XmlElement ele in node.GetElementsByTagName("creaturename"))
{
if (ele != null)
{
names.Add(ele.InnerText);
}
}
}
return names;
}
}

View file

@ -0,0 +1,715 @@
using Server.Commands.Generic;
using Server.Network;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using CPA = Server.CommandPropertyAttribute;
/*
** modified properties gumps taken from RC0 properties gump scripts to support the special XmlSpawner properties gump
*/
namespace Server.Gumps;
public class XmlPropertiesGump : Gump
{
private readonly ArrayList m_List;
private int m_Page;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int NameWidth = 103;
private static readonly int ValueWidth = 82;
private static readonly int EntryCount = 66;
private static readonly int ColumnEntryCount = 22;
private static readonly int TypeWidth = NameWidth + OffsetSize + ValueWidth;
private static readonly int TotalWidth = OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize;
public XmlPropertiesGump(Mobile mobile, object o) : base(GumpOffsetX, GumpOffsetY)
{
m_Mobile = mobile;
m_Object = o;
m_List = BuildList();
Initialize(0);
}
public XmlPropertiesGump(Mobile mobile, object o, Stack<StackEntry> stack, StackEntry parent) : base(GumpOffsetX, GumpOffsetY)
{
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_List = BuildList();
if (parent != null)
{
if (m_Stack == null)
{
m_Stack = new Stack<Server.Gumps.StackEntry>();
}
m_Stack.Push(parent);
}
Initialize(0);
}
public XmlPropertiesGump(Mobile mobile, object o, Stack<StackEntry> stack, ArrayList list, int page) : base(GumpOffsetX, GumpOffsetY)
{
m_Mobile = mobile;
m_Object = o;
m_List = list;
m_Stack = stack;
Initialize(page);
}
private void Initialize(int page)
{
m_Page = page;
var count = m_List.Count - page * EntryCount;
if (count < 0)
{
count = 0;
}
else if (count > EntryCount)
{
count = EntryCount;
}
var lastIndex = page * EntryCount + count - 1;
if (lastIndex >= 0 && lastIndex < m_List.Count && m_List[lastIndex] == null)
{
--count;
}
var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (ColumnEntryCount + 1);
AddPage(0);
AddBackground(0, 0, TotalWidth * 3 + BorderSize * 2, BorderSize + totalHeight + BorderSize, BackGumpID);
AddImageTiled(BorderSize, BorderSize + EntryHeight, (TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0)) * 3, totalHeight - EntryHeight, OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize;
if (m_Object is Item item)
{
AddLabelCropped(x + TextOffsetX, y, TypeWidth - TextOffsetX, EntryHeight, TextHue, item.Name);
}
var propcount = 0;
for (int i = 0, index = page * EntryCount; i <= count && index < m_List.Count; ++i, ++index)
{
// do the multi column display
var column = propcount / ColumnEntryCount;
if (propcount % ColumnEntryCount == 0)
{
y = BorderSize;
}
x = BorderSize + OffsetSize + column * (ValueWidth + NameWidth + OffsetSize * 2 + SetOffsetX + SetWidth);
y += EntryHeight + OffsetSize;
var o = m_List[index];
if (o == null)
{
AddImageTiled(x - OffsetSize, y, TotalWidth, EntryHeight, BackGumpID + 4);
propcount++;
}
else if (o is PropertyInfo prop)
{
propcount++;
// look for the default value of the equivalent property in the XmlSpawnerDefaults.DefaultEntry class
var huemodifier = TextHue;
var de = new Mobiles.XmlSpawnerDefaults.DefaultEntry();
var ftype = de.GetType();
var finfo = ftype.GetField(prop.Name);
// is there an equivalent default field?
if (finfo != null)
{
// see if the value is different from the default
if (ValueToString(finfo.GetValue(de)) != ValueToString(prop))
{
huemodifier = 68;
}
}
AddImageTiled(x, y, NameWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, NameWidth - TextOffsetX, EntryHeight, huemodifier, prop.Name);
x += NameWidth + OffsetSize;
AddImageTiled(x, y, ValueWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, ValueWidth - TextOffsetX, EntryHeight, huemodifier, ValueToString(prop));
x += ValueWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
var cpa = GetCPA(prop);
if (prop.CanWrite && cpa != null && m_Mobile.AccessLevel >= cpa.WriteLevel)
{
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 3);
}
}
}
}
public static string[] m_BoolNames = { "True", "False" };
public static object[] m_BoolValues = { true, false };
public static string[] m_PoisonNames = { "None", "Lesser", "Regular", "Greater", "Deadly", "Lethal" };
public static object[] m_PoisonValues = { null, Poison.Lesser, Poison.Regular, Poison.Greater, Poison.Deadly, Poison.Lethal };
public override void OnResponse(NetState state, RelayInfo info)
{
var from = state.Mobile;
if (!BaseCommand.IsAccessible(from, m_Object))
{
from.SendMessage("You may no longer access their properties.");
return;
}
switch (info.ButtonID)
{
case 0: // Closed
{
if (m_Stack != null && m_Stack.Count > 0)
{
var entry = m_Stack.Pop();
from.SendGump(new XmlPropertiesGump(from, entry.m_Object, m_Stack, null));
}
break;
}
case 1: // Previous
{
if (m_Page > 0)
{
from.SendGump(new XmlPropertiesGump(from, m_Object, m_Stack, m_List, m_Page - 1));
}
break;
}
case 2: // Next
{
if ((m_Page + 1) * EntryCount < m_List.Count)
{
from.SendGump(new XmlPropertiesGump(from, m_Object, m_Stack, m_List, m_Page + 1));
}
break;
}
default:
{
var index = m_Page * EntryCount + (info.ButtonID - 3);
if (index >= 0 && index < m_List.Count)
{
var prop = m_List[index] as PropertyInfo;
if (prop == null)
{
return;
}
var attr = GetCPA(prop);
if (!prop.CanWrite || attr == null || from.AccessLevel < attr.WriteLevel)
{
return;
}
var type = prop.PropertyType;
if (IsType(type, typeofMobile) || IsType(type, typeofItem))
{
from.SendGump(new XmlSetObjectGump(prop, from, m_Object, m_Stack, type, m_Page, m_List));
}
else if (IsType(type, typeofType))
{
from.Target = new XmlSetObjectTarget(prop, from, m_Object, m_Stack, type, m_Page, m_List);
}
else if (IsType(type, typeofPoint3D))
{
from.SendGump(new XmlSetPoint3DGump(prop, from, m_Object, m_Stack, m_Page, m_List));
}
else if (IsType(type, typeofPoint2D))
{
from.SendGump(new XmlSetPoint2DGump(prop, from, m_Object, m_Stack, m_Page, m_List));
}
else if (IsType(type, typeofTimeSpan))
{
from.SendGump(new XmlSetTimeSpanGump(prop, from, m_Object, m_Stack, m_Page, m_List));
}
else if (IsCustomEnum(type))
{
from.SendGump(new XmlSetCustomEnumGump(prop, from, m_Object, m_Stack, m_Page, m_List, GetCustomEnumNames(type)));
}
else if (IsType(type, typeofEnum))
{
from.SendGump(new XmlSetListOptionGump(prop, from, m_Object, m_Stack, m_Page, m_List, Enum.GetNames(type), GetObjects(Enum.GetValues(type))));
}
else if (IsType(type, typeofBool))
{
from.SendGump(new XmlSetListOptionGump(prop, from, m_Object, m_Stack, m_Page, m_List, m_BoolNames, m_BoolValues));
}
else if (IsType(type, typeofString) || IsType(type, typeofReal) || IsType(type, typeofNumeric))
{
from.SendGump(new XmlSetGump(prop, from, m_Object, m_Stack, m_Page, m_List));
}
else if (IsType(type, typeofPoison))
{
from.SendGump(new XmlSetListOptionGump(prop, from, m_Object, m_Stack, m_Page, m_List, m_PoisonNames, m_PoisonValues));
}
else if (IsType(type, typeofMap))
{
from.SendGump(new XmlSetListOptionGump(prop, from, m_Object, m_Stack, m_Page, m_List, Map.GetMapNames(), Map.GetMapValues()));
}
else if (IsType(type, typeofSkills) && m_Object is Mobile mobile)
{
from.SendGump(new XmlPropertiesGump(from, mobile, m_Stack, m_List, m_Page));
from.SendGump(new SkillsGump(from, mobile));
}
else if (HasAttribute(type, typeofPropertyObject, true))
{
var obj = prop.GetValue(m_Object, null);
from.SendGump(obj != null
? new XmlPropertiesGump(from, obj, m_Stack,
new StackEntry(m_Object, prop))
: new XmlPropertiesGump(from, m_Object, m_Stack, m_List, m_Page));
}
}
break;
}
}
}
private static object[] GetObjects(Array a)
{
var list = new object[a.Length];
for (var i = 0; i < list.Length; ++i)
{
list[i] = a.GetValue(i);
}
return list;
}
private static bool IsCustomEnum(Type type) => type.IsDefined(typeofCustomEnum, false);
private static string[] GetCustomEnumNames(Type type)
{
var attrs = type.GetCustomAttributes(typeofCustomEnum, false);
if (attrs.Length == 0)
{
return new string[0];
}
var ce = attrs[0] as CustomEnumAttribute;
if (ce == null)
{
return new string[0];
}
return ce.Names;
}
private static bool HasAttribute(Type type, Type check, bool inherit)
{
var objs = type.GetCustomAttributes(check, inherit);
return objs.Length > 0;
}
private static bool IsType(Type type, Type check) => type == check || type.IsSubclassOf(check);
private static bool IsType(Type type, Type[] check)
{
for (var i = 0; i < check.Length; ++i)
{
if (IsType(type, check[i]))
{
return true;
}
}
return false;
}
private static readonly Type typeofMobile = typeof(Mobile);
private static readonly Type typeofItem = typeof(Item);
private static readonly Type typeofType = typeof(Type);
private static readonly Type typeofPoint3D = typeof(Point3D);
private static readonly Type typeofPoint2D = typeof(Point2D);
private static readonly Type typeofTimeSpan = typeof(TimeSpan);
private static readonly Type typeofCustomEnum = typeof(CustomEnumAttribute);
private static readonly Type typeofEnum = typeof(Enum);
private static readonly Type typeofBool = typeof(bool);
private static readonly Type typeofString = typeof(string);
private static readonly Type typeofPoison = typeof(Poison);
private static readonly Type typeofMap = typeof(Map);
private static readonly Type typeofSkills = typeof(Skills);
private static readonly Type typeofPropertyObject = typeof(PropertyObjectAttribute);
private static readonly Type typeofNoSort = typeof(NoSortAttribute);
private static readonly Type[] typeofReal =
{
typeof(float),
typeof(double)
};
private static readonly Type[] typeofNumeric =
{
typeof(byte),
typeof(short),
typeof(int),
typeof(long),
typeof(sbyte),
typeof(ushort),
typeof(uint),
typeof(ulong)
};
private string ValueToString(PropertyInfo prop) => ValueToString(m_Object, prop);
public static string ValueToString(object obj, PropertyInfo prop)
{
try
{
return ValueToString(prop.GetValue(obj, null));
}
catch (Exception e)
{
return $"!{e.GetType()}!";
}
}
public static string ValueToString(object o)
{
if (o == null)
{
return "-null-";
}
if (o is string s1)
{
return $"\"{s1}\"";
}
if (o is bool)
{
return o.ToString();
}
if (o is char c)
{
return $"0x{(int)c:X} '{c}'";
}
if (o is Serial s)
{
if (s.IsValid)
{
if (s.IsItem)
{
return $"(I) 0x{s.Value:X}";
}
if (s.IsMobile)
{
return $"(M) 0x{s.Value:X}";
}
}
return $"(?) 0x{s.Value:X}";
}
if (o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong)
{
return string.Format("{0} (0x{0:X})", o);
}
if (o is double)
{
return o.ToString();
}
if (o is Mobile mobile)
{
return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\"";
}
if (o is Item item)
{
return $"(I) 0x{item.Serial:X}";
}
if (o is Type type)
{
return type.Name;
}
return o.ToString();
}
private ArrayList BuildList()
{
var type = m_Object.GetType();
var props = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
var groups = GetGroups(type, props);
var list = new ArrayList();
for (var i = 0; i < groups.Count; ++i)
{
var de = (DictionaryEntry)groups[i];
var groupList = (ArrayList)de.Value;
if (!HasAttribute((Type)de.Key, typeofNoSort, false))
{
groupList.Sort(PropertySorter.Instance);
}
if (i != 0)
{
list.Add(null);
}
list.Add(de.Key);
list.AddRange(groupList);
}
return list;
}
private static readonly Type typeofCPA = typeof(CPA);
private static readonly Type typeofObject = typeof(object);
private static CPA GetCPA(PropertyInfo prop)
{
var attrs = prop.GetCustomAttributes(typeofCPA, false);
if (attrs.Length > 0)
{
return attrs[0] as CPA;
}
return null;
}
private ArrayList GetGroups(Type objectType, PropertyInfo[] props)
{
var groups = new Hashtable();
for (var i = 0; i < props.Length; ++i)
{
var prop = props[i];
if (prop.CanRead)
{
var attr = GetCPA(prop);
if (attr != null && m_Mobile.AccessLevel >= attr.ReadLevel)
{
var type = prop.DeclaringType;
while (true)
{
var baseType = type.BaseType;
if (baseType == null || baseType == typeofObject)
{
break;
}
if (baseType.GetProperty(prop.Name, prop.PropertyType) != null)
{
type = baseType;
}
else
{
break;
}
}
var list = (ArrayList)groups[type];
if (list == null)
{
groups[type] = list = new ArrayList();
}
list.Add(prop);
}
}
}
var sorted = new ArrayList(groups);
sorted.Sort(new GroupComparer(objectType));
return sorted;
}
public static object GetObjectFromString(Type t, string s)
{
if (t == typeof(string))
{
return s;
}
if (t == typeof(byte) || t == typeof(sbyte) || t == typeof(short) || t == typeof(ushort) || t == typeof(int) || t == typeof(uint) || t == typeof(long) || t == typeof(ulong))
{
if (s.StartsWith("0x"))
{
if (t == typeof(ulong) || t == typeof(uint) || t == typeof(ushort) || t == typeof(byte))
{
return Convert.ChangeType(Convert.ToUInt64(s[2..], 16), t);
}
return Convert.ChangeType(Convert.ToInt64(s[2..], 16), t);
}
return Convert.ChangeType(s, t);
}
if (t == typeof(double) || t == typeof(float))
{
return Convert.ChangeType(s, t);
}
throw new Exception("bad");
}
private class PropertySorter : IComparer
{
public static readonly PropertySorter Instance = new();
private PropertySorter()
{
}
public int Compare(object x, object y)
{
if (x == null && y == null)
{
return 0;
}
if (x == null)
{
return -1;
}
if (y == null)
{
return 1;
}
var a = x as PropertyInfo;
var b = y as PropertyInfo;
if (a == null || b == null)
{
throw new ArgumentException();
}
return a.Name.CompareTo(b.Name);
}
}
private class GroupComparer : IComparer
{
private readonly Type m_Start;
public GroupComparer(Type start) => m_Start = start;
private static readonly Type typeofObject = typeof(object);
private int GetDistance(Type type)
{
var current = m_Start;
int dist;
for (dist = 0; current != null && current != typeofObject && current != type; ++dist)
{
current = current.BaseType;
}
return dist;
}
public int Compare(object x, object y)
{
if (x == null && y == null)
{
return 0;
}
if (x == null)
{
return -1;
}
if (y == null)
{
return 1;
}
if (!(x is DictionaryEntry de1) || !(y is DictionaryEntry de2))
{
throw new ArgumentException();
}
var a = (Type)de1.Key;
var b = (Type)de2.Key;
return GetDistance(a).CompareTo(GetDistance(b));
}
}
}

View file

@ -0,0 +1,47 @@
using Server.Commands;
using Server.Network;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetCustomEnumGump : XmlSetListOptionGump
{
private readonly string[] m_Names;
public XmlSetCustomEnumGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int propspage, ArrayList list, string[] names) : base(prop, mobile, o, stack, propspage, list, names, null)
{
m_Names = names;
}
public override void OnResponse(NetState sender, RelayInfo relayInfo)
{
var index = relayInfo.ButtonID - 1;
if (index >= 0 && index < m_Names.Length)
{
try
{
var info = m_Property.PropertyType.GetMethod("Parse", new[] { typeof(string) });
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, m_Names[index]);
if (info != null)
{
m_Property.SetValue(m_Object, info.Invoke(null, new object[] { m_Names[index] }), null);
}
else if (m_Property.PropertyType == typeof(Enum) || m_Property.PropertyType.IsSubclassOf(typeof(Enum)))
{
m_Property.SetValue(m_Object, Enum.Parse(m_Property.PropertyType, m_Names[index], false), null);
}
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}

View file

@ -0,0 +1,250 @@
using Server.Commands;
using Server.HuePickers;
using Server.Network;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetGump : Gump
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int EntryWidth = 212;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int TotalHeight = OffsetSize + 2 * (EntryHeight + OffsetSize);
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize;
public XmlSetGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(GumpOffsetX, GumpOffsetY)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
var canNull = !prop.PropertyType.IsValueType;
var canDye = prop.IsDefined(typeof(HueAttribute), false);
var xextend = 0;
if (prop.PropertyType == typeof(string))
{
xextend = 300;
}
var val = prop.GetValue(m_Object, null);
var initialText = val == null ? "" : val.ToString();
AddPage(0);
AddBackground(0, 0, BackWidth + xextend, BackHeight + (canNull ? EntryHeight + OffsetSize : 0) + (canDye ? EntryHeight + OffsetSize : 0), BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth + xextend - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight + (canNull ? EntryHeight + OffsetSize : 0) + (canDye ? EntryHeight + OffsetSize : 0), OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize;
AddImageTiled(x, y, EntryWidth + xextend, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth + xextend - TextOffsetX, EntryHeight, TextHue, prop.Name);
x += EntryWidth + xextend + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth + xextend, EntryHeight, EntryGumpID);
AddTextEntry(x + TextOffsetX, y, EntryWidth + xextend - TextOffsetX, EntryHeight, TextHue, 0, initialText);
x += EntryWidth + xextend + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1);
if (canNull)
{
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth + xextend, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth + xextend - TextOffsetX, EntryHeight, TextHue, "Null");
x += EntryWidth + xextend + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2);
}
if (canDye)
{
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth + xextend, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth + xextend - TextOffsetX, EntryHeight, TextHue, "Hue Picker");
x += EntryWidth + xextend + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3);
}
}
private class InternalPicker : HuePicker
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public InternalPicker(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(((IHued)o).HuedItemID)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
}
public override void OnResponse(int hue)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, hue.ToString());
m_Property.SetValue(m_Object, hue, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
object toSet;
bool shouldSet, shouldSend = true;
switch (info.ButtonID)
{
case 1:
{
var text = info.GetTextEntry(0);
if (text != null)
{
try
{
toSet = XmlPropertiesGump.GetObjectFromString(m_Property.PropertyType, text.Text);
shouldSet = true;
}
catch
{
toSet = null;
shouldSet = false;
m_Mobile.SendMessage("Bad format");
}
}
else
{
toSet = null;
shouldSet = false;
}
break;
}
case 2: // Null
{
toSet = null;
shouldSet = true;
break;
}
case 3: // Hue Picker
{
toSet = null;
shouldSet = false;
shouldSend = false;
m_Mobile.SendHuePicker(new InternalPicker(m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List));
break;
}
default:
{
toSet = null;
shouldSet = false;
break;
}
}
if (shouldSet)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, toSet == null ? "(null)" : toSet.ToString());
m_Property.SetValue(m_Object, toSet, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
if (shouldSend)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
}

View file

@ -0,0 +1,196 @@
using Server.Commands;
using Server.Network;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetListOptionGump : Gump
{
protected PropertyInfo m_Property;
protected Mobile m_Mobile;
protected object m_Object;
protected Stack<StackEntry> m_Stack;
protected int m_Page;
protected ArrayList m_List;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int PrevWidth = PropsConfig.PrevWidth;
public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY;
public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1;
public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2;
public static readonly int NextWidth = PropsConfig.NextWidth;
public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY;
public static readonly int NextButtonID1 = PropsConfig.NextButtonID1;
public static readonly int NextButtonID2 = PropsConfig.NextButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int EntryWidth = 212;
private static readonly int EntryCount = 13;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly bool PrevLabel = OldStyle, NextLabel = OldStyle;
private static readonly int PrevLabelOffsetX = PrevWidth + 1;
private static readonly int PrevLabelOffsetY = 0;
private static readonly int NextLabelOffsetX = -29;
private static readonly int NextLabelOffsetY = 0;
protected object[] m_Values;
public XmlSetListOptionGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int propspage, ArrayList list, string[] names, object[] values) : base(GumpOffsetX, GumpOffsetY)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = propspage;
m_List = list;
m_Values = values;
var pages = (names.Length + EntryCount - 1) / EntryCount;
var index = 0;
for (var page = 1; page <= pages; ++page)
{
AddPage(page);
var start = (page - 1) * EntryCount;
var count = names.Length - start;
if (count > EntryCount)
{
count = EntryCount;
}
var totalHeight = OffsetSize + (count + 2) * (EntryHeight + OffsetSize);
var backHeight = BorderSize + totalHeight + BorderSize;
AddBackground(0, 0, BackWidth, backHeight, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize;
var emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4 - (OldStyle ? SetWidth + OffsetSize : 0);
AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID);
if (page > 1)
{
AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 0, GumpButtonType.Page, page - 1);
if (PrevLabel)
{
AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous");
}
}
x += PrevWidth + OffsetSize;
if (!OldStyle)
{
AddImageTiled(x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, HeaderGumpID);
}
x += emptyWidth + OffsetSize;
if (!OldStyle)
{
AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID);
}
if (page < pages)
{
AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 0, GumpButtonType.Page, page + 1);
if (NextLabel)
{
AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next");
}
}
AddRect(0, prop.Name, 0);
for (var i = 0; i < count; ++i)
{
AddRect(i + 1, names[index], ++index);
}
}
}
private void AddRect(int index, string str, int button)
{
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize + (index + 1) * (EntryHeight + OffsetSize);
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, str);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
if (button != 0)
{
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, button);
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
var index = info.ButtonID - 1;
if (index >= 0 && index < m_Values.Length)
{
try
{
var toSet = m_Values[index];
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, toSet == null ? "(-null-)" : toSet.ToString());
m_Property.SetValue(m_Object, toSet, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}

View file

@ -0,0 +1,302 @@
using Server.Commands;
using Server.Commands.Generic;
using Server.Network;
using Server.Prompts;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetObjectGump : Gump
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly Type m_Type;
private readonly int m_Page;
private readonly ArrayList m_List;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int EntryWidth = 212;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int TotalHeight = OffsetSize + 5 * (EntryHeight + OffsetSize);
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize;
public XmlSetObjectGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, Type type, int page, ArrayList list) : base(GumpOffsetX, GumpOffsetY)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Type = type;
m_Page = page;
m_List = list;
var initialText = XmlPropertiesGump.ValueToString(o, prop);
AddPage(0);
AddBackground(0, 0, BackWidth, BackHeight, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, initialText);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Change by Serial");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Nullify");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "View Properties");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 4);
}
private class InternalPrompt : Prompt
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly Type m_Type;
private readonly int m_Page;
private readonly ArrayList m_List;
public InternalPrompt(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, Type type, int page, ArrayList list)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Type = type;
m_Page = page;
m_List = list;
}
public override void OnCancel(Mobile from)
{
m_Mobile.SendGump(new XmlSetObjectGump(m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List));
}
public override void OnResponse(Mobile from, string text)
{
object toSet;
bool shouldSet;
try
{
var serial = Utility.ToUInt32(text);
toSet = World.FindEntity((Serial)serial);
if (toSet == null)
{
shouldSet = false;
m_Mobile.SendMessage("No object with that serial was found.");
}
else if (!m_Type.IsInstanceOfType(toSet))
{
toSet = null;
shouldSet = false;
m_Mobile.SendMessage($"The object with that serial could not be assigned to a property of type : {m_Type.Name}");
}
else
{
shouldSet = true;
}
}
catch
{
toSet = null;
shouldSet = false;
m_Mobile.SendMessage("Bad format");
}
if (shouldSet)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, toSet.ToString());
m_Property.SetValue(m_Object, toSet, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
m_Mobile.SendGump(new XmlSetObjectGump(m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List));
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
bool shouldSet, shouldSend = true;
object viewProps = null;
switch (info.ButtonID)
{
case 0: // closed
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
shouldSet = false;
shouldSend = false;
break;
}
case 1: // Change by Target
{
m_Mobile.Target = new XmlSetObjectTarget(m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List);
shouldSet = false;
shouldSend = false;
break;
}
case 2: // Change by Serial
{
shouldSet = false;
shouldSend = false;
m_Mobile.SendMessage("Enter the serial you wish to find:");
m_Mobile.Prompt = new InternalPrompt(m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List);
break;
}
case 3: // Nullify
{
shouldSet = true;
break;
}
case 4: // View Properties
{
shouldSet = false;
var obj = m_Property.GetValue(m_Object, null);
if (obj == null)
{
m_Mobile.SendMessage("The property is null and so you cannot view its properties.");
}
else if (!BaseCommand.IsAccessible(m_Mobile, obj))
{
m_Mobile.SendMessage("You may not view their properties.");
}
else
{
viewProps = obj;
}
break;
}
default:
{
shouldSet = false;
break;
}
}
if (shouldSet)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, "(null)");
m_Property.SetValue(m_Object, null, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
if (shouldSend)
{
m_Mobile.SendGump(new XmlSetObjectGump(m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List));
}
if (viewProps != null)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, viewProps));
}
}
}

View file

@ -0,0 +1,72 @@
using Server.Commands;
using Server.Items;
using Server.Targeting;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetObjectTarget : Target
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly Type m_Type;
private readonly int m_Page;
private readonly ArrayList m_List;
public XmlSetObjectTarget(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, Type type, int page, ArrayList list) : base(-1, false, TargetFlags.None)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Type = type;
m_Page = page;
m_List = list;
}
protected override void OnTarget(Mobile from, object targeted)
{
try
{
if (m_Type == typeof(Type))
{
targeted = targeted.GetType();
}
else if ((m_Type == typeof(BaseAddon) || m_Type.IsAssignableFrom(typeof(BaseAddon))) && targeted is AddonComponent component)
{
targeted = component.Addon;
}
if (m_Type.IsInstanceOfType(targeted))
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, targeted.ToString());
m_Property.SetValue(m_Object, targeted, null);
}
else
{
m_Mobile.SendMessage($"That cannot be assigned to a property of type : {m_Type.Name}");
}
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
protected override void OnTargetFinish(Mobile from)
{
if (m_Type == typeof(Type))
{
from.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
else
{
from.SendGump(new XmlSetObjectGump(m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List));
}
}
}

View file

@ -0,0 +1,235 @@
using Server.Commands;
using Server.Network;
using Server.Targeting;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetPoint2DGump : Gump
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int CoordWidth = 105;
private static readonly int EntryWidth = CoordWidth + OffsetSize + CoordWidth;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int TotalHeight = OffsetSize + 4 * (EntryHeight + OffsetSize);
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize;
public XmlSetPoint2DGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(GumpOffsetX, GumpOffsetY)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
var p = (Point2D)prop.GetValue(o, null);
AddPage(0);
AddBackground(0, 0, BackWidth, BackHeight, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Use your location");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Target a location");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "X:");
AddTextEntry(x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 0, p.X.ToString());
x += CoordWidth + OffsetSize;
AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "Y:");
AddTextEntry(x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 1, p.Y.ToString());
x += CoordWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3);
}
private class InternalTarget : Target
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public InternalTarget(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(-1, true, TargetFlags.None)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
}
protected override void OnTarget(Mobile from, object targeted)
{
var p = targeted as IPoint3D;
if (p != null)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, new Point2D(p.X, p.Y).ToString());
m_Property.SetValue(m_Object, new Point2D(p.X, p.Y), null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
}
protected override void OnTargetFinish(Mobile from)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Point2D toSet;
bool shouldSet, shouldSend;
switch (info.ButtonID)
{
case 1: // Current location
{
toSet = new Point2D(m_Mobile.X, m_Mobile.Y);
shouldSet = true;
shouldSend = true;
break;
}
case 2: // Pick location
{
m_Mobile.Target = new InternalTarget(m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List);
toSet = Point2D.Zero;
shouldSet = false;
shouldSend = false;
break;
}
case 3: // Use values
{
var x = info.GetTextEntry(0);
var y = info.GetTextEntry(1);
toSet = new Point2D(x == null ? 0 : Utility.ToInt32(x.Text), y == null ? 0 : Utility.ToInt32(y.Text));
shouldSet = true;
shouldSend = true;
break;
}
default:
{
toSet = Point2D.Zero;
shouldSet = false;
shouldSend = true;
break;
}
}
if (shouldSet)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, toSet.ToString());
m_Property.SetValue(m_Object, toSet, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
if (shouldSend)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
}

View file

@ -0,0 +1,241 @@
using Server.Commands;
using Server.Network;
using Server.Targeting;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetPoint3DGump : Gump
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int CoordWidth = 70;
private static readonly int EntryWidth = CoordWidth + OffsetSize + CoordWidth + OffsetSize + CoordWidth;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int TotalHeight = OffsetSize + 4 * (EntryHeight + OffsetSize);
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize;
public XmlSetPoint3DGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(GumpOffsetX, GumpOffsetY)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
var p = (Point3D)prop.GetValue(o, null);
AddPage(0);
AddBackground(0, 0, BackWidth, BackHeight, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Use your location");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Target a location");
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2);
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "X:");
AddTextEntry(x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 0, p.X.ToString());
x += CoordWidth + OffsetSize;
AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "Y:");
AddTextEntry(x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 1, p.Y.ToString());
x += CoordWidth + OffsetSize;
AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "Z:");
AddTextEntry(x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 2, p.Z.ToString());
x += CoordWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3);
}
private class InternalTarget : Target
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public InternalTarget(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(-1, true, TargetFlags.None)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
}
protected override void OnTarget(Mobile from, object targeted)
{
var p = targeted as IPoint3D;
if (p != null)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, new Point3D(p).ToString());
m_Property.SetValue(m_Object, new Point3D(p), null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
}
protected override void OnTargetFinish(Mobile from)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Point3D toSet;
bool shouldSet, shouldSend;
switch (info.ButtonID)
{
case 1: // Current location
{
toSet = m_Mobile.Location;
shouldSet = true;
shouldSend = true;
break;
}
case 2: // Pick location
{
m_Mobile.Target = new InternalTarget(m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List);
toSet = Point3D.Zero;
shouldSet = false;
shouldSend = false;
break;
}
case 3: // Use values
{
var x = info.GetTextEntry(0);
var y = info.GetTextEntry(1);
var z = info.GetTextEntry(2);
toSet = new Point3D(x == null ? 0 : Utility.ToInt32(x.Text), y == null ? 0 : Utility.ToInt32(y.Text), z == null ? 0 : Utility.ToInt32(z.Text));
shouldSet = true;
shouldSend = true;
break;
}
default:
{
toSet = Point3D.Zero;
shouldSet = false;
shouldSend = true;
break;
}
}
if (shouldSet)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, toSet.ToString());
m_Property.SetValue(m_Object, toSet, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
if (shouldSend)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
}

View file

@ -0,0 +1,240 @@
using Server.Commands;
using Server.Network;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlSetTimeSpanGump : Gump
{
private readonly PropertyInfo m_Property;
private readonly Mobile m_Mobile;
private readonly object m_Object;
private readonly Stack<StackEntry> m_Stack;
private readonly int m_Page;
private readonly ArrayList m_List;
public static readonly bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = PropsConfig.EntryHeight;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly int EntryWidth = 212;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int TotalHeight = OffsetSize + 7 * (EntryHeight + OffsetSize);
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize;
public XmlSetTimeSpanGump(PropertyInfo prop, Mobile mobile, object o, Stack<StackEntry> stack, int page, ArrayList list) : base(GumpOffsetX, GumpOffsetY)
{
m_Property = prop;
m_Mobile = mobile;
m_Object = o;
m_Stack = stack;
m_Page = page;
m_List = list;
var ts = (TimeSpan)prop.GetValue(o, null);
AddPage(0);
AddBackground(0, 0, BackWidth, BackHeight, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID);
AddRect(0, prop.Name, 0, -1);
AddRect(1, ts.ToString(), 0, -1);
AddRect(2, "Zero", 1, -1);
AddRect(3, "From H:M:S", 2, -1);
AddRect(4, "H:", 3, 0);
AddRect(5, "M:", 4, 1);
AddRect(6, "S:", 5, 2);
}
private void AddRect(int index, string str, int button, int text)
{
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize + index * (EntryHeight + OffsetSize);
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, str);
if (text != -1)
{
AddTextEntry(x + 16 + TextOffsetX, y, EntryWidth - TextOffsetX - 16, EntryHeight, TextHue, text, "");
}
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
if (button != 0)
{
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, button);
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
TimeSpan toSet;
bool shouldSet, shouldSend;
var h = info.GetTextEntry(0);
var m = info.GetTextEntry(1);
var s = info.GetTextEntry(2);
switch (info.ButtonID)
{
case 1: // Zero
{
toSet = TimeSpan.Zero;
shouldSet = true;
shouldSend = true;
break;
}
case 2: // From H:M:S
{
if (h != null && m != null && s != null)
{
try
{
toSet = TimeSpan.Parse($"{h.Text}:{m.Text}:{s.Text}");
shouldSet = true;
shouldSend = true;
break;
}
catch
{
}
}
toSet = TimeSpan.Zero;
shouldSet = false;
shouldSend = false;
break;
}
case 3: // From H
{
if (h != null)
{
try
{
toSet = TimeSpan.FromHours(Utility.ToDouble(h.Text));
shouldSet = true;
shouldSend = true;
break;
}
catch
{
}
}
toSet = TimeSpan.Zero;
shouldSet = false;
shouldSend = false;
break;
}
case 4: // From M
{
if (m != null)
{
try
{
toSet = TimeSpan.FromMinutes(Utility.ToDouble(m.Text));
shouldSet = true;
shouldSend = true;
break;
}
catch
{
}
}
toSet = TimeSpan.Zero;
shouldSet = false;
shouldSend = false;
break;
}
case 5: // From S
{
if (s != null)
{
try
{
toSet = TimeSpan.FromSeconds(Utility.ToDouble(s.Text));
shouldSet = true;
shouldSend = true;
break;
}
catch
{
}
}
toSet = TimeSpan.Zero;
shouldSet = false;
shouldSend = false;
break;
}
default:
{
toSet = TimeSpan.Zero;
shouldSet = false;
shouldSend = true;
break;
}
}
if (shouldSet)
{
try
{
CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, toSet.ToString());
m_Property.SetValue(m_Object, toSet, null);
}
catch
{
m_Mobile.SendMessage("An exception was caught. The property may not have changed.");
}
}
if (shouldSend)
{
m_Mobile.SendGump(new XmlPropertiesGump(m_Mobile, m_Object, m_Stack, m_List, m_Page));
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,282 @@
using System.Collections;
using Server.Items;
using CPA = Server.CommandPropertyAttribute;
using Server.Misc;
namespace Server.Mobiles;
public class XmlSpawnerSkillCheck
{
// alternate skillcheck hooks to replace those in SkillCheck.cs
public static bool Mobile_SkillCheckLocation(Mobile from, SkillName skillName, double minSkill, double maxSkill)
{
var skill = from.Skills[skillName];
if (skill == null)
{
return false;
}
// call the default skillcheck handler
var success = SkillCheck.Mobile_SkillCheckLocation( from, skillName, minSkill, maxSkill);
// call the xmlspawner skillcheck handler
CheckSkillUse(from, skill, success);
return success;
}
public static bool Mobile_SkillCheckDirectLocation(Mobile from, SkillName skillName, double chance)
{
var skill = from.Skills[skillName];
if (skill == null)
{
return false;
}
// call the default skillcheck handler
var success = SkillCheck.Mobile_SkillCheckDirectLocation( from, skillName, chance);
// call the xmlspawner skillcheck handler
CheckSkillUse(from, skill, success);
return success;
}
public static bool Mobile_SkillCheckTarget(Mobile from, SkillName skillName, object target, double minSkill, double maxSkill)
{
var skill = from.Skills[skillName];
if (skill == null)
{
return false;
}
// call the default skillcheck handler
var success = SkillCheck.Mobile_SkillCheckTarget( from, skillName, target, minSkill, maxSkill);
// call the xmlspawner skillcheck handler
CheckSkillUse(from, skill, success);
return success;
}
public static bool Mobile_SkillCheckDirectTarget(Mobile from, SkillName skillName, object target, double chance)
{
var skill = from.Skills[skillName];
if (skill == null)
{
return false;
}
// call the default skillcheck handler
var success = SkillCheck.Mobile_SkillCheckDirectTarget( from, skillName, target, chance);
// call the xmlspawner skillcheck handler
CheckSkillUse(from, skill, success);
return success;
}
public class RegisteredSkill
{
public const int MaxSkills = 52;
public const SkillName Invalid = (SkillName)(-1);
public object target;
public SkillName sid;
// note the extra skill MaxSkills +1 is used for any unknown skill that falls outside of the known 52
private static ArrayList[] m_FeluccaSkillList = new ArrayList[MaxSkills+1];
private static ArrayList[] m_TrammelSkillList = new ArrayList[MaxSkills+1];
private static ArrayList[] m_MalasSkillList = new ArrayList[MaxSkills+1];
private static ArrayList[] m_IlshenarSkillList = new ArrayList[MaxSkills+1];
private static ArrayList[] m_TokunoSkillList = new ArrayList[MaxSkills+1];
// primary function that returns the list of objects (spawners) that are associated with a given skillname by map
public static ArrayList TriggerList(SkillName index, Map map)
{
if (map == null || map == Map.Internal)
{
return null;
}
ArrayList[] maplist;
// get the list for the specified map
if (map == Map.Felucca)
{
maplist = m_FeluccaSkillList;
}
else if (map == Map.Ilshenar)
{
maplist = m_IlshenarSkillList;
}
else if (map == Map.Malas)
{
maplist = m_MalasSkillList;
}
else if (map == Map.Trammel)
{
maplist = m_TrammelSkillList;
}
else if (map == Map.Tokuno)
{
maplist = m_TokunoSkillList;
}
else
{
return null;
}
// is it one of the standard 52 skills
if ((int)index >= 0 && (int)index < MaxSkills)
{
return maplist[(int)index] ??= new ArrayList();
}
// otherwise pull it out of the final slot for unknown skills. I dont know of a condition that would lead to
// additional skills being registered but it will support them if they are
return maplist[MaxSkills] ??= new ArrayList();
}
}
public static void RegisterSkillTrigger(object o, SkillName s, Map map)
{
if (o == null || s == RegisteredSkill.Invalid)
{
return;
}
// go through the list and if the spawner is not on it yet, then add it
var found = false;
var skilllist = RegisteredSkill.TriggerList(s, map);
if (skilllist == null)
{
return;
}
foreach(RegisteredSkill rs in skilllist)
{
if (rs.target == o && rs.sid == s)
{
found = true;
// dont register a skill if it is already on the list for this spawner
break;
}
}
// if it hasnt already been added to the list, then add it
if (!found)
{
var newrs = new RegisteredSkill();
newrs.target = o;
newrs.sid = s;
skilllist.Add(newrs);
}
}
public static void UnRegisterSkillTrigger(object o, SkillName s, Map map, bool all)
{
if (o == null || s == RegisteredSkill.Invalid)
{
return;
}
// go through the list and if the spawner is on it regardless of the skill registered, then remove it
if (all)
{
for(var i = 0;i<RegisteredSkill.MaxSkills+1;i++)
{
var skilllist = RegisteredSkill.TriggerList((SkillName)i, map);
if (skilllist == null)
{
return;
}
foreach(RegisteredSkill rs in skilllist)
{
if (rs.target == o)
{
skilllist.Remove(rs);
break;
}
}
}
}
else
{
var skilllist = RegisteredSkill.TriggerList(s, map);
if (skilllist == null)
{
return;
}
// if the all flag is not set then just remove the spawner from the list for the specified skill
foreach(RegisteredSkill rs in skilllist)
{
if (rs.target == o && rs.sid == s)
{
skilllist.Remove(rs);
break;
}
}
}
}
// determines whether XmlSpawner, XmlAttachment, or XmlQuest OnSkillUse methods should be invoked.
public static void CheckSkillUse(Mobile m, Skill skill, bool success)
{
if (!(m is PlayerMobile) || skill == null)
{
return;
}
/*
// first check for any attachments that might support OnSkillUse
ArrayList list = XmlAttach.FindAttachments(m);
if (list != null && list.Count > 0)
{
foreach(XmlAttachment a in list)
{
if (a != null && !a.Deleted && a.HandlesOnSkillUse)
{
a.OnSkillUse(m, skill, success);
}
}
}
*/
// then check for registered skills
var skilllist = RegisteredSkill.TriggerList(skill.SkillName, m.Map);
if (skilllist == null)
{
return;
}
// determine whether there are any registered objects for this skill
foreach(RegisteredSkill rs in skilllist)
{
// if so then invoke their skill handlers
// call the spawner handler
if (rs.sid == skill.SkillName && rs.target is XmlSpawner spawner && spawner.HandlesOnSkillUse)
{
spawner.OnSkillUse(m, skill, success);
}
}
}
}

View file

@ -0,0 +1,80 @@
namespace Server.Items;
public class XmlTextEntryBook : BaseBook
{
public XmlTextEntryBook(int itemID, string title, string author, int pageCount, bool writable) : base(itemID, title, author, pageCount, writable)
{
}
public XmlTextEntryBook(Serial serial) : base(serial)
{
}
public void FillTextEntryBook(string text)
{
var pagenum = 0;
var current = 0;
// break up the text into single line length pieces
while (text != null && current < text.Length)
{
var lineCount = 10;
var lines = new string[lineCount];
// place the line on the page
for (var i = 0; i < lineCount; i++)
{
if (current < text.Length)
{
// make each line 25 chars long
var length = text.Length - current;
if (length > 20)
{
length = 20;
}
lines[i] = text.Substring(current, length);
current += length;
}
else
{
// fill up the remaining lines
lines[i] = string.Empty;
}
}
if (pagenum >= PagesCount)
{
return;
}
Pages[pagenum].Lines = lines;
pagenum++;
}
// empty the remaining contents
for (var j = pagenum; j < PagesCount; j++)
{
if (Pages[j].Lines.Length > 0)
{
for (var i = 0; i < Pages[j].Lines.Length; i++)
{
Pages[j].Lines[i] = string.Empty;
}
}
}
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
reader.ReadInt();
Delete();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,477 @@
using Server.Mobiles;
using Server.Network;
using System;
using System.Collections;
using System.IO;
using System.Xml;
namespace Server.Gumps;
public abstract class XmlAddCAGNode
{
public abstract string Caption { get; }
public abstract void OnClick(Mobile from, int page, int index, Gump gump);
}
public class XmlAddCAGObject : XmlAddCAGNode
{
private readonly Type m_Type;
private readonly XmlAddCAGCategory m_Parent;
public int ItemID { get; }
public override string Caption => m_Type == null ? "bad type" : m_Type.Name;
public override void OnClick(Mobile from, int page, int index, Gump gump)
{
if (m_Type == null)
{
from.SendMessage("That is an invalid type name.");
}
else
{
if (gump is XmlAddGump xmladdgump)
{
//Commands.Handle(from, String.Format("{0}Add {1}", Commands.CommandPrefix, m_Type.Name));
if (xmladdgump.defs?.NameList != null && index >= 0 && index < xmladdgump.defs.NameList.Length)
{
xmladdgump.defs.NameList[index] = m_Type.Name;
XmlAddGump.Refresh(from, true);
}
from.SendGump(new XmlCategorizedAddGump(from, m_Parent, page, index, xmladdgump));
}
else if (gump is XmlSpawnerGump spawnerGump)
{
var m_Spawner = spawnerGump.m_Spawner;
if (m_Spawner != null)
{
var xg = m_Spawner.SpawnerGump;
if (xg != null)
{
xg.Rentry = new XmlSpawnerGump.ReplacementEntry
{
Typename = m_Type.Name,
Index = index,
Color = 0x1436
};
Timer.DelayCall(TimeSpan.Zero, XmlSpawnerGump.RefreshSpawnerGumps, from);
}
}
}
}
}
public XmlAddCAGObject(XmlAddCAGCategory parent, XmlReader xml)
{
m_Parent = parent;
if (xml.MoveToAttribute("type"))
{
m_Type = AssemblyHandler.FindTypeByFullName(xml.Value, false);
}
if (xml.MoveToAttribute("gfx"))
{
ItemID = XmlConvert.ToInt32(xml.Value);
}
if (xml.MoveToAttribute("hue"))
{
XmlConvert.ToInt32(xml.Value);
}
}
}
public class XmlAddCAGCategory : XmlAddCAGNode
{
private readonly string m_Title;
public XmlAddCAGNode[] Nodes { get; }
public XmlAddCAGCategory Parent { get; }
public override string Caption => m_Title;
public override void OnClick(Mobile from, int page, int index, Gump gump)
{
from.SendGump(new XmlCategorizedAddGump(from, this, 0, index, gump));
}
private XmlAddCAGCategory()
{
m_Title = "no data";
Nodes = new XmlAddCAGNode[0];
}
public XmlAddCAGCategory(XmlAddCAGCategory parent, XmlReader xml)
{
Parent = parent;
if (xml.MoveToAttribute("title"))
{
m_Title = xml.Value == "Add Menu" ? "XmlAdd Menu" : xml.Value;
}
else
{
m_Title = "empty";
}
if (m_Title == "Docked")
{
m_Title = "Docked 2";
}
if (xml.IsEmptyElement)
{
Nodes = new XmlAddCAGNode[0];
}
else
{
var nodes = new ArrayList();
try
{
while (xml.Read() && xml.NodeType != XmlNodeType.EndElement)
{
if (xml.NodeType == XmlNodeType.Element && xml.Name == "object")
{
nodes.Add(new XmlAddCAGObject(this, xml));
}
else if (xml.NodeType == XmlNodeType.Element && xml.Name == "category")
{
if (!xml.IsEmptyElement)
{
nodes.Add(new XmlAddCAGCategory(this, xml));
}
}
else
{
xml.Skip();
}
}
}
catch (Exception ex)
{
Console.WriteLine("XmlCategorizedAddGump: Corrupted Data/objects.xml file detected. Not all XmlCAG objects loaded. {0}", ex);
}
Nodes = (XmlAddCAGNode[])nodes.ToArray(typeof(XmlAddCAGNode));
}
}
private static XmlAddCAGCategory m_Root;
public static XmlAddCAGCategory Root => m_Root ?? (m_Root = Load("Data/objects.xml"));
public static XmlAddCAGCategory Load(string path)
{
if (File.Exists(path))
{
var xml = new XmlTextReader(path)
{
WhitespaceHandling = WhitespaceHandling.None
};
while (xml.Read())
{
if (xml.Name == "category" && xml.NodeType == XmlNodeType.Element)
{
var cat = new XmlAddCAGCategory(null, xml);
xml.Close();
return cat;
}
}
}
return new XmlAddCAGCategory();
}
}
public class XmlCategorizedAddGump : Gump
{
public static bool OldStyle = PropsConfig.OldStyle;
public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX;
public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY;
public static readonly int TextHue = PropsConfig.TextHue;
public static readonly int TextOffsetX = PropsConfig.TextOffsetX;
public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID;
public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID;
public static readonly int EntryGumpID = PropsConfig.EntryGumpID;
public static readonly int BackGumpID = PropsConfig.BackGumpID;
public static readonly int SetGumpID = PropsConfig.SetGumpID;
public static readonly int SetWidth = PropsConfig.SetWidth;
public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY /*+ (((EntryHeight - 20) / 2) / 2)*/;
public static readonly int SetButtonID1 = PropsConfig.SetButtonID1;
public static readonly int SetButtonID2 = PropsConfig.SetButtonID2;
public static readonly int PrevWidth = PropsConfig.PrevWidth;
public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY /*+ (((EntryHeight - 20) / 2) / 2)*/;
public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1;
public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2;
public static readonly int NextWidth = PropsConfig.NextWidth;
public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY /*+ (((EntryHeight - 20) / 2) / 2)*/;
public static readonly int NextButtonID1 = PropsConfig.NextButtonID1;
public static readonly int NextButtonID2 = PropsConfig.NextButtonID2;
public static readonly int OffsetSize = PropsConfig.OffsetSize;
public static readonly int EntryHeight = 24;
public static readonly int BorderSize = PropsConfig.BorderSize;
private static readonly bool PrevLabel = false, NextLabel = false;
private static readonly int PrevLabelOffsetX = PrevWidth + 1;
private const int PrevLabelOffsetY = 0;
private const int NextLabelOffsetX = -29;
private const int NextLabelOffsetY = 0;
private const int EntryWidth = 180;
private const int EntryCount = 15;
private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize;
private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize;
private readonly Mobile m_Owner;
private readonly XmlAddCAGCategory m_Category;
private int m_Page;
private readonly int m_Index;
private readonly Gump m_Gump;
public XmlCategorizedAddGump(Mobile owner, int index, Gump gump) : this(owner, XmlAddCAGCategory.Root, 0, index, gump)
{
}
public XmlCategorizedAddGump(Mobile owner, XmlAddCAGCategory category, int page, int index, Gump gump) : base(GumpOffsetX, GumpOffsetY)
{
if (category == null)
{
category = XmlAddCAGCategory.Root;
page = 0;
}
owner.CloseGump<WhoGump>();
m_Owner = owner;
m_Category = category;
m_Index = index;
m_Gump = gump;
if (gump is XmlAddGump xmladdgump)
{
if (xmladdgump.defs != null)
{
xmladdgump.defs.CurrentCategory = category;
xmladdgump.defs.CurrentCategoryPage = page;
}
}
Initialize(page);
}
public void Initialize(int page)
{
m_Page = page;
var nodes = m_Category.Nodes;
var count = nodes.Length - page * EntryCount;
if (count < 0)
{
count = 0;
}
else if (count > EntryCount)
{
count = EntryCount;
}
var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1);
AddPage(0);
AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID);
AddImageTiled(BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID);
var x = BorderSize + OffsetSize;
var y = BorderSize + OffsetSize;
if (OldStyle)
{
AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID);
}
else
{
AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID);
}
if (m_Category.Parent != null)
{
AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1);
if (PrevLabel)
{
AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous");
}
}
x += PrevWidth + OffsetSize;
var emptyWidth = TotalWidth - PrevWidth * 2 - NextWidth - OffsetSize * 5 - (OldStyle ? SetWidth + OffsetSize : 0);
if (!OldStyle)
{
AddImageTiled(x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, EntryGumpID);
}
AddHtml(x + TextOffsetX, y + (EntryHeight - 20) / 2, emptyWidth - TextOffsetX, EntryHeight,
$"<center>{m_Category.Caption}</center>");
x += emptyWidth + OffsetSize;
if (OldStyle)
{
AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID);
}
else
{
AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID);
}
if (page > 0)
{
AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 2);
if (PrevLabel)
{
AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous");
}
}
x += PrevWidth + OffsetSize;
if (!OldStyle)
{
AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID);
}
if ((page + 1) * EntryCount < nodes.Length)
{
AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 3, GumpButtonType.Reply, 1);
if (NextLabel)
{
AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next");
}
}
for (int i = 0, index = page * EntryCount; i < EntryCount && index < nodes.Length; ++i, ++index)
{
x = BorderSize + OffsetSize;
y += EntryHeight + OffsetSize;
var node = nodes[index];
AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID);
AddLabelCropped(x + TextOffsetX, y + (EntryHeight - 20) / 2, EntryWidth - TextOffsetX, EntryHeight, TextHue, node.Caption);
x += EntryWidth + OffsetSize;
if (SetGumpID != 0)
{
AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID);
}
AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 4);
if (node is XmlAddCAGObject obj)
{
var itemID = obj.ItemID;
var bounds = ItemBounds.Table[itemID];
if (itemID != 1 && bounds.Height < EntryHeight * 2)
{
if (bounds.Height < EntryHeight)
{
AddItem(x - OffsetSize - 22 - i % 2 * 44 - bounds.Width / 2 - bounds.X, y + EntryHeight / 2 - bounds.Height / 2 - bounds.Y, itemID);
}
else
{
AddItem(x - OffsetSize - 22 - i % 2 * 44 - bounds.Width / 2 - bounds.X, y + EntryHeight - 1 - bounds.Height - bounds.Y, itemID);
}
}
}
}
}
public override void OnResponse(NetState state, RelayInfo info)
{
var from = m_Owner;
switch (info.ButtonID)
{
case 0: // Closed
{
return;
}
case 1: // Up
{
if (m_Category.Parent != null)
{
var index = Array.IndexOf(m_Category.Parent.Nodes, m_Category) / EntryCount;
if (index < 0)
{
index = 0;
}
from.SendGump(new XmlCategorizedAddGump(from, m_Category.Parent, index, m_Index, m_Gump));
}
break;
}
case 2: // Previous
{
if (m_Page > 0)
{
from.SendGump(new XmlCategorizedAddGump(from, m_Category, m_Page - 1, m_Index, m_Gump));
}
break;
}
case 3: // Next
{
if ((m_Page + 1) * EntryCount < m_Category.Nodes.Length)
{
from.SendGump(new XmlCategorizedAddGump(from, m_Category, m_Page + 1, m_Index, m_Gump));
}
break;
}
default:
{
var index = m_Page * EntryCount + (info.ButtonID - 4);
if (index >= 0 && index < m_Category.Nodes.Length)
{
m_Category.Nodes[index].OnClick(from, m_Page, m_Index, m_Gump);
}
break;
}
}
}
}

View file

@ -0,0 +1,266 @@
using Server.Mobiles;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Gumps;
public class XmlPartialCategorizedAddGump : Gump
{
private readonly string m_SearchString;
private readonly ArrayList m_SearchResults;
private readonly int m_Page;
private readonly Gump m_Gump;
private readonly int m_EntryIndex;
private readonly XmlSpawner m_Spawner;
public XmlPartialCategorizedAddGump(Mobile from, string searchString, int page, ArrayList searchResults, bool explicitSearch, int entryindex, Gump gump) : base(50, 50)
{
if (gump is XmlSpawnerGump spawnerGump)
{
// keep track of the spawner for xmlspawnergumps
m_Spawner = spawnerGump.m_Spawner;
}
// keep track of the gump
m_Gump = gump;
m_SearchString = searchString;
m_SearchResults = searchResults;
m_Page = page;
m_EntryIndex = entryindex;
from.CloseGump<XmlPartialCategorizedAddGump>();
AddPage(0);
AddBackground(0, 0, 420, 280, 5054);
AddImageTiled(10, 10, 400, 20, 2624);
AddAlphaRegion(10, 10, 400, 20);
AddImageTiled(41, 11, 184, 18, 0xBBC);
AddImageTiled(42, 12, 182, 16, 2624);
AddAlphaRegion(42, 12, 182, 16);
AddButton(10, 9, 4011, 4013, 1);
AddTextEntry(44, 10, 180, 20, 0x480, 0, searchString);
AddHtmlLocalized(230, 10, 100, 20, 3010005, 0x7FFF);
AddImageTiled(10, 40, 400, 200, 2624);
AddAlphaRegion(10, 40, 400, 200);
if (searchResults.Count > 0)
{
for (var i = page * 10; i < (page + 1) * 10 && i < searchResults.Count; ++i)
{
var index = i % 10;
var se = (SearchEntry)searchResults[i];
var labelstr = se.EntryType.Name;
if (se.Parameters.Length > 0)
{
for (var j = 0; j < se.Parameters.Length; j++)
{
labelstr += $", {se.Parameters[j].Name}";
}
}
AddLabel(44, 39 + index * 20, 0x480, labelstr);
AddButton(10, 39 + index * 20, 4023, 4025, 4 + i);
}
}
else
{
AddLabel(15, 44, 0x480, explicitSearch ? "Nothing matched your search terms." : "No results to display.");
}
AddImageTiled(10, 250, 400, 20, 2624);
AddAlphaRegion(10, 250, 400, 20);
if (m_Page > 0)
{
AddButton(10, 249, 4014, 4016, 2);
}
else
{
AddImage(10, 249, 4014);
}
AddHtmlLocalized(44, 250, 170, 20, 1061028, m_Page > 0 ? 0x7FFF : 0x5EF7); // Previous page
if ((m_Page + 1) * 10 < searchResults.Count)
{
AddButton(210, 249, 4005, 4007, 3);
}
else
{
AddImage(210, 249, 4005);
}
AddHtmlLocalized(244, 250, 170, 20, 1061027, (m_Page + 1) * 10 < searchResults.Count ? 0x7FFF : 0x5EF7); // Next page
}
private static readonly Type typeofItem = typeof(Item), typeofMobile = typeof(Mobile);
private class SearchEntry
{
public Type EntryType;
public ParameterInfo[] Parameters;
}
private static void Match(string match, IReadOnlyList<Type> types, IList results)
{
if (match.Length == 0)
{
return;
}
match = match.ToLower();
for (var i = 0; i < types.Count; ++i)
{
var t = types[i];
if ((typeofMobile.IsAssignableFrom(t) || typeofItem.IsAssignableFrom(t)) && t.Name.ToLower().IndexOf(match) >= 0 && !results.Contains(t))
{
var ctors = t.GetConstructors();
for (var j = 0; j < ctors.Length; ++j)
{
if (/*ctors[j].GetParameters().Length == 0 && */ ctors[j].IsDefined(typeof(ConstructibleAttribute), false))
{
var s = new SearchEntry
{
EntryType = t,
Parameters = ctors[j].GetParameters()
};
//results.Add(t);
results.Add(s);
//break;
}
}
}
}
}
public static ArrayList Match(string match)
{
var results = new ArrayList();
Type[] types;
var asms = AssemblyHandler.Assemblies;
for (var i = 0; i < asms.Length; ++i)
{
types = AssemblyHandler.GetTypeCache(asms[i]).Types;
Match(match, types, results);
}
types = AssemblyHandler.GetTypeCache(Core.Assembly).Types;
Match(match, types, results);
results.Sort(new TypeNameComparer());
return results;
}
private class TypeNameComparer : IComparer
{
public int Compare(object x, object y)
{
var a = x as SearchEntry;
var b = y as SearchEntry;
return a.EntryType.Name.CompareTo(b.EntryType.Name);
}
}
public override void OnResponse(Network.NetState sender, RelayInfo info)
{
var from = sender.Mobile;
switch (info.ButtonID)
{
case 1: // Search
{
var te = info.GetTextEntry(0);
var match = te == null ? "" : te.Text.Trim();
if (match.Length < 3)
{
from.SendMessage("Invalid search string.");
from.SendGump(new XmlPartialCategorizedAddGump(from, match, m_Page, m_SearchResults, false, m_EntryIndex, m_Gump));
}
else
{
from.SendGump(new XmlPartialCategorizedAddGump(from, match, 0, Match(match), true, m_EntryIndex, m_Gump));
}
break;
}
case 2: // Previous page
{
if (m_Page > 0)
{
from.SendGump(new XmlPartialCategorizedAddGump(from, m_SearchString, m_Page - 1, m_SearchResults, true, m_EntryIndex, m_Gump));
}
break;
}
case 3: // Next page
{
if ((m_Page + 1) * 10 < m_SearchResults.Count)
{
from.SendGump(new XmlPartialCategorizedAddGump(from, m_SearchString, m_Page + 1, m_SearchResults, true, m_EntryIndex, m_Gump));
}
break;
}
default:
{
var index = info.ButtonID - 4;
if (index >= 0 && index < m_SearchResults.Count)
{
var type = ((SearchEntry)m_SearchResults[index]).EntryType;
if (m_Gump is XmlAddGump mXmlAddGump && type != null)
{
if (mXmlAddGump.defs?.NameList != null && m_EntryIndex >= 0 && m_EntryIndex < mXmlAddGump.defs.NameList.Length)
{
mXmlAddGump.defs.NameList[m_EntryIndex] = type.Name;
XmlAddGump.Refresh(from, true);
}
}
else if (m_Spawner != null && type != null)
{
var xg = m_Spawner.SpawnerGump;
if (xg != null)
{
xg.Rentry = new XmlSpawnerGump.ReplacementEntry
{
Typename = type.Name,
Index = m_EntryIndex,
Color = 0x1436
};
Timer.DelayCall(TimeSpan.Zero, XmlSpawnerGump.RefreshSpawnerGumps, from);
//from.CloseGump<XmlSpawnerGump>();
//from.SendGump(new XmlSpawnerGump(xg.m_Spawner, xg.X, xg.Y, xg.m_ShowGump, xg.xoffset, xg.page, xg.Rentry));
}
}
}
break;
}
}
}
}