1012 lines
34 KiB
C#
1012 lines
34 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Server.Commands;
|
|
using Server.ContextMenus;
|
|
using Server.Gumps;
|
|
using Server.Items;
|
|
using Server.Network;
|
|
using Vertex = Server.Mobiles.GuideHelper.GuideVertex;
|
|
|
|
namespace Server.Mobiles
|
|
{
|
|
public static class GuideHelper
|
|
{
|
|
private static readonly Dictionary<string, List<GuideVertex>> m_GraphDefinitions = new Dictionary<string, List<GuideVertex>>();
|
|
private static readonly List<int> m_ShopDefinitions = new List<int>();
|
|
private static readonly char[] m_Separators = new char[] { '\t', ' ' };
|
|
private static readonly string m_Delimiter = "--------------------------------------------------------------------------";
|
|
public static void LogMessage(string line)
|
|
{
|
|
try
|
|
{
|
|
using (FileStream stream = new FileStream("Guide.log", FileMode.Append))
|
|
using (StreamWriter writer = new StreamWriter(stream))
|
|
{
|
|
writer.WriteLine(line);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex.Message);
|
|
Console.WriteLine(ex.StackTrace);
|
|
}
|
|
}
|
|
|
|
public static GuideVertex FindVertex(List<GuideVertex> list, int id)
|
|
{
|
|
foreach (GuideVertex v in list)
|
|
{
|
|
if (v.ID == id)
|
|
return v;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static int FindShopName(int id)
|
|
{
|
|
if (id < 0 || id > m_ShopDefinitions.Count)
|
|
return -1;
|
|
|
|
return m_ShopDefinitions[id];
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
CommandSystem.Register("GuideEdit", AccessLevel.GameMaster, new CommandEventHandler(VertexEdit_OnCommand));
|
|
|
|
try
|
|
{
|
|
using (FileStream stream = File.OpenRead(Path.Combine("Data", "Guide", "Definitions.cfg")))
|
|
using (StreamReader reader = new StreamReader(stream))
|
|
{
|
|
while (!reader.EndOfStream)
|
|
{
|
|
string line = reader.ReadLine();
|
|
|
|
if (!String.IsNullOrEmpty(line) && !line.StartsWith("#"))
|
|
{
|
|
string[] split = line.Split(m_Separators, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if (split != null && split.Length > 1)
|
|
m_ShopDefinitions.Add(Int32.Parse(split[1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (string file in Directory.GetFiles(Path.Combine("Data", "Guide"), "*.graph"))
|
|
{
|
|
using (FileStream stream = File.OpenRead(file))
|
|
using (StreamReader reader = new StreamReader(stream))
|
|
{
|
|
List<GuideVertex> list = new List<GuideVertex>();
|
|
GuideVertex current = null;
|
|
GuideVertex neighbour = null;
|
|
|
|
while (!reader.EndOfStream)
|
|
{
|
|
string line = reader.ReadLine();
|
|
|
|
if (!String.IsNullOrEmpty(line))
|
|
{
|
|
string[] split = line.Split(m_Separators, StringSplitOptions.RemoveEmptyEntries);
|
|
int num;
|
|
|
|
if (line.StartsWith("N:"))
|
|
{
|
|
if (current != null)
|
|
{
|
|
for (int i = 1; i < split.Length; i++)
|
|
{
|
|
num = Int32.Parse(split[i]);
|
|
neighbour = FindVertex(list, num);
|
|
|
|
if (neighbour == null)
|
|
{
|
|
neighbour = new GuideVertex(num);
|
|
list.Add(neighbour);
|
|
}
|
|
|
|
current.Vertices.Add(neighbour);
|
|
}
|
|
}
|
|
}
|
|
else if (line.StartsWith("S:"))
|
|
{
|
|
if (current != null)
|
|
{
|
|
for (int i = 1; i < split.Length; i++)
|
|
{
|
|
num = Int32.Parse(split[i]);
|
|
|
|
if (num >= 0 && num < m_ShopDefinitions.Count)
|
|
current.Shops.Add(num);
|
|
else
|
|
throw new Exception(String.Format("Invalid shop ID: {0}", num));
|
|
}
|
|
}
|
|
}
|
|
else if (line.StartsWith("V:"))
|
|
{
|
|
if (split.Length > 5)
|
|
{
|
|
num = Int32.Parse(split[1]);
|
|
neighbour = FindVertex(list, num);
|
|
|
|
if (neighbour != null)
|
|
current = neighbour;
|
|
else
|
|
{
|
|
current = new GuideVertex(num);
|
|
list.Add(current);
|
|
}
|
|
|
|
Point3D location = new Point3D();
|
|
location.X = Int32.Parse(split[2]);
|
|
location.Y = Int32.Parse(split[3]);
|
|
location.Z = Int32.Parse(split[4]);
|
|
current.Location = location;
|
|
current.Teleporter = Boolean.Parse(split[5]);
|
|
}
|
|
else
|
|
throw new Exception(String.Format("Incomplete vertex definition!"));
|
|
}
|
|
}
|
|
}
|
|
|
|
m_GraphDefinitions.Add(Path.GetFileNameWithoutExtension(file), list);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogMessage(ex.Message);
|
|
LogMessage(ex.StackTrace);
|
|
LogMessage(m_Delimiter);
|
|
}
|
|
}
|
|
|
|
public static void VertexEdit_OnCommand(CommandEventArgs e)
|
|
{
|
|
Mobile m = e.Mobile;
|
|
|
|
if (m.Region != null)
|
|
{
|
|
GuideVertex closest = ClosestVetrex(m.Region.Name, m.Location);
|
|
|
|
if (closest != null)
|
|
m.SendGump(new GuideVertexEditGump(closest, m.Map, m.Region.Name));
|
|
else
|
|
m.SendLocalizedMessage(1076113); // There are no shops nearby. Please try again when you get to a town or city.
|
|
}
|
|
else
|
|
m.SendLocalizedMessage(1076113); // There are no shops nearby. Please try again when you get to a town or city.
|
|
}
|
|
|
|
public static GuideVertex ClosestVetrex(string town, Point3D location)
|
|
{
|
|
if (town == null || !m_GraphDefinitions.ContainsKey(town))
|
|
return null;
|
|
|
|
List<GuideVertex> vertices = m_GraphDefinitions[town];
|
|
|
|
GuideVertex closest = null;
|
|
double min = Int32.MaxValue;
|
|
double distance;
|
|
|
|
foreach (GuideVertex v in vertices)
|
|
{
|
|
distance = Math.Sqrt(Math.Pow(location.X - v.Location.X, 2) + Math.Pow(location.Y - v.Location.Y, 2));
|
|
|
|
if (distance < min)
|
|
{
|
|
closest = v;
|
|
min = distance;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
public static Dictionary<int, GuideVertex> FindShops(string town, Point3D location)
|
|
{
|
|
if (town == null || !m_GraphDefinitions.ContainsKey(town))
|
|
return null;
|
|
|
|
List<GuideVertex> vertices = m_GraphDefinitions[town];
|
|
Dictionary<int, GuideVertex> shops = new Dictionary<int, GuideVertex>();
|
|
|
|
foreach (GuideVertex v in vertices)
|
|
{
|
|
foreach (int shop in v.Shops)
|
|
{
|
|
if (shops.ContainsKey(shop))
|
|
{
|
|
GuideVertex d = shops[shop];
|
|
|
|
if (v.DistanceTo(location) < d.DistanceTo(location))
|
|
shops[shop] = v;
|
|
}
|
|
else
|
|
shops.Add(shop, v);
|
|
}
|
|
}
|
|
|
|
if (shops.Count > 0)
|
|
return shops;
|
|
|
|
return null;
|
|
}
|
|
|
|
public static List<GuideVertex> Dijkstra(string town, GuideVertex source, GuideVertex destination)
|
|
{
|
|
if (town == null || !m_GraphDefinitions.ContainsKey(town))
|
|
return null;
|
|
|
|
Heap<GuideVertex> heap = new Heap<GuideVertex>();
|
|
List<GuideVertex> path = new List<GuideVertex>();
|
|
heap.Push(source);
|
|
|
|
foreach (GuideVertex v in m_GraphDefinitions[town])
|
|
{
|
|
v.Distance = Int32.MaxValue;
|
|
v.Previous = null;
|
|
v.Visited = false;
|
|
v.Removed = false;
|
|
}
|
|
|
|
source.Distance = 0;
|
|
GuideVertex from ;
|
|
int dist = 0;
|
|
|
|
while (heap.Count > 0)
|
|
{
|
|
from = heap.Pop();
|
|
from.Removed = true;
|
|
|
|
if (from == destination)
|
|
{
|
|
while (from != source)
|
|
{
|
|
path.Add(from);
|
|
from = from.Previous;
|
|
}
|
|
|
|
path.Add(source);
|
|
return path;
|
|
}
|
|
|
|
foreach (GuideVertex v in from.Vertices)
|
|
{
|
|
if (!v.Removed)
|
|
{
|
|
dist = from.Distance + (from.Teleporter ? 1 : from.DistanceTo(v));
|
|
|
|
if (dist < v.Distance)
|
|
{
|
|
v.Distance = dist;
|
|
v.Previous = from;
|
|
|
|
if (!v.Visited)
|
|
heap.Push(v);
|
|
else
|
|
heap.Fix(v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public class GuideVertexEditGump : Gump
|
|
{
|
|
private readonly GuideVertex m_Vertex;
|
|
private readonly Map m_Map;
|
|
private readonly string m_Town;
|
|
private readonly Item m_Item;
|
|
public GuideVertexEditGump(GuideVertex vertex, Map map, string town)
|
|
: base(50, 50)
|
|
{
|
|
this.m_Vertex = vertex;
|
|
this.m_Map = map;
|
|
this.m_Town = town;
|
|
|
|
this.Closable = true;
|
|
this.Disposable = true;
|
|
this.Dragable = true;
|
|
this.Resizable = false;
|
|
|
|
int size = m_ShopDefinitions.Count;
|
|
bool on = false;
|
|
|
|
this.AddPage(0);
|
|
this.AddBackground(0, 0, 540, 35 + size * 30 / 2, 9200);
|
|
this.AddAlphaRegion(15, 10, 510, 15 + size * 30 / 2);
|
|
|
|
for (int i = 0; i < size; i += 2)
|
|
{
|
|
on = this.m_Vertex.Shops.Contains(i);
|
|
this.AddButton(25, 25 + i * 30 / 2, on ? 2361 : 2360, on ? 2360 : 2361, i + 1, GumpButtonType.Reply, 0);
|
|
this.AddHtmlLocalized(50, 20 + i * 30 / 2, 200, 20, m_ShopDefinitions[i], 0x7773, false, false);
|
|
|
|
if (i + 1 < size)
|
|
{
|
|
on = this.m_Vertex.Shops.Contains(i + 1);
|
|
this.AddButton(280, 25 + i * 30 / 2, on ? 2361 : 2360, on ? 2360 : 2361, i + 2, GumpButtonType.Reply, 0);
|
|
this.AddHtmlLocalized(305, 20 + i * 30 / 2, 200, 20, m_ShopDefinitions[i + 1], 0x7773, false, false);
|
|
}
|
|
}
|
|
|
|
this.m_Item = new Item(0x1183);
|
|
this.m_Item.Visible = false;
|
|
this.m_Item.MoveToWorld(this.m_Vertex.Location, map);
|
|
}
|
|
|
|
public override void OnResponse(NetState sender, RelayInfo info)
|
|
{
|
|
if (info.ButtonID > 0)
|
|
{
|
|
if (this.m_Vertex.Shops.Contains(info.ButtonID - 1))
|
|
this.m_Vertex.Shops.Remove(info.ButtonID - 1);
|
|
else
|
|
this.m_Vertex.Shops.Add(info.ButtonID - 1);
|
|
|
|
sender.Mobile.SendGump(new GuideVertexEditGump(this.m_Vertex, this.m_Map, this.m_Town));
|
|
}
|
|
else if (this.m_Item != null && !this.m_Item.Deleted)
|
|
{
|
|
this.m_Item.Delete();
|
|
this.Save(this.m_Town);
|
|
}
|
|
}
|
|
|
|
private void Save(string town)
|
|
{
|
|
if (!m_GraphDefinitions.ContainsKey(town))
|
|
return;
|
|
|
|
List<GuideVertex> list = m_GraphDefinitions[town];
|
|
string path = Core.BaseDirectory + String.Format("\\Data\\Guide\\{0}.graph", town);
|
|
|
|
using (FileStream stream = new FileStream(path, FileMode.Create))
|
|
using (StreamWriter writer = new StreamWriter(stream))
|
|
{
|
|
writer.WriteLine("# Graph vertices");
|
|
writer.WriteLine("# {V:}VertexID{tab, }X{tab, }Y{tab, }Z{tab, }IsTeleporter");
|
|
writer.WriteLine("# {S:}ShopID{tab, }ShopID{tab, }...");
|
|
writer.WriteLine("# {N:}VertexID{tab, }VertexID{tab, }...");
|
|
|
|
foreach (GuideVertex v in list)
|
|
{
|
|
writer.WriteLine(String.Format("V:\t{0}\t{1}\t{2}\t{3}\t{4}", v.ID, v.Location.X, v.Location.Y, v.Location.Z, v.Teleporter.ToString()));
|
|
|
|
if (v.Shops.Count > 0)
|
|
{
|
|
writer.Write("S:");
|
|
|
|
foreach (int i in v.Shops)
|
|
writer.Write(String.Format("\t{0}", i));
|
|
|
|
writer.WriteLine();
|
|
}
|
|
|
|
if (v.Vertices.Count > 0)
|
|
{
|
|
writer.Write("N:");
|
|
|
|
foreach (GuideVertex n in v.Vertices)
|
|
writer.Write(String.Format("\t{0}", n.ID));
|
|
|
|
writer.WriteLine();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class GuideVertex : IComparable<GuideVertex>
|
|
{
|
|
public bool m_Visited;
|
|
public bool m_Removed;
|
|
private readonly int m_ID;
|
|
private readonly List<GuideVertex> m_Vertices = new List<GuideVertex>();
|
|
private readonly List<int> m_Shops = new List<int>();
|
|
private Point3D m_Location;
|
|
private bool m_Teleporter;
|
|
private GuideVertex m_Previous;
|
|
private int m_Distance;
|
|
public GuideVertex(int id)
|
|
: this(id, Point3D.Zero)
|
|
{
|
|
}
|
|
|
|
public GuideVertex(int id, Point3D location)
|
|
{
|
|
this.m_ID = id;
|
|
this.m_Location = location;
|
|
this.m_Previous = null;
|
|
this.m_Distance = Int32.MaxValue;
|
|
this.m_Visited = false;
|
|
this.m_Removed = false;
|
|
}
|
|
|
|
public int ID
|
|
{
|
|
get
|
|
{
|
|
return this.m_ID;
|
|
}
|
|
}
|
|
public Point3D Location
|
|
{
|
|
get
|
|
{
|
|
return this.m_Location;
|
|
}
|
|
set
|
|
{
|
|
this.m_Location = value;
|
|
}
|
|
}
|
|
public List<GuideVertex> Vertices
|
|
{
|
|
get
|
|
{
|
|
return this.m_Vertices;
|
|
}
|
|
}
|
|
public List<int> Shops
|
|
{
|
|
get
|
|
{
|
|
return this.m_Shops;
|
|
}
|
|
}
|
|
public bool Teleporter
|
|
{
|
|
get
|
|
{
|
|
return this.m_Teleporter;
|
|
}
|
|
set
|
|
{
|
|
this.m_Teleporter = value;
|
|
}
|
|
}
|
|
public GuideVertex Previous
|
|
{
|
|
get
|
|
{
|
|
return this.m_Previous;
|
|
}
|
|
set
|
|
{
|
|
this.m_Previous = value;
|
|
}
|
|
}
|
|
public int Distance
|
|
{
|
|
get
|
|
{
|
|
return this.m_Distance;
|
|
}
|
|
set
|
|
{
|
|
this.m_Distance = value;
|
|
}
|
|
}
|
|
public bool Visited
|
|
{
|
|
get
|
|
{
|
|
return this.m_Visited;
|
|
}
|
|
set
|
|
{
|
|
this.m_Visited = value;
|
|
}
|
|
}
|
|
public bool Removed
|
|
{
|
|
get
|
|
{
|
|
return this.m_Removed;
|
|
}
|
|
set
|
|
{
|
|
this.m_Removed = value;
|
|
}
|
|
}
|
|
public int DistanceTo(GuideVertex to)
|
|
{
|
|
return Math.Abs(this.m_Location.X - to.Location.X) + Math.Abs(this.m_Location.Y - to.Location.Y);
|
|
}
|
|
|
|
public int DistanceTo(Point3D to)
|
|
{
|
|
return Math.Abs(this.m_Location.X - to.X) + Math.Abs(this.m_Location.Y - to.Y);
|
|
}
|
|
|
|
public int CompareTo(GuideVertex o)
|
|
{
|
|
if (o != null)
|
|
return this.m_Distance - o.Distance;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public class Heap<T> where T : IComparable<T>
|
|
{
|
|
private readonly List<T> m_List;
|
|
public Heap()
|
|
{
|
|
this.m_List = new List<T>();
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return this.m_List.Count;
|
|
}
|
|
}
|
|
public T Top
|
|
{
|
|
get
|
|
{
|
|
return this.m_List[0];
|
|
}
|
|
}
|
|
public bool Contains(T item)
|
|
{
|
|
return this.m_List.Contains(item);
|
|
}
|
|
|
|
public void Push(T item)
|
|
{
|
|
this.m_List.Add(item);
|
|
|
|
int child = this.m_List.Count - 1;
|
|
int parent = (child - 1) / 2;
|
|
T temp;
|
|
|
|
while (item.CompareTo(this.m_List[parent]) < 0 && child > 0)
|
|
{
|
|
temp = this.m_List[child];
|
|
this.m_List[child] = this.m_List[parent];
|
|
this.m_List[parent] = temp;
|
|
|
|
child = parent;
|
|
parent = (child - 1) / 2;
|
|
}
|
|
}
|
|
|
|
public void Fix(T item)
|
|
{
|
|
int child = this.m_List.IndexOf(item);
|
|
int parent = (child - 1) / 2;
|
|
T temp;
|
|
|
|
while (item.CompareTo(this.m_List[parent]) < 0 && child > 0)
|
|
{
|
|
temp = this.m_List[child];
|
|
this.m_List[child] = this.m_List[parent];
|
|
this.m_List[parent] = temp;
|
|
|
|
child = parent;
|
|
parent = (child - 1) / 2;
|
|
}
|
|
}
|
|
|
|
public T Pop()
|
|
{
|
|
if (this.m_List.Count == 0)
|
|
return default( T );
|
|
|
|
T top = this.m_List[0];
|
|
T temp;
|
|
|
|
this.m_List[0] = this.m_List[this.m_List.Count - 1];
|
|
this.m_List.RemoveAt(this.m_List.Count - 1);
|
|
|
|
int parent = 0;
|
|
int lchild;
|
|
int rchild;
|
|
|
|
bool ltl, ltr, cltc;
|
|
|
|
do
|
|
{
|
|
lchild = parent * 2 + 1;
|
|
rchild = parent * 2 + 2;
|
|
ltl = ltr = cltc = false;
|
|
|
|
if (this.m_List.Count > lchild)
|
|
ltl = (this.m_List[parent].CompareTo(this.m_List[lchild]) > 0);
|
|
|
|
if (this.m_List.Count > rchild)
|
|
ltr = (this.m_List[parent].CompareTo(this.m_List[rchild]) > 0);
|
|
|
|
if (ltl && ltr)
|
|
cltc = (this.m_List[lchild].CompareTo(this.m_List[rchild]) > 0);
|
|
|
|
if (ltl && !ltr)
|
|
{
|
|
temp = this.m_List[parent];
|
|
this.m_List[parent] = this.m_List[lchild];
|
|
this.m_List[lchild] = temp;
|
|
|
|
parent = lchild;
|
|
}
|
|
else if (!ltl && ltr)
|
|
{
|
|
temp = this.m_List[parent];
|
|
this.m_List[parent] = this.m_List[rchild];
|
|
this.m_List[rchild] = temp;
|
|
|
|
parent = rchild;
|
|
}
|
|
else if (ltl && ltr && cltc)
|
|
{
|
|
temp = this.m_List[parent];
|
|
this.m_List[parent] = this.m_List[rchild];
|
|
this.m_List[rchild] = temp;
|
|
|
|
parent = rchild;
|
|
}
|
|
else if (ltl && ltr)
|
|
{
|
|
temp = this.m_List[parent];
|
|
this.m_List[parent] = this.m_List[lchild];
|
|
this.m_List[lchild] = temp;
|
|
|
|
parent = lchild;
|
|
}
|
|
}
|
|
while (ltl || ltr);
|
|
|
|
return top;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class AttendantGuide : PersonalAttendant
|
|
{
|
|
private List<Vertex> m_Path;
|
|
public AttendantGuide()
|
|
: base("the Guide")
|
|
{
|
|
this.m_Path = null;
|
|
}
|
|
|
|
public AttendantGuide(Serial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
public List<Vertex> Path
|
|
{
|
|
get
|
|
{
|
|
return this.m_Path;
|
|
}
|
|
}
|
|
public override void OnDoubleClick(Mobile from)
|
|
{
|
|
if (from.Alive && this.IsOwner(from))
|
|
{
|
|
Dictionary<int, Vertex> m_Shops = GuideHelper.FindShops(this.Region != null ? this.Region.Name : null, this.Location);
|
|
|
|
if (m_Shops != null)
|
|
{
|
|
from.CloseGump(typeof(InternalGump));
|
|
from.SendGump(new InternalGump(this, m_Shops));
|
|
}
|
|
else
|
|
from.SendLocalizedMessage(1076113); // There are no shops nearby. Please try again when you get to a town or city.
|
|
}
|
|
}
|
|
|
|
public override void AddCustomContextEntries(Mobile from, List<ContextMenuEntry> list)
|
|
{
|
|
if (from.Alive && this.IsOwner(from))
|
|
list.Add(new AttendantUseEntry(this, 6249));
|
|
|
|
base.AddCustomContextEntries(from, list);
|
|
}
|
|
|
|
public override void OnThink()
|
|
{
|
|
base.OnThink();
|
|
|
|
if (this.ControlMaster != null && this.m_Path != null && this.m_Path.Count > 0)
|
|
{
|
|
Vertex v = this.m_Path[this.m_Path.Count - 1];
|
|
Mobile m = this.ControlMaster;
|
|
|
|
if (m.NetState != null)
|
|
{
|
|
if (Math.Abs(v.DistanceTo(m.Location) - v.DistanceTo(this.Location)) > 10)
|
|
this.Frozen = true;
|
|
else
|
|
this.Frozen = false;
|
|
|
|
if (this.CurrentWayPoint == null)
|
|
{
|
|
this.CurrentWayPoint = new WayPoint();
|
|
this.CurrentWayPoint.MoveToWorld(v.Location, this.Map);
|
|
}
|
|
|
|
int dist = v.DistanceTo(this.Location);
|
|
|
|
if (dist < (v.Teleporter ? 1 : 3) || dist > 100)
|
|
{
|
|
this.m_Path.RemoveAt(this.m_Path.Count - 1);
|
|
|
|
if (this.m_Path.Count > 0)
|
|
{
|
|
if (this.CurrentWayPoint == null)
|
|
this.CurrentWayPoint = new WayPoint();
|
|
|
|
this.CurrentWayPoint.MoveToWorld(this.m_Path[this.m_Path.Count - 1].Location, this.Map);
|
|
}
|
|
else
|
|
{
|
|
Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(3), new TimerStateCallback<Mobile>(CommandFollow), m);
|
|
this.Say(1076051); // We have reached our destination
|
|
this.CommandStop(m);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void CommandFollow(Mobile by)
|
|
{
|
|
this.StopGuiding();
|
|
|
|
base.CommandFollow(by);
|
|
}
|
|
|
|
public override void CommandStop(Mobile by)
|
|
{
|
|
this.StopGuiding();
|
|
|
|
base.CommandStop(by);
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.WriteEncodedInt(0); // version
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadEncodedInt();
|
|
}
|
|
|
|
public void StopGuiding()
|
|
{
|
|
this.CurrentSpeed = this.PassiveSpeed;
|
|
|
|
if (this.CurrentWayPoint != null)
|
|
this.CurrentWayPoint.Delete();
|
|
|
|
if (this.m_Path != null)
|
|
this.m_Path.Clear();
|
|
|
|
this.Controlled = true;
|
|
this.m_Path = null;
|
|
}
|
|
|
|
public void StartGuiding(List<Vertex> path)
|
|
{
|
|
this.m_Path = path;
|
|
|
|
if (this.ControlMaster != null && this.m_Path != null && this.m_Path.Count > 0)
|
|
{
|
|
if (this.m_Path.Count > 1)
|
|
this.m_Path.RemoveAt(this.m_Path.Count - 1);
|
|
|
|
if (this.CurrentWayPoint == null)
|
|
this.CurrentWayPoint = new WayPoint();
|
|
|
|
this.CurrentWayPoint.MoveToWorld(this.m_Path[this.m_Path.Count - 1].Location, this.Map);
|
|
|
|
this.AIObject.Action = ActionType.Wander;
|
|
this.CurrentSpeed = this.ActiveSpeed;
|
|
this.Controlled = false;
|
|
this.Say(1076114); // Please follow me.
|
|
}
|
|
}
|
|
|
|
private class InternalGump : Gump
|
|
{
|
|
private const int ShopsPerPage = 10;
|
|
private const int ShopHeight = 24;
|
|
private readonly AttendantGuide m_Guide;
|
|
private readonly Dictionary<int, Vertex> m_Shops;
|
|
public InternalGump(AttendantGuide guide, Dictionary<int, Vertex> shops)
|
|
: base(60, 36)
|
|
{
|
|
this.m_Guide = guide;
|
|
this.m_Shops = shops;
|
|
|
|
this.AddPage(0);
|
|
|
|
this.AddBackground(0, 0, 273, 84 + ShopsPerPage * ShopHeight, 0x13BE);
|
|
this.AddImageTiled(10, 10, 253, 20, 0xA40);
|
|
this.AddImageTiled(10, 40, 253, 4 + ShopsPerPage * ShopHeight, 0xA40);
|
|
this.AddImageTiled(10, 54 + ShopsPerPage * ShopHeight, 253, 20, 0xA40);
|
|
this.AddAlphaRegion(10, 10, 253, 64 + ShopsPerPage * ShopHeight);
|
|
this.AddButton(10, 54 + ShopsPerPage * ShopHeight, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
|
|
this.AddHtmlLocalized(45, 54 + ShopsPerPage * ShopHeight, 450, 20, 1060051, 0x7FFF, false, false); // CANCEL
|
|
this.AddHtmlLocalized(14, 12, 273, 20, 1076029, 0x7FFF, false, false); // What sort of shop do you seek?
|
|
|
|
int i = 0;
|
|
int page = 0;
|
|
int iPage = 0;
|
|
|
|
foreach (KeyValuePair<int, Vertex> kvp in shops)
|
|
{
|
|
if (i % ShopsPerPage == 0)
|
|
{
|
|
if (page > 0)
|
|
{
|
|
this.AddButton(188, 54 + ShopsPerPage * ShopHeight, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page + 1);
|
|
this.AddHtmlLocalized(228, 56 + ShopsPerPage * ShopHeight, 60, 20, 1043353, 0x7FFF, false, false); // Next
|
|
}
|
|
|
|
this.AddPage(page + 1);
|
|
|
|
if (page > 0)
|
|
{
|
|
this.AddButton(113, 54 + ShopsPerPage * ShopHeight, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page);
|
|
this.AddHtmlLocalized(153, 56 + ShopsPerPage * ShopHeight, 60, 20, 1011393, 0x7FFF, false, false); // Back
|
|
}
|
|
|
|
iPage = 0;
|
|
page++;
|
|
}
|
|
|
|
this.AddButton(19, 49 + iPage * ShopHeight, 0x845, 0x846, 100 + kvp.Key, GumpButtonType.Reply, 0);
|
|
this.AddHtmlLocalized(44, 47 + iPage * ShopHeight, 213, 20, GuideHelper.FindShopName(kvp.Key), 0x7FFF, false, false);
|
|
|
|
i++;
|
|
iPage++;
|
|
}
|
|
}
|
|
|
|
public override void OnResponse(NetState sender, RelayInfo info)
|
|
{
|
|
int shop = info.ButtonID - 100;
|
|
|
|
if (this.m_Guide == null || this.m_Guide.Deleted || this.m_Guide.Region == null || info.ButtonID == 0)
|
|
return;
|
|
|
|
Vertex source = GuideHelper.ClosestVetrex(this.m_Guide.Region.Name, this.m_Guide.Location);
|
|
|
|
if (this.m_Shops.ContainsKey(shop))
|
|
{
|
|
Vertex destination = this.m_Shops[shop];
|
|
List<Vertex> path = GuideHelper.Dijkstra(this.m_Guide.Region.Name, source, destination);
|
|
this.m_Guide.StartGuiding(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class AttendantMaleGuide : AttendantGuide
|
|
{
|
|
[Constructable]
|
|
public AttendantMaleGuide()
|
|
: base()
|
|
{
|
|
}
|
|
|
|
public AttendantMaleGuide(Serial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
public override void InitBody()
|
|
{
|
|
this.SetStr(50, 60);
|
|
this.SetDex(20, 30);
|
|
this.SetInt(100, 110);
|
|
|
|
this.Name = NameList.RandomName("male");
|
|
this.Female = false;
|
|
this.Race = Race.Human;
|
|
this.Hue = this.Race.RandomSkinHue();
|
|
|
|
this.HairItemID = this.Race.RandomHair(this.Female);
|
|
this.HairHue = this.Race.RandomHairHue();
|
|
}
|
|
|
|
public override void InitOutfit()
|
|
{
|
|
this.AddItem(new SamuraiTabi());
|
|
this.AddItem(new Kasa());
|
|
this.AddItem(new MaleKimono(Utility.RandomGreenHue()));
|
|
this.AddItem(new ShepherdsCrook());
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.WriteEncodedInt(0); // version
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadEncodedInt();
|
|
}
|
|
}
|
|
|
|
public class AttendantFemaleGuide : AttendantGuide
|
|
{
|
|
[Constructable]
|
|
public AttendantFemaleGuide()
|
|
: base()
|
|
{
|
|
}
|
|
|
|
public AttendantFemaleGuide(Serial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
public override void InitBody()
|
|
{
|
|
this.SetStr(50, 60);
|
|
this.SetDex(20, 30);
|
|
this.SetInt(100, 110);
|
|
|
|
this.Name = NameList.RandomName("female");
|
|
this.Female = true;
|
|
this.Race = Race.Human;
|
|
this.Hue = this.Race.RandomSkinHue();
|
|
|
|
this.HairItemID = this.Race.RandomHair(this.Female);
|
|
this.HairHue = this.Race.RandomHairHue();
|
|
}
|
|
|
|
public override void InitOutfit()
|
|
{
|
|
this.AddItem(new Shoes(Utility.RandomBlueHue()));
|
|
this.AddItem(new Shirt(0x8FD));
|
|
this.AddItem(new FeatheredHat(Utility.RandomBlueHue()));
|
|
this.AddItem(new Kilt(Utility.RandomBlueHue()));
|
|
|
|
Item item = new Spellbook();
|
|
item.Hue = Utility.RandomBlueHue();
|
|
this.AddItem(item);
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.WriteEncodedInt(0); // version
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadEncodedInt();
|
|
}
|
|
}
|
|
}
|