context: using System;
  using Server;
  using Server.Mobiles;
  using Server.Items;
  using System.Linq;
  using System.Collections.Generic;
  using Server.Commands;
  
  namespace Server.Engines.MyrmidexInvasion
  {
      public class BattleSpawner : Item
      {
          public const int MaxWaves = 2;
          public const int WaveCount = 15;
          public const int WaveDuration = 180;
          public const int MinCredit = 25;
  
          public static bool Active = true;
          public static BattleSpawner Instance { get; set; }
  
          private Rectangle2D MyrmidexLeg = new Rectangle2D(949, 1776, 59, 136);
          private Rectangle2D MiddleLeg = new Rectangle2D(889, 1776, 60, 136);
          private Rectangle2D TribeLeg = new Rectangle2D(832, 1776, 57, 136);
  
          public Rectangle2D Bounds = new Rectangle2D(828, 1762, 195, 158);
  
          private Rectangle2D _MyrmidexSpawnZone = new Rectangle2D(971, 1856, 31, 10);
          private Rectangle2D _TribeSpawnZone = new Rectangle2D(844, 1800, 42, 10);
  
          private Rectangle2D _MyrmidexObjective = new Rectangle2D(909, 1871, 10, 10);
          private Rectangle2D _TribalObjective = new Rectangle2D(908, 1782, 10, 10);
  
          public BattleRegion BattleRegion { get; set; }
          public Timer Timer { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public BattleFlag MyrmidexFlag { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public BattleFlag TribalFlag { get; set; }
  
          public Dictionary<int, List<BaseCreature>> MyrmidexTeam { get; set; }
          public Dictionary<int, List<BaseCreature>> TribeTeam { get; set; }
  
          public Dictionary<PlayerMobile, int> Players { get; set; }
  
          public DateTime LastPlayers { get; set; }
          public DateTime LastMyrmidexWave { get; set; }
          public DateTime LastTribeWave { get; set; }
  
          public BattleSpawner()
              : base(40106)
          {
              Visible = false;
              Movable = false;
  
              Instance = this;
  
              MyrmidexTeam = new Dictionary<int, List<BaseCreature>>();
              TribeTeam = new Dictionary<int, List<BaseCreature>>();
              Players = new Dictionary<PlayerMobile, int>();
  
              BattleRegion = Region.Regions.FirstOrDefault(r => r.GetType() == typeof(BattleRegion)) as BattleRegion;
              BattleRegion.Spawner = Instance;
  
              Timer = Timer.DelayCall(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), OnTick);
              Timer.Start();
          }
  
          public static void Initialize()
          {
              if (Instance != null)
              {
                  Instance.BattleRegion = Region.Regions.FirstOrDefault(r => r.GetType() == typeof(BattleRegion)) as BattleRegion;
                  Instance.BattleRegion.Spawner = Instance;
              }
  
              CommandSystem.Register("CheckPoints", AccessLevel.GameMaster, e =>
              {
                  if (Instance != null)
                      CheckPoints(e);
              });
          }
  
          public void OnTick()
          {
              if (MyrmidexFlag == null || TribalFlag == null)
                  AssignFlags();
  
              CheckPlayers();
              CheckAdvance();
              CheckWaves();
          }
  
          public void CheckPlayers()
          {
              if (HasPlayers())
              {
                  if (LastPlayers != DateTime.MinValue)
                      LastPlayers = DateTime.MinValue;
  
                  if (LastMyrmidexWave + TimeSpan.FromSeconds(WaveDuration) < DateTime.UtcNow && MyrmidexTeam.Count < MaxWaves)
                      SpawnWave(Allegiance.Myrmidex);
  
                  if (LastTribeWave + TimeSpan.FromSeconds(WaveDuration) < DateTime.UtcNow && TribeTeam.Count < MaxWaves)
                      SpawnWave(Allegiance.Tribes);
              }
              else if (LastPlayers == DateTime.MinValue)
              {
                  LastPlayers = DateTime.UtcNow;
              }
              else if (LastPlayers + TimeSpan.FromMinutes(30) < DateTime.UtcNow)
              {
                  Reset();
              }
          }
  
          public void CheckAdvance()
          {
              List<BaseCreature> list = GetAll(Allegiance.Myrmidex);
              IEnumerable<PlayerMobile> winners = null;
  
              foreach (BaseCreature bc in list.Where(b => IsInMyrmidexObjective(b.Location) && 0.25 > Utility.RandomDouble()))
              {
                  ClearWave(Allegiance.Myrmidex, GetWave(MyrmidexTeam, bc));
                  winners = GetPlayers(Allegiance.Myrmidex);
                  RegionMessage(1156630); // The Myrmidex are victorious!  If you are allied to the Myrmidex visit the Tinker in the Barrab village to continue the quest!  Otherwise, continue the fight until your team is victorious!
                  break;
              }
  
              if (winners == null)
              {
                  ColUtility.Free(list);
                  list = GetAll(Allegiance.Tribes);
  
                  foreach (BaseCreature bc in list.Where(b => IsInTribalObjective(b.Location) && 0.25 > Utility.RandomDouble()))
                  {
                      ClearWave(Allegiance.Tribes, GetWave(TribeTeam, bc));
                      winners = GetPlayers(Allegiance.Tribes);
                      RegionMessage(1156631); // The Eodonians are victorious!  If you are allied to the Eodonians visit the Tinker in the Barrab village to continue the quest!  Otherwise, continue the fight until your team is victorious!
                      break;
                  }
              }
  
              if (winners != null)
              {
                  ColUtility.ForEach(winners.Where(pm => Players.ContainsKey(pm) && Players[pm] > MinCredit), pm =>
                  {
                      AllianceEntry entry = MyrmidexInvasionSystem.GetEntry(pm);
  
                      if (entry != null && !entry.CanRecieveQuest)
                          entry.CanRecieveQuest = true;
  
                      if (Players.ContainsKey(pm))
                          Players.Remove(pm);
                  });
              }
  
              ColUtility.Free(list);
          }
  
          private void ClearWave(Allegiance allegiance, int wave)
          {
              if (wave < 0)
                  return;
  
              Dictionary<int, List<BaseCreature>> list;
  
              if (allegiance == Allegiance.Myrmidex)
              {
                  list = MyrmidexTeam;
              }
              else
              {
                  list = TribeTeam;
              }
  
              if (list.ContainsKey(wave))
              {
                  ColUtility.ForEach(list[wave].Where(bc => bc.Alive), bc => bc.Delete());
                  ColUtility.Free(list[wave]);
                  list.Remove(wave);
              }
          }
  
          private void Reset()
          {
              if (BattleRegion == null)
                  return;
  
              IEnumerable<BaseCreature> list = BattleRegion.GetEnumeratedMobiles().OfType<BaseCreature>();
  
              foreach (BaseCreature bc in list.Where(b => b.Alive && !b.Controlled && !b.Summoned && b.GetMaster() == null))
              {
                  bc.Kill();
              }
  
              foreach (KeyValuePair<int, List<BaseCreature>> kvp in MyrmidexTeam)
              {
                  ColUtility.Free(kvp.Value);
              }
  
              foreach (KeyValuePair<int, List<BaseCreature>> kvp in TribeTeam)
              {
                  ColUtility.Free(kvp.Value);
              }
  
              MyrmidexTeam.Clear();
              TribeTeam.Clear();
          }
  
          public void CheckWaves()
          {
              ColUtility.For<int, List<BaseCreature>>(MyrmidexTeam, (i, key, value) =>
              {
                  if (value.Where(bc => bc != null && !bc.Deleted && bc.Alive).Count() == 0)
                  {
                      ColUtility.Free(MyrmidexTeam[key]);
                      MyrmidexTeam.Remove(key);
  
                      RegionMessage(i == 0 ? 1156604 : 1156605); // The Eodonians have secured new ground, the front line has moved up!
                  }
              });
  
              ColUtility.For<int, List<BaseCreature>>(TribeTeam, (i, key, value) =>
              {
                  if (value.Where(bc => bc != null && !bc.Deleted && bc.Alive).Count() == 0)
                  {
                      ColUtility.Free(TribeTeam[key]);
                      TribeTeam.Remove(key);
  
                      RegionMessage(i == 0 ? 1156602 : 1156603); // The Myrmidex have secured new ground, the front line has moved up!
                  }
              });
          }
  
          public void SpawnWave(Allegiance allegiance)
          {
              if (allegiance == Allegiance.Myrmidex)
              {
                  int wave = MyrmidexTeam.Count;
                  MyrmidexTeam[wave] = new List<BaseCreature>();
                  List<BaseCreature> list = MyrmidexTeam[wave];
  
                  for (int i = 0; i < WaveCount; i++)
                  {
                      BaseCreature bc;
                      Type type = _MyrmidexTypes[Utility.Random(_MyrmidexTypes.Length)];
  
                      if (type.IsSubclassOf(typeof(BaseEodonTribesman)))
                          bc = Activator.CreateInstance(type, new object[] { EodonTribe.Barrab }) as BaseCreature;
                      else
                          bc = Activator.CreateInstance(type) as BaseCreature;
  
                      bc.NoLootOnDeath = true;
  
                      if (bc != null)
                      {
                          for (int j = 0; j < 20; j++)
                          {
                              Point3D p = _MyrmidexSpawnZone.GetRandomSpawnPoint(Map.TerMur);
  
                              if (Map.TerMur.CanSpawnMobile(p.X, p.Y, p.Z))
                              {
                                  bc.MoveToWorld(p, Map.TerMur);
                                  list.Add(bc);
  
                                  AssignNavpoints(bc, Allegiance.Myrmidex);
                                  break;
                              }
                          }
                      }
                  }
  
                  LastMyrmidexWave = DateTime.UtcNow;
              }
              else
              {
                  int wave = TribeTeam.Count;
                  TribeTeam[wave] = new List<BaseCreature>();
                  List<BaseCreature> list = TribeTeam[wave];
  
                  for (int i = 0; i < WaveCount; i++)
                  {
                      BaseCreature bc;
                      Type type = _TribeTypes[Utility.Random(_TribeTypes.Length)];
  
                      if (type.IsSubclassOf(typeof(BaseEodonTribesman)))
                      {
                          EodonTribe tribe = Utility.RandomList<EodonTribe>(EodonTribe.Jukari, EodonTribe.Kurak, EodonTribe.Barako, EodonTribe.Urali, EodonTribe.Sakkhra);
                          bc = Activator.CreateInstance(type, new object[] { tribe }) as BaseCreature;
                      }
                      else
                          bc = Activator.CreateInstance(type) as BaseCreature;
  
                      bc.NoLootOnDeath = true;
  
                      if (bc != null)
                      {
                          for (int j = 0; j < 20; j++)
                          {
                              Point3D p = _TribeSpawnZone.GetRandomSpawnPoint(Map.TerMur);
  
                              if (Map.TerMur.CanSpawnMobile(p.X, p.Y, p.Z))
                              {
                                  bc.MoveToWorld(p, Map.TerMur);
                                  list.Add(bc);
  
                                  AssignNavpoints(bc, Allegiance.Tribes);
                                  break;
                              }
                          }
                      }
                  }
  
                  LastTribeWave = DateTime.UtcNow;
              }
          }
  
          public void OnLeaveRegion(PlayerMobile pm)
          {
              if (Players.ContainsKey(pm))
                  Players.Remove(pm);
          }
  
          public bool IsFrontLine(Mobile killer, BaseCreature bc)
          {
              if (bc == null)
                  return false;
  
              Map map = bc.Corpse != null ? bc.Corpse.Map : bc.Map;
              Point3D p = bc.Corpse != null ? bc.Corpse.Location : bc.Location;
  
              if (map != null)
              {
                  IPooledEnumerable eable = map.GetMobilesInRange(p, 12);
  
                  foreach (Mobile m in eable)
                  {
                      if (m != killer && IsSameLeg(bc, m) && MyrmidexInvasionSystem.IsEnemies(bc, m))
                      {
                          eable.Free();
                          return true;
                      }
                  }
  
                  eable.Free();
              }
  
              return false;
          }
  
          public IEnumerable<PlayerMobile> GetPlayers(Allegiance allegiance)
          {
              if (BattleRegion == null)
                  return null;
  
              return BattleRegion.GetEnumeratedMobiles().OfType<PlayerMobile>().Where(p => MyrmidexInvasionSystem.IsAlliedWith(p, allegiance));
          }
  
          public bool HasPlayers(bool ignorestaff = true)
          {
              if (BattleRegion == null)
                  return false;
  
              return BattleRegion.GetEnumeratedMobiles().Where(m => m is PlayerMobile && (!ignorestaff || m.AccessLevel == AccessLevel.Player)).Count() > 0;
          }
  
          public bool IsInMyrmidexObjective(Point3D p)
          {
              return _MyrmidexObjective.Contains(p);
          }
  
          public bool IsInTribalObjective(Point3D p)
          {
              return _TribalObjective.Contains(p);
          }
  
          public void RegionMessage(int message)
          {
              ColUtility.ForEach(BattleRegion.GetEnumeratedMobiles().OfType<PlayerMobile>(), pm => pm.SendLocalizedMessage(message));
          }
  
          public bool IsSameLeg(IPoint2D p1, IPoint2D p2)
          {
              return (MyrmidexLeg.Contains(p1) && MyrmidexLeg.Contains(p2)) ||
                     (MiddleLeg.Contains(p1) && MiddleLeg.Contains(p2)) ||
                     (TribeLeg.Contains(p1) && TribeLeg.Contains(p2));
          }
  
          public int GetWave(Dictionary<int, List<BaseCreature>> list, BaseCreature bc)
          {
              foreach (KeyValuePair<int, List<BaseCreature>> kvp in list)
              {
                  if (kvp.Value.Contains(bc))
                      return kvp.Key;
              }
  
              return -1;
          }
  
          public List<BaseCreature> GetAll(Allegiance allegiance)
          {
              Dictionary<int, List<BaseCreature>> list;
  
              if (allegiance == Allegiance.Myrmidex)
                  list = MyrmidexTeam;
              else
                  list = TribeTeam;
  
              List<BaseCreature> bclist = new List<BaseCreature>();
  
              ColUtility.ForEach(list, (key, value) =>
              {
                  bclist.AddRange(value.Where(bc => bc != null && !bc.Deleted && bc.Alive));
              });
  
              return bclist;
          }
  
          public void RegisterDeath(BaseCreature bc)
          {
              List<DamageStore> rights = bc.GetLootingRights();
  
              ColUtility.ForEach(rights.Where(ds => ds.m_Mobile is PlayerMobile && ds.m_HasRight && MyrmidexInvasionSystem.IsEnemies(ds.m_Mobile, bc)), ds =>
              {
                  if (MyrmidexInvasionSystem.IsAlliedWith(bc, Allegiance.Myrmidex))
                  {
                      int points = 1;
                      if (IsFrontLine(ds.m_Mobile, bc))
                      {
                          ds.m_Mobile.SendLocalizedMessage(1156599); // You assist the Eodonians in pushing back the Myrmidex!
                          points *= 4;
                      }
                      else
                          ds.m_Mobile.SendLocalizedMessage(1156600); // You kill one of the Myrmidex away from the front ranks and gain little recognition.
  
                      if (!Players.ContainsKey((PlayerMobile)ds.m_Mobile))
                          Players[(PlayerMobile)ds.m_Mobile] = points;
                      else
                          Players[(PlayerMobile)ds.m_Mobile] += points;
                  }
                  else
                  {
                      int points = 1;
                      if (IsFrontLine(ds.m_Mobile, bc))
                      {
                          ds.m_Mobile.SendLocalizedMessage(1156598); // You assist the Myrmidex in pushing back the Eodonians!
                          points *= 4;
                      }
                      else
                          ds.m_Mobile.SendLocalizedMessage(1156601); // You kill one of the Eodonians away from the front ranks and gain little recognition.
  
                      if (!Players.ContainsKey((PlayerMobile)ds.m_Mobile))
                          Players[(PlayerMobile)ds.m_Mobile] = points;
                      else
                          Players[(PlayerMobile)ds.m_Mobile] += points;
                  }
              });
          }
  
          private Type[] _MyrmidexTypes =
          {
              typeof(MyrmidexDrone), typeof(MyrmidexWarrior), typeof(TribeWarrior), typeof(TribeShaman)
          };
  
          private Type[] _TribeTypes =
          {
              typeof(BritannianInfantry)
          };
  
          public void AssignNavPoints()
          {
              int myrcount = 0;
              int trcount = 0;
  
              ColUtility.ForEach<int, List<BaseCreature>>(MyrmidexTeam, (key, value) => myrcount += value.Count);
              ColUtility.ForEach<int, List<BaseCreature>>(TribeTeam, (key, value) => trcount += value.Count);
  
              ColUtility.ForEach<int, List<BaseCreature>>(MyrmidexTeam, (key, value) =>
              {
                  value.ForEach(bc => AssignNavpoints(bc, Allegiance.Myrmidex));
              });
  
              ColUtility.ForEach<int, List<BaseCreature>>(TribeTeam, (key, value) =>
              {
                  value.ForEach(bc => AssignNavpoints(bc, Allegiance.Tribes));
              });
          }
  
          public bool AssignNavpoints(BaseCreature bc, Allegiance allegiance)
          {
              int lane = 0;
              int leg = GetLeg(new Point2D(bc.X, bc.Y), out lane, allegiance);
  
              if (leg == -1)
              {
                  bc.Kill();
                  return false;
              }
  
              Point2D[][][] uselist = allegiance == Allegiance.Myrmidex ? _NavPoints2 : _NavPoints1;
              Point2D[] list = uselist[lane][leg];
  
              int index = 0;
              Point2D nearest = GetNearest(list, bc, out index);
  
              if (nearest != Point2D.Zero)
              {
                  List<Point2D> points = new List<Point2D>();
                  points.AddRange(uselist[lane][0]);
                  points.AddRange(uselist[lane][1]);
  
                  if (allegiance == Allegiance.Myrmidex)
                      points.Reverse();
  
                  bc.CurrentNavPoint = Array.IndexOf(points.ToArray(), nearest);
  
                  //Randomize
                  points.ForEach(point =>
                  {
                      if (lane == 0)
                      {
  
gt:                         point.X = point.X + (Utility.RandomMinMax(0, 2));