context: using System;
  using System.IO;
  using System.Collections.Generic;
  
  using Server;
  using Server.Items;
  using Server.Mobiles;
  using Server.Engines.CityLoyalty;
  using Server.Spells;
  using Server.Network;
  using Server.Commands;
  using Server.Gumps;
  
  namespace Server.Engines.SeasonalEvents
  {
      [PropertyObject]
      public class KrampusEncounter
      {
          public static string FilePath = Path.Combine("Saves/Misc", "KrampusEncounter.bin");
  
          public static bool Enabled { get { return SeasonalEventSystem.IsActive(EventType.KrampusEncounter); } }
          public static KrampusEncounter Encounter { get; set; }
  
          public static readonly int MinComplete = 20;
  
          public static void Configure()
          {
              EventSink.WorldSave += OnSave;
              EventSink.WorldLoad += OnLoad;
          }
  
          public static void Initialize()
          {
              CommandSystem.Register("KrampusEncounter", AccessLevel.Administrator, e =>
              {
                  if (Encounter != null)
                  {
                      e.Mobile.SendGump(new PropertiesGump(e.Mobile, Encounter));
                  }
                  else
                  {
                      e.Mobile.SendMessage("Encounter null");
                  }
              });
          }
  
          public static void OnSave(WorldSaveEventArgs e)
          {
              CheckEnabled();
  
              Persistence.Serialize(
                  FilePath,
                  writer =>
                  {
                      writer.Write(0);
  
                      if (Encounter != null)
                      {
                          writer.Write(1);
                          Encounter.Serialize(writer);
                      }
                      else
                      {
                          writer.Write(0);
                      }
                  });
          }
  
          public static void OnLoad()
          {
              Persistence.Deserialize(
                  FilePath,
                  reader =>
                  {
                      reader.ReadInt(); // version
  
                      if (reader.ReadInt() == 1)
                      {
                          Encounter = new KrampusEncounter();
                          Encounter.Deserialize(reader);
                      }
                  });
          }
  
          public static void CheckEnabled()
          {
              if (Enabled)
              {
                  if (Encounter == null)
                  {
                      Encounter = new KrampusEncounter();
                  }
              }
              else
              {
                  if (Encounter != null && Encounter.Krampus == null)
                  {
                      Encounter = null;
                  }
              }
          }
  
          public static bool KrampusSpawned()
          {
              return Enabled && Encounter != null && Encounter.Krampus != null && !Encounter.Krampus.Deleted;
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int TotalTradesComplete { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int Wave { get { return (int)Math.Max(1, (int)Math.Min(6, (double)TotalTradesComplete / 4.1)); } }
  
          public Dictionary<PlayerMobile, int> CompleteTable { get; set; } = new Dictionary<PlayerMobile, int>();
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Krampus Krampus { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Point3D SpawnLocation { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool KrampusSpawning { get { return SpawnMap != null && SpawnLocation != Point3D.Zero; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Map SpawnMap { get; set; }
  
          public KrampusEncounter()
          {
          }
  
          public void OnTradeComplete(Mobile m, TradeEntry entry)
          {
              bool distCheck = entry.Distance > 0;
  
              // 0 distance indicates they used a moongate
              if (m is PlayerMobile && distCheck)
              {
                  var pm = (PlayerMobile)m;
  
                  if (!CompleteTable.ContainsKey(pm))
                  {
                      CompleteTable[pm] = 1;
                  }
                  else
                  {
                      CompleteTable[pm]++;
                  }
              }
  
              if (TotalTradesComplete > MinComplete || distCheck)
              {
                  TotalTradesComplete++;
              }
          }
  
          public Type[] GetCreatureTypes(Mobile m, bool wet)
          {
              if (Krampus != null || KrampusSpawning)
              {
                  return null;
              }
  
              if (wet)
              {
                  return _WetSpawnTypes;
              }
              else
              {
                  var wave = (int)Math.Max(1, (int)Math.Min(6, (double)TotalTradesComplete / 4.1)); // TODO: Is this right?
  
                  if (wave == 6)
                  {
                      if ((m.Map == Map.Trammel || (Siege.SiegeShard && m.Map == Map.Felucca)) && !SpellHelper.IsAnyT2A(m.Map, m.Location))
                      {
                          SpawnKrampus(m);
  
                          return null;
                      }
  
                      return _SpawnTypes[4];
                  }
  
                  return _SpawnTypes[wave - 1];
              }
          }
  
          private void SpawnKrampus(Mobile m)
          {
              SpawnMap = m.Map;
              var p = m.Location;
  
              for (int i = 0; i < 25; i++)
              {
                  int x = p.X + (Utility.RandomMinMax(-3, 3));
                  int y = p.Y + (Utility.RandomMinMax(-3, 3));
                  int z = m.Map.GetAverageZ(x, y);
  
                  if (SpawnMap.CanSpawnMobile(x, y, z))
                  {
                      p = new Point3D(x, y, z);
                      break;
                  }
              }
  
              SpawnLocation = p;
  
              foreach (var ns in NetState.Instances)
              {
                  var mob = ns.Mobile;
  
                  if (mob != null && CityTradeSystem.HasTrade(mob))
                  {
                      mob.LocalOverheadMessage(MessageType.Regular, 1150, 1158832, String.Format("{0}\t{1}", WorldLocationInfo.GetLocationString(SpawnLocation, SpawnMap), Sextant.GetCoords(SpawnLocation, SpawnMap))); // *You sense Krampus has been spotted near ~2_where~ at ~1_coords~!*
                  }
              }
  
              Timer.DelayCall(TimeSpan.FromMinutes(5), () =>
              {
                  SpawnKrampus();
              });
          }
  
          private void SpawnKrampus()
          {
              Krampus = new Krampus();
              Krampus.SpawnLocation = SpawnLocation;
              Krampus.Home = SpawnLocation;
              Krampus.RangeHome = 5;
  
              Krampus.MoveToWorld(SpawnLocation, SpawnMap);
              Krampus.Summon(Krampus, true);
  
              var rec = new Rectangle2D(SpawnLocation.X - 10, SpawnLocation.Y - 10, 20, 20);
  
              for (var i = 0; i < 2; i++)
              {
                  var drake = new FrostDrake();
  
                  Point3D p = new Point3D(SpawnLocation);
  
  
gt:                 for (int j = 0; i < 10; j++)