context: using System;
  using System.Collections;
  using Server.Gumps;
  using Server.Network;
  using Server.Mobiles;
  
  namespace Server.Items
  {
      public class TigerCage : BaseAddon
      {
          [Constructable]
          public TigerCage()
              : this(Utility.RandomMinMax(3, 6))
          {
          }
  
          [Constructable]
          public TigerCage(int sideLength)
          {
              AddGeneratorComponent(2147, 2, 1, 11);
              AddGeneratorComponent(2147, 2, 1, 0);
  
              AddGeneratorComponent(2147, 0, 1, 11);
              AddGeneratorComponent(2147, 0, 1, 0);
              AddGeneratorComponent(2148, 2, 1, 11);
  
              AddGeneratorComponent(2148, 2, 0, 11);
              AddGeneratorComponent(2148, 2, -1, 11);
              AddGeneratorComponent(2147, 2, -2, 11);
  
              AddGeneratorComponent(2147, 1, -2, 11);
              AddGeneratorComponent(2147, 0, -2, 11);
              AddGeneratorComponent(2148, -1, 1, 11);//12
  
              AddGeneratorComponent(2148, -1, 0, 11);
              AddGeneratorComponent(2148, -1, -1, 11);
              AddGeneratorComponent(2149, -1, -2, 11);
  
              AddGeneratorComponent(2149, -1, -2, 0);
              AddGeneratorComponent(2148, -1, 1, 0);
              AddGeneratorComponent(2148, -1, 0, 0);//18
  
              AddGeneratorComponent(2148, -1, -1, 0);
              AddGeneratorComponent(2148, 2, -1, 0);
              AddGeneratorComponent(2147, 2, -2, 0);
  
              AddGeneratorComponent(2147, 0, -2, 0);
              AddGeneratorComponent(2147, 1, -2, 0);
              AddGeneratorComponent(2148, 2, 0, 0);
  
              AddGeneratorComponent(2148, 2, 1, 0);
  
              AddComponent(new TigerCageDoor(sideLength), 1, 2, 0);
          }
  
          public TigerCage(Serial serial)
              : base(serial)
          {
          }
  
          public override bool ShareHue
          {
              get
              {
                  return false;
              }
          }
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.WriteEncodedInt((int)0); // version
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadEncodedInt();
          }
  
          private void AddGeneratorComponent(int itemID, int x, int y, int z)
          {
              AddonComponent component = new AddonComponent(itemID);
              component.Name = "a Cage";
  
              AddComponent(component, x, y, z);
          }
      }
  
      public class TigerCageDoor : AddonComponent
      {
          private static readonly TimeSpan m_UseTimeout = TimeSpan.FromMinutes(2.0);
          private readonly Hashtable m_DamageTable = new Hashtable();
          private int m_SideLength;
          private Node[] m_Path;
          private Mobile m_User;
          private DateTime m_LastUse;
  
          public TigerCageDoor(int sideLength)
              : base(1678)
          {
              SideLength = sideLength;
          }
  
          public TigerCageDoor(Serial serial)
              : base(serial)
          {
          }
  
          private enum PathDirection
          {
              Left,
              Up,
              Right,
              Down
          }
          [CommandProperty(AccessLevel.GameMaster)]
          public int SideLength
          {
              get
              {
                  return m_SideLength;
              }
              set
              {
                  if (value < 3)
                  {
                      value = 3;
                  }
                  else if (value > 6)
                  {
                      value = 6;
                  }
  
                  if (m_SideLength != value)
                  {
                      m_SideLength = value;
                      InitPath();
                  }
              }
          }
          public Node[] Path
          {
              get
              {
                  return m_Path;
              }
          }
          public override string DefaultName
          {
              get
              {
                  return "Cage Door";
              }
          }
          public void InitPath()
          {
              // Depth-First Search algorithm
              int totalNodes = SideLength * SideLength;
  
              Node[] stack = new Node[totalNodes];
              Node current = stack[0] = new Node(0, 0);
              int stackSize = 1;
  
              bool[,] visited = new bool[SideLength, SideLength];
              visited[0, 0] = true;
  
              while (true)
              {
                  PathDirection[] choices = new PathDirection[4];
                  int count = 0;
  
                  if (current.X > 0 && !visited[current.X - 1, current.Y])
                  {
                      choices[count++] = PathDirection.Left;
                  }
  
                  if (current.Y > 0 && !visited[current.X, current.Y - 1])
                  {
                      choices[count++] = PathDirection.Up;
                  }
  
                  if (current.X < SideLength - 1 && !visited[current.X + 1, current.Y])
                  {
                      choices[count++] = PathDirection.Right;
                  }
  
                  if (current.Y < SideLength - 1 && !visited[current.X, current.Y + 1])
                  {
                      choices[count++] = PathDirection.Down;
                  }
  
                  if (count > 0)
                  {
                      PathDirection dir = choices[Utility.Random(count)];
  
                      switch ( dir )
                      {
                          case PathDirection.Left:
                              current = new Node(current.X - 1, current.Y);
                              break;
                          case PathDirection.Up:
                              current = new Node(current.X, current.Y - 1);
                              break;
                          case PathDirection.Right:
                              current = new Node(current.X + 1, current.Y);
                              break;
                          default:
                              current = new Node(current.X, current.Y + 1);
                              break;
                      }
  
                      stack[stackSize++] = current;
  
                      if (current.X == SideLength - 1 && current.Y == SideLength - 1)
                      {
                          break;
                      }
  
                      visited[current.X, current.Y] = true;
                  }
                  else
                  {
                      current = stack[--stackSize - 1];
                  }
              }
  
              m_Path = new Node[stackSize];
  
              for (int i = 0; i < stackSize; i++)
              {
                  m_Path[i] = stack[i];
              }
  
              if (m_User != null)
              {
                  m_User.CloseGump(typeof(DoorCageGump));
                  m_User = null;
              }
          }
  
          public override void OnDoubleClick(Mobile from)
          {
              if (!from.InRange(this, 3))
              {
                  from.SendLocalizedMessage(500446); // That is too far away.
                  return;
              }
  
              if (m_User != null)
              {
                  if (m_User == from)
                  {
                      return;
                  }
  
                  if (m_User.Deleted || m_User.Map != Map || !m_User.InRange(this, 3) ||
                      m_User.NetState == null || DateTime.UtcNow - m_LastUse >= m_UseTimeout)
                  {
                      m_User.CloseGump(typeof(DoorCageGump));
                  }
                  else
                  {
                      from.SendMessage("Someone is currently unlocking this cage.");
                      return;
                  }
              }
  
              m_User = from;
              m_LastUse = DateTime.UtcNow;
  
              from.SendGump(new DoorCageGump(this, from, 0, false));
              from.RevealingAction();
          }
  
          public void DoPosion(Mobile to)
          {
              if (!to.Player || !to.Alive || to.AccessLevel > AccessLevel.Player)
              {
                  return;
              }
  
              Effects.SendLocationEffect(to, to.Map, 0x113A, 20, 10);
              to.PlaySound(0x231);
              to.LocalOverheadMessage(MessageType.Regular, 0x22, 1156493); // *A poisonous dart embeds itself into your neck!*
  
              to.ApplyPoison(to, Poison.Regular);
          }
  
          public void Ambushers(Mobile from)
          {
              for (int i = 0; i < 4; i++)
              {
                  try
                  {
                      BaseCreature ambusher = (BaseCreature)Activator.CreateInstance(m_AmbusherTypes[Utility.Random(m_AmbusherTypes.Length)]);
  
                      ambusher.MoveToWorld(this.Map.GetSpawnPosition(this.Location, 1), this.Map);
                  }
                  catch
                  {
                  }
              }
          }
  
          private static Type[] m_AmbusherTypes = new Type[]
              {
                  typeof(Ambusher),    typeof(Ambusher),
                  typeof(Ambusher),   typeof(Ambusher)
              };
  
          public void Solve(Mobile from)
          {
              Effects.PlaySound(Location, Map, 0x211);
              Effects.PlaySound(Location, Map, 0x1F3);
  
              Effects.SendLocationEffect(Location, Map, 0x36B0, 4, 4);
              Effects.SendLocationEffect(new Point3D(X - 1, Y - 1, Z + 2), Map, 0x36B0, 4, 4);
              Effects.SendLocationEffect(new Point3D(X - 2, Y - 1, Z + 2), Map, 0x36B0, 4, 4);
  
              from.SendMessage("You Open the Cage for the tiger cub to escape");
  
              Delete();
          }
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.WriteEncodedInt((int)0); // version
  
              writer.WriteEncodedInt((int)m_SideLength);
  
              writer.WriteEncodedInt((int)m_Path.Length);
              for (int i = 0; i < m_Path.Length; i++)
              {
                  Node cur = m_Path[i];
  
                  writer.WriteEncodedInt(cur.X);
                  writer.WriteEncodedInt(cur.Y);
              }
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadEncodedInt();
  
              m_SideLength = reader.ReadEncodedInt();
  
              m_Path = new Node[reader.ReadEncodedInt()];
              for (int i = 0; i < m_Path.Length; i++)
              {
                  m_Path[i] = new Node(reader.ReadEncodedInt(), reader.ReadEncodedInt());
              }
          }
  
          public struct Node
          {
              private int m_X;
              private int m_Y;
              public Node(int x, int y)
              {
                  m_X = x;
                  m_Y = y;
              }
  
              public int X
              {
                  get
                  {
                      return m_X;
                  }
                  set
                  {
                      m_X = value;
                  }
              }
              public int Y
              {
                  get
                  {
                      return m_Y;
                  }
                  set
                  {
                      m_Y = value;
                  }
              }
          }
  
          private class DoorCageGump : Gump
          {
              private readonly TigerCageDoor m_Panel;
              private readonly Mobile m_From;
              private readonly int m_Step;
              public DoorCageGump(TigerCageDoor panel, Mobile from, int step, bool hint)
                  : base(5, 30)
              {
                  m_Panel = panel;
                  m_From = from;
                  m_Step = step;
  
                  int sideLength = panel.SideLength;
  
                  AddBackground(50, 0, 530, 410, 0xA28);
  
                  AddImage(0, 0, 0x28C8);
                  AddImage(547, 0, 0x28C9);
  
                  AddBackground(95, 20, 442, 90, 0xA28);
  
                  AddHtml(229, 35, 300, 45, "Enclosure Locking Mechanism", false, false);//1156490
  
                  AddHtml(223, 60, 300, 70, "Use the Directional Controls to", false, false);
                  AddHtml(253, 75, 300, 85, "Unlock the Enclosure", false, false);
  
                  AddImage(140, 40, 0x28D3);
                  AddImage(420, 40, 0x28D3);
  
                  AddBackground(365, 120, 178, 210, 0x1400);
  
                  AddImage(365, 115, 0x28D4);
                  AddImage(365, 288, 0x28D4);
  
                  AddImage(414, 189, 0x589);
                  AddImage(435, 210, 0xA52);
  
                  AddButton(408, 222, 0x29EA, 0x29EC, 1, GumpButtonType.Reply, 0); // Left
                  AddButton(448, 185, 0x29CC, 0x29CE, 2, GumpButtonType.Reply, 0); // Up
                  AddButton(473, 222, 0x29D6, 0x29D8, 3, GumpButtonType.Reply, 0); // Right
                  AddButton(448, 243, 0x29E0, 0x29E2, 4, GumpButtonType.Reply, 0); // Down
  
                  AddBackground(90, 115, 30 + 40 * sideLength, 30 + 40 * sideLength, 0xA28);
                  AddBackground(100, 125, 10 + 40 * sideLength, 10 + 40 * sideLength, 0x1400);
  
                  for (int i = 0; i < sideLength; i++)
                  {
                      for (int j = 0; j < sideLength - 1; j++)
                      {
                          AddImage(120 + 40 * i, 162 + 40 * j, 0x13F9);
                      }
                  }
  
                  for (int i = 0; i < sideLength - 1; i++)
                  {
                      for (int j = 0; j < sideLength; j++)
                      {
                          AddImage(138 + 40 * i, 147 + 40 * j, 0x13FD);
                      }
                  }
  
                  Node[] path = panel.Path;
  
                  NodeHue[,] hues = new NodeHue[sideLength, sideLength];
  
                  for (int i = 0; i <= step; i++)
                  {
                      Node n = path[i];
                      hues[n.X, n.Y] = NodeHue.Blue;
                  }
  
                  Node lastNode = path[path.Length - 1];
                  hues[lastNode.X, lastNode.Y] = NodeHue.Red;
  
                  for (int i = 0; i < sideLength; i++)
                  {
                      for (int j = 0; j < sideLength; j++)
                      {
  
gt:                         AddNode(110 + 40 * i, 135 + 40 * j, hues[i, j]);