context: using System;
  using Server.Items;
  using Server.Mobiles;
  using Server.Targeting;
  
  namespace Server.Engines.Harvest
  {
      public class Mining : HarvestSystem
      {
          private static Mining m_System;
  
          public static Mining System
          {
              get
              {
                  if (m_System == null)
                      m_System = new Mining();
  
                  return m_System;
              }
          }
  
          private readonly HarvestDefinition m_OreAndStone;
  
          private readonly HarvestDefinition m_Sand;
  
          public HarvestDefinition OreAndStone
          {
              get
              {
                  return this.m_OreAndStone;
              }
          }
  
          public HarvestDefinition Sand
          {
              get
              {
                  return this.m_Sand;
              }
          }
  
          private Mining()
          {
              HarvestResource[] res;
              HarvestVein[] veins;
  
              #region Mining for ore and stone
              HarvestDefinition oreAndStone = this.m_OreAndStone = new HarvestDefinition();
  
              // Resource banks are every 8x8 tiles
              oreAndStone.BankWidth = 8;
              oreAndStone.BankHeight = 8;
  
              // Every bank holds from 10 to 34 ore
              oreAndStone.MinTotal = 10;
              oreAndStone.MaxTotal = 34;
  
              // A resource bank will respawn its content every 10 to 20 minutes
              oreAndStone.MinRespawn = TimeSpan.FromMinutes(10.0);
              oreAndStone.MaxRespawn = TimeSpan.FromMinutes(20.0);
  
              // Skill checking is done on the Mining skill
              oreAndStone.Skill = SkillName.Mining;
  
              // Set the list of harvestable tiles
              oreAndStone.Tiles = m_MountainAndCaveTiles;
  
              // Players must be within 2 tiles to harvest
              oreAndStone.MaxRange = 2;
  
              // One ore per harvest action
              oreAndStone.ConsumedPerHarvest = 1;
              oreAndStone.ConsumedPerFeluccaHarvest = 2;
  
              // The digging effect
              oreAndStone.EffectActions = new int[] { 11 };
              oreAndStone.EffectSounds = new int[] { 0x125, 0x126 };
              oreAndStone.EffectCounts = new int[] { 1 };
              oreAndStone.EffectDelay = TimeSpan.FromSeconds(1.6);
              oreAndStone.EffectSoundDelay = TimeSpan.FromSeconds(0.9);
  
              oreAndStone.NoResourcesMessage = 503040; // There is no metal here to mine.
              oreAndStone.DoubleHarvestMessage = 503042; // Someone has gotten to the metal before you.
              oreAndStone.TimedOutOfRangeMessage = 503041; // You have moved too far away to continue mining.
              oreAndStone.OutOfRangeMessage = 500446; // That is too far away.
              oreAndStone.FailMessage = 503043; // You loosen some rocks but fail to find any useable ore.
              oreAndStone.PackFullMessage = 1010481; // Your backpack is full, so the ore you mined is lost.
              oreAndStone.ToolBrokeMessage = 1044038; // You have worn out your tool!
  
              res = new HarvestResource[]
              {
                  new HarvestResource(00.0, 00.0, 100.0, 1007072, typeof(IronOre), typeof(Granite)),
                  new HarvestResource(65.0, 25.0, 105.0, 1007073, typeof(DullCopperOre),	typeof(DullCopperGranite), typeof(DullCopperElemental)),
                  new HarvestResource(70.0, 30.0, 110.0, 1007074, typeof(ShadowIronOre),	typeof(ShadowIronGranite), typeof(ShadowIronElemental)),
                  new HarvestResource(75.0, 35.0, 115.0, 1007075, typeof(CopperOre), typeof(CopperGranite), typeof(CopperElemental)),
                  new HarvestResource(80.0, 40.0, 120.0, 1007076, typeof(BronzeOre), typeof(BronzeGranite), typeof(BronzeElemental)),
                  new HarvestResource(85.0, 45.0, 125.0, 1007077, typeof(GoldOre), typeof(GoldGranite), typeof(GoldenElemental)),
                  new HarvestResource(90.0, 50.0, 130.0, 1007078, typeof(AgapiteOre), typeof(AgapiteGranite), typeof(AgapiteElemental)),
                  new HarvestResource(95.0, 55.0, 135.0, 1007079, typeof(VeriteOre), typeof(VeriteGranite), typeof(VeriteElemental)),
                  new HarvestResource(99.0, 59.0, 139.0, 1007080, typeof(ValoriteOre), typeof(ValoriteGranite), typeof(ValoriteElemental))
              };
  
              veins = new HarvestVein[]
              {
                  new HarvestVein(49.6, 0.0, res[0], null), // Iron
                  new HarvestVein(11.2, 0.5, res[1], res[0]), // Dull Copper
                  new HarvestVein(09.8, 0.5, res[2], res[0]), // Shadow Iron
                  new HarvestVein(08.4, 0.5, res[3], res[0]), // Copper
                  new HarvestVein(07.0, 0.5, res[4], res[0]), // Bronze
                  new HarvestVein(05.6, 0.5, res[5], res[0]), // Gold
                  new HarvestVein(04.2, 0.5, res[6], res[0]), // Agapite
                  new HarvestVein(02.8, 0.5, res[7], res[0]), // Verite
                  new HarvestVein(01.4, 0.5, res[8], res[0])// Valorite
              };
  
              oreAndStone.Resources = res;
              oreAndStone.Veins = veins;
  
              if (Core.ML)
              {
                  oreAndStone.BonusResources = new BonusHarvestResource[]
                  {
                      new BonusHarvestResource(0, 99.2, null, null), //Nothing
                      new BonusHarvestResource(100, .1, 1072562, typeof(BlueDiamond)),
                      new BonusHarvestResource(100, .1, 1072567, typeof(DarkSapphire)),
                      new BonusHarvestResource(100, .1, 1072570, typeof(EcruCitrine)),
                      new BonusHarvestResource(100, .1, 1072564, typeof(FireRuby)),
                      new BonusHarvestResource(100, .1, 1072566, typeof(PerfectEmerald)),
                      new BonusHarvestResource(100, .1, 1072568, typeof(Turquoise)),
                      new BonusHarvestResource(100, .1, 1077180, typeof(SmallPieceofBlackrock)),
                      new BonusHarvestResource(100, .1, 1113344, typeof(CrystallineBlackrock), Map.TerMur)
  				};
              }
  
              oreAndStone.RaceBonus = Core.ML;
              oreAndStone.RandomizeVeins = Core.ML;
  
              this.Definitions.Add(oreAndStone);
              #endregion
  
              #region Mining for sand
              HarvestDefinition sand = this.m_Sand = new HarvestDefinition();
  
              // Resource banks are every 8x8 tiles
              sand.BankWidth = 8;
              sand.BankHeight = 8;
  
              // Every bank holds from 6 to 12 sand
              sand.MinTotal = 6;
              sand.MaxTotal = 13;
  
              // A resource bank will respawn its content every 10 to 20 minutes
              sand.MinRespawn = TimeSpan.FromMinutes(10.0);
              sand.MaxRespawn = TimeSpan.FromMinutes(20.0);
  
              // Skill checking is done on the Mining skill
              sand.Skill = SkillName.Mining;
  
              // Set the list of harvestable tiles
              sand.Tiles = m_SandTiles;
  
              // Players must be within 2 tiles to harvest
              sand.MaxRange = 2;
  
              // One sand per harvest action
              sand.ConsumedPerHarvest = 1;
              sand.ConsumedPerFeluccaHarvest = 2;
  
              // The digging effect
              sand.EffectActions = new int[] { 11 };
              sand.EffectSounds = new int[] { 0x125, 0x126 };
              sand.EffectCounts = new int[] { 6 };
              sand.EffectDelay = TimeSpan.FromSeconds(1.6);
              sand.EffectSoundDelay = TimeSpan.FromSeconds(0.9);
  
              sand.NoResourcesMessage = 1044629; // There is no sand here to mine.
              sand.DoubleHarvestMessage = 1044629; // There is no sand here to mine.
              sand.TimedOutOfRangeMessage = 503041; // You have moved too far away to continue mining.
              sand.OutOfRangeMessage = 500446; // That is too far away.
              sand.FailMessage = 1044630; // You dig for a while but fail to find any of sufficient quality for glassblowing.
              sand.PackFullMessage = 1044632; // Your backpack can't hold the sand, and it is lost!
              sand.ToolBrokeMessage = 1044038; // You have worn out your tool!
  
              res = new HarvestResource[]
              {
                  new HarvestResource(100.0, 70.0, 100.0, 1044631, typeof(Sand))
              };
  
              veins = new HarvestVein[]
              {
                  new HarvestVein(100.0, 0.0, res[0], null)
              };
  
              sand.Resources = res;
              sand.Veins = veins;
  
              this.Definitions.Add(sand);
              #endregion
          }
  
          public override Type GetResourceType(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource)
          {
              if (def == this.m_OreAndStone)
              {
                  #region Void Pool Items
                  HarvestMap hmap = HarvestMap.CheckMapOnHarvest(from, loc, def);
  
                  if (hmap != null && hmap.Resource >= CraftResource.Iron && hmap.Resource <= CraftResource.Valorite)
                  {
                      hmap.UsesRemaining--;
                      hmap.InvalidateProperties();
  
                      CraftResourceInfo info = CraftResources.GetInfo(hmap.Resource);
  
                      if (info != null)
                          return info.ResourceTypes[1];
                  }
                  #endregion
  
                  PlayerMobile pm = from as PlayerMobile;
  
                  if (pm != null && pm.GemMining && pm.ToggleMiningGem && from.Skills[SkillName.Mining].Base >= 100.0 && 0.1 > Utility.RandomDouble())
                      return Loot.RandomGem().GetType();
  
                  double chance = tool is RockHammer ? 0.50 : 0.15;
  
                  if (pm != null && pm.StoneMining && pm.ToggleMiningStone && from.Skills[SkillName.Mining].Base >= 100.0 && chance > Utility.RandomDouble())
                      return resource.Types[1];
  
                  return resource.Types[0];
              }
  
              return base.GetResourceType(from, tool, def, map, loc, resource);
          }
  
          public override bool CheckResources(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed)
          {
              if (HarvestMap.CheckMapOnHarvest(from, loc, def) == null)
                  return base.CheckResources(from, tool, def, map, loc, timed);
  
              return true;
          }
  
          public override bool CheckHarvest(Mobile from, Item tool)
          {
              if (!base.CheckHarvest(from, tool))
                  return false;
  
              if (from.Mounted)
              {
                  from.SendLocalizedMessage(501864); // You can't mine while riding.
                  return false;
              }
              else if (from.IsBodyMod && !from.Body.IsHuman)
              {
                  from.SendLocalizedMessage(501865); // You can't mine while polymorphed.
                  return false;
              }
  
              return true;
          }
  
          public override void SendSuccessTo(Mobile from, Item item, HarvestResource resource)
          {
              if (item is BaseGranite)
                  from.SendLocalizedMessage(1044606); // You carefully extract some workable stone from the ore vein!
              else if (item is IGem)
                  from.SendLocalizedMessage(1112233); // You carefully extract a glistening gem from the vein!
              else
                  base.SendSuccessTo(from, item, resource);
          }
  
          public override bool CheckHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
          {
              if (!base.CheckHarvest(from, tool, def, toHarvest))
                  return false;
  
              if (def == this.m_Sand && !(from is PlayerMobile && from.Skills[SkillName.Mining].Base >= 100.0 && ((PlayerMobile)from).SandMining))
              {
                  this.OnBadHarvestTarget(from, tool, toHarvest);
                  return false;
              }
              else if (from.Mounted)
              {
                  from.SendLocalizedMessage(501864); // You can't mine while riding.
                  return false;
              }
              else if (from.IsBodyMod && !from.Body.IsHuman)
              {
                  from.SendLocalizedMessage(501865); // You can't mine while polymorphed.
                  return false;
              }
  
              return true;
          }
  
          public override HarvestVein MutateVein(Mobile from, Item tool, HarvestDefinition def, HarvestBank bank, object toHarvest, HarvestVein vein)
          {
              if (tool is GargoylesPickaxe && def == this.m_OreAndStone)
              {
                  int veinIndex = Array.IndexOf(def.Veins, vein);
  
                  if (veinIndex >= 0 && veinIndex < (def.Veins.Length - 1))
                      return def.Veins[veinIndex + 1];
              }
  
              return base.MutateVein(from, tool, def, bank, toHarvest, vein);
          }
  
          private static readonly int[] m_Offsets = new int[]
          {
              -1, -1,
              -1, 0,
              -1, 1,
              0, -1,
              0, 1,
              1, -1,
              1, 0,
              1, 1
          };
  
          public override void OnHarvestFinished(Mobile from, Item tool, HarvestDefinition def, HarvestVein vein, HarvestBank bank, HarvestResource resource, object harvested)
          {
              if (tool is GargoylesPickaxe && def == this.m_OreAndStone && 0.1 > Utility.RandomDouble() && HarvestMap.CheckMapOnHarvest(from, harvested, def) == null)
              {
                  HarvestResource res = vein.PrimaryResource;
  
                  if (res == resource && res.Types.Length >= 3)
                  {
                      try
                      {
                          Map map = from.Map;
  
                          if (map == null)
                              return;
  
                          BaseCreature spawned = Activator.CreateInstance(res.Types[2], new object[] { 25 }) as BaseCreature;
  
                          if (spawned != null)
                          {
                              int offset = Utility.Random(8) * 2;
  
                              for (int i = 0; i < m_Offsets.Length; i += 2)
                              {
                                  int x = from.X + m_Offsets[(offset + i) % m_Offsets.Length];
                                  int y = from.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length];
  
                                  if (map.CanSpawnMobile(x, y, from.Z))
                                  {
                                      spawned.OnBeforeSpawn(new Point3D(x, y, from.Z), map);
                                      spawned.MoveToWorld(new Point3D(x, y, from.Z), map);
                                      spawned.Combatant = from;
                                      return;
                                  }
                                  else
                                  {
                                      int z = map.GetAverageZ(x, y);
  
                                      if (Math.Abs(z - from.Z) < 10 && map.CanSpawnMobile(x, y, z))
                                      {
                                          spawned.OnBeforeSpawn(new Point3D(x, y, z), map);
                                          spawned.MoveToWorld(new Point3D(x, y, z), map);
                                          spawned.Combatant = from;
                                          return;
                                      }
                                  }
                              }
  
                              spawned.OnBeforeSpawn(from.Location, from.Map);
                              spawned.MoveToWorld(from.Location, from.Map);
                              spawned.Combatant = from;
                          }
                      }
                      catch
                      {
                      }
                  }
              }
          }
  
          #region High Seas
          public override bool SpecialHarvest(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc)
          {
              if (!Core.HS)
                  return base.SpecialHarvest(from, tool, def, map, loc);
  
              HarvestBank bank = def.GetBank(map, loc.X, loc.Y);
  
              if (bank == null)
                  return false;
  
              bool boat = Server.Multis.BaseBoat.FindBoatAt(from, from.Map) != null;
              bool dungeon = IsDungeonRegion(from);
  
              if (!boat && !dungeon)
                  return false;
  
              if (boat || !NiterDeposit.HasBeenChecked(bank))
              {
                  double bonus = (from.Skills[SkillName.Mining].Value / 9999) + ((double)from.Luck / 150000);
  
                  if (boat)
                      bonus -= (bonus * .33);
  
                  if (dungeon)
                      NiterDeposit.AddBank(bank);
  
                  if (Utility.RandomDouble() < bonus)
                  {
                      int size = Utility.RandomMinMax(1, 5);
  
                      if (from.Luck / 2500 > Utility.RandomDouble())
                          size++;
  
                      NiterDeposit niter = new NiterDeposit(size);
  
                      if (!dungeon)
                      {
                          niter.MoveToWorld(new Point3D(loc.X, loc.Y, from.Z + 3), from.Map);
                          from.SendLocalizedMessage(1149918, niter.Size.ToString()); //You have uncovered a ~1_SIZE~ deposit of niter! Mine it to obtain saltpeter.
                          NiterDeposit.AddBank(bank);
                          return true;
                      }
                      else
                      {
                          for (int i = 0; i < 50; i++)
                          {
                              int x = Utility.RandomMinMax(loc.X - 2, loc.X + 2);
                              int y = Utility.RandomMinMax(loc.Y - 2, loc.Y + 2);
                              int z = from.Z;
  
                              if (from.Map.CanSpawnMobile(x, y, z))
                              {
                                  niter.MoveToWorld(new Point3D(x, y, z), from.Map);
                                  from.SendLocalizedMessage(1149918, niter.Size.ToString()); //You have uncovered a ~1_SIZE~ deposit of niter! Mine it to obtain saltpeter.
                                  return true;
                              }
                          }
                      }
  
                      niter.Delete();
                  }
              }
  
              return false;
          }
  
          private bool IsDungeonRegion(Mobile from)
          {
  
gt:             if (from == null)