context: using System;
  using Server;
  using System.Collections.Generic;
  using System.Linq;
  using Server.Mobiles;
  using Server.Items;
  using Server.Engines.CityLoyalty;
  using Server.Misc;
  
  namespace Server.Engines.Blackthorn
  {
      public enum InvasionType
      {
          Dragons,
          Undead,
          Elemental,
          Daemon,
          Orc,
          Wildlings,
          Frost,
          Arachnid,
          Fey,
          Nature
      }
  
  	public class InvasionController : Item
  	{
          public static bool Enabled = true;
          public static int WaveCountMin = 8;
          public static int WaveCountMax = 10;
          public static int MaxWaves = 2;
  
          [CommandProperty(AccessLevel.Administrator)]
  		public static InvasionController TramInstance { get; set; }
  
          [CommandProperty(AccessLevel.Administrator)]
  		public static InvasionController FelInstance { get; set; }
  
          [CommandProperty(AccessLevel.Administrator)]
          public bool ForceRespawn
          {
              get
              {
                  return false;
              }
              set
              {
                  if (!value)
                      return;
  
                  RemoveSpawn();
                  OnEndInvasion();
                  Cleanup();
                  BeginInvasion();
              }
          }
  
          public static Dictionary<City, InvasionDefinition> Defs;
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public City CurrentInvasion { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public InvasionType InvasionType { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public InvasionBeacon Beacon { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int SpawnCount
          {
              get
              {
                  if (Spawn == null || Spawn.Count == 0)
                      return 0;
  
                  int count = 0;
  
                  foreach (KeyValuePair<BaseCreature, List<BaseCreature>> kvp in Spawn)
                  {
                      if (kvp.Key.Alive)
                          count++;
  
                      count += kvp.Value.Where(bc => bc.Alive).Count();
                  }
  
                  return count;
              }
          }
  
  		public Dictionary<BaseCreature, List<BaseCreature>> Spawn { get; set; }
  		
  		public List<Rectangle2D> SpawnZones { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
  		public int CurrentWave { get; private set; }
  		
  		public bool BeaconVulnerable { get { return Beacon != null && (Spawn == null || Spawn.Count == 0); } }
  		
  		public InvasionController(Map map) : base(3796)
  		{
              Movable = false;
              Visible = false;
  
  			Spawn = new Dictionary<BaseCreature, List<BaseCreature>>();
  			SpawnZones = new List<Rectangle2D>();
  
              if (Enabled)
                  Timer.DelayCall(TimeSpan.FromSeconds(10), BeginInvasion);
  		}
  
          public override void OnDoubleClick(Mobile from)
          {
              if (from.AccessLevel > AccessLevel.GameMaster)
              {
                  from.SendGump(new Server.Gumps.PropertiesGump(from, this));
              }
          }
  		
  		private Type[][] _SpawnTable =
  		{
              new Type[] { typeof(Dragon), typeof(Drake), typeof(GiantSerpent), typeof(Reptalon), typeof(Hydra) },
              new Type[] { typeof(Lich), typeof(Wraith), typeof(Mummy), typeof(Zombie), typeof(SkeletalKnight), typeof(BoneKnight) },
  			new Type[] { typeof(MudElemental), typeof(MoltenEarthElemental), typeof(DiseasedBloodElemental), typeof(GreaterAirElemental),
                           typeof(GreaterBloodElemental), typeof(GreaterEarthElemental), typeof(GreaterWaterElemental), typeof(ShameGreaterPoisonElemental),
                           typeof(StoneElemental) },
  			new Type[] { typeof(Daemon), typeof(Succubus), typeof(Imp), typeof(ChaosDaemon), typeof(BoneDemon) },
  			new Type[] { typeof(Orc), typeof(OrcBomber), typeof(OrcishMage), typeof(OrcishLord) },
              new Type[] { typeof(SwampTentacle), typeof(PlagueBeast), typeof(Bogling), typeof(FeralTreefellow) },
              new Type[] { typeof(IceElemental), typeof(SnowElemental), typeof(IceFiend), typeof(FrostTroll), typeof(IceSerpent) }, 
              new Type[] { typeof(DreadSpider), typeof(GiantBlackWidow), typeof(Scorpion), typeof(TerathanWarrior), typeof(WolfSpider) },
              new Type[] { typeof(Satyr), typeof(Centaur), typeof(CuSidhe), typeof(Wisp), typeof(MLDryad) },
              new Type[] { typeof(DireWolf), typeof(GiantRat), typeof(Troglodyte), typeof(RagingGrizzlyBear), typeof(GreaterMongbat) }, 
  		};
  		
  		public void BeginInvasion()
  		{
              if (!Enabled)
                  return;
  
              RemoveSpawn();
  
  			CurrentWave = 1;
  			InvasionType newType;
              City newCity;
  			
  			do
  			{
  				newType = (InvasionType)Utility.Random(10);
  			}
  			while(newType == this.InvasionType);
  
              do
              {
                  newCity = (City)Utility.Random(9);
              }
              while (newCity == this.CurrentInvasion);
  
              this.CurrentInvasion = newCity;
  			this.InvasionType = newType;
  			SpawnZones = Defs[CurrentInvasion].SpawnRecs.ToList();
  
  			Beacon = new InvasionBeacon(this);
              Beacon.MoveToWorld(Defs[CurrentInvasion].BeaconLoc, this.Map);
  
  			// Shuffle zones
  			for(int i = 0; i < 8; i++)
  			{
  				var rec = SpawnZones[Utility.Random(SpawnZones.Count)];
  				SpawnZones.Remove(rec);
  				SpawnZones.Insert(0, rec);
  			}
  
  			SpawnWave();
  		}
  		
  		public void SpawnWave()
  		{
  			List<Rectangle2D> zones = new List<Rectangle2D>(SpawnZones);
  
  			for(int j = 0; j < 2; j++)
  			{
  				Rectangle2D spawnrec = zones[Utility.Random(zones.Count)];
  				zones.Remove(spawnrec);
  
  				int count = Utility.RandomMinMax(WaveCountMin, WaveCountMax);
                  List<BaseCreature> list = new List<BaseCreature>();
  
  				for(int i = 0; i < count; i++)
  				{
                      BaseCreature bc = Activator.CreateInstance(_SpawnTable[(int)this.InvasionType][Utility.Random(_SpawnTable[(int)this.InvasionType].Length)]) as BaseCreature;
  
                      Server.Engines.XmlSpawner2.XmlAttach.AttachTo(bc, new Server.Engines.XmlSpawner2.XmlData("Notoriety", "red"));
  
                      if (SpawnMobile(bc, spawnrec))
                      {
                          list.Add(bc);
                      }
                      else
                      {
                          bc.Delete();
                      }
  				}
  
                  for (int i = 0; i < 3; i++)
                  {
                      Invader invader = new Invader(this.InvasionType);
  
                      if (SpawnMobile(invader, spawnrec))
                      {
                          list.Add(invader);
                      }
                      else
                          invader.Delete();
                  }
  
                  InvaderCaptain capt = new InvaderCaptain(this.InvasionType);
                  capt.Blessed = true;
  
                  if (SpawnMobile(capt, spawnrec) || SpawnMobile(capt, new Rectangle2D(Defs[CurrentInvasion].BeaconLoc.X - 10, Defs[CurrentInvasion].BeaconLoc.Y - 10, 20, 20)))
                  {
                      Spawn[capt] = list;
                  }
  			}
  		}
  
          private bool SpawnMobile(BaseCreature bc, Rectangle2D spawnrec)
          {
              if (bc != null)
              {
                  for (int i = 0; i < 25; i++)
                  {
                      Point3D p = spawnrec.GetRandomSpawnPoint(this.Map);
                      bool exempt = false;
  
                      if (spawnrec.X == 6444 && spawnrec.Y == 2446)
                      {
                          exempt = true;
                          p.Z = -2;
                      }
  
                      if (exempt || this.Map.CanFit(p.X, p.Y, p.Z, 16, false, false, true))
                      {
                          bc.MoveToWorld(p, this.Map);
                          bc.Home = Defs[CurrentInvasion].BeaconLoc;
                          bc.SeeksHome = true;
                          bc.RangeHome = Utility.RandomMinMax(5, 10);
  
                          bc.Tamable = false;
  
                          return true;
                      }
                  }
              }
  
              return false;
          }
  
          public void OnDeath(BaseCreature bc)
          {
              if (bc == null || bc.Controlled || bc.Summoned)
                  return;
  
              if (Spawn.ContainsKey(bc))
              {
                  Spawn.Remove(bc);
  
                  bool wavecomplete = true;
                  foreach (KeyValuePair<BaseCreature, List<BaseCreature>> kvp in Spawn)
                  {
                      if (kvp.Key != null && kvp.Key.Alive)
                      {
                          wavecomplete = false;
                          break;
                      }
                  }
  
                  if (wavecomplete)
                      CompleteWave();
              }
              else
              {
                  foreach (KeyValuePair<BaseCreature, List<BaseCreature>> kvp in Spawn)
                  {
                      if (kvp.Value.Contains(bc))
                          kvp.Value.Remove(bc);
  
                      int count = kvp.Value.Where(b => b != null && b.Alive).Count();
  
                      if (count == 0 && kvp.Key.Alive)
                      {
                          kvp.Key.Blessed = false;
                          kvp.Key.Delta(MobileDelta.Noto);
                      }
                  }
              }
          }
  
          public void CleanupSpawn()
          {
              if (Spawn == null)
                  return;
  
              List<BaseCreature> list = null;
  
              foreach (KeyValuePair<BaseCreature, List<BaseCreature>> kvp in Spawn)
              {
                  if (kvp.Value != null)
                  {
                      list = new List<BaseCreature>(kvp.Value);
  
                      foreach (BaseCreature b in list.Where(bc => bc == null || !bc.Alive || bc.Deleted))
                          kvp.Value.Remove(b);
                  }
  
                  if (list != null && list.Count > 0)
                  {
                      list.Clear();
                      list.TrimExcess();
                  }
              }
          }
  		
  		private void CompleteWave()
  		{
  			if(CurrentWave == MaxWaves)
  			{
                  DoMessage();
  			}
  			else
  			{
                  DoMessage();
  				CurrentWave++;
  			}
  		}
  
          private void DoMessage()
          {
              if (this.Map == null)
                  return;
  
              IPooledEnumerable eable = this.Map.GetMobilesInRange(Beacon.Location, 20);
  
              foreach (Mobile m in eable)
              {
                  if (m != null && m.NetState != null)
                      m.PrivateOverheadMessage(Server.Network.MessageType.Regular, 1154, 1154550, m.NetState); // *A sound roars in the distance...Minax's Beacon is vulnerable to attack!!*
              }
  
              eable.Free();
          }
  
          public void OnBeaconDestroyed()
          {
              OnEndInvasion();
  
              Timer.DelayCall(TimeSpan.FromMinutes(2), () =>
                  {
                      Cleanup();
                      BeginInvasion();
                  });
          }
  
          public void OnEndInvasion()
          {
              if (Beacon != null)
              {
                  List<Mobile> rights = Beacon.GetLootingRights();
  
                  if (rights != null)
                  {
                      foreach (Mobile damager in rights.Where(mob => mob.InRange(Beacon.Location, 12)))
                      {
                          Item i = Loot.RandomArmorOrShieldOrWeaponOrJewelry(LootPackEntry.IsInTokuno(damager), LootPackEntry.IsMondain(damager), LootPackEntry.IsStygian(damager));
  
                          if (i != null)
                          {
                              RunicReforging.GenerateRandomItem(i, damager, Utility.RandomMinMax(700, 800), damager is PlayerMobile ? ((PlayerMobile)damager).RealLuck : damager.Luck, ReforgedPrefix.None, ReforgedSuffix.Minax);
  
                              damager.PlaySound(0x5B4);
                              damager.SendLocalizedMessage(1154554); // You recover an artifact bearing the crest of Minax from the rubble.
  
                              if (!damager.PlaceInBackpack(i))
                              {
                                  if (damager.BankBox != null && damager.BankBox.TryDropItem(damager, i, false))
                                      damager.SendLocalizedMessage(1079730); // The item has been placed into your bank box.
                                  else
                                  {
                                      damager.SendLocalizedMessage(1072523); // You find an artifact, but your backpack and bank are too full to hold it.
                                      i.MoveToWorld(damager.Location, damager.Map);
                                  }
                              }
                          }
                      }
                  }
              }
          }
  
          public void Cleanup()
          {
              if (Beacon != null)
                  Beacon.Delete();
          }
  
          public void RemoveSpawn()
          {
              if (Spawn == null)
                  return;
  
              Dictionary<BaseCreature, List<BaseCreature>> copy = Spawn;
              Spawn = new Dictionary<BaseCreature, List<BaseCreature>>();
  
              foreach (KeyValuePair<BaseCreature, List<BaseCreature>> kvp in copy)
              {
                  foreach (BaseCreature bc in kvp.Value.Where(b => b.Alive))
                      bc.Kill();
  
                  if (kvp.Key.Alive)
                  {
                      kvp.Key.Blessed = false;
                      kvp.Key.Kill();
                  }
              }
  
              copy.Clear();
          }
  		
  		public InvasionController(Serial serial) : base(serial)
  		{
  		}
  		
  		public override void Serialize(GenericWriter writer)
  		{
  			base.Serialize(writer);
  			writer.Write(0);
  			
  			writer.Write((int)CurrentInvasion);
  			writer.Write((int)InvasionType);
  			writer.Write(Beacon);
  			writer.Write(CurrentWave);
  			
  
gt: 			writer.Write(SpawnZones == null ? 0 : SpawnZones.Count);