context: using System;
  using System.Collections.Generic;
  using System.Text;
  using Server;
  using Server.Mobiles;
  using Server.Network;
  using Server.Spells;
  
  namespace Server.Items
  {
      public class Teleporter : Item
      {
          private bool m_Active, m_Creatures, m_CombatCheck, m_CriminalCheck;
          private Point3D m_PointDest;
          private Map m_MapDest;
          private bool m_SourceEffect;
          private bool m_DestEffect;
          private int m_SoundID;
          private TimeSpan m_Delay;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool SourceEffect
          {
              get { return m_SourceEffect; }
              set { m_SourceEffect = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool DestEffect
          {
              get { return m_DestEffect; }
              set { m_DestEffect = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int SoundID
          {
              get { return m_SoundID; }
              set { m_SoundID = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public TimeSpan Delay
          {
              get { return m_Delay; }
              set { m_Delay = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool Active
          {
              get { return m_Active; }
              set { m_Active = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Point3D PointDest
          {
              get { return m_PointDest; }
              set { m_PointDest = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Map MapDest
          {
              get { return m_MapDest; }
              set { m_MapDest = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool Creatures
          {
              get { return m_Creatures; }
              set { m_Creatures = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool CombatCheck
          {
              get { return m_CombatCheck; }
              set { m_CombatCheck = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool CriminalCheck
          {
              get { return m_CriminalCheck; }
              set { m_CriminalCheck = value; InvalidateProperties(); }
          }
  
          public override int LabelNumber { get { return 1026095; } } // teleporter
  
          [Constructable]
          public Teleporter()
              : this(new Point3D(0, 0, 0), null, false)
          {
          }
  
          [Constructable]
          public Teleporter(Point3D pointDest, Map mapDest)
              : this(pointDest, mapDest, false)
          {
          }
  
          [Constructable]
          public Teleporter(Point3D pointDest, Map mapDest, bool creatures)
              : base(0x1BC3)
          {
              Movable = false;
              Visible = false;
  
              m_Active = true;
              m_PointDest = pointDest;
              m_MapDest = mapDest;
              m_Creatures = creatures;
  
              m_CombatCheck = false;
              m_CriminalCheck = false;
          }
  
          public override void GetProperties(ObjectPropertyList list)
          {
              base.GetProperties(list);
  
              if (m_Active)
                  list.Add(1060742); // active
              else
                  list.Add(1060743); // inactive
  
              if (m_MapDest != null)
                  list.Add(1060658, "Map\t{0}", m_MapDest);
  
              if (m_PointDest != Point3D.Zero)
                  list.Add(1060659, "Coords\t{0}", m_PointDest);
  
              list.Add(1060660, "Creatures\t{0}", m_Creatures ? "Yes" : "No");
          }
  
          public override void OnSingleClick(Mobile from)
          {
              base.OnSingleClick(from);
  
              if (m_Active)
              {
                  if (m_MapDest != null && m_PointDest != Point3D.Zero)
                      LabelTo(from, "{0} [{1}]", m_PointDest, m_MapDest);
                  else if (m_MapDest != null)
                      LabelTo(from, "[{0}]", m_MapDest);
                  else if (m_PointDest != Point3D.Zero)
                      LabelTo(from, m_PointDest.ToString());
              }
              else
              {
                  LabelTo(from, "(inactive)");
              }
          }
  
          public virtual bool CanTeleport(Mobile m)
          {
              if (!m_Creatures && !m.Player)
              {
                  return false;
              }
              else if (m_CriminalCheck && m.Criminal)
              {
                  m.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily.
                  return false;
              }
              else if (m_CombatCheck && SpellHelper.CheckCombat(m))
              {
                  m.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle??
                  return false;
              }
  
              return true;
          }
  
          public virtual void StartTeleport(Mobile m)
          {
              if (m_Delay == TimeSpan.Zero)
                  DoTeleport(m);
              else
                  Timer.DelayCall<Mobile>(m_Delay, DoTeleport, m);
          }
  
          public virtual void DoTeleport(Mobile m)
          {
              Map map = m_MapDest;
  
              if (map == null || map == Map.Internal)
                  map = m.Map;
  
              Point3D p = m_PointDest;
  
              if (p == Point3D.Zero)
                  p = m.Location;
  
              Server.Mobiles.BaseCreature.TeleportPets(m, p, map);
  
              bool sendEffect = (!m.Hidden || m.AccessLevel == AccessLevel.Player);
  
              if (m_SourceEffect && sendEffect)
                  Effects.SendLocationEffect(m.Location, m.Map, 0x3728, 10, 10);
  
              m.MoveToWorld(p, map);
  
              if (m_DestEffect && sendEffect)
                  Effects.SendLocationEffect(m.Location, m.Map, 0x3728, 10, 10);
  
              if (m_SoundID > 0 && sendEffect)
                  Effects.PlaySound(m.Location, m.Map, m_SoundID);
          }
  
          public override bool OnMoveOver(Mobile m)
          {
              if (m_Active && CanTeleport(m))
              {
                  StartTeleport(m);
                  return false;
              }
  
              return true;
          }
  
          public Teleporter(Serial serial)
              : base(serial)
          {
          }
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.Write((int)4); // version
  
              writer.Write((bool)m_CriminalCheck);
              writer.Write((bool)m_CombatCheck);
  
              writer.Write((bool)m_SourceEffect);
              writer.Write((bool)m_DestEffect);
              writer.Write((TimeSpan)m_Delay);
              writer.WriteEncodedInt((int)m_SoundID);
  
              writer.Write(m_Creatures);
  
              writer.Write(m_Active);
              writer.Write(m_PointDest);
              writer.Write(m_MapDest);
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadInt();
  
              switch (version)
              {
                  case 4:
                      {
                          m_CriminalCheck = reader.ReadBool();
                          goto case 3;
                      }
                  case 3:
                      {
                          m_CombatCheck = reader.ReadBool();
                          goto case 2;
                      }
                  case 2:
                      {
                          m_SourceEffect = reader.ReadBool();
                          m_DestEffect = reader.ReadBool();
                          m_Delay = reader.ReadTimeSpan();
                          m_SoundID = reader.ReadEncodedInt();
  
                          goto case 1;
                      }
                  case 1:
                      {
                          m_Creatures = reader.ReadBool();
  
                          goto case 0;
                      }
                  case 0:
                      {
                          m_Active = reader.ReadBool();
                          m_PointDest = reader.ReadPoint3D();
                          m_MapDest = reader.ReadMap();
  
                          break;
                      }
              }
          }
      }
  
      public class SkillTeleporter : Teleporter
      {
          private SkillName m_Skill;
          private double m_Required;
          private string m_MessageString;
          private int m_MessageNumber;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public SkillName Skill
          {
              get { return m_Skill; }
              set { m_Skill = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public double Required
          {
              get { return m_Required; }
              set { m_Required = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public string MessageString
          {
              get { return m_MessageString; }
              set { m_MessageString = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int MessageNumber
          {
              get { return m_MessageNumber; }
              set { m_MessageNumber = value; InvalidateProperties(); }
          }
  
          private void EndMessageLock(object state)
          {
              ((Mobile)state).EndAction(this);
          }
  
          public override bool CanTeleport(Mobile m)
          {
              if (!base.CanTeleport(m))
                  return false;
  
              Skill sk = m.Skills[m_Skill];
  
              if (sk == null || sk.Base < m_Required)
              {
                  if (m.BeginAction(this))
                  {
                      if (m_MessageString != null)
                          m.Send(new UnicodeMessage(Serial, ItemID, MessageType.Regular, 0x3B2, 3, "ENU", null, m_MessageString));
                      else if (m_MessageNumber != 0)
                          m.Send(new MessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, m_MessageNumber, null, ""));
  
                      Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerStateCallback(EndMessageLock), m);
                  }
  
                  return false;
              }
  
              return true;
          }
  
          public override void GetProperties(ObjectPropertyList list)
          {
              base.GetProperties(list);
  
              int skillIndex = (int)m_Skill;
              string skillName;
  
              if (skillIndex >= 0 && skillIndex < SkillInfo.Table.Length)
                  skillName = SkillInfo.Table[skillIndex].Name;
              else
                  skillName = "(Invalid)";
  
              list.Add(1060661, "{0}\t{1:F1}", skillName, m_Required);
  
              if (m_MessageString != null)
                  list.Add(1060662, "Message\t{0}", m_MessageString);
              else if (m_MessageNumber != 0)
                  list.Add(1060662, "Message\t#{0}", m_MessageNumber);
          }
  
          [Constructable]
          public SkillTeleporter()
          {
          }
  
          public SkillTeleporter(Serial serial)
              : base(serial)
          {
          }
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.Write((int)0); // version
  
              writer.Write((int)m_Skill);
              writer.Write((double)m_Required);
              writer.Write((string)m_MessageString);
              writer.Write((int)m_MessageNumber);
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadInt();
  
              switch (version)
              {
                  case 0:
                      {
                          m_Skill = (SkillName)reader.ReadInt();
                          m_Required = reader.ReadDouble();
                          m_MessageString = reader.ReadString();
                          m_MessageNumber = reader.ReadInt();
  
                          break;
                      }
              }
          }
      }
  
      public class KeywordTeleporter : Teleporter
      {
          private string m_Substring;
          private int m_Keyword;
          private int m_Range;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public string Substring
          {
              get { return m_Substring; }
              set { m_Substring = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int Keyword
          {
              get { return m_Keyword; }
              set { m_Keyword = value; InvalidateProperties(); }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int Range
          {
              get { return m_Range; }
              set { m_Range = value; InvalidateProperties(); }
          }
  
          public override bool HandlesOnSpeech { get { return true; } }
  
          public override void OnSpeech(SpeechEventArgs e)
          {
              if (!e.Handled && Active)
              {
                  Mobile m = e.Mobile;
  
                  if (!m.InRange(GetWorldLocation(), m_Range))
                      return;
  
                  bool isMatch = false;
  
                  if (m_Keyword >= 0 && e.HasKeyword(m_Keyword))
                      isMatch = true;
                  else if (m_Substring != null && e.Speech.ToLower().IndexOf(m_Substring.ToLower()) >= 0)
                      isMatch = true;
  
                  if (!isMatch || !CanTeleport(m))
                      return;
  
                  e.Handled = true;
                  StartTeleport(m);
              }
          }
  
          public override void DoTeleport(Mobile m)
          {
              if (!m.InRange(GetWorldLocation(), m_Range) || m.Map != Map)
                  return;
  
              base.DoTeleport(m);
          }
  
          public override bool OnMoveOver(Mobile m)
          {
              return true;
          }
  
          public override void GetProperties(ObjectPropertyList list)
          {
              base.GetProperties(list);
  
              list.Add(1060661, "Range\t{0}", m_Range);
  
              if (m_Keyword >= 0)
                  list.Add(1060662, "Keyword\t{0}", m_Keyword);
  
              if (m_Substring != null)
                  list.Add(1060663, "Substring\t{0}", m_Substring);
          }
  
          [Constructable]
          public KeywordTeleporter()
          {
              m_Keyword = -1;
              m_Substring = null;
          }
  
          public KeywordTeleporter(Serial serial)
              : base(serial)
          {
          }
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.Write((int)0); // version
  
              writer.Write(m_Substring);
              writer.Write(m_Keyword);
              writer.Write(m_Range);
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadInt();
  
              switch (version)
              {
                  case 0:
                      {
                          m_Substring = reader.ReadString();
                          m_Keyword = reader.ReadInt();
                          m_Range = reader.ReadInt();
  
                          break;
                      }
              }
          }
      }
  
      public class WaitTeleporter : KeywordTeleporter
      {
          private static Dictionary<Mobile, TeleportingInfo> m_Table;
  
          public static void Initialize()
          {
              m_Table = new Dictionary<Mobile, TeleportingInfo>();
  
              EventSink.Logout += new LogoutEventHandler(EventSink_Logout);
          }
  
          public static void EventSink_Logout(LogoutEventArgs e)
          {
              Mobile from = e.Mobile;
              TeleportingInfo info;
  
              if (from == null || !m_Table.TryGetValue(from, out info))
                  return;
  
              info.Timer.Stop();
              m_Table.Remove(from);
          }
  
          private int m_StartNumber;
          private string m_StartMessage;
          private int m_ProgressNumber;
          private string m_ProgressMessage;
          private bool m_ShowTimeRemaining;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int StartNumber
          {
              get { return m_StartNumber; }
              set { m_StartNumber = value; }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public string StartMessage
          {
              get { return m_StartMessage; }
              set { m_StartMessage = value; }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int ProgressNumber
          {
              get { return m_ProgressNumber; }
              set { m_ProgressNumber = value; }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public string ProgressMessage
          {
              get { return m_ProgressMessage; }
              set { m_ProgressMessage = value; }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool ShowTimeRemaining
          {
              get { return m_ShowTimeRemaining; }
              set { m_ShowTimeRemaining = value; }
          }
  
          [Constructable]
          public WaitTeleporter()
          {
          }
  
          public static string FormatTime(TimeSpan ts)
          {
              if (ts.TotalHours >= 1)
              {
                  int h = (int)Math.Round(ts.TotalHours);
                  return String.Format("{0} hour{1}", h, (h == 1) ? "" : "s");
              }
              else if (ts.TotalMinutes >= 1)
              {
                  int m = (int)Math.Round(ts.TotalMinutes);
                  return String.Format("{0} minute{1}", m, (m == 1) ? "" : "s");
              }
  
              int s = Math.Max((int)Math.Round(ts.TotalSeconds), 0);
              return String.Format("{0} second{1}", s, (s == 1) ? "" : "s");
          }
  
          private void EndLock(Mobile m)
          {
              m.EndAction(this);
          }
  
          public override void StartTeleport(Mobile m)
          {
              TeleportingInfo info;
  
              if (m_Table.TryGetValue(m, out info))
              {
                  if (info.Teleporter == this)
                  {
                      if (m.BeginAction(this))
                      {
                          if (m_ProgressMessage != null)
                              m.SendMessage(m_ProgressMessage);
                          else if (m_ProgressNumber != 0)
                              m.SendLocalizedMessage(m_ProgressNumber);
  
                          if (m_ShowTimeRemaining)
                              m.SendMessage("Time remaining: {0}", FormatTime(m_Table[m].Timer.Next - DateTime.UtcNow));
  
                          Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(5), EndLock, m);
                      }
  
                      return;
                  }
                  else
                  {
                      info.Timer.Stop();
                  }
              }
  
              if (m_StartMessage != null)
                  m.SendMessage(m_StartMessage);
              else if (m_StartNumber != 0)
                  m.SendLocalizedMessage(m_StartNumber);
  
              if (Delay == TimeSpan.Zero)
                  DoTeleport(m);
              else
                  m_Table[m] = new TeleportingInfo(this, Timer.DelayCall<Mobile>(Delay, DoTeleport, m));
          }
  
          public override void DoTeleport(Mobile m)
          {
              m_Table.Remove(m);
  
              base.DoTeleport(m);
          }
  
          public WaitTeleporter(Serial serial)
              : base(serial)
          {
          }
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.Write((int)0); // version
  
              writer.Write(m_StartNumber);
              writer.Write(m_StartMessage);
              writer.Write(m_ProgressNumber);
              writer.Write(m_ProgressMessage);
              writer.Write(m_ShowTimeRemaining);
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadInt();
  
              m_StartNumber = reader.ReadInt();
              m_StartMessage = reader.ReadString();
              m_ProgressNumber = reader.ReadInt();
              m_ProgressMessage = reader.ReadString();
              m_ShowTimeRemaining = reader.ReadBool();
          }
  
          private class TeleportingInfo
          {
              private WaitTeleporter m_Teleporter;
              private Timer m_Timer;
  
              public WaitTeleporter Teleporter { get { return m_Teleporter; } }
              public Timer Timer { get { return m_Timer; } }
  
              public TeleportingInfo(WaitTeleporter tele, Timer t)
              {
                  m_Teleporter = tele;
                  m_Timer = t;
              }
          }
      }
  
      public class TimeoutTeleporter : Teleporter
      {
          private TimeSpan m_TimeoutDelay;
          private Dictionary<Mobile, Timer> m_Teleporting;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public TimeSpan TimeoutDelay
          {
              get { return m_TimeoutDelay; }
              set { m_TimeoutDelay = value; }
          }
  
          [Constructable]
          public TimeoutTeleporter()
              : this(new Point3D(0, 0, 0), null, false)
          {
          }
  
          [Constructable]
          public TimeoutTeleporter(Point3D pointDest, Map mapDest)
              : this(pointDest, mapDest, false)
          {
          }
  
          [Constructable]
          public TimeoutTeleporter(Point3D pointDest, Map mapDest, bool creatures)
              : base(pointDest, mapDest, creatures)
          {
              m_Teleporting = new Dictionary<Mobile, Timer>();
          }
  
          public void StartTimer(Mobile m)
          {
              StartTimer(m, m_TimeoutDelay);
          }
  
          private void StartTimer(Mobile m, TimeSpan delay)
          {
              Timer t;
  
              if (m_Teleporting.TryGetValue(m, out t))
                  t.Stop();
  
              m_Teleporting[m] = Timer.DelayCall<Mobile>(delay, StartTeleport, m);
          }
  
          public void StopTimer(Mobile m)
          {
              Timer t;
  
              if (m_Teleporting.TryGetValue(m, out t))
              {
                  t.Stop();
                  m_Teleporting.Remove(m);
              }
          }
  
          public override void DoTeleport(Mobile m)
          {
              m_Teleporting.Remove(m);
  
              base.DoTeleport(m);
          }
  
          public override bool OnMoveOver(Mobile m)
          {
              if (Active)
              {
                  if (!CanTeleport(m))
                      return false;
  
                  StartTimer(m);
              }
  
              return true;
          }
  
          public TimeoutTeleporter(Serial serial)
              : base(serial)
          {
          }
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.Write((int)0); // version
  
              writer.Write(m_TimeoutDelay);
              writer.Write(m_Teleporting.Count);
  
              foreach (KeyValuePair<Mobile, Timer> kvp in m_Teleporting)
              {
                  writer.Write(kvp.Key);
                  writer.Write(kvp.Value.Next);
              }
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadInt();
  
              m_TimeoutDelay = reader.ReadTimeSpan();
              m_Teleporting = new Dictionary<Mobile, Timer>();
  
              int count = reader.ReadInt();
  
              for (int i = 0; i < count; ++i)
              {
                  Mobile m = reader.ReadMobile();
                  DateTime end = reader.ReadDateTime();
  
  
gt:                 StartTimer(m, end - DateTime.UtcNow);