context: using Server.Commands;
  using Server.Gumps;
  using Server.Items;
  using Server.Mobiles;
  using Server.Multis;
  using System;
  using System.Collections.Generic;
  
  namespace Server.Engines.Quests
  {
      public class BountyQuestSpawner : Item
      {
          public static void Configure()
          {
              m_ActiveZones = new Dictionary<SpawnZone, List<BaseShipCaptain>>();
  
              foreach (int i in Enum.GetValues(typeof(SpawnZone)))
                  m_ActiveZones.Add((SpawnZone)i, new List<BaseShipCaptain>());
          }
  
          public static void GenerateShipSpawner()
          {
              if (m_Instance == null)
              {
                  m_Instance = new BountyQuestSpawner();
                  m_Instance.MoveToWorld(new Point3D(4558, 2347, 0), Map.Trammel);
              }
          }
  
          private static readonly int[] GoldRange = new int[] { 1000, 10000 };
  
          private static BountyQuestSpawner m_Instance;
          public static BountyQuestSpawner Instance => m_Instance;
  
          private static readonly Dictionary<Mobile, int> m_Bounties = new Dictionary<Mobile, int>();
          public static Dictionary<Mobile, int> Bounties => m_Bounties;
  
          private static readonly Dictionary<SpawnZone, SpawnDefinition> m_Zones = new Dictionary<SpawnZone, SpawnDefinition>();
          public static Dictionary<SpawnZone, SpawnDefinition> Zones => m_Zones;
  
          private Timer m_Timer;
          public Timer Timer => m_Timer;
  
          private static Dictionary<SpawnZone, List<BaseShipCaptain>> m_ActiveZones;
  
          private int m_MaxTram;
          private int m_MaxFel;
          private int m_MaxTokuno;
          private TimeSpan m_SpawnTime;
          private bool m_Active;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aFelMoonglowCount => m_ActiveZones[SpawnZone.FelMoonglow].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aTramMoonglowCount => m_ActiveZones[SpawnZone.TramMoonglow].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aTramJhelomCount => m_ActiveZones[SpawnZone.TramJhelom].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aFelJhelomCount => m_ActiveZones[SpawnZone.FelJhelom].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aTokunoPirateCount => m_ActiveZones[SpawnZone.TokunoPirate].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aTramMerch1Count => m_ActiveZones[SpawnZone.TramMerch1].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aFelMerch1Count => m_ActiveZones[SpawnZone.FelMerch1].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aTramMerch2Count => m_ActiveZones[SpawnZone.TramMerch2].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aFelMerch2Count => m_ActiveZones[SpawnZone.FelMerch2].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int aTokunoMerchCount => m_ActiveZones[SpawnZone.TokunoMerch].Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int MaxTram { get { return m_MaxTram; } set { m_MaxTram = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int MaxFel { get { return m_MaxFel; } set { m_MaxFel = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int MaxTokuno { get { return m_MaxTokuno; } set { m_MaxTokuno = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public TimeSpan SpawnTime
          {
              get { return m_SpawnTime; }
              set
              {
                  m_SpawnTime = value;
  
                  if (m_Timer != null)
                      m_Timer.Stop();
  
                  m_Timer = Timer.DelayCall(m_SpawnTime, m_SpawnTime, new TimerCallback(OnTick));
  
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int bMerchantCount
          {
              get
              {
                  int count = 0;
  
                  foreach (List<BaseShipCaptain> list in m_ActiveZones.Values)
                  {
                      foreach (BaseShipCaptain capt in list)
                      {
                          if (capt is MerchantCaptain)
                              count++;
                      }
                  }
  
                  return count;
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int bPirateCount => m_Bounties.Count;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool Active
          {
              get { return m_Active; }
              set
              {
                  if (m_Active != value)
                  {
                      if (value)
                      {
                          if (m_Timer != null)
                          {
                              m_Timer.Stop();
                              m_Timer = null;
                          }
  
                          m_Timer = Timer.DelayCall(m_SpawnTime, m_SpawnTime, new TimerCallback(OnTick));
                      }
                      else
                      {
                          if (m_Timer != null)
                              m_Timer.Stop();
  
                          m_Timer = null;
  
                          RemoveSpawns();
                      }
                  }
  
                  m_Active = value;
              }
          }
  
          [Constructable]
          public BountyQuestSpawner() : base(0xED4)
          {
              if (m_Instance != null && !m_Instance.Deleted)
              {
                  Active = false;
                  Delete();
              }
  
              Name = "Pirate/Merchant Spawner";
              Visible = false;
              Movable = false;
              m_SpawnTime = TimeSpan.FromMinutes(15);
  
              m_MaxTram = 1;
              m_MaxFel = 1;
              m_MaxTokuno = 1;
  
              Active = true;
              m_Instance = this;
          }
  
          public override void Delete()
          {
              base.Delete();
  
              Active = false;
          }
  
          public static void AddZone(SpawnDefinition def)
          {
              if (!m_Zones.ContainsKey(def.Zone))
                  m_Zones.Add(def.Zone, def);
          }
  
          public void HandleDeath(BaseShipCaptain captain)
          {
              if (captain is PirateCaptain)
                  RemoveBounty(captain);
  
              else if (captain is MerchantCaptain)
                  RemoveMerchant(captain);
          }
  
          public void RemoveBounty(BaseShipCaptain pirate)
          {
              SpawnZone zone = pirate.Zone;
  
              if (m_ActiveZones[zone].Contains(pirate))
                  m_ActiveZones[zone].Remove(pirate);
  
              if (m_Bounties.ContainsKey(pirate))
                  m_Bounties.Remove(pirate);
          }
  
          public void RemoveMerchant(BaseShipCaptain merchant)
          {
              SpawnZone zone = merchant.Zone;
  
              if (m_ActiveZones[zone].Contains(merchant))
                  m_ActiveZones[zone].Remove(merchant);
          }
  
          public void OnTick()
          {
              if (m_Active)
                  SpawnRandom();
          }
  
          public void SpawnRandom()
          {
              foreach (int i in Enum.GetValues(typeof(SpawnZone)))
              {
                  SpawnZone zone = (SpawnZone)i;
  
                  switch (zone)
                  {
                      case SpawnZone.TramJhelom:
                          if (m_ActiveZones[zone].Count < m_MaxTram)
                              SpawnPirateAndGalleon(zone, Map.Trammel);
                          break;
                      case SpawnZone.FelJhelom:
                          if (m_ActiveZones[zone].Count < m_MaxFel)
                              SpawnPirateAndGalleon(zone, Map.Felucca);
                          break;
                      case SpawnZone.TramMoonglow:
                          if (m_ActiveZones[zone].Count < m_MaxTram)
                              SpawnPirateAndGalleon(zone, Map.Trammel);
                          break;
                      case SpawnZone.FelMoonglow:
                          if (m_ActiveZones[zone].Count < m_MaxFel)
                              SpawnPirateAndGalleon(zone, Map.Felucca);
                          break;
                      case SpawnZone.TokunoPirate:
                          if (m_ActiveZones[zone].Count < m_MaxTokuno)
                              SpawnPirateAndGalleon(zone, Map.Tokuno);
                          break;
                      case SpawnZone.TramMerch1:
                          if (m_ActiveZones[zone].Count < m_MaxTram)
                              SpawnMerchantAndGalleon(zone, Map.Trammel);
                          break;
                      case SpawnZone.TramMerch2:
                          if (m_ActiveZones[zone].Count < m_MaxTram)
                              SpawnMerchantAndGalleon(zone, Map.Trammel);
                          break;
                      case SpawnZone.FelMerch1:
                          if (m_ActiveZones[zone].Count < m_MaxFel)
                              SpawnMerchantAndGalleon(zone, Map.Felucca);
                          break;
                      case SpawnZone.FelMerch2:
                          if (m_ActiveZones[zone].Count < m_MaxFel)
                              SpawnMerchantAndGalleon(zone, Map.Felucca);
                          break;
                      case SpawnZone.TokunoMerch:
                          if (m_ActiveZones[zone].Count < m_MaxTokuno)
                              SpawnMerchantAndGalleon(zone, Map.Tokuno);
                          break;
                  }
              }
          }
  
          public void RemoveSpawns()
          {
              List<BaseShipCaptain> ToRemove = new List<BaseShipCaptain>();
  
              foreach (List<BaseShipCaptain> list in m_ActiveZones.Values)
              {
                  foreach (BaseShipCaptain capt in list)
                      ToRemove.Add(capt);
              }
  
              foreach (BaseShipCaptain cap in ToRemove)
                  cap.TryDecayGalleon(cap.Galleon);
          }
  
          public void SpawnPirateAndGalleon(SpawnZone zone, Map map)
          {
              SpawnDefinition def = m_Zones[zone];
  
              if (map != Map.Internal && map != null)
              {
                  Rectangle2D rec = def.SpawnRegion;
                  OrcishGalleon gal = new OrcishGalleon(Direction.North);
                  PirateCaptain pirate = new PirateCaptain(gal);
                  pirate.Zone = zone;
                  gal.Owner = pirate;
                  Point3D p = Point3D.Zero;
                  bool spawned = false;
                  for (int i = 0; i < 25; i++)
                  {
                      int x = Utility.Random(rec.X, rec.Width);
                      int y = Utility.Random(rec.Y, rec.Height);
                      p = new Point3D(x, y, -5);
  
                      if (gal.CanFit(p, map, gal.ItemID))
                      {
                          spawned = true;
                          break;
                      }
                  }
  
                  if (!spawned)
                  {
                      gal.Delete();
                      pirate.Delete();
                      return;
                  }
  
                  int gold = Utility.RandomMinMax(GoldRange[0], GoldRange[1]);
                  gal.MoveToWorld(p, map);
                  gal.AutoAddCannons(pirate);
                  pirate.MoveToWorld(new Point3D(gal.X, gal.Y - 1, gal.ZSurface), map);
  
                  int crewCount = Utility.RandomMinMax(3, 5);
  
                  for (int i = 0; i < crewCount; i++)
                  {
                      Mobile crew = new PirateCrew();
  
                      if (i == 0)
                          crew.Title = "the orc captain";
  
                      pirate.AddToCrew(crew);
                      crew.MoveToWorld(new Point3D(gal.X + Utility.RandomList(-1, 1), gal.Y + Utility.RandomList(-1, 0, 1), gal.ZSurface), map);
                  }
  
                  Point2D[] course = def.GetRandomWaypoints();
                  gal.BoatCourse = new BoatCourse(gal, new List<Point2D>(def.GetRandomWaypoints()));
  
                  gal.NextNavPoint = 0;
                  gal.StartCourse(false, false);
  
                  FillHold(gal);
  
                  m_Bounties.Add(pirate, gold);
                  m_ActiveZones[zone].Add(pirate);
              }
          }
  
          public void SpawnMerchantAndGalleon(SpawnZone zone, Map map)
          {
              SpawnDefinition def = m_Zones[zone];
  
              if (map != Map.Internal && map != null)
              {
                  Rectangle2D rec = def.SpawnRegion;
                  bool garg = Utility.RandomBool();
                  BaseGalleon gal;
                  Point3D p = Point3D.Zero;
                  bool spawned = false;
  
                  if (garg)
                      gal = new GargishGalleon(Direction.North);
                  else
                      gal = new TokunoGalleon(Direction.North);
  
                  MerchantCaptain captain = new MerchantCaptain(gal);
  
                  for (int i = 0; i < 25; i++)
                  {
                      int x = Utility.Random(rec.X, rec.Width);
                      int y = Utility.Random(rec.Y, rec.Height);
                      p = new Point3D(x, y, -5);
  
                      if (gal.CanFit(p, map, gal.ItemID))
                      {
                          spawned = true;
                          break;
                      }
                  }
  
                  if (!spawned)
                  {
                      gal.Delete();
                      captain.Delete();
                      return;
                  }
  
                  gal.Owner = captain;
                  captain.Zone = zone;
                  gal.MoveToWorld(p, map);
                  gal.AutoAddCannons(captain);
                  captain.MoveToWorld(new Point3D(gal.X, gal.Y - 1, gal.ZSurface), map);
  
                  int crewCount = Utility.RandomMinMax(3, 5);
  
                  for (int i = 0; i < crewCount; i++)
                  {
                      Mobile crew = new MerchantCrew();
                      captain.AddToCrew(crew);
                      crew.MoveToWorld(new Point3D(gal.X + Utility.RandomList(-1, 1), gal.Y + Utility.RandomList(-1, 0, 1), gal.ZSurface), map);
                  }
  
                  Point2D[] course = def.GetRandomWaypoints();
                  gal.BoatCourse = new BoatCourse(gal, new List<Point2D>(def.GetRandomWaypoints()));
  
                  gal.NextNavPoint = 0;
                  gal.StartCourse(false, false);
  
                  FillHold(gal);
  
                  m_ActiveZones[zone].Add(captain);
              }
          }
  
          public static void ResetNavPoints(BaseBoat boat)
          {
              boat.NextNavPoint = 0;
              boat.StartCourse(false, false);
          }
  
          public static void FillHold(BaseGalleon galleon)
          {
              if (galleon == null)
                  return;
  
              Container hold = galleon.GalleonHold;
  
              if (hold != null)
              {
                  int cnt = Utility.RandomMinMax(7, 14);
  
                  for (int i = 0; i < cnt; i++)
                  {
                      Item item = RunicReforging.GenerateRandomItem(galleon);
  
                      if (item != null)
                          hold.DropItem(item);
                  }
  
                  hold.DropItem(new Swab());
                  hold.DropItem(new Ramrod());
                  hold.DropItem(new Matches(Utility.RandomMinMax(25, 30)));
                  hold.DropItem(new HeavyCannonball(Utility.RandomMinMax(7, 10)));
                  hold.DropItem(new LightCannonball(Utility.RandomMinMax(7, 10)));
                  hold.DropItem(new HeavyGrapeshot(Utility.RandomMinMax(7, 10)));
                  hold.DropItem(new LightGrapeshot(Utility.RandomMinMax(7, 10)));
                  hold.DropItem(new HeavyPowderCharge(Utility.RandomMinMax(7, 10)));
                  hold.DropItem(new LightPowderCharge(Utility.RandomMinMax(7, 10)));
                  hold.DropItem(new FuseCord(Utility.RandomMinMax(7, 10)));
  
                  if (.10 >= Utility.RandomDouble())
                      hold.DropItem(new SmugglersCache());
  
                  if (.10 >= Utility.RandomDouble())
                  {
                      FishSteak steaks = new FishSteak(5);
                      switch (Utility.Random(5))
                      {
                          case 0:
                              steaks.Name = "Spiced Salmon";
                              steaks.Hue = 1759;
                              break;
                          case 1:
                              steaks.Name = "Dried Tuna";
                              steaks.Hue = 2108;
                              break;
                          case 2:
                              steaks.Name = "Salted Snapper";
                              steaks.Hue = 1864;
                              break;
                          case 3:
                              steaks.Name = "Salted Herring";
                              steaks.Hue = 2302;
                              break;
                          case 4:
                              steaks.Name = "Spiced Salmon";
                              steaks.Hue = 1637;
                              break;
                      }
  
                      hold.DropItem(steaks);
                  }
  
                  hold.DropItem(new Gold(Utility.RandomMinMax(5000, 25000)));
  
                  if (0.50 > Utility.RandomDouble())
                  {
                      switch (Utility.Random(4))
                      {
                          case 0:
                          case 1:
                          case 2: hold.DropItem(new IronWire(Utility.RandomMinMax(1, 5))); break;
                          case 3:
                          case 4:
                          case 5: hold.DropItem(new CopperWire(Utility.RandomMinMax(1, 5))); break;
                          case 6:
                          case 7: hold.DropItem(new SilverWire(Utility.RandomMinMax(1, 5))); break;
                          case 8: hold.DropItem(new GoldWire(Utility.RandomMinMax(1, 5))); break;
                      }
                  }
  
                  switch (Utility.Random(8))
                  {
                      case 0:
                          if (Utility.RandomBool())
                              hold.DropItem(new IronOre(Utility.RandomMinMax(40, 50)));
                          else
                              hold.DropItem(new IronIngot(Utility.RandomMinMax(40, 50)));
                          break;
                      case 1:
                          if (Utility.RandomBool())
                              hold.DropItem(new DullCopperOre(Utility.RandomMinMax(40, 50)));
                          else
                              hold.DropItem(new DullCopperIngot(Utility.RandomMinMax(40, 50)));
                          break;
                      case 2:
                          if (Utility.RandomBool())
                              hold.DropItem(new ShadowIronOre(Utility.RandomMinMax(40, 50)));
                          else
                              hold.DropItem(new ShadowIronIngot(Utility.RandomMinMax(40, 50)));
                          break;
                      case 3:
                          if (Utility.RandomBool())
                              hold.DropItem(new CopperOre(Utility.RandomMinMax(40, 50)));
                          else
                              hold.DropItem(new CopperIngot(Utility.RandomMinMax(40, 50)));
                          break;
                      case 4:
                          if (Utility.RandomBool())
                              hold.DropItem(new BronzeOre(Utility.RandomMinMax(40, 50)));
                          else
                              hold.DropItem(new BronzeIngot(Utility.RandomMinMax(40, 50)));
                          break;
                      case 5:
                          if (Utility.RandomBool())
  
gt:                             hold.DropItem(new AgapiteOre(Utility.RandomMinMax(40, 50)));