using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Mapper.Scripting; using ProxyCore; using ProxyCore.Input; using ProxyCore.Output; using ProxyCore.Scripting; using System.IO; using System.Runtime.Serialization; namespace Mapper { public partial class Mapper : Plugin { public Mapper() : base("mapper", "Mapper") { Author = "Duckbat"; Version = 15; Description = "This plugin will record what rooms you have been in and from that can generate speedwalks for you to easily move around the world of Aardwolf."; UpdateUrl = "www.duckbat.com/plugins/update.mapper.txt"; Website = "code.google.com/p/proxymud/"; Config = new MapperConfig(); RegisterCommand("pm", "", CommandMapper); RegisterCommand("all", @"(.+)", CommandAll, 0, CMDFlags.None, "pm"); RegisterCommand("count", "", CommandCount, 0, CMDFlags.None, "pm"); RegisterCommand("createexit", "^(\\d+)(\\s+\\d+)?\\s+\"(.+)\"$", CommandCreateExit, 0, CMDFlags.None, "pm"); RegisterCommand("createportal", "^(\\d+)\\s+\"(.+)\"$", CommandCreatePortal, 0, CMDFlags.None, "pm"); RegisterCommand("delete", @"^(area|room|exit|portal)\s+(\d+)", CommandDelete, 3, CMDFlags.None, "pm"); RegisterCommand("exits", @"^(help)?(room\s+\d+)?(\d+)?(\s+.+)?", CommandExit, 4, CMDFlags.None, "pm"); RegisterCommand("find", @"^(room|area)(\s+exact)?(\s+case)?\s+(.+)", CommandFind, 1, CMDFlags.None, "pm"); RegisterCommand("portals", "", CommandPortal, 4, CMDFlags.None, "pm"); RegisterCommand("goto", @"(.+)", CommandGoto, 2, CMDFlags.None, "pm"); RegisterCommand("import", @"(.+)", CommandImport, 0, CMDFlags.None, "pm"); RegisterCommand("roominfo", @"^(help)?(\d+)?(\s+.+)?", CommandRoomInfo, 4, CMDFlags.None, "pm"); RegisterCommand("save", @"(.+)", CommandSave, 4, CMDFlags.None, "pm"); RegisterCommand("unmapped", @"^(go|all)$", CommandUnmapped, 2, CMDFlags.None, "pm"); RegisterCommand("unreconed", @"^(go|all)$", CommandUnreconed, 3, CMDFlags.None, "pm"); RegisterTrigger("room.id", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomInfoNum); RegisterTrigger("room.name", @"^\$gmcp\.room\.info\.name (.*)$", TriggerRoomInfoName); RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); RegisterTrigger("room.exit", @"^\$gmcp\.room\.info\.exits\.(\w) (-?\d+)$", TriggerRoomInfoExits); RegisterTrigger("room.finish", @"^\$gmcp\.room\.info\.coords?\.", TriggerRoomInfoFinish); RegisterTrigger("char.level", @"^\$gmcp\.char\.status\.level (\d+)$", TriggerCharStatusLevel); RegisterTrigger("char.tier", @"^\$gmcp\.char\.base\.tier (\d+)$", TriggerCharBaseTier); RegisterTrigger("char.remorts", @"^\$gmcp\.char\.base\.remorts (\d+)$", TriggerCharBaseRemorts); RegisterTrigger("gq.join", @"@wYou have now joined the quest. See 'help gquest' for available commands.", TriggerJoinedGQ, TriggerFlags.NotRegex); RegisterTrigger("gq.left", @"@wYou are no longer part of the current quest.", TriggerLeftGQ, TriggerFlags.NotRegex); RegisterTrigger("gq.win", @"^@RGlobal Quest@Y: @wThe global quest has been won by @Y\w+ @w- @Y\d+.. @wwin\.$", TriggerLeftGQ); RegisterTrigger("gq.quit", @"@wYou are no longer part of the current quest.", TriggerLeftGQ, TriggerFlags.NotRegex); RegisterTrigger("where.name", @"^@GYou are in area : (.+)", TriggerWhereName); RegisterTrigger("where.level", @"^@GLevel range is : @R(\d+) to (\d+)", TriggerWhereLevel); RegisterTrigger("areas.start", @"^@WFrom To Lock Keyword Area Name", TriggerAreasStart); RegisterTrigger("areas.end", @"@w---------------------------------------------------------------", TriggerAreasEnd, TriggerFlags.NotRegex); RegisterTrigger("areas.entry", @"^\s+@w(\d+)\s+(\d+)\s+(@R\d+\s+)?@g([\d\w]+)\s+@c(.+)", TriggerAreasEntry); RegisterTrigger("home", @"@wYou cannot return home from this room.", TriggerNoRecall, TriggerFlags.NotRegex); RegisterTrigger("recall", @"@wYou cannot recall from this room.", TriggerNoRecall, TriggerFlags.NotRegex); RegisterTrigger("portal", @"@wMagic walls bounce you back.", TriggerPrison, TriggerFlags.NotRegex); RegisterTrigger("recon.area", @"^@wArea Name : (.+)", TriggerReconArea); RegisterTrigger("recon.sector", @"^@wSector type is : (.+)", TriggerReconSector); RegisterTrigger("recon.flags", @"^@wBase flags :(.*)", TriggerReconFlags); RegisterTrigger("recon.healrate", @"^@wHeal rate : @c(-?\d+)", TriggerReconHealRate); RegisterTrigger("recon.manarate", @"^@wMana rate : @c(-?\d+)", TriggerReconManaRate); RegisterTrigger("recon.unable", "@wYou cannot perform reconnaissance in this room.", TriggerNoRecon, TriggerFlags.NotRegex); RegisterTrigger("tags.exits", @"^(@w)?\{exits\}(.*)", TriggerTagsExits); Load(); // If we don't have this unmapped area set then create it, just so we can save rooms we haven't explored yet if(GetArea(uint.MaxValue) == null) { Area a = new Area(uint.MaxValue); a.Keyword = "mapper_unmapped"; a.Name = "Mapper unmapped rooms"; IAreas[a.Entry] = a; } Path_Init(); } private const string DBFileName = "mapperdb.xml"; private const string DBFileBackup = "mapperdb_backup.xml"; private int Level = 1; private int Tier = 0; private bool ListenArea = false; private int Remorts = 1; private bool HasGQ = false; private long WhenSave = 0; private void OnDeleted(Room r) { List del = new List(); foreach(KeyValuePair x in IExits) { if(x.Key == uint.MaxValue || x.Key == 0) continue; if(!IRooms.ContainsKey(x.Value.ToRoom)) del.Add(x.Key); } foreach(uint x in del) { IExits[x].From.exits.Remove(IExits[x]); IExits.Remove(x); } } /// /// Fill a pathfinder with our current character values and return if we were successful in doing that. /// /// public bool FillPathFinder(Pathfinder p) { Room r; if(CurrentRoomId == uint.MaxValue || (r = GetRoom(CurrentRoomId)) == null || r.Area.Entry == uint.MaxValue) return false; p.CharacterLevel = Level; p.CharacterTier = Tier; p.CanUseRecalls = true; p.CanUsePortals = true; p.IsGlobalQuest = HasGQ; p.IsSingleClassTier0 = Tier == 0 && Remorts == 1; p.StartRooms = new[] { r }; return true; } #region Commands private bool CommandAll(InputData i) { string[] spl; if(!i.Arguments.Success || (spl = i.Arguments.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)).Length == 0) { World.Instance.SendMessage("@wSyntax: map all [room id] ...", i.ClientMask); return true; } List rooms = new List(); bool hadConfirm = false; foreach(string x in spl) { uint u; if(uint.TryParse(x, out u)) { Room r = GetRoom(u); if(r != null && !rooms.Contains(r)) rooms.Add(r); } else if(x.ToLower() == "confirm") hadConfirm = true; } if(rooms.Count < 2) { World.Instance.SendMessage("@wSyntax: map all [room id] ...", i.ClientMask); return true; } if(rooms.Count > 10 && !hadConfirm) { World.Instance.SendMessage("@RWarning! @wEntered more than 10 rooms. This may be too slow to finish. Enter confirm at the end if you are sure you wish to do this pathfind.", i.ClientMask); return true; } int ms = Environment.TickCount; PathFinder_VisitAll pf = new PathFinder_VisitAll(this, rooms.ToArray()); FillPathFinder(pf); PathfindResult pr = Get(pf); ms = Environment.TickCount - ms; World.Instance.SendMessage("@wPathfind took @W" + ms + " @wms.", i.ClientMask); if(!pr.Success) { World.Instance.SendMessage("@wPathfind failed.", i.ClientMask); return true; } string sw = PathfindResult.Speedwalk(pr.Path); World.Instance.SendMessage("@wCost: @W" + pr.Cost, i.ClientMask); World.Instance.SendMessage("@wSW: " + sw, i.ClientMask); return true; } private bool CommandImport(InputData i) { if(!i.Arguments.Success) { World.Instance.SendMessage("@wSyntax: map import ", i.ClientMask); return true; } StreamReader f; try { f = new StreamReader(i.Arguments.Groups[1].Value); } catch { World.Instance.SendMessage("@wFailed opening file. Make sure it is in the same folder as ProxyMud.exe and make sure you entered the file extension too.", i.ClientMask); return true; } string l; while((l = f.ReadLine()) != null) { if(l.StartsWith("{area}")) { l = l.Substring(6); string key = l.Substring(0, l.IndexOf('\t')).Trim().ToLower(); l = l.Substring(l.IndexOf('\t') + 1); if(string.IsNullOrEmpty(key)) continue; if(GetArea(key) == null) { Area a = new Area(++_guidArea); a.Name = l; a.Keyword = key; IAreas[a.Entry] = a; } else if(string.IsNullOrEmpty(GetArea(key).Name)) GetArea(key).Name = l; } else if(l.StartsWith("{room}")) { l = l.Substring(6); string key = l.Substring(0, l.IndexOf('\t')); uint k; if(!uint.TryParse(key, out k)) continue; l = l.Substring(l.IndexOf('\t') + 1); string name = l.Substring(0, l.IndexOf('\t')); l = l.Substring(l.IndexOf('\t') + 1).Trim().ToLower(); Area a = GetArea(l); if(a == null) { a = new Area(++_guidArea); a.Keyword = l; IAreas[a.Entry] = a; } Room r = GetRoom(k); if(r == null) { r = new Room(k); IRooms[k] = r; r.Name = name; r.Area = a; } else { if(r.Area != a) r.Area = a; r.Name = name; } } else if(l.StartsWith("{exit}")) { l = l.Substring(6); string cmd = l.Substring(0, l.IndexOf('\t')); l = l.Substring(l.IndexOf('\t') + 1); string fr = l.Substring(0, l.IndexOf('\t')); l = l.Substring(l.IndexOf('\t') + 1); string tr = l.Substring(0, l.IndexOf('\t')); l = l.Substring(l.IndexOf('\t') + 1); uint from; uint to; int minLevel; if(!uint.TryParse(fr, out from) || !uint.TryParse(tr, out to) || !int.TryParse(l, out minLevel)) continue; Room fromroom = GetRoom(from); Room toroom = GetRoom(to); if(fromroom == null || toroom == null) continue; string door = ""; if(cmd.Contains(';')) { door = cmd.Substring(0, cmd.IndexOf(';')); if(!door.StartsWith("open ") && !door.StartsWith("ope ") && !door.StartsWith("op ") && !door.StartsWith("o ")) door = ""; else { door = door.Substring(door.IndexOf(' ') + 1).Trim(); char dir = PathfindResult.IsDirectionCommand(door); if(dir == 'x') door = "open " + door; else door = "o"; cmd = cmd.Substring(cmd.IndexOf(';') + 1); } } Exit e = null; if(PathfindResult.IsDirectionCommand(cmd) != 'x') e = fromroom.GetExit(PathfindResult.IsDirectionCommand(cmd)); else e = fromroom.GetExit(cmd); if(e == null) { e = new Exit(++_guidExit); e.Command = PathfindResult.IsDirectionCommand(cmd) != 'x' ? PathfindResult.IsDirectionCommand(cmd).ToString() : cmd; e.From = fromroom; e.To = toroom; e.From.exits.Add(e); e.From.UpdateExits(); IExits[e.Entry] = e; if(door == "o") e.AddFlag("door"); else if(!string.IsNullOrEmpty(door)) e.DoorCommand = door; } } else if(l.Length == 0) continue; else { f.Close(); World.Instance.SendMessage("@wInvalid mapper format. You must convert it to readable format with the converter.", i.ClientMask); return true; } } f.Close(); World.Instance.SendMessage("@wDone importing missing rooms / areas / exits from MUSH database.", i.ClientMask); return true; } private bool CommandSave(InputData i) { string fileName = DBFileName; if(i.Arguments.Success) fileName = i.Arguments.Groups[0].Value; Save(fileName); World.Instance.SendMessage("@wSaved mapper database to '@W" + fileName + "@w'.", i.ClientMask); return true; } private bool CommandUnmapped(InputData i) { if(!i.Arguments.Success) { Room r = GetRoom(CurrentRoomId); if(r == null) { World.Instance.SendMessage("@wYou are in an unkown room.", i.ClientMask); return true; } uint c = 0; foreach(KeyValuePair x in IRooms) { if(x.Value.Area.Entry != r.Area.Entry) continue; bool f = false; foreach(Exit e in x.Value.exits) { if(e.To.Area.Entry == uint.MaxValue) { f = true; break; } } if(f) c++; } if(c == 0) { World.Instance.SendMessage("@wDidn't find any rooms with exits to unmapped rooms in this area.", i.ClientMask); } else { World.Instance.SendMessage( "@wFound @C" + c + " @wroom" + (c != 1 ? "s" : "") + " in this area that have exits to unmapped rooms.", i.ClientMask); } World.Instance.SendMessage("@wUse '@Wmap unmapped all@w' to see areas where exits to unmapped rooms are found.", i.ClientMask); World.Instance.SendMessage("@wUse '@Wmap unmapped go@w' to go to the closest room where an exit to unmapped room is found (in current area only).", i.ClientMask); return true; } if(i.Arguments.Groups[1].Value.ToLower() == "all") { SortedDictionary> Unmapped = new SortedDictionary>(); foreach(KeyValuePair x in IRooms) { bool f = false; foreach(Exit e in x.Value.exits) { if(e.To.Area.Entry == uint.MaxValue) { f = true; break; } } if(f) { string n = x.Value.Area.Name; if(string.IsNullOrEmpty(n)) n = x.Value.Area.Keyword; if(!Unmapped.ContainsKey(n)) Unmapped[n] = new Dictionary(); if(!Unmapped[n].ContainsKey(x.Value.Area)) Unmapped[n][x.Value.Area] = 1; else Unmapped[n][x.Value.Area]++; } } if(Unmapped.Count == 0) World.Instance.SendMessage("@wDidn't find any rooms in any area that have exits to unmapped rooms.", i.ClientMask); else { World.Instance.SendMessage("@WEntry Area Unmapped rooms", i.ClientMask); World.Instance.SendMessage("@G===== ============================================ ==============", i.ClientMask); int c = 0; foreach(KeyValuePair> x in Unmapped) { foreach(KeyValuePair y in x.Value) { c++; World.Instance.SendMessage("@Y" + string.Format("{0,-5}", y.Key.Entry) + " @M" + string.Format("{0,-" + "============================================".Length + "}", (!string.IsNullOrEmpty(y.Key.Name) ? y.Key.Name : y.Key.Keyword)) + " @C" + y.Value, i.ClientMask); } } World.Instance.SendMessage("@wFound @C" + c + " @warea" + (c != 1 ? "s" : "") + " with exits to unmapped rooms.", i.ClientMask); } return true; } if(GetRoom(CurrentRoomId) == null) { World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); return true; } Pathfinder_Unmapped p = new Pathfinder_Unmapped(false); FillPathFinder(p); PathfindResult pr = Get(p); if(!pr.Success) { World.Instance.SendMessage("@wCouldn't find a path or there weren't any rooms in this area with exits that lead to unmapped rooms.", i.ClientMask); return true; } Goto(pr); return true; } private bool CommandUnreconed(InputData i) { if(!i.Arguments.Success) { Room r = GetRoom(CurrentRoomId); if(r == null) { World.Instance.SendMessage("@wYou are in an unknown room.", i.ClientMask); return true; } uint c = 0; foreach(KeyValuePair x in IRooms) { if(x.Value.Area.Entry != r.Area.Entry) continue; bool f = false; foreach(Exit e in x.Value.exits) { if(e.To.Area.Entry == uint.MaxValue) { f = true; break; } } if(!f && !x.Value.HasCustomFlag("reconed")) f = true; if(f) c++; } if(c == 0) { World.Instance.SendMessage("@wDidn't find any rooms with exits to unmapped rooms or unreconed in this area.", i.ClientMask); } else { World.Instance.SendMessage( "@wFound @C" + c + " @wroom" + (c != 1 ? "s" : "") + " in this area that have exits to unmapped rooms or aren't reconed.", i.ClientMask); } World.Instance.SendMessage("@wUse '@Wmap unreconed all@w' to see areas where exits to unmapped rooms or rooms that aren't reconed are found.", i.ClientMask); World.Instance.SendMessage("@wUse '@Wmap unreconed go@w' to go to the closest room where an exit to unmapped room is found or that isn't reconed (in current area only).", i.ClientMask); return true; } if(i.Arguments.Groups[1].Value.ToLower() == "all") { SortedDictionary> Unmapped = new SortedDictionary>(); foreach(KeyValuePair x in IRooms) { bool f = false; foreach(Exit e in x.Value.exits) { if(e.To.Area.Entry == uint.MaxValue) { f = true; break; } } if(!f && !x.Value.HasCustomFlag("reconed")) f = true; if(f) { string n = x.Value.Area.Name; if(string.IsNullOrEmpty(n)) n = x.Value.Area.Keyword; if(!Unmapped.ContainsKey(n)) Unmapped[n] = new Dictionary(); if(!Unmapped[n].ContainsKey(x.Value.Area)) Unmapped[n][x.Value.Area] = 1; else Unmapped[n][x.Value.Area]++; } } if(Unmapped.Count == 0) World.Instance.SendMessage("@wDidn't find any rooms in any area that have exits to unmapped rooms or that aren't reconed.", i.ClientMask); else { World.Instance.SendMessage("@WEntry Area Unmapped/Unreconed rooms", i.ClientMask); World.Instance.SendMessage("@G===== ============================================ ========================", i.ClientMask); int c = 0; foreach(KeyValuePair> x in Unmapped) { foreach(KeyValuePair y in x.Value) { c++; World.Instance.SendMessage("@Y" + string.Format("{0,-5}", y.Key.Entry) + " @M" + string.Format("{0,-" + "============================================".Length + "}", (!string.IsNullOrEmpty(y.Key.Name) ? y.Key.Name : y.Key.Keyword)) + " @C" + y.Value, i.ClientMask); } } World.Instance.SendMessage("@wFound @C" + c + " @warea" + (c != 1 ? "s" : "") + " with exits to unmapped rooms or unreconed rooms.", i.ClientMask); } return true; } if(GetRoom(CurrentRoomId) == null) { World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); return true; } Pathfinder_Unmapped p = new Pathfinder_Unmapped(true); FillPathFinder(p); PathfindResult pr = Get(p); if(!pr.Success) { World.Instance.SendMessage("@wCouldn't find a path or there weren't any rooms in this area with exits that lead to unmapped rooms or that aren't reconed.", i.ClientMask); return true; } Goto(pr); return true; } private bool CommandDelete(InputData i) { // @"^(area|room|exit|portal)\s+(\d+)" uint id; if(!i.Arguments.Success || !uint.TryParse(i.Arguments.Groups[2].Value, out id) || id == uint.MaxValue) { World.Instance.SendMessage("@wSyntax: map delete area ", i.ClientMask); World.Instance.SendMessage(" @wmap delete exit ", i.ClientMask); World.Instance.SendMessage(" @wmap delete portal ", i.ClientMask); World.Instance.SendMessage(" @wmap delete room ", i.ClientMask); return true; } string w = i.Arguments.Groups[1].Value.ToLower(); switch(w) { case "area": { Area a = GetArea(id); if(a == null) { World.Instance.SendMessage("@wNo such area (@R" + id + "@w).", i.ClientMask); return true; } IAreas.Remove(a.Entry); foreach(Room r in a.rooms) { IRooms.Remove(r.Entry); foreach(Exit e in r.exits) IExits.Remove(e.Entry); OnDeleted(r); } if(!string.IsNullOrEmpty(a.Name)) World.Instance.SendMessage("@wDeleted area '@M" + a.Name + "@w'.", i.ClientMask); else World.Instance.SendMessage("@wDeleted area '@w" + a.Keyword + "@w'.", i.ClientMask); } break; case "exit": case "portal": { Exit e = GetExit(id); if(e == null) e = IPortals.ContainsKey(id) ? IPortals[id] : null; if(e == null) { World.Instance.SendMessage("@wNo such exit or portal (@R" + id + "@w).", i.ClientMask); return true; } IExits.Remove(e.Entry); IPortals.Remove(e.Entry); if(!e.HasFlag("portal")) e.From.exits.Remove(e); else e.To.Area.Portals.Remove(e); World.Instance.SendMessage("@wDeleted exit '@Y" + e.Entry + "@w'.", i.ClientMask); } break; case "room": { Room r = GetRoom(id); if(r == null) { World.Instance.SendMessage("@wNo such room (@R" + id + "@w).", i.ClientMask); return true; } IRooms.Remove(r.Entry); r.Area.rooms.Remove(r); OnDeleted(r); World.Instance.SendMessage("@wDeleted room '@G" + r.Name + "@w'.", i.ClientMask); } break; } return true; } private bool CommandFind(InputData i) { // @"^(room|area)(\s+exact)?(\s+case)?\s+(.+)" if(!i.Arguments.Success) { World.Instance.SendMessage("@wSyntax: map find room [exact] [case] ", i.ClientMask); World.Instance.SendMessage(" @wmap find area [exact] [case] ", i.ClientMask); return true; } if(i.Arguments.Groups[1].Value.ToLower() == "room") { string Str = i.Arguments.Groups[4].Value; if(i.Arguments.Groups[3].Length == 0) Str = Str.ToLower(); List Found = new List(); foreach(KeyValuePair x in IRooms) { string Name = x.Value.Name; if(string.IsNullOrEmpty(Name)) continue; if(i.Arguments.Groups[3].Length == 0) Name = Name.ToLower(); if(i.Arguments.Groups[2].Length != 0) { if(Name == Str) Found.Add(x.Value); } else if(Name.Contains(Str)) Found.Add(x.Value); } if(Found.Count == 0) World.Instance.SendMessage("@wFound nothing!", i.ClientMask); else { World.Instance.SendMessage("@WEntry Name Area", i.ClientMask); World.Instance.SendMessage("@G====== ================================ ===============================", i.ClientMask); foreach(Room f in Found) { World.Instance.SendMessage("@Y" + string.Format("{0,6}", f.Entry) + " @G" + Utility.FormatColoredString(f.Name, -"================================".Length) + " " + (!string.IsNullOrEmpty(f.Area.Name) ? ("@M" + f.Area.Name) : ("@w" + f.Area.Keyword)), i.ClientMask); } World.Instance.SendMessage("@wFound @C" + Found.Count + " @wroom" + (Found.Count != 1 ? "s" : "") + ".", i.ClientMask); } } else { string Str = i.Arguments.Groups[4].Value; if(i.Arguments.Groups[3].Length == 0) Str = Str.ToLower(); List Found = new List(); foreach(KeyValuePair x in IAreas) { string Name = x.Value.Name; if(string.IsNullOrEmpty(Name)) continue; if(i.Arguments.Groups[3].Length == 0) Name = Name.ToLower(); if(i.Arguments.Groups[2].Length != 0) { if(Name == Str) Found.Add(x.Value); } else if(Name.Contains(Str)) Found.Add(x.Value); } if(Found.Count == 0) World.Instance.SendMessage("@wFound nothing!", i.ClientMask); else { World.Instance.SendMessage("@WEntry Keyword Name", i.ClientMask); World.Instance.SendMessage("@G====== ========== ========================================", i.ClientMask); foreach(Area f in Found) { World.Instance.SendMessage("@Y" + string.Format("{0,6}", f.Entry) + " @w" + Utility.FormatColoredString(f.Keyword, -10) + " @M" + f.Name, i.ClientMask); } World.Instance.SendMessage("@wFound @C" + Found.Count + " @warea" + (Found.Count != 1 ? "s" : "") + ".", i.ClientMask); } } return true; } private bool CommandMapper(InputData i) { World.Instance.SendMessage("@wAvailable mapper commands:", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map all") + " @w- Shows speedwalk that runs through all the rooms (IDs) you entered starting from your current location.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map count") + " @w- Shows how many rooms you have mapped.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map createexit") + " @w- Create a new exit.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map createportal") + " @w- Create a new portal.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map delete") + " @w- Delete a room, area or an exit.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map exits") + " @w- Show exits information or edit them.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map find") + " @w- Find rooms or areas in mapper.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map goto") + " @w- Goto a room or an area.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map import") + " @w- Import data file converted from mush mapper.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map portals") + " @w- List all portals.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map roominfo") + " @w- Show more information about a room or edit it.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map save") + " @w- Save the mapper database now. Enter argument to save to another file.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map unmapped") + " @w- Find unmapped rooms.", i.ClientMask); World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map unreconed") + " @w- Find unmapped or unreconed rooms.", i.ClientMask); return true; } private bool CommandGoto(InputData i) { if(!i.Arguments.Success || i.Arguments.Groups[1].Value.Trim().Length == 0) { World.Instance.SendMessage("@wGo to the closest room entered.", i.ClientMask); World.Instance.SendMessage("@wSyntax: map goto [room id] [room id]", i.ClientMask); World.Instance.SendMessage(" @wmap goto ", i.ClientMask); World.Instance.SendMessage(" @wmap goto ", i.ClientMask); return true; } if(CurrentRoomId == uint.MaxValue || GetRoom(CurrentRoomId) == null) { World.Instance.SendMessage("@wWe are in an unknown room.", i.ClientMask); return true; } List roomId = new List(); Area aTarget = null; string[] testIds = i.Arguments.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach(string x in testIds) { uint u; if(uint.TryParse(x, out u)) { if(GetRoom(u) == null) { World.Instance.SendMessage("@wNo such room (@R" + u + "@w)!", i.ClientMask); continue; } if(!roomId.Contains(u)) roomId.Add(u); } else { roomId = null; break; } } if(roomId == null) aTarget = GetArea(i.Arguments.Groups[1].Value); Pathfinder e; if(roomId != null) e = new Pathfinder_Entry(roomId.ToArray()); else if(aTarget != null) { if(aTarget.StartRoom != 0) e = new Pathfinder_Entry(aTarget.StartRoom); else e = new Pathfinder_Area(aTarget.Entry); } else e = new Pathfinder_Name(NameTypes.Partial | NameTypes.CaseInsensitive, i.Arguments.Groups[1].Value); FillPathFinder(e); PathfindResult pr = Get(e); if(!pr.Success) { World.Instance.SendMessage("@wCouldn't find a path to any of the rooms entered.", i.ClientMask); return true; } if(aTarget != null) World.Instance.SendMessage("@wGoing to area '@C" + pr.Target.Area.Name + "@w'...", i.ClientMask); else World.Instance.SendMessage("@wGoing to room '@G" + pr.Target.Name + "@w'...", i.ClientMask); Goto(pr); return true; } private bool CommandCount(InputData i) { int thisArea = 0; int total = 0; foreach(KeyValuePair x in IRooms) { if(x.Value.Area.Keyword == RoomInfoArea) thisArea++; if(x.Value.Area.Entry != uint.MaxValue) total++; } World.Instance.SendMessage("@wYou have mapped @G" + thisArea + " @wrooms in this area.", i.ClientMask); World.Instance.SendMessage("@wYou have @G" + (IAreas.Count - 1) + " @wareas in mapper.", i.ClientMask); World.Instance.SendMessage("@wYou have mapped @G" + total + " @wrooms of Aardwolf.", i.ClientMask); return true; } #endregion #region Triggers private bool TriggerNoRecon(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r != null) { r.AddCustomFlag("reconed"); r.AddCustomFlag("norecon"); } return false; } private bool TriggerTagsExits(TriggerData t) { if(Config.GetInt32("Tags.Exits", 1) == 0) return false; Room r = GetRoom(CurrentRoomId); StringBuilder str = new StringBuilder(); string[] Exits = t.Match.Groups[2].Value.ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); CheckExit("north", 'n', Exits.Contains("north") || Exits.Contains("(north)"), Exits.Contains("(north)"), RoomInfoExits.ContainsKey('n') ? RoomInfoExits['n'] : 0, str); CheckExit("east", 'e', Exits.Contains("east") || Exits.Contains("(east)"), Exits.Contains("(east)"), RoomInfoExits.ContainsKey('e') ? RoomInfoExits['e'] : 0, str); CheckExit("south", 's', Exits.Contains("south") || Exits.Contains("(south)"), Exits.Contains("(south)"), RoomInfoExits.ContainsKey('s') ? RoomInfoExits['s'] : 0, str); CheckExit("west", 'w', Exits.Contains("west") || Exits.Contains("(west)"), Exits.Contains("(west)"), RoomInfoExits.ContainsKey('w') ? RoomInfoExits['w'] : 0, str); CheckExit("up", 'u', Exits.Contains("up") || Exits.Contains("(up)"), Exits.Contains("(up)"), RoomInfoExits.ContainsKey('u') ? RoomInfoExits['u'] : 0, str); CheckExit("down", 'd', Exits.Contains("down") || Exits.Contains("(down)"), Exits.Contains("(down)"), RoomInfoExits.ContainsKey('d') ? RoomInfoExits['d'] : 0, str); if(r != null) { bool hadCustom = false; foreach(Exit e in r.exits) { if(PathfindResult.IsDirectionCommand(e.Command) != 'x') continue; hadCustom = true; if(str.Length > 0) str.Append(" "); str.Append("@w'@C" + e.Command + "@w'"); } if(!hadCustom && Exits.Contains("custom")) { if(str.Length > 0) str.Append(" "); str.Append("@Rcustom"); } } if(str.Length == 0) str.Append("@Gnone"); t.Msg.Msg = "@g[Exits: " + str.ToString() + "@g]"; return false; } private void CheckExit(string Word, char Dir, bool HasExits, bool IsClosed, uint LeadTo, StringBuilder str) { Room r = GetRoom(CurrentRoomId); if(r == null) { if(!HasExits) return; if(str.Length > 0) str.Append(" "); if(LeadTo == uint.MaxValue) str.Append("@Y"); else str.Append("@G"); str.Append(IsClosed ? ("(" + Word + ")") : Word); return; } if(LeadTo == uint.MaxValue) { if(str.Length > 0) str.Append(" "); str.Append("@Y"); str.Append(IsClosed ? ("(" + Word + ")") : Word); return; } Exit e = r.GetExit(Dir); if(e == null) return; if(!HasExits) { if(e.HasFlag("hidden")) { if(str.Length > 0) str.Append(" "); str.Append("@y(" + Word + ")"); return; } if(str.Length > 0) str.Append(" "); str.Append("@D(" + Word + ")"); return; } if(str.Length > 0) str.Append(" "); if(e.To.Area.Entry == uint.MaxValue) str.Append("@R"); else if(!e.To.HasCustomFlag("reconed") && Config.GetInt32("Tags.Exits.Recon", 0) != 0) str.Append("@r"); else str.Append("@G"); if(IsClosed) str.Append("(" + Word + ")"); else str.Append(Word); } private bool TriggerNoRecall(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r != null) r.AddFlag("norecall"); return false; } private bool TriggerPrison(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r != null) r.AddFlag("prison"); return false; } private bool TriggerReconArea(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r == null) return false; string msg = Colors.RemoveColors(t.Match.Groups[1].Value, false); string name = msg.Substring(0, msg.LastIndexOf('(')).Trim(); msg = msg.Substring(msg.LastIndexOf('(') + 1); r.Area.Name = name; try { int min, max; if(int.TryParse(msg.Substring(0, msg.IndexOf(' ')), out min)) r.Area.MinLevel = min; msg = msg.Substring(msg.IndexOf("to ") + 3); if(int.TryParse(msg.Substring(0, msg.IndexOf(' ')), out max)) r.Area.MaxLevel = max; } catch { } r.AddCustomFlag("reconed"); return false; } private bool TriggerReconSector(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r == null) return false; r.Sector = Colors.RemoveColors(t.Match.Groups[1].Value, false); return false; } private bool TriggerReconHealRate(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r == null) return false; int rate; if(int.TryParse(t.Match.Groups[1].Value, out rate)) r.HealRate = rate; return false; } private bool TriggerReconManaRate(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r == null) return false; int rate; if(int.TryParse(t.Match.Groups[1].Value, out rate)) r.ManaRate = rate; return false; } private bool TriggerReconFlags(TriggerData t) { Room r = GetRoom(CurrentRoomId); if(r == null) return false; string[] fl = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim().ToLower().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if(r.IFlags != null) r.IFlags.Clear(); foreach(string x in fl) r.AddFlag(x.Trim()); return false; } private bool TriggerAreasStart(TriggerData t) { ListenArea = true; return false; } private bool TriggerAreasEnd(TriggerData t) { ListenArea = false; return false; } private bool TriggerAreasEntry(TriggerData t) { if(!ListenArea) return false; int minLevel, maxLevel, levelLock = 0; if(!int.TryParse(t.Match.Groups[1].Value, out minLevel) || !int.TryParse(t.Match.Groups[2].Value, out maxLevel)) return false; if(t.Match.Groups[3].Length != 0) { if(!int.TryParse(t.Match.Groups[3].Value, out levelLock)) levelLock = 0; } string keyWord = t.Match.Groups[4].Value; string areaName = t.Match.Groups[5].Value.Trim(); Area a = GetArea(keyWord); if(a == null) { a = new Area(++_guidArea); IAreas[a.Entry] = a; } a.Keyword = keyWord; a.LevelLock = levelLock; a.MaxLevel = maxLevel; a.MinLevel = minLevel; if(string.IsNullOrEmpty(a.Name) || a.Name.Length <= areaName.Length || !a.Name.StartsWith(areaName)) a.Name = areaName; return false; } private bool TriggerWhereLevel(TriggerData t) { int minLevel, maxLevel; if(!int.TryParse(t.Match.Groups[1].Value, out minLevel) || !int.TryParse(t.Match.Groups[2].Value, out maxLevel)) return false; Room cur = GetRoom(CurrentRoomId); if(cur != null) { cur.Area.MinLevel = minLevel; cur.Area.MaxLevel = maxLevel; } return false; } private bool TriggerWhereName(TriggerData t) { string aName = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(); Room cur = GetRoom(CurrentRoomId); if(cur != null) cur.Area.Name = aName; return false; } private bool TriggerJoinedGQ(TriggerData t) { HasGQ = true; return false; } private bool TriggerLeftGQ(TriggerData t) { HasGQ = false; return false; } private bool TriggerCharStatusLevel(TriggerData t) { int i; if(int.TryParse(t.Match.Groups[1].Value, out i)) Level = i; return false; } private bool TriggerCharBaseTier(TriggerData t) { int i; if(int.TryParse(t.Match.Groups[1].Value, out i)) Tier = i; return false; } private bool TriggerCharBaseRemorts(TriggerData t) { int i; if(int.TryParse(t.Match.Groups[1].Value, out i)) Remorts = i; return false; } private bool TriggerRoomInfoNum(TriggerData t) { RoomInfoPrevious = RoomInfoEntry; RoomInfoListen = true; RoomInfoExits.Clear(); uint i; if(!uint.TryParse(t.Match.Groups[1].Value, out i)) { RoomInfoEntry = uint.MaxValue; return false; } RoomInfoEntry = i; return false; } private bool TriggerRoomInfoName(TriggerData t) { RoomInfoName = Colors.RemoveColors(t.Match.Groups[1].Value, true).Trim(); return false; } private bool TriggerRoomInfoArea(TriggerData t) { RoomInfoArea = t.Match.Groups[1].Value.Trim(); return false; } private bool TriggerRoomInfoExits(TriggerData t) { uint exitId; RoomInfoExits[t.Match.Groups[1].Value.ToLower()[0]] = !uint.TryParse(t.Match.Groups[2].Value, out exitId) ? uint.MaxValue : exitId; return false; } private bool TriggerRoomInfoFinish(TriggerData t) { if(!RoomInfoListen) return false; RoomInfoListen = false; UpdateRoom(); return false; } #endregion #region Data internal Dictionary IAreas = new Dictionary(); internal Dictionary IRooms = new Dictionary(); internal Dictionary IExits = new Dictionary(); internal Dictionary IPortals = new Dictionary(); /// /// Collection of all areas. /// public IEnumerable Areas { get { return IAreas.Values; } } /// /// Collection of all rooms. /// public IEnumerable Rooms { get { return IRooms.Values; } } /// /// Collection of all exits. /// public IEnumerable Exits { get { return IExits.Values; } } /// /// Collection of all portals. /// public IEnumerable Portals { get { return IPortals.Values; } } private uint _guidArea = 0; private uint _guidExit = 0; private void UpdateRoom() { CurrentRoomId = RoomInfoEntry; if(RoomInfoEntry == uint.MaxValue) { if(CurrentRoomId != RoomInfoPrevious) { Room prev = GetRoom(RoomInfoPrevious); if(prev != null) { GetScript(prev).OnLeaveRoom(prev, null); GetScript(prev).OnLeaveArea(prev.Area, null); } } return; } Room r = null; Area a = null; if(!IRooms.ContainsKey(CurrentRoomId)) { r = new Room(CurrentRoomId); IRooms[r.Entry] = r; } else r = IRooms[CurrentRoomId]; a = GetArea(RoomInfoArea); if(a == null) { a = new Area(++_guidArea); IAreas[a.Entry] = a; a.Keyword = RoomInfoArea; } if(r.Area != a) r.Area = a; r.Name = RoomInfoName; bool u = false; foreach(KeyValuePair e in RoomInfoExits) { UpdateRoom(e.Value); if(e.Value == uint.MaxValue) continue; Exit prev = r.GetExit(e.Key); if(prev != null && prev.ToRoom == e.Value) continue; if(prev != null) r.exits.Remove(prev); Exit newExit = new Exit(++_guidExit); newExit.To = GetRoom(e.Value); newExit.Command = e.Key.ToString(); newExit.From = r; r.exits.Add(newExit); IExits[newExit.Entry] = newExit; u = true; } if(u) r.UpdateExits(); if(CurrentRoomId != RoomInfoPrevious) { Room prev = GetRoom(RoomInfoPrevious); if(prev != null) { GetScript(prev).OnLeaveRoom(prev, r); GetScript(prev).OnLeaveArea(prev.Area, r.Area); } GetScript(r).OnEnterArea(r.Area, prev != null ? prev.Area : null); GetScript(r).OnEnterRoom(r, prev); } } private void UpdateRoom(uint Entry) { if(Entry == uint.MaxValue) return; Room r = GetRoom(Entry); if(r == null) { r = new Room(Entry); IRooms[r.Entry] = r; r.Area = GetArea(uint.MaxValue); } } internal uint CurrentRoomId; private uint RoomInfoPrevious = uint.MaxValue; private uint RoomInfoEntry = uint.MaxValue; private string RoomInfoName; private string RoomInfoArea; private bool RoomInfoListen; private Dictionary RoomInfoExits = new Dictionary(); /// /// Get room by ID. /// /// ID of the room to get. /// public Room GetRoom(uint Entry) { return IRooms.ContainsKey(Entry) ? IRooms[Entry] : null; } /// /// Get exit by ID. /// /// ID of the exit to get. /// public Exit GetExit(uint Entry) { return IExits.ContainsKey(Entry) ? IExits[Entry] : null; } /// /// Get area by ID. /// /// ID of the area to get. /// public Area GetArea(uint Entry) { return IAreas.ContainsKey(Entry) ? IAreas[Entry] : null; } /// /// Get area by keyword. /// /// Keyword of area to get. /// public Area GetArea(string Keyword) { foreach(KeyValuePair x in IAreas) { if(x.Value.Keyword == Keyword) return x.Value; } return null; } #endregion public override void Shutdown() { base.Shutdown(); Save(DBFileName); } /// /// Use mapper to go to the end of pathresult. /// /// public void Goto(PathfindResult pr) { string sw = PathfindResult.Speedwalk(pr.Path); if(string.IsNullOrEmpty(sw)) sw = ""; if(Config.GetInt32("Speedwalk.Echo", 0) != 0) { World.Instance.SendMessage("@w{mapperpath}" + sw, Config.GetUInt64("Speedwalk.Echo.AuthMask", ulong.MaxValue)); } else if(!string.IsNullOrEmpty(sw)) { string[] swp = sw.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach(string x in swp) World.Instance.Execute(x, true); } else World.Instance.SendMessage("@wTrying to execute an empty path. Using goto when you are already at target?"); } #region Saving private void Load() { StreamReader f; try { f = new StreamReader(DBFileName); } catch { // No database exists or we aren't allowed to read it. Make a new database. return; } Area[] data; try { DataContractSerializer x = new DataContractSerializer(typeof(Area[])); data = x.ReadObject(f.BaseStream) as Area[]; } catch { f.Close(); Log.Error("Failed loading mapper database! File corrupted?"); return; } f.Close(); if(data == null) return; foreach(Area a in data) { if(a == null) continue; IAreas[a.Entry] = a; if(a.Entry > _guidArea && a.Entry != uint.MaxValue) _guidArea = a.Entry; foreach(Room r in a.rooms) { if(r == null) continue; r.Area = a; IRooms[r.Entry] = r; foreach(Exit e in r.exits) { if(e == null) continue; e.From = r; IExits[e.Entry] = e; if(e.Entry > _guidExit) _guidExit = e.Entry; } r.UpdateExits(); } foreach(Exit p in a.Portals) { p.To = GetRoom(p.ToRoom); IPortals[p.Entry] = p; if(p.Entry > _guidExit) _guidExit = p.Entry; } } foreach(KeyValuePair e in IExits) { if(IPortals.ContainsKey(e.Key)) { IPortals[e.Key].To.Area.Portals.Remove(IPortals[e.Key]); IPortals.Remove(e.Key); } } List toDelete = new List(); foreach(KeyValuePair e in IExits) { e.Value.To = IRooms.ContainsKey(e.Value.ToRoom) ? IRooms[e.Value.ToRoom] : null; if(e.Value.To == null) toDelete.Add(e.Value); } foreach(Exit e in toDelete) { IExits.Remove(e.Entry); e.From.exits.Remove(e); } // Successfully loaded a database. Now make a backup because we have a working copy at the moment. File.Delete(DBFileBackup); File.Copy(DBFileName, DBFileBackup); } private void Save(string fileName) { if(IAreas.Count == 0) return; StreamWriter f = new StreamWriter(fileName, false); try { DataContractSerializer x = new DataContractSerializer(typeof(Area[])); x.WriteObject(f.BaseStream, IAreas.Values.ToArray()); } catch(Exception e) { f.Close(); throw e; } f.Close(); if(Config.GetInt32("AutoSave", 0) != 0) WhenSave = World.Instance.MSTime + Config.GetInt32("AutoSave", 0) * 1000; } #endregion public override void Update(long msTime) { base.Update(msTime); if(WhenSave == 0 && Config.GetInt32("AutoSave", 0) != 0) WhenSave = Config.GetInt32("AutoSave", 0) * 1000 + msTime; else if(WhenSave > 0 && WhenSave <= msTime) Save(DBFileName); } } public class MapperConfig : ConfigFile { protected override void OnCreated() { base.OnCreated(); CreateSetting("Tags.Exits", 1, "Display extra information about exits using {exits} tag. You need to have \"tags exits on\"."); CreateSetting("Tags.Exits.Recon", 0, "Display exits that lead to rooms that haven't been reconed yet in dark red."); CreateSetting("Speedwalk.Echo", 0, "Echo speedwalk to clients instead of executing the command when using goto. This is useful if you like your MUD client to process it instead of mapper just sending to MUD, for example portal alias etc."); CreateSetting("Speedwalk.Echo.AuthMask", ulong.MaxValue, "Security mask of who to echo the commands if you set Echo to 1."); CreateSetting("AutoSave", 0, "Save mapper database every X seconds. For example enter 600 to save mapper database every 10 minutes. Enter 0 to disable this feature. The map is also saved on shutdown of program. You can also type \"map save\" to manually save the database."); } } }