context: using System;
  using System.Collections.Generic;
  using Server;
  using Server.Mobiles;
  using Server.Items;
  using Server.Gumps;
  using Server.Engines.PartySystem;
  using System.Linq;
  
  namespace Server.Engines.Shadowguard	
  {
  	public class BarEncounter : ShadowguardEncounter
  	{
          public const int LiquorCount = 10;
  
          public override Type AddonType { get { return typeof(BarAddon); } }
  
  		public List<Mobile> Pirates { get; set; }
  		public int Wave { get; set; }
  	
  		public List<ShadowguardBottleOfLiquor> Bottles { get; set; }
  
          public BarEncounter()
              : base(EncounterType.Bar)
          {
          }
  
          public BarEncounter(ShadowguardInstance instance)
              : base(EncounterType.Bar, instance)
  		{
  		}
  		
  		public override void Setup()
  		{
  			Pirates = new List<Mobile>();
              Bottles = new List<ShadowguardBottleOfLiquor>();
              Wave = 0;
  
              int toSpawn = Math.Max(3, PartySize() * 3);
  
              for (int i = 0; i < toSpawn; i++)
                  SpawnRandomPirate();
  
              for (int i = 0; i < LiquorCount; i++)
  				SpawnRandomLiquor();
  		}
  		
  		public override void CheckEncounter()
  		{
  			if(Wave >= 4 || Bottles == null)
  				return;
  
              int liquorCount = Bottles.Where(b => b != null && !b.Deleted).Count();
  
  			if(liquorCount < LiquorCount)
  			{
  				SpawnRandomLiquor();
  			}
  		}
  
          private void SpawnRandomLiquor()
          {
              int row = Utility.Random(4);
              Rectangle2D rec = SpawnRecs[Utility.Random(SpawnRecs.Length)];
  
              ConvertOffset(ref rec);
  
              int x = Utility.RandomMinMax(rec.X, rec.X + rec.Width);
  			int y = Utility.RandomMinMax(rec.Y, rec.Y + rec.Height);
  			int z = -14;
  
              x = x + (9 * row);
  			
  			ShadowguardBottleOfLiquor bottle = new ShadowguardBottleOfLiquor(this);
  			bottle.MoveToWorld(new Point3D(x, y, z), Map.TerMur);
  			
  			Bottles.Add(bottle);
          }
  
          private void SpawnRandomPirate()
          {
              if (Pirates == null)
                  return;
  
              int row = Utility.Random(8);
              Point3D ranPnt = SpawnPoints[Utility.Random(SpawnPoints.Length)];
  
              ConvertOffset(ref ranPnt);
  
              int a = row % 2 == 0 ? 0 : 3;
              int startX = ranPnt.X + a;
              int x = startX + ((row / 2) * 9);
  
              ShadowguardPirate pirate = new ShadowguardPirate();
  			pirate.MoveToWorld(new Point3D(x, ranPnt.Y, ranPnt.Z), Map.TerMur);
  			Pirates.Add(pirate);
          }
  		
  		public override void OnCreatureKilled(BaseCreature bc)
  		{
              if (!(bc is ShadowguardPirate) || Pirates == null)
                  return;
  
  			if(Pirates.Contains(bc))
  				Pirates.Remove(bc);
  
  			if(Pirates.Count <= 0)
  			{
  				Wave++;
  				Pirates.Clear();
  
                  int toSpawn = Math.Max(3, PartySize() * 3);
  				
  				if(Wave < 4)
  				{
                      for (int i = 0; i < toSpawn; i++)
  					{
                          SpawnRandomPirate();
  					}
  				}
  				else if (Wave == 4)
  				{
                      var pirate = new ShantyThePirate();
                      Point3D p = SpawnPoints[Utility.Random(SpawnPoints.Length)];
                      ConvertOffset(ref p);
                      pirate.MoveToWorld(p, Map.TerMur);
  					Pirates.Add(pirate);
  				}
  				else
  					CompleteEncounter();
  			}
  		}
  		
  		public override void ClearItems()
  		{
              if (Bottles != null)
              {
                  List<ShadowguardBottleOfLiquor> list = new List<ShadowguardBottleOfLiquor>(Bottles.Where(b => b != null && !b.Deleted));
  
                  foreach (var bottle in list)
                      bottle.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(Bottles);
                  Bottles = null;
              }
  
              if (Pirates != null)
              {
                  ColUtility.Free(Pirates);
                  Pirates = null;
              }
  		}
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
              writer.Write(0);
  
              writer.Write(Wave);
  
              writer.Write(Pirates == null ? 0 : Pirates.Count);
              if (Pirates != null) Pirates.ForEach(p => writer.Write(p));
  
              writer.Write(Bottles == null ? 0 : Bottles.Count);
              if (Bottles != null) Bottles.ForEach(b => writer.Write(b));
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
              int version = reader.ReadInt();
  
              Pirates = new List<Mobile>();
              Bottles = new List<ShadowguardBottleOfLiquor>();
  
              Wave = reader.ReadInt();
  
              int count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  Mobile p = reader.ReadMobile();
                  if (p != null)
                      Pirates.Add(p);
              }
  
              count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  ShadowguardBottleOfLiquor b = reader.ReadItem() as ShadowguardBottleOfLiquor;
                  if (b != null)
                      Bottles.Add(b);
              }
  
              if (Pirates == null || Pirates.Count < 6)
              {
                  int toSpawn = Pirates == null ? 6 : 6 - Pirates.Count;
  
                  for (int i = 0; i < toSpawn; i++)
                  {
                      SpawnRandomPirate();
                  }
              }
          }
  	}
  	
  	public class OrchardEncounter : ShadowguardEncounter
  	{
  		public List<ShadowguardCypress> Trees { get; set; }
          public List<BaseCreature> Spawn { get; set; }
  
          public Item Bones { get; set; }
  
          public override Type AddonType { get { return typeof(OrchardAddon); } }
  
          public OrchardEncounter()
              : base(EncounterType.Orchard)
          {
          }
  
          public OrchardEncounter(ShadowguardInstance instance)
              : base(EncounterType.Orchard, instance)
  		{
  		}
  		
  		public override void Setup()
  		{
  			Trees = new List<ShadowguardCypress>();
              Spawn = new List<BaseCreature>();
  			
  			List<Point3D> points = new List<Point3D>();
              for(int i = 0; i < SpawnPoints.Length; i++)
              {
                  Point3D p = SpawnPoints[i];
                  ConvertOffset(ref p);
                  points.Add(p);
              }
  					
  			foreach(int i in Enum.GetValues(typeof(VirtueType)))
  			{
  				if(i > 7)
  					break;
  					
  				ShadowguardCypress tree = new ShadowguardCypress(this, (VirtueType)i);
  				Point3D p = points[Utility.Random(points.Count)];
  
  				tree.MoveToWorld(p, Map.TerMur);
  				points.Remove(p);
  				Trees.Add(tree);
  				
  				tree = new ShadowguardCypress(this, (VirtueType)i + 8);
  				p = points[Utility.Random(points.Count)];
  
  				tree.MoveToWorld(p, Map.TerMur);
  				points.Remove(p);
  				Trees.Add(tree);
  			}
  
              Item bones = new WitheringBones();
              Point3D pnt = new Point3D(-15, -11, 0);
              ConvertOffset(ref pnt);
              bones.MoveToWorld(pnt, Map.TerMur);
              Bones = bones;
  
              ColUtility.Free(points);
  		}
  		
  		public override void CheckEncounter()
  		{
              if (Trees == null)
                  return;
  
  			int treeCount = Trees.Count;
  			
  			foreach(ShadowguardCypress tree in Trees)
  			{
  				if(tree == null || tree.Deleted)
  					treeCount--;
  			}
  			
  			if(treeCount <= 0)
  				CompleteEncounter();
  		}
  		
  		public void AddSpawn(BaseCreature bc)
  		{
              if(Spawn != null)
  			    Spawn.Add(bc);
  		}
  		
  		public override void ClearItems()
  		{
              if (Spawn != null)
              {
                  List<BaseCreature> list = new List<BaseCreature>(Spawn.Where(e => e != null && e.Alive));
  
                  foreach (var spawn in list)
                      spawn.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(Spawn);
                  Spawn = null;
              }
  
              if (Trees != null)
              {
                  List<ShadowguardCypress> list = new List<ShadowguardCypress>(Trees.Where(t => t != null && !t.Deleted));
  
                  foreach (var tree in list)
                      tree.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(Trees);
                  Trees = null;
              }
  
              if (Bones != null)
              {
                  Bones.Delete();
                  Bones = null;
              }
  		}
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
              writer.Write(0);
  
              writer.Write(Trees == null ? 0 : Trees.Count);
              if (Trees != null) Trees.ForEach(t => writer.Write(t));
  
              writer.Write(Spawn == null ? 0 : Spawn.Count);
              if (Spawn != null) Spawn.ForEach(s => writer.Write(s));
  
              writer.Write(Bones);
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
              int version = reader.ReadInt();
  
              Trees = new List<ShadowguardCypress>();
  
              int count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  ShadowguardCypress tree = reader.ReadItem() as ShadowguardCypress;
  
                  if (tree != null)
                      Trees.Add(tree);
              }
  
              count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  if (Spawn == null)
                      Spawn = new List<BaseCreature>();
  
                  BaseCreature bc = reader.ReadMobile() as BaseCreature;
  
                  if (bc != null)
                      Spawn.Add(bc);
              }
  
              Bones = reader.ReadItem();
          }
  	}
  	
  	public class ArmoryEncounter : ShadowguardEncounter
  	{
  		public List<Item> Armor { get; set; }
  		public List<Item> DestroyedArmor { get; set; }
          public List<Item> Items { get; set; }
  		public List<BaseCreature> Spawn { get; set; }
  
          public override Type AddonType { get { return typeof(ArmoryAddon); } }
  
          public ArmoryEncounter()
              : base(EncounterType.Armory)
          {
          }
  
          public ArmoryEncounter(ShadowguardInstance instance)
              : base(EncounterType.Armory, instance)
  		{
  		}
  		
  		public override void Setup()
  		{
              Armor = new List<Item>();
  			DestroyedArmor = new List<Item>();
  			Spawn = new List<BaseCreature>();
              Items = new List<Item>();
  
              int toSpawn = 1 + (PartySize() * 2);
  
              ColUtility.For(SpawnPoints, (i, p) =>
  			{
                  ConvertOffset(ref p);
  
  				var armor = new CursedSuitOfArmor(this);
  				armor.MoveToWorld(p, Map.TerMur);
  				Armor.Add(armor);
  
                  if(i > 13)
                      armor.ItemID = 0x1512;
  			});
  			
  			for(int i = 0; i < toSpawn; i++)
  			{
  				SpawnRandom();
  			}
  
              Item item = new Static(3633);
              Point3D pnt = new Point3D(-4, 2, 0);
              ConvertOffset(ref pnt);
              item.MoveToWorld(pnt, Map.TerMur);
              Items.Add(item);
  
              item = new Static(3633);
              pnt = new Point3D(-4, -4, 0);
              ConvertOffset(ref pnt);
              item.MoveToWorld(pnt, Map.TerMur);
              Items.Add(item);
  
              item = new Static(3633);
              pnt = new Point3D(2, -4, 0);
              ConvertOffset(ref pnt);
              item.MoveToWorld(pnt, Map.TerMur);
              Items.Add(item);
  
              item = new PurifyingFlames();
              pnt = new Point3D(-4, 2, 8);
              ConvertOffset(ref pnt);
              item.MoveToWorld(pnt, Map.TerMur);
              Items.Add(item);
  
              item = new PurifyingFlames();
              pnt = new Point3D(-4, -4, 8);
              ConvertOffset(ref pnt);
              item.MoveToWorld(pnt, Map.TerMur);
              Items.Add(item);
  
              item = new PurifyingFlames();
              pnt = new Point3D(2, -4, 8);
              ConvertOffset(ref pnt);
              item.MoveToWorld(pnt, Map.TerMur);
              Items.Add(item);
  		}
  		
  		public override void CheckEncounter()
  		{
  			if(Armor != null && Armor.Where(a => a != null && !a.Deleted).Count() == 0)
  				CompleteEncounter();
  		}
  		
  		public override void OnCreatureKilled(BaseCreature bc)
  		{
              if (Spawn != null && Spawn.Contains(bc))
  			{
  				Spawn.Remove(bc);
  				Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), SpawnRandom);
  			}
  		}
  		
  		public void AddDestroyedArmor(Item item)
  		{
              if(DestroyedArmor != null)
  			    DestroyedArmor.Add(item);
  		}
  		
  		public override void ClearItems()
  		{
              if (Armor != null)
              {
                  List<Item> list = new List<Item>(Armor.Where(i => i != null && !i.Deleted));
  
                  foreach (Item armor in list)
                      armor.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(Armor);
                  Armor = null;
              }
  
              if (DestroyedArmor != null)
              {
                  List<Item> list = new List<Item>(DestroyedArmor.Where(i => i != null && !i.Deleted));
  
                  foreach (Item dest in list)
                      dest.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(DestroyedArmor);
                  DestroyedArmor = null;
              }
  
              if (Spawn != null)
              {
                  List<BaseCreature> list = new List<BaseCreature>(Spawn.Where(s => s != null && !s.Deleted));
  
                  foreach (BaseCreature spawn in list)
                      spawn.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(Spawn);
                  Spawn = null;
              }
  
              if (Items != null)
              {
                  List<Item> list = new List<Item>(Items.Where(i => i != null && !i.Deleted));
  
                  foreach (Item item in list)
                      item.Delete();
  
                  ColUtility.Free(list);
  
                  ColUtility.Free(Items);
                  Items = null;
              }
  		}
  		
  		private void SpawnRandom()
  		{
              if (Spawn == null)
                  return;
  
  			Rectangle2D rec = SpawnRecs[Utility.Random(SpawnRecs.Length)];
              ConvertOffset(ref rec);
  
  			while(true)
  			{
  				int x = Utility.RandomMinMax(rec.X, rec.X + rec.Width);
  				int y = Utility.RandomMinMax(rec.Y, rec.Y + rec.Height);
  				int z = Map.TerMur.GetAverageZ(x, y);
  				
  				if(Map.TerMur.CanSpawnMobile(x, y, z))
  				{
  					var armor = new EnsorcelledArmor(this);
  					armor.MoveToWorld(new Point3D(x, y, z), Map.TerMur);
  					Spawn.Add(armor);
  					break;
  				}
  			}
  		}
  
          public override void Serialize(GenericWriter writer)
          {
              base.Serialize(writer);
  
              writer.Write(0);
  
              writer.Write(Armor == null ? 0 : Armor.Count);
              if (Armor != null) Armor.ForEach(a => writer.Write(a));
  
              writer.Write(DestroyedArmor == null ? 0 : DestroyedArmor.Count);
              if (DestroyedArmor != null) DestroyedArmor.ForEach(a => writer.Write(a));
  
              writer.Write(Spawn == null ? 0 : Spawn.Count);
              if (Spawn != null) Spawn.ForEach(a => writer.Write(a));
  
              writer.Write(Items == null ? 0 : Items.Count);
              if (Items != null) Items.ForEach(i => writer.Write(i));
          }
  
          public override void Deserialize(GenericReader reader)
          {
              base.Deserialize(reader);
  
              int version = reader.ReadInt();
  
              Armor = new List<Item>();
              DestroyedArmor = new List<Item>();
              Items = new List<Item>();
  
              int count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  Item it = reader.ReadItem();
  
                  if (it != null)
                  {
                      if(it is CursedSuitOfArmor)
                          ((CursedSuitOfArmor)it).Encounter = this;
  
                      Armor.Add(it);
                  }
              }
  
              count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  Item it = reader.ReadItem();
  
                  if (it != null)
                      DestroyedArmor.Add(it);
              }
  
              count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  if (Spawn == null)
                      Spawn = new List<BaseCreature>();
  
                  BaseCreature bc = reader.ReadMobile() as BaseCreature;
                  if (bc != null)
                  {
                      if (bc is EnsorcelledArmor)
                          ((EnsorcelledArmor)bc).Encounter = this;
  
                      Spawn.Add(bc);
                  }
              }
  
              count = reader.ReadInt();
              for (int i = 0; i < count; i++)
              {
                  if (Items == null)
                      Items = new List<Item>();
  
                  Item item = reader.ReadItem();
                  if (item != null)
                  {
                      Items.Add(item);
                  }
              }
  
              if (Spawn == null || Spawn.Count < 4)
              {
                  int toSpawn = Spawn == null ? 4 : 4 - Spawn.Count;
                  for (int i = 0; i < toSpawn; i++)
                  {
                      if(Spawn == null)
                          Spawn = new List<BaseCreature>();
  
                      SpawnRandom();
                  }
              }
          }
  	}
  	
  	public class FountainEncounter : ShadowguardEncounter
  	{
          public static readonly int MaxElementals = 30;
  
  		public List<BaseCreature> Elementals { get; set; }
  		public List<Item> ShadowguardCanals { get; set; }
  	
  		public List<FlowChecker> FlowCheckers { get; set; }
  
          public DateTime _NextSpawn;
  
          public override Type AddonType { get { return typeof(ShadowguardFountainAddon); } }
  
          public FountainEncounter()
              : base(EncounterType.Fountain)
          {
          }
  
          public FountainEncounter(ShadowguardInstance instance)
              : base(EncounterType.Fountain, instance)
  		{
  		}
  		
  		public void UseSpigot(ShadowguardSpigot spigot, Mobile m)
  		{
              if (FlowCheckers == null)
                  return;
  
  			foreach(FlowChecker checker in FlowCheckers)
  			{
  				if(checker.CheckUse(spigot, m))
  					return;
  			}
  		}
  
          public override void CheckEncounter()
          {
              if (FlowCheckers != null && FlowCheckers.Where(c => c.Complete).Count() == 4)
                  CompleteEncounter();
          }
  		
  		public override void OnCreatureKilled(BaseCreature bc)
  		{
              if (Elementals != null && Elementals.Contains(bc))
  			{
  				Elementals.Remove(bc);
  				Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(1, 5)), SpawnRandomElemental);
  			}
  		}
  
          public override void OnTick()
          {
              if (_NextSpawn < DateTime.UtcNow && (Elementals == null || Elementals.Count < MaxElementals))
              {
                  SpawnRandomElemental();
                  _NextSpawn = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 45));
              }
          }
  		
  		public override void Setup()
  		{
  			ShadowguardCanals = new List<Item>();
  			Elementals = new List<BaseCreature>();
  			FlowCheckers = new List<FlowChecker>();
  
              int toSpawn = 3 + (PartySize() * 2);
  
  			Timer.DelayCall(ShadowguardController.ReadyDuration + TimeSpan.FromSeconds(30), () =>
  			{
  				for(int i = 0; i < toSpawn; i++)
  					SpawnRandomElemental();
  			});
  			
  			for(int i = 0; i < 4; i++)
  			{
                  ShadowguardSpigot spigot = new ShadowguardSpigot(i < 2 ? 0x9BF2 : 0x9BE5);
                  Point3D p = SpawnPoints[i];
  
                  ConvertOffset(ref p);
  
  				spigot.MoveToWorld(p, Map.TerMur);
                  AddShadowguardCanal(spigot);
  				FlowCheckers.Add(new FlowChecker(spigot, this));
  			}
  
              Rectangle2D rec1 = SpawnRecs[4];
  
gt:             Rectangle2D rec2 = SpawnRecs[5];