using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using Server.Commands.Generic; using Server.Engines.BulkOrders; using Server.Items; using Server.Network; namespace Server.Commands { public class Docs { private const int Iron = 0xCCCCDD; private const int DullCopper = 0xAAAAAA; private const int ShadowIron = 0x777799; private const int Copper = 0xDDCC99; private const int Bronze = 0xAA8866; private const int Gold = 0xDDCC55; private const int Agapite = 0xDDAAAA; private const int Verite = 0x99CC77; private const int Valorite = 0x88AABB; private const int Cloth = 0xDDDDDD; private const int Plain = 0xCCAA88; private const int SpinedAOS = 0x99BBBB; private const int HornedAOS = 0xCC8888; private const int BarbedAOS = 0xAABBAA; private const int SpinedLBR = 0xAA8833; private const int HornedLBR = 0xBBBBAA; private const int BarbedLBR = 0xCCAA88; private const string HtmlNewLine = " "; private const string RefString = "ref "; private const string GetString = " get;"; private const string SetString = " set;"; private const string InString = "in "; private const string OutString = "out "; private const string VirtString = "virtual "; private const string CtorString = "(ctor) "; private const string StaticString = "(static) "; private static Dictionary m_Types; private static Dictionary> m_Namespaces; private static readonly char[] ReplaceChars = "<>".ToCharArray(); private static readonly string m_RootDirectory = Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]); private static readonly string[,] m_Aliases = { { "System.Object", "object" }, { "System.String", "string" }, { "System.Boolean", "bool" }, { "System.Byte", "byte" }, { "System.SByte", "sbyte" }, { "System.Int16", "short" }, { "System.UInt16", "ushort" }, { "System.Int32", "int" }, { "System.UInt32", "uint" }, { "System.Int64", "long" }, { "System.UInt64", "ulong" }, { "System.Single", "float" }, { "System.Double", "double" }, { "System.Decimal", "decimal" }, { "System.Char", "char" }, { "System.Void", "void" } }; private static readonly int m_AliasLength = m_Aliases.GetLength(0); private static readonly Type typeofItem = typeof(Item); private static readonly Type typeofMobile = typeof(Mobile); private static readonly Type typeofMap = typeof(Map); private static readonly Type typeofCustomEnum = typeof(CustomEnumAttribute); private static readonly object[,] m_Tooltips = { { typeof(byte), "Numeric value in the range from 0 to 255, inclusive." }, { typeof(sbyte), "Numeric value in the range from negative 128 to positive 127, inclusive." }, { typeof(ushort), "Numeric value in the range from 0 to 65,535, inclusive." }, { typeof(short), "Numeric value in the range from negative 32,768 to positive 32,767, inclusive." }, { typeof(uint), "Numeric value in the range from 0 to 4,294,967,295, inclusive." }, { typeof(int), "Numeric value in the range from negative 2,147,483,648 to positive 2,147,483,647, inclusive." }, { typeof(ulong), "Numeric value in the range from 0 through about 10^20." }, { typeof(long), "Numeric value in the approximate range from negative 10^19 through 10^19." }, { typeof(string), "Text value. To specify a value containing spaces, encapsulate the value in quote characters:{0}{0}"Spaced text example"" }, { typeof(bool), "Boolean value which can be either True or False." }, { typeof(Map), "Map or facet name. Possible values include:{0}{0}- Felucca{0}- Trammel{0}- Ilshenar{0}- Malas" }, { typeof(Poison), "Poison name or level. Possible values include:{0}{0}- Lesser{0}- Regular{0}- Greater{0}- Deadly{0}- Lethal" }, { typeof(Point3D), "Three-dimensional coordinate value. Format as follows:{0}{0}"(, , )"" } }; public static void Initialize() { CommandSystem.Register("DocGen", AccessLevel.Administrator, DocGen_OnCommand); } [Usage("DocGen")] [Description("Generates RunUO documentation.")] private static void DocGen_OnCommand(CommandEventArgs e) { World.Broadcast(0x35, true, "Documentation is being generated, please wait."); Console.WriteLine("Documentation is being generated, please wait."); NetState.Pause(); var startTime = DateTime.UtcNow; var generated = Document(); var endTime = DateTime.UtcNow; NetState.Resume(); if (generated) { World.Broadcast( 0x35, true, "Documentation has been completed. The entire process took {0:F1} seconds.", (endTime - startTime).TotalSeconds ); Console.WriteLine("Documentation complete."); } else { World.Broadcast( 0x35, true, "Docmentation failed: Documentation directories are locked and in use. Please close all open files and directories and try again." ); Console.WriteLine("Documentation failed."); } } private static void LoadTypes(Assembly a, Assembly[] asms) { var types = a.GetTypes(); for (var i = 0; i < types.Length; ++i) { var type = types[i]; var nspace = type.Namespace; if (nspace == null || type.IsSpecialName) { continue; } var info = new TypeInfo(type); m_Types[type] = info; if (!m_Namespaces.TryGetValue(nspace, out var nspaces)) { m_Namespaces[nspace] = nspaces = new List(); } nspaces.Add(info); var baseType = info.m_BaseType; if (baseType != null && InAssemblies(baseType, asms)) { m_Types.TryGetValue(baseType, out var baseInfo); if (baseInfo == null) { m_Types[baseType] = baseInfo = new TypeInfo(baseType); } baseInfo.m_Derived ??= new List(); baseInfo.m_Derived.Add(info); } var decType = info.m_Declaring; if (decType != null) { m_Types.TryGetValue(decType, out var decInfo); if (decInfo == null) { m_Types[decType] = decInfo = new TypeInfo(decType); } decInfo.m_Nested ??= new List(); decInfo.m_Nested.Add(info); } for (var j = 0; j < info.m_Interfaces.Length; ++j) { var iface = info.m_Interfaces[j]; if (!InAssemblies(iface, asms)) { continue; } m_Types.TryGetValue(iface, out var ifaceInfo); if (ifaceInfo == null) { m_Types[iface] = ifaceInfo = new TypeInfo(iface); } ifaceInfo.m_Derived ??= new List(); ifaceInfo.m_Derived.Add(info); } } } private static bool InAssemblies(Type t, Assembly[] asms) { var a = t.Assembly; for (var i = 0; i < asms.Length; ++i) { if (a == asms[i]) { return true; } } return false; } private static void DocumentLoadedTypes() { using var indexHtml = GetWriter("docs/", "overview.html"); indexHtml.WriteLine(""); indexHtml.WriteLine(" "); indexHtml.WriteLine(" RunUO Documentation - Class Overview"); indexHtml.WriteLine(" "); indexHtml.WriteLine( " " ); indexHtml.WriteLine("

Back to the index

"); indexHtml.WriteLine("

Namespaces

"); var nspaces = new SortedList>(m_Namespaces); foreach (var kvp in nspaces) { kvp.Value.Sort(new TypeComparer()); SaveNamespace(kvp.Key, kvp.Value, indexHtml); } indexHtml.WriteLine(" "); indexHtml.WriteLine(""); } private static void SaveNamespace(string name, List types, StreamWriter indexHtml) { var fileName = GetFileName("docs/namespaces/", name, ".html"); indexHtml.WriteLine(" {1}
", fileName, name); using var nsHtml = GetWriter("docs/namespaces/", fileName); nsHtml.WriteLine(""); nsHtml.WriteLine(" "); nsHtml.WriteLine(" RunUO Documentation - Class Overview - {0}", name); nsHtml.WriteLine(" "); nsHtml.WriteLine( " " ); nsHtml.WriteLine("

Back to the namespace index

"); nsHtml.WriteLine("

{0}

", name); for (var i = 0; i < types.Count; ++i) { SaveType(types[i], nsHtml, fileName, name); } nsHtml.WriteLine(" "); nsHtml.WriteLine(""); } private static void SaveType(TypeInfo info, StreamWriter nsHtml, string nsFileName, string nsName) { if (info.m_Declaring == null) { nsHtml.WriteLine($" {info.LinkName("../types/")}
"); } using var typeHtml = GetWriter(info.FileName); typeHtml.WriteLine(""); typeHtml.WriteLine(" "); typeHtml.WriteLine(" RunUO Documentation - Class Overview - {0}", info.TypeName); typeHtml.WriteLine(" "); typeHtml.WriteLine( " " ); typeHtml.WriteLine("

Back to {1}

", nsFileName, nsName); if (info.m_Type.IsEnum) { WriteEnum(info, typeHtml); } else { WriteType(info, typeHtml); } typeHtml.WriteLine(" "); typeHtml.WriteLine(""); } public static void FormatGeneric(Type type, out string typeName, out string fileName, out string linkName) { string name = null; string fnam = null; string link = null; if (type.IsGenericType) { var index = type.Name.IndexOf('`'); if (index > 0) { var rootType = type.Name.Substring(0, index); var nameBuilder = new StringBuilder(rootType); var fnamBuilder = new StringBuilder($"docs/types/{SanitizeType(rootType)}"); StringBuilder linkBuilder; linkBuilder = DontLink(type) ? new StringBuilder($"{rootType}") : new StringBuilder($"{rootType}"); nameBuilder.Append("<"); fnamBuilder.Append("-"); linkBuilder.Append("<"); var typeArguments = type.GetGenericArguments(); for (var i = 0; i < typeArguments.Length; i++) { if (i != 0) { nameBuilder.Append(','); fnamBuilder.Append(','); linkBuilder.Append(','); } var sanitizedName = SanitizeType(typeArguments[i].Name); var aliasedName = AliasForName(sanitizedName); nameBuilder.Append(sanitizedName); fnamBuilder.Append("T"); if (DontLink(typeArguments[i])) { linkBuilder.Append($"{aliasedName}"); } else { linkBuilder.Append( $"{aliasedName}" ); } } nameBuilder.Append(">"); fnamBuilder.Append("-"); linkBuilder.Append(">"); name = nameBuilder.ToString(); fnam = fnamBuilder.ToString(); link = linkBuilder.ToString(); } } typeName = name ?? type.Name; fileName = fnam == null ? $"docs/types/{SanitizeType(type.Name)}.html" : $"{fnam}.html"; if (link == null) { linkName = DontLink(type) ? $"{SanitizeType(type.Name)}" : $"{SanitizeType(type.Name)}"; } else { linkName = link; } // Console.WriteLine( typeName+":"+fileName+":"+linkName ); } public static string SanitizeType(string name) { var anonymousType = name.Contains("<"); var sb = new StringBuilder(name); for (var i = 0; i < ReplaceChars.Length; ++i) { sb.Replace(ReplaceChars[i], '-'); } if (anonymousType) { return $"(Anonymous-Type){sb}"; } return sb.ToString(); } public static string AliasForName(string name) { for (var i = 0; i < m_AliasLength; ++i) { if (m_Aliases[i, 0] == name) { return m_Aliases[i, 1]; } } return name; } /* // For stuff we don't want to links to private static string[] m_DontLink = new string[] { "List", "Stack", "Queue", "Dictionary", "LinkedList", "SortedList", "SortedDictionary", "IComparable", "IComparer", "ICloneable", "Type" }; public static bool DontLink( string name ) { foreach( string dontLink in m_DontLink ) if (dontLink == name ) return true; return false; } */ public static bool DontLink(Type type) { if (type.Name == "T" || string.IsNullOrEmpty(type.Namespace) || m_Namespaces == null) { return true; } if (type.Namespace.StartsWith("Server")) { return false; } return !m_Namespaces.ContainsKey(type.Namespace); } public static string GetFileName(string root, string name, string ext) { if (name.IndexOfAny(ReplaceChars) >= 0) { var sb = new StringBuilder(name); for (var i = 0; i < ReplaceChars.Length; ++i) { sb.Replace(ReplaceChars[i], '-'); } name = sb.ToString(); } var index = 0; var file = string.Concat(name, ext); while (File.Exists(Path.Combine(root, file))) { file = string.Concat(name, ++index, ext); } return file; } private static void EnsureDirectory(string path) { path = Path.Combine(m_RootDirectory, path); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } private static void DeleteDirectory(string path) { path = Path.Combine(m_RootDirectory, path); if (Directory.Exists(path)) { Directory.Delete(path, true); } } private static StreamWriter GetWriter(string root, string name) => new StreamWriter(Path.Combine(Path.Combine(m_RootDirectory, root), name)); private static StreamWriter GetWriter(string path) => new StreamWriter(Path.Combine(m_RootDirectory, path)); public static string GetPair(Type varType, string name, bool ignoreRef) { var prepend = ""; var append = new StringBuilder(); var realType = varType; if (varType.IsByRef) { if (!ignoreRef) { prepend = RefString; } realType = varType.GetElementType(); } if (realType?.IsPointer == true) { if (realType.IsArray) { append.Append('*'); do { append.Append('['); for (var i = 1; i < realType.GetArrayRank(); ++i) { append.Append(','); } append.Append(']'); realType = realType.GetElementType(); } while (realType?.IsArray == true); append.Append(' '); } else { realType = realType.GetElementType(); append.Append(" *"); } } else if (realType?.IsArray == true) { do { append.Append('['); for (var i = 1; i < realType.GetArrayRank(); ++i) { append.Append(','); } append.Append(']'); realType = realType.GetElementType(); } while (realType?.IsArray == true); append.Append(' '); } else { append.Append(' '); } var fullName = realType?.FullName ?? "(-null-)"; string aliased = null; // = realType.Name; if (realType != null && m_Types.TryGetValue(realType, out var info)) { aliased = $"{info.LinkName(null)}"; } else { if (realType?.IsGenericType == true) { FormatGeneric(realType, out _, out _, out var linkName); aliased = linkName.Replace("@directory@", null); } else { for (var i = 0; i < m_AliasLength; ++i) { if (m_Aliases[i, 0] == fullName) { aliased = m_Aliases[i, 1]; break; } } } aliased ??= realType?.Name ?? ""; } return string.Concat(prepend, aliased, append, name); } private static bool Document() { try { DeleteDirectory("docs/"); } catch { return false; } EnsureDirectory("docs/"); EnsureDirectory("docs/namespaces/"); EnsureDirectory("docs/types/"); EnsureDirectory("docs/bods/"); GenerateStyles(); GenerateIndex(); DocumentCommands(); DocumentKeywords(); DocumentBodies(); DocumentBulkOrders(); m_Types = new Dictionary(); m_Namespaces = new Dictionary>(); var assemblies = new List { Core.Assembly }; foreach (var asm in AssemblyHandler.Assemblies) { assemblies.Add(asm); } var asms = assemblies.ToArray(); for (var i = 0; i < asms.Length; ++i) { LoadTypes(asms[i], asms); } DocumentLoadedTypes(); DocumentConstructibleObjects(); return true; } private static void AddIndexLink(StreamWriter html, string filePath, string label, string desc) { html.WriteLine("

{2}

", filePath, desc, label); } private static void GenerateStyles() { using var css = GetWriter("docs/", "styles.css"); css.WriteLine("body { background-color: #FFFFFF; font-family: verdana, arial; font-size: 11px; }"); css.WriteLine("a { color: #28435E; }"); css.WriteLine("a:hover { color: #4878A9; }"); css.WriteLine("td.header { background-color: #9696AA; font-weight: bold; font-size: 12px; }"); css.WriteLine("td.lentry { background-color: #D7D7EB; width: 10%; }"); css.WriteLine("td.rentry { background-color: #FFFFFF; width: 90%; }"); css.WriteLine("td.entry { background-color: #FFFFFF; }"); css.WriteLine("td { font-size: 11px; }"); css.WriteLine(".tbl-border { background-color: #46465A; }"); css.WriteLine("td.ir {{ background-color: #{0:X6}; }}", Iron); css.WriteLine("td.du {{ background-color: #{0:X6}; }}", DullCopper); css.WriteLine("td.sh {{ background-color: #{0:X6}; }}", ShadowIron); css.WriteLine("td.co {{ background-color: #{0:X6}; }}", Copper); css.WriteLine("td.br {{ background-color: #{0:X6}; }}", Bronze); css.WriteLine("td.go {{ background-color: #{0:X6}; }}", Gold); css.WriteLine("td.ag {{ background-color: #{0:X6}; }}", Agapite); css.WriteLine("td.ve {{ background-color: #{0:X6}; }}", Verite); css.WriteLine("td.va {{ background-color: #{0:X6}; }}", Valorite); css.WriteLine("td.cl {{ background-color: #{0:X6}; }}", Cloth); css.WriteLine("td.pl {{ background-color: #{0:X6}; }}", Plain); css.WriteLine("td.sp {{ background-color: #{0:X6}; }}", Core.AOS ? SpinedAOS : SpinedLBR); css.WriteLine("td.ho {{ background-color: #{0:X6}; }}", Core.AOS ? HornedAOS : HornedLBR); css.WriteLine("td.ba {{ background-color: #{0:X6}; }}", Core.AOS ? BarbedAOS : BarbedLBR); } private static void GenerateIndex() { using var html = GetWriter("docs/", "index.html"); html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Index"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); AddIndexLink( html, "commands.html", "Commands", "Every available command. This contains command name, usage, aliases, and description." ); AddIndexLink( html, "objects.html", "Constructible Objects", "Every constructible item or npc. This contains object name and usage. Hover mouse over parameters to see type description." ); AddIndexLink( html, "keywords.html", "Speech Keywords", "Lists speech keyword numbers and associated match patterns. These are used in some scripts for multi-language matching of client speech." ); AddIndexLink( html, "bodies.html", "Body List", "Every usable body number and name. Table is generated from a UO:3D client datafile. If you do not have UO:3D installed, this may be blank." ); AddIndexLink( html, "overview.html", "Class Overview", "Scripting reference. Contains every class type and contained methods in the core and scripts." ); AddIndexLink( html, "bods/bod_smith_rewards.html", "Bulk Order Rewards: Smithing", "Reference table for large and small smithing bulk order deed rewards." ); AddIndexLink( html, "bods/bod_tailor_rewards.html", "Bulk Order Rewards: Tailoring", "Reference table for large and small tailoring bulk order deed rewards." ); html.WriteLine(" "); html.WriteLine(""); } private static void DocumentBulkOrders() { using (var html = GetWriter("docs/bods/", "bod_smith_rewards.html")) { html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Bulk Orders - Smith Rewards"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); SmallBOD sbod = new SmallSmithBOD(); sbod.Type = typeof(Katana); sbod.Material = BulkMaterialType.None; sbod.AmountMax = 10; WriteSmithBODHeader(html, "(Small) Weapons"); sbod.RequireExceptional = false; DocumentSmithBOD(html, sbod.ComputeRewards(true), "10, 15, 20: Normal", sbod.Material); sbod.RequireExceptional = true; DocumentSmithBOD(html, sbod.ComputeRewards(true), "10, 15, 20: Exceptional", sbod.Material); WriteSmithBODFooter(html); html.WriteLine("

"); html.WriteLine("

"); sbod.Type = typeof(PlateArms); WriteSmithBODHeader(html, "(Small) Armor: Normal"); sbod.RequireExceptional = false; for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat) { sbod.Material = mat; sbod.AmountMax = 10; DocumentSmithBOD(html, sbod.ComputeRewards(true), "10, 15, 20", sbod.Material); } WriteSmithBODFooter(html); html.WriteLine("

"); WriteSmithBODHeader(html, "(Small) Armor: Exceptional"); sbod.RequireExceptional = true; for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat) { sbod.Material = mat; for (var amt = 15; amt <= 20; amt += 5) { sbod.AmountMax = amt; DocumentSmithBOD(html, sbod.ComputeRewards(true), amt == 20 ? "20" : "10, 15", sbod.Material); } } WriteSmithBODFooter(html); html.WriteLine("

"); html.WriteLine("

"); sbod.Delete(); WriteSmithLBOD(html, "Ringmail", LargeBulkEntry.LargeRing); WriteSmithLBOD(html, "Chainmail", LargeBulkEntry.LargeChain); WriteSmithLBOD(html, "Platemail", LargeBulkEntry.LargePlate); html.WriteLine(" "); html.WriteLine(""); } using (var html = GetWriter("docs/bods/", "bod_tailor_rewards.html")) { html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Bulk Orders - Tailor Rewards"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); SmallBOD sbod = new SmallTailorBOD(); WriteTailorBODHeader(html, "Small Bulk Order"); html.WriteLine(" "); html.WriteLine(" Regular: 10, 15"); html.WriteLine(" "); sbod.AmountMax = 10; sbod.RequireExceptional = false; sbod.Type = typeof(SkullCap); sbod.Material = BulkMaterialType.None; DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type); sbod.Type = typeof(LeatherCap); for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat) { if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite) { continue; } sbod.Material = mat; DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type); } html.WriteLine(" "); html.WriteLine(" Regular: 20"); html.WriteLine(" "); sbod.AmountMax = 20; sbod.RequireExceptional = false; sbod.Type = typeof(SkullCap); sbod.Material = BulkMaterialType.None; DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type); sbod.Type = typeof(LeatherCap); for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat) { if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite) { continue; } sbod.Material = mat; DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type); } html.WriteLine(" "); html.WriteLine( " Exceptional: 10, 15" ); html.WriteLine(" "); sbod.AmountMax = 10; sbod.RequireExceptional = true; sbod.Type = typeof(SkullCap); sbod.Material = BulkMaterialType.None; DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type); sbod.Type = typeof(LeatherCap); for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat) { if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite) { continue; } sbod.Material = mat; DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type); } html.WriteLine(" "); html.WriteLine(" Exceptional: 20"); html.WriteLine(" "); sbod.AmountMax = 20; sbod.RequireExceptional = true; sbod.Type = typeof(SkullCap); sbod.Material = BulkMaterialType.None; DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type); sbod.Type = typeof(LeatherCap); for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat) { if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite) { continue; } sbod.Material = mat; DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type); } WriteTailorBODFooter(html); html.WriteLine("

"); html.WriteLine("

"); sbod.Delete(); WriteTailorLBOD(html, "Large Bulk Order: 4-part", LargeBulkEntry.Gypsy, true, true); WriteTailorLBOD(html, "Large Bulk Order: 5-part", LargeBulkEntry.TownCrier, true, true); WriteTailorLBOD(html, "Large Bulk Order: 6-part", LargeBulkEntry.MaleLeatherSet, false, true); html.WriteLine(" "); html.WriteLine(""); } } private static void WriteTailorLBOD( StreamWriter html, string name, SmallBulkEntry[] entries, bool expandCloth, bool expandPlain ) { WriteTailorBODHeader(html, name); LargeBOD lbod = new LargeTailorBOD(); lbod.Entries = LargeBulkEntry.ConvertEntries(lbod, entries); var type = entries[0].Type; var showCloth = !(type.IsSubclassOf(typeof(BaseArmor)) || type.IsSubclassOf(typeof(BaseShoes))); html.WriteLine(" "); html.WriteLine(" Regular"); html.WriteLine(" "); lbod.RequireExceptional = false; lbod.AmountMax = 10; if (showCloth) { lbod.Material = BulkMaterialType.None; if (expandCloth) { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type); lbod.AmountMax = 20; DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type); } else { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, type); } } lbod.Material = BulkMaterialType.None; if (expandPlain) { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap)); lbod.AmountMax = 20; DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, typeof(LeatherCap)); } else { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap)); } for (var mat = BulkMaterialType.Spined; mat <= BulkMaterialType.Barbed; ++mat) { lbod.Material = mat; lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type); lbod.AmountMax = 20; DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type); } html.WriteLine(" "); html.WriteLine(" Exceptional"); html.WriteLine(" "); lbod.RequireExceptional = true; lbod.AmountMax = 10; if (showCloth) { lbod.Material = BulkMaterialType.None; if (expandCloth) { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type); lbod.AmountMax = 20; DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type); } else { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, type); } } lbod.Material = BulkMaterialType.None; if (expandPlain) { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap)); lbod.AmountMax = 20; DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, typeof(LeatherCap)); } else { lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap)); } for (var mat = BulkMaterialType.Spined; mat <= BulkMaterialType.Barbed; ++mat) { lbod.Material = mat; lbod.AmountMax = 10; DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type); lbod.AmountMax = 20; DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type); } WriteTailorBODFooter(html); html.WriteLine("

"); html.WriteLine("

"); } private static void WriteTailorBODHeader(StreamWriter html, string title) { html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" ", title); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); } private static void WriteTailorBODFooter(StreamWriter html) { html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine("
{0}
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
Power Scrolls
\"Small
\"Medium
\"Light
\"Dark
\"Brown
\"Polar
\"Clothing
Runic Kits
+5
+10
+15
+20
\"Runic
\"Runic
\"Runic
 
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
+5
+10
+15
+20
\"Small
\"Medium
\"Light
\"Dark
\"Brown
\"Polar
\"Clothing
\"Runic
\"Runic
\"Runic
Power Scrolls
Runic Kits
"); } private static void DocumentTailorBOD( StreamWriter html, List items, string amt, BulkMaterialType material, Type type ) { var rewards = new bool[20]; for (var i = 0; i < items.Count; ++i) { var item = items[i].Construct(); if (item is Sandals) { rewards[5] = true; } else if (item is SmallStretchedHideEastDeed || item is SmallStretchedHideSouthDeed) { rewards[10] = rewards[11] = true; } else if (item is MediumStretchedHideEastDeed || item is MediumStretchedHideSouthDeed) { rewards[10] = rewards[11] = true; } else if (item is LightFlowerTapestryEastDeed || item is LightFlowerTapestrySouthDeed) { rewards[12] = rewards[13] = true; } else if (item is DarkFlowerTapestryEastDeed || item is DarkFlowerTapestrySouthDeed) { rewards[12] = rewards[13] = true; } else if (item is BrownBearRugEastDeed || item is BrownBearRugSouthDeed) { rewards[14] = rewards[15] = true; } else if (item is PolarBearRugEastDeed || item is PolarBearRugSouthDeed) { rewards[14] = rewards[15] = true; } else if (item is ClothingBlessDeed) { rewards[16] = true; } else if (item is PowerScroll ps) { if (ps.Value == 105.0) { rewards[6] = true; } else if (ps.Value == 110.0) { rewards[7] = true; } else if (ps.Value == 115.0) { rewards[8] = true; } else if (ps.Value == 120.0) { rewards[9] = true; } } else if (item is UncutCloth) { if (item.Hue == 0x483 || item.Hue == 0x48C || item.Hue == 0x488 || item.Hue == 0x48A) { rewards[0] = true; } else if (item.Hue == 0x495 || item.Hue == 0x48B || item.Hue == 0x486 || item.Hue == 0x485) { rewards[1] = true; } else if (item.Hue == 0x48D || item.Hue == 0x490 || item.Hue == 0x48E || item.Hue == 0x491) { rewards[2] = true; } else if (item.Hue == 0x48F || item.Hue == 0x494 || item.Hue == 0x484 || item.Hue == 0x497) { rewards[3] = true; } else { rewards[4] = true; } } else if (item is RunicSewingKit rkit) { rewards[16 + CraftResources.GetIndex(rkit.Resource)] = true; } item.Delete(); } string style = null; string name = null; switch (material) { case BulkMaterialType.None: { if (type.IsSubclassOf(typeof(BaseArmor)) || type.IsSubclassOf(typeof(BaseShoes))) { style = "pl"; name = "Plain"; } else { style = "cl"; name = "Cloth"; } break; } case BulkMaterialType.Spined: style = "sp"; name = "Spined"; break; case BulkMaterialType.Horned: style = "ho"; name = "Horned"; break; case BulkMaterialType.Barbed: style = "ba"; name = "Barbed"; break; } html.WriteLine(" "); html.WriteLine( "  - {0} {1}", name, amt ); var index = 0; while (index < 20) { if (rewards[index]) { html.WriteLine("
X
", style); ++index; } else { var count = 0; while (index < 20 && !rewards[index]) { ++count; ++index; if (index == 5 || index == 6 || index == 10 || index == 17) { break; } } html.WriteLine( "  ", count * 25, count == 1 ? "" : $" colspan=\"{count}\"" ); } } html.WriteLine(" "); } private static void WriteSmithLBOD(StreamWriter html, string name, SmallBulkEntry[] entries) { LargeBOD lbod = new LargeSmithBOD(); lbod.Entries = LargeBulkEntry.ConvertEntries(lbod, entries); WriteSmithBODHeader(html, $"(Large) {name}: Normal"); lbod.RequireExceptional = false; for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat) { lbod.Material = mat; lbod.AmountMax = 10; DocumentSmithBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material); } WriteSmithBODFooter(html); html.WriteLine("

"); WriteSmithBODHeader(html, $"(Large) {name}: Exceptional"); lbod.RequireExceptional = true; for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat) { lbod.Material = mat; for (var amt = 15; amt <= 20; amt += 5) { lbod.AmountMax = amt; DocumentSmithBOD(html, lbod.ComputeRewards(true), amt == 20 ? "20" : "10, 15", lbod.Material); } } WriteSmithBODFooter(html); html.WriteLine("

"); html.WriteLine("

"); } private static void WriteSmithBODHeader(StreamWriter html, string title) { html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" ", title); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); } private static void WriteSmithBODFooter(StreamWriter html) { html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine( " " ); html.WriteLine(" "); html.WriteLine("
{0}
\"Sturdy
Gloves
\"Gargoyles
\"Prospectors
\"Powder
\"Colored
Power Scrolls
Runic Hammers
Ancient Hammers
+1
+3
+5
+5
+10
+15
+20
Du
Sh
Co
Br
Go
Ag
Ve
Va
+10
+15
+30
+60
 
\"Sturdy
+1
 
+3
 
+5
 
\"Gargoyles
\"Prospectors
\"Powder
\"Colored
+5
+10
+15
+20
Du
Sh
Co
Br
Go
Ag
Ve
Va
+10
+15
+30
+60
Gloves
Power Scrolls
Runic Hammers
Ancient Hammers
"); } private static void DocumentSmithBOD( StreamWriter html, List items, string amt, BulkMaterialType material ) { var rewards = new bool[24]; for (var i = 0; i < items.Count; ++i) { var item = items[i].Construct(); if (item is SturdyPickaxe || item is SturdyShovel) { rewards[0] = true; } else if (item is LeatherGlovesOfMining) { rewards[1] = true; } else if (item is StuddedGlovesOfMining) { rewards[2] = true; } else if (item is RingmailGlovesOfMining) { rewards[3] = true; } else if (item is GargoylesPickaxe) { rewards[4] = true; } else if (item is ProspectorsTool) { rewards[5] = true; } else if (item is PowderOfTemperament) { rewards[6] = true; } else if (item is ColoredAnvil) { rewards[7] = true; } else if (item is PowerScroll ps) { if (ps.Value == 105.0) { rewards[8] = true; } else if (ps.Value == 110.0) { rewards[9] = true; } else if (ps.Value == 115.0) { rewards[10] = true; } else if (ps.Value == 120.0) { rewards[11] = true; } } else if (item is RunicHammer rh) { rewards[11 + CraftResources.GetIndex(rh.Resource)] = true; } else if (item is AncientSmithyHammer ash) { if (ash.Bonus == 10) { rewards[20] = true; } else if (ash.Bonus == 15) { rewards[21] = true; } else if (ash.Bonus == 30) { rewards[22] = true; } else if (ash.Bonus == 60) { rewards[23] = true; } } item.Delete(); } string style = null; string name = null; switch (material) { case BulkMaterialType.None: style = "ir"; name = "Iron"; break; case BulkMaterialType.DullCopper: style = "du"; name = "Dull Copper"; break; case BulkMaterialType.ShadowIron: style = "sh"; name = "Shadow Iron"; break; case BulkMaterialType.Copper: style = "co"; name = "Copper"; break; case BulkMaterialType.Bronze: style = "br"; name = "Bronze"; break; case BulkMaterialType.Gold: style = "go"; name = "Gold"; break; case BulkMaterialType.Agapite: style = "ag"; name = "Agapite"; break; case BulkMaterialType.Verite: style = "ve"; name = "Verite"; break; case BulkMaterialType.Valorite: style = "va"; name = "Valorite"; break; } html.WriteLine(" "); html.WriteLine( " {0} {1}", name, amt ); var index = 0; while (index < 24) { if (rewards[index]) { html.WriteLine("
X
", style); ++index; } else { var count = 0; while (index < 24 && !rewards[index]) { ++count; ++index; if (index == 4 || index == 8 || index == 12 || index == 20) { break; } } html.WriteLine( "  ", count * 25, count == 1 ? "" : $" colspan=\"{count}\"" ); } } html.WriteLine(" "); } public static List LoadBodies() { var list = new List(); var path = Path.Combine(Core.BaseDirectory, "Data/models.txt"); if (File.Exists(path)) { using var ip = new StreamReader(path); string line; while ((line = ip.ReadLine()) != null) { line = line.Trim(); if (line.Length == 0 || line.StartsWith("#")) { continue; } var split = line.Split('\t'); if (split.Length >= 9) { Body body = Utility.ToInt32(split[0]); var type = (ModelBodyType)Utility.ToInt32(split[1]); var name = split[8]; var entry = new BodyEntry(body, type, name); if (!list.Contains(entry)) { list.Add(entry); } } } } return list; } private static void DocumentBodies() { var list = LoadBodies(); using var html = GetWriter("docs/", "bodies.html"); html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Body List"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine("

Back to the index

"); if (list.Count > 0) { html.WriteLine("

Body List

"); list.Sort(new BodyEntrySorter()); var lastType = ModelBodyType.Invalid; for (var i = 0; i < list.Count; ++i) { var entry = list[i]; var type = entry.BodyType; if (type != lastType) { if (lastType != ModelBodyType.Invalid) { html.WriteLine("
"); } lastType = type; html.WriteLine(" ", type); switch (type) { case ModelBodyType.Monsters: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; case ModelBodyType.Sea: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; case ModelBodyType.Animals: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; case ModelBodyType.Human: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; case ModelBodyType.Equipment: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; } html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine( " ", type ); } html.WriteLine( " ", entry.Body.BodyID, entry.Name ); } html.WriteLine("
{0}
{0}{1}
"); } else { html.WriteLine(" This feature requires a UO:3D installation."); } html.WriteLine(" "); html.WriteLine(""); } private static void DocumentKeywords() { var tables = LoadSpeechFile(); using var html = GetWriter("docs/", "keywords.html"); html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Speech Keywords"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine("

Back to the index

"); html.WriteLine("

Speech Keywords

"); for (var p = 0; p < 1 && p < tables.Count; ++p) { var table = tables[p]; html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine(" "); var list = new List(table.Values); list.Sort(new SpeechEntrySorter()); for (var i = 0; i < list.Count; ++i) { var entry = list[i]; html.Write(" "); } html.WriteLine("
NumberText
0x{0:X4}", entry.Index); entry.Strings.Sort(); // ( new EnglishPrioStringSorter() ); for (var j = 0; j < entry.Strings.Count; ++j) { if (j > 0) { html.Write("
"); } var v = entry.Strings[j]; for (var k = 0; k < v.Length; ++k) { var c = v[k]; if (c == '<') { html.Write("<"); } else if (c == '>') { html.Write(">"); } else if (c == '&') { html.Write("&"); } else if (c == '"') { html.Write("""); } else if (c == '\'') { html.Write("'"); } else if (c >= 0x20 && c < 0x7F) { html.Write(c); } else { html.Write("&#{0};", (int)c); } } } html.WriteLine("
"); } html.WriteLine(" "); html.WriteLine(""); } private static List> LoadSpeechFile() { var tables = new List>(); var lastIndex = -1; Dictionary table = null; var path = Core.FindDataFile("speech.mul", false); if (File.Exists(path)) { using var ip = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); var bin = new BinaryReader(ip); while (bin.PeekChar() >= 0) { var index = (bin.ReadByte() << 8) | bin.ReadByte(); var length = (bin.ReadByte() << 8) | bin.ReadByte(); var text = Encoding.UTF8.GetString(bin.ReadBytes(length)).Trim(); if (text.Length == 0) { continue; } if (table == null || lastIndex > index) { if (index == 0 && text == "*withdraw*") { tables.Insert(0, table = new Dictionary()); } else { tables.Add(table = new Dictionary()); } } lastIndex = index; if (!table.TryGetValue(index, out var entry)) { table[index] = entry = new SpeechEntry(index); } entry.Strings.Add(text); } } return tables; } private static void DocumentCommands() { using var html = GetWriter("docs/", "commands.html"); html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Commands"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine("

Back to the index

"); html.WriteLine("

Commands

"); var commands = new List(CommandSystem.Entries.Values); var list = new List(); commands.Sort(); commands.Reverse(); Clean(commands); for (var i = 0; i < commands.Count; ++i) { var e = commands[i]; var mi = e.Handler.Method; var attrs = mi.GetCustomAttributes(typeof(UsageAttribute), false); if (attrs.Length == 0) { continue; } var usage = attrs[0] as UsageAttribute; attrs = mi.GetCustomAttributes(typeof(DescriptionAttribute), false); if (attrs.Length == 0) { continue; } if (usage == null || !(attrs[0] is DescriptionAttribute desc)) { continue; } attrs = mi.GetCustomAttributes(typeof(AliasesAttribute), false); var aliases = attrs.Length == 0 ? null : attrs[0] as AliasesAttribute; var descString = desc.Description.Replace("<", "<").Replace(">", ">"); if (aliases == null) { list.Add(new DocCommandEntry(e.AccessLevel, e.Command, null, usage.Usage, descString)); } else { list.Add(new DocCommandEntry(e.AccessLevel, e.Command, aliases.Aliases, usage.Usage, descString)); } } for (var i = 0; i < TargetCommands.AllCommands.Count; ++i) { var command = TargetCommands.AllCommands[i]; var usage = command.Usage; var desc = command.Description; if (usage == null || desc == null) { continue; } var cmds = command.Commands; var cmd = cmds[0]; var aliases = new string[cmds.Length - 1]; for (var j = 0; j < aliases.Length; ++j) { aliases[j] = cmds[j + 1]; } desc = desc.Replace("<", "<").Replace(">", ">"); if (command.Supports != CommandSupport.Single) { var sb = new StringBuilder(50 + desc.Length); sb.Append("Modifiers: "); if ((command.Supports & CommandSupport.Global) != 0) { sb.Append("Global, "); } if ((command.Supports & CommandSupport.Online) != 0) { sb.Append("Online, "); } if ((command.Supports & CommandSupport.Region) != 0) { sb.Append("Region, "); } if ((command.Supports & CommandSupport.Contained) != 0) { sb.Append("Contained, "); } if ((command.Supports & CommandSupport.Multi) != 0) { sb.Append("Multi, "); } if ((command.Supports & CommandSupport.Area) != 0) { sb.Append("Area, "); } if ((command.Supports & CommandSupport.Self) != 0) { sb.Append("Self, "); } sb.Remove(sb.Length - 2, 2); sb.Append("
"); sb.Append(desc); desc = sb.ToString(); } list.Add(new DocCommandEntry(command.AccessLevel, cmd, aliases, usage, desc)); } var commandImpls = BaseCommandImplementor.Implementors; for (var i = 0; i < commandImpls.Count; ++i) { var command = commandImpls[i]; var usage = command.Usage; var desc = command.Description; if (usage == null || desc == null) { continue; } var cmds = command.Accessors; var cmd = cmds[0]; var aliases = new string[cmds.Length - 1]; for (var j = 0; j < aliases.Length; ++j) { aliases[j] = cmds[j + 1]; } desc = desc.Replace("<", "<").Replace(">", ">"); list.Add(new DocCommandEntry(command.AccessLevel, cmd, aliases, usage, desc)); } list.Sort(new CommandEntrySorter()); var last = AccessLevel.Player; foreach (var e in list) { if (e.AccessLevel != last) { if (last != AccessLevel.Player) { html.WriteLine("

"); } last = e.AccessLevel; html.WriteLine(" ", last); switch (last) { case AccessLevel.Administrator: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; case AccessLevel.GameMaster: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; case AccessLevel.Seer: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; case AccessLevel.Counselor: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; case AccessLevel.Player: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; } html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine( " ", last == AccessLevel.GameMaster ? "Game Master" : last.ToString() ); } DocumentCommand(html, e); } html.WriteLine("
{0}
"); html.WriteLine(" "); html.WriteLine(""); } public static void Clean(List list) { for (var i = 0; i < list.Count; ++i) { var e = list[i]; for (var j = i + 1; j < list.Count; ++j) { var c = list[j]; if (e.Handler.Method == c.Handler.Method) { list.RemoveAt(j); --j; } } } } private static void DocumentCommand(StreamWriter html, DocCommandEntry e) { var usage = e.Usage; var desc = e.Description; var aliases = e.Aliases; html.Write(" {0}", e.Name); if (aliases == null || aliases.Length == 0) { html.Write( "Usage: {0}
{1}", usage.Replace("<", "<").Replace(">", ">"), desc ); } else { html.Write( "Usage: {0}
Alias{1}: ", usage.Replace("<", "<").Replace(">", ">"), aliases.Length == 1 ? "" : "es" ); for (var i = 0; i < aliases.Length; ++i) { if (i != 0) { html.Write(", "); } html.Write(aliases[i]); } html.Write("
{0}", desc); } html.WriteLine(""); } private static bool IsConstructible(Type t, out bool isItem) => (isItem = typeofItem.IsAssignableFrom(t)) || typeofMobile.IsAssignableFrom(t); private static bool IsConstructible(ConstructorInfo ctor) => ctor.IsDefined(typeof(ConstructibleAttribute), false); private static void DocumentConstructibleObjects() { var types = new List(m_Types.Values); types.Sort(new TypeComparer()); var items = new List<(Type, ConstructorInfo[])>(); var mobiles = new List<(Type, ConstructorInfo[])>(); for (var i = 0; i < types.Count; ++i) { var t = types[i].m_Type; if (t.IsAbstract || !IsConstructible(t, out var isItem)) { continue; } var ctors = t.GetConstructors(); var anyConstructible = false; for (var j = 0; !anyConstructible && j < ctors.Length; ++j) { anyConstructible = IsConstructible(ctors[j]); } if (anyConstructible) { (isItem ? items : mobiles).Add((t, ctors)); } } using var html = GetWriter("docs/", "objects.html"); html.WriteLine(""); html.WriteLine(" "); html.WriteLine(" RunUO Documentation - Constructible Objects"); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine("

Back to the index

"); html.WriteLine( "

Constructible Items and Mobiles

" ); html.WriteLine(" "); html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine(" "); items.ForEach( tuple => { var (type, constructors) = tuple; DocumentConstructibleObject(html, type, constructors); } ); html.WriteLine("
Item NameUsage


"); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine("
"); html.WriteLine(" "); html.WriteLine(" "); mobiles.ForEach( tuple => { var (type, constructors) = tuple; DocumentConstructibleObject(html, type, constructors); } ); html.WriteLine("
Mobile NameUsage
"); html.WriteLine(" "); html.WriteLine(""); } private static void DocumentConstructibleObject(StreamWriter html, Type t, ConstructorInfo[] ctors) { html.Write(" {0}", t.Name); var first = true; for (var i = 0; i < ctors.Length; ++i) { var ctor = ctors[i]; if (!IsConstructible(ctor)) { continue; } if (!first) { html.Write("
"); } first = false; html.Write("{0}Add {1}", CommandSystem.Prefix, t.Name); var parms = ctor.GetParameters(); for (var j = 0; j < parms.Length; ++j) { html.Write("
{1}", GetTooltipFor(parms[j]), parms[j].Name); } } html.WriteLine(""); } private static string GetTooltipFor(ParameterInfo param) { var paramType = param.ParameterType; for (var i = 0; i < m_Tooltips.GetLength(0); ++i) { var checkType = (Type)m_Tooltips[i, 0]; if (paramType == checkType) { return string.Format((string)m_Tooltips[i, 1], HtmlNewLine); } } if (paramType.IsEnum) { var sb = new StringBuilder(); sb.AppendFormat("Enumeration value or name. Possible named values include:{0}", HtmlNewLine); var names = Enum.GetNames(paramType); for (var i = 0; i < names.Length; ++i) { sb.AppendFormat("{0}- {1}", HtmlNewLine, names[i]); } return sb.ToString(); } if (paramType.IsDefined(typeofCustomEnum, false)) { var attributes = paramType.GetCustomAttributes(typeofCustomEnum, false); if (attributes.Length > 0 && attributes[0] is CustomEnumAttribute attr) { var sb = new StringBuilder(); sb.AppendFormat("Enumeration value or name. Possible named values include:{0}", HtmlNewLine); var names = attr.Names; for (var i = 0; i < names.Length; ++i) { sb.AppendFormat("{0}- {1}", HtmlNewLine, names[i]); } return sb.ToString(); } } else if (paramType == typeofMap) { var sb = new StringBuilder(); sb.AppendFormat("Enumeration value or name. Possible named values include:{0}", HtmlNewLine); var names = Map.GetMapNames(); for (var i = 0; i < names.Length; ++i) { sb.AppendFormat("{0}- {1}", HtmlNewLine, names[i]); } return sb.ToString(); } return ""; } private static void WriteEnum(TypeInfo info, StreamWriter typeHtml) { var type = info.m_Type; typeHtml.WriteLine("

{0} (Enum)

", info.TypeName); var names = Enum.GetNames(type); var flags = type.IsDefined(typeof(FlagsAttribute), false); string format; if (flags) { format = " {0:G} = 0x{1:X}{2}
"; } else { format = " {0:G} = {1:D}{2}
"; } for (var i = 0; i < names.Length; ++i) { var value = Enum.Parse(type, names[i]); typeHtml.WriteLine(format, names[i], value, i < names.Length - 1 ? "," : ""); } } private static void WriteType(TypeInfo info, StreamWriter typeHtml) { var type = info.m_Type; typeHtml.Write("

"); var decType = info.m_Declaring; if (decType != null) { // We are a nested type typeHtml.Write('('); m_Types.TryGetValue(decType, out var decInfo); if (decInfo == null) { typeHtml.Write(decType.Name); } else // typeHtml.Write( "{1}", decInfo.m_FileName, decInfo.m_TypeName ); { typeHtml.Write(decInfo.LinkName(null)); } typeHtml.Write(") - "); } typeHtml.Write(info.TypeName); var ifaces = info.m_Interfaces; var baseType = info.m_BaseType; var extendCount = 0; if (baseType != typeof(object) && baseType != typeof(ValueType) && baseType?.IsPrimitive == false) { typeHtml.Write(" : "); m_Types.TryGetValue(baseType, out var baseInfo); if (baseInfo == null) { typeHtml.Write(baseType.Name); } else { typeHtml.Write($"{baseInfo.LinkName(null)}"); } ++extendCount; } if (ifaces.Length > 0) { if (extendCount == 0) { typeHtml.Write(" : "); } for (var i = 0; i < ifaces.Length; ++i) { var iface = ifaces[i]; m_Types.TryGetValue(iface, out var ifaceInfo); if (extendCount != 0) { typeHtml.Write(", "); } ++extendCount; if (ifaceInfo == null) { FormatGeneric(iface, out _, out _, out var linkName); typeHtml.Write($"{linkName.Replace("@directory@", null)}"); } else { typeHtml.Write($"{ifaceInfo.LinkName(null)}"); } } } typeHtml.WriteLine("

"); var derived = info.m_Derived; if (derived != null) { typeHtml.Write("

Derived Types: "); derived.Sort(new TypeComparer()); for (var i = 0; i < derived.Count; ++i) { var derivedInfo = derived[i]; if (i != 0) { typeHtml.Write(", "); } // typeHtml.Write( "{1}", derivedInfo.m_FileName, derivedInfo.m_TypeName ); typeHtml.Write($"{derivedInfo.LinkName(null)}"); } typeHtml.WriteLine("

"); } var nested = info.m_Nested; if (nested != null) { typeHtml.Write("

Nested Types: "); nested.Sort(new TypeComparer()); for (var i = 0; i < nested.Count; ++i) { var nestedInfo = nested[i]; if (i != 0) { typeHtml.Write(", "); } // typeHtml.Write( "{1}", nestedInfo.m_FileName, nestedInfo.m_TypeName ); typeHtml.Write($"{nestedInfo.LinkName(null)}"); } typeHtml.WriteLine("

"); } var membs = type.GetMembers( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly ); Array.Sort(membs, new MemberComparer()); for (var i = 0; i < membs.Length; ++i) { var mi = membs[i]; if (mi is PropertyInfo propertyInfo) { WriteProperty(propertyInfo, typeHtml); } else if (mi is ConstructorInfo constructorInfo) { WriteCtor(info.TypeName, constructorInfo, typeHtml); } else if (mi is MethodInfo methodInfo) { WriteMethod(methodInfo, typeHtml); } } } private static void WriteProperty(PropertyInfo pi, StreamWriter html) { html.Write(" "); var getMethod = pi.GetGetMethod(); var setMethod = pi.GetSetMethod(); if (getMethod?.IsStatic == true || setMethod?.IsStatic == true) { html.Write(StaticString); } html.Write(GetPair(pi.PropertyType, pi.Name, false)); html.Write('('); if (pi.CanRead) { html.Write(GetString); } if (pi.CanWrite) { html.Write(SetString); } html.WriteLine(" )
"); } private static void WriteCtor(string name, ConstructorInfo ctor, StreamWriter html) { if (ctor.IsStatic) { return; } html.Write(" "); html.Write(CtorString); html.Write(name); html.Write('('); var parms = ctor.GetParameters(); if (parms.Length > 0) { html.Write(' '); for (var i = 0; i < parms.Length; ++i) { var pi = parms[i]; if (i != 0) { html.Write(", "); } if (pi.IsIn) { html.Write(InString); } else if (pi.IsOut) { html.Write(OutString); } html.Write(GetPair(pi.ParameterType, pi.Name, pi.IsOut)); } html.Write(' '); } html.WriteLine(")
"); } private static void WriteMethod(MethodInfo mi, StreamWriter html) { if (mi.IsSpecialName) { return; } html.Write(" "); if (mi.IsStatic) { html.Write(StaticString); } if (mi.IsVirtual) { html.Write(VirtString); } html.Write(GetPair(mi.ReturnType, mi.Name, false)); html.Write('('); var parms = mi.GetParameters(); if (parms.Length > 0) { html.Write(' '); for (var i = 0; i < parms.Length; ++i) { var pi = parms[i]; if (i != 0) { html.Write(", "); } if (pi.IsIn) { html.Write(InString); } else if (pi.IsOut) { html.Write(OutString); } html.Write(GetPair(pi.ParameterType, pi.Name, pi.IsOut)); } html.Write(' '); } html.WriteLine(")
"); } private class MemberComparer : IComparer { public int Compare(object x, object y) { if (x == y) { return 0; } var aCtor = x as ConstructorInfo; var bCtor = y as ConstructorInfo; var aProp = x as PropertyInfo; var bProp = y as PropertyInfo; var aMethod = x as MethodInfo; var bMethod = y as MethodInfo; var aStatic = GetStaticFor(aCtor, aProp, aMethod); var bStatic = GetStaticFor(bCtor, bProp, bMethod); if (aStatic && !bStatic) { return -1; } if (!aStatic && bStatic) { return 1; } var v = 0; if (aCtor != null) { if (bCtor == null) { v = -1; } } else if (bCtor != null) { v = 1; } else if (aProp != null) { if (bProp == null) { v = -1; } } else if (bProp != null) { v = 1; } if (v == 0) { v = string.CompareOrdinal( GetNameFrom(aCtor, aProp, aMethod), GetNameFrom(bCtor, bProp, bMethod) ); } if (v == 0 && aCtor != null && bCtor != null) { v = aCtor.GetParameters().Length.CompareTo(bCtor.GetParameters().Length); } else if (v == 0 && aMethod != null && bMethod != null) { v = aMethod.GetParameters().Length.CompareTo(bMethod.GetParameters().Length); } return v; } private bool GetStaticFor(ConstructorInfo ctor, PropertyInfo prop, MethodInfo method) { if (ctor != null) { return ctor.IsStatic; } if (method != null) { return method.IsStatic; } if (prop != null) { var getMethod = prop.GetGetMethod(); var setMethod = prop.GetGetMethod(); return getMethod?.IsStatic == true || setMethod?.IsStatic == true; } return false; } private string GetNameFrom(ConstructorInfo ctor, PropertyInfo prop, MethodInfo method) => ctor?.DeclaringType?.Name ?? prop?.Name ?? method?.Name ?? ""; } private class TypeComparer : IComparer { public int Compare(TypeInfo x, TypeInfo y) => string.CompareOrdinal(x?.TypeName, y?.TypeName); } private class TypeInfo { public readonly Type m_BaseType; public readonly Type m_Declaring; private readonly string m_FileName; public readonly Type[] m_Interfaces; private readonly string m_LinkName; public readonly Type m_Type; private readonly string m_TypeName; public List m_Derived, m_Nested; public TypeInfo(Type type) { m_Type = type; m_BaseType = type.BaseType; m_Declaring = type.DeclaringType; m_Interfaces = type.GetInterfaces(); FormatGeneric(m_Type, out m_TypeName, out m_FileName, out m_LinkName); } public string FileName => m_FileName; public string TypeName => m_TypeName; public string LinkName(string dirRoot) => m_LinkName.Replace("@directory@", dirRoot); } private class SpeechEntry { public SpeechEntry(int index) { Index = index; Strings = new List(); } public int Index { get; } public List Strings { get; } } private class SpeechEntrySorter : IComparer { public int Compare(SpeechEntry x, SpeechEntry y) { if (x == null && y == null) { return 0; } return x?.Index.CompareTo(y?.Index) ?? 1; } } public class DocCommandEntry { public DocCommandEntry(AccessLevel accessLevel, string name, string[] aliases, string usage, string description) { AccessLevel = accessLevel; Name = name; Aliases = aliases; Usage = usage; Description = description; } public AccessLevel AccessLevel { get; } public string Name { get; } public string[] Aliases { get; } public string Usage { get; } public string Description { get; } } public class CommandEntrySorter : IComparer { public int Compare(DocCommandEntry a, DocCommandEntry b) { if (a == null && b == null) { return 0; } var v = b?.AccessLevel.CompareTo(a?.AccessLevel) ?? 1; return v != 0 ? v : string.CompareOrdinal(a?.Name, b?.Name); } } } public enum ModelBodyType { Invalid = -1, Monsters, Sea, Animals, Human, Equipment } public class BodyEntry { public BodyEntry(Body body, ModelBodyType bodyType, string name) { Body = body; BodyType = bodyType; Name = name; } public Body Body { get; } public ModelBodyType BodyType { get; } public string Name { get; } public override bool Equals(object obj) { var e = obj as BodyEntry; return Body == e?.Body && BodyType == e.BodyType && Name == e.Name; } public override int GetHashCode() => Body.BodyID ^ (int)BodyType ^ Name.GetHashCode(); } public class BodyEntrySorter : IComparer { public int Compare(BodyEntry a, BodyEntry b) { if (a == null && b == null) { return 0; } var v = a?.BodyType.CompareTo(b?.BodyType) ?? 1; if (v == 0) { v = a?.Body.BodyID.CompareTo(b?.Body.BodyID) ?? 1; } return v != 0 ? v : string.CompareOrdinal(a?.Name, b?.Name); } } }