context: using System;
  using System.Collections;
  
  namespace Server.Items
  {
      public abstract class BaseRunicTool : BaseTool
      {
          private static readonly SkillName[] m_PossibleBonusSkills = new SkillName[]
          {
              SkillName.Swords,
              SkillName.Fencing,
              SkillName.Macing,
              SkillName.Archery,
              SkillName.Wrestling,
              SkillName.Parry,
              SkillName.Tactics,
              SkillName.Anatomy,
              SkillName.Healing,
              SkillName.Magery,
              SkillName.Meditation,
              SkillName.EvalInt,
              SkillName.MagicResist,
              SkillName.AnimalTaming,
              SkillName.AnimalLore,
              SkillName.Veterinary,
              SkillName.Musicianship,
              SkillName.Provocation,
              SkillName.Discordance,
              SkillName.Peacemaking,
              SkillName.Chivalry,
              SkillName.Focus,
              SkillName.Necromancy,
              SkillName.Stealing,
              SkillName.Stealth,
              SkillName.SpiritSpeak,
              SkillName.Bushido,
              SkillName.Ninjitsu
          };
  
          private static readonly SkillName[] m_PossibleSpellbookSkills = new SkillName[]
          {
              SkillName.Magery,
              SkillName.Meditation,
              SkillName.EvalInt,
              SkillName.MagicResist
          };
  
          private static readonly BitArray m_Props = new BitArray(MaxProperties);
          private static readonly int[] m_Possible = new int[MaxProperties];
  
          private static bool m_PlayerMade;
          private static int m_LuckChance;
  
          private const int MaxProperties = 32;
  
          public BaseRunicTool(CraftResource resource, int itemID)
              : base(itemID)
          {
              Resource = resource;
          }
  
          public BaseRunicTool(CraftResource resource, int uses, int itemID)
              : base(uses, itemID)
          {
              Resource = resource;
          }
  
          public BaseRunicTool(Serial serial)
              : base(serial)
          {
          }
  
          #region Runic Reforging
          public override void OnDoubleClick(Mobile from)
          {
              bool hasSkill = from.Skills[SkillName.Imbuing].Value >= 65;
  
              IPooledEnumerable eable = from.Map.GetItemsInRange(from.Location, 2);
  
              foreach (Item item in eable)
              {
                  if ((item.ItemID >= 0x4263 && item.ItemID <= 0x4272) || (item.ItemID >= 0x4277 && item.ItemID <= 0x4286) || (item.ItemID >= 17607 && item.ItemID <= 17610))
                  {
                      if (!hasSkill)
                      {
                          from.SendLocalizedMessage(1152333); // You do not have enough Imbuing skill to re-forge items. Using standard Runic Crafting instead.
                          break;
                      }
  
                      from.Target = new RunicReforgingTarget(this);
                      from.SendLocalizedMessage(1152112); // Target the item to reforge.
                      eable.Free();
                      return;
                  }
              }
  
              if (hasSkill)
                  from.SendLocalizedMessage(1152334); // You must be near a Soul Forge to re-forge items. Using standard Runic Crafting instead.
  
              eable.Free();
              base.OnDoubleClick(from);
          }
          #endregion
  
          public static int GetUniqueRandom(int count)
          {
              int avail = 0;
  
              for (int i = 0; i < count; ++i)
              {
                  if (!m_Props[i])
                      m_Possible[avail++] = i;
              }
  
              if (avail == 0)
                  return -1;
  
              int v = m_Possible[Utility.Random(avail)];
  
              m_Props.Set(v, true);
  
              return v;
          }
  
          public static void ApplyAttributesTo(Item item, int attributeCount, int min, int max)
          {
              if (item is FishingPole)
              {
                  ApplyAttributesTo((FishingPole)item, attributeCount, min, max);
              }
              else if (item is BaseWeapon)
              {
                  ApplyAttributesTo((BaseWeapon)item, attributeCount, min, max);
              }
              else if (item is BaseArmor)
              {
                  ApplyAttributesTo((BaseArmor)item, attributeCount, min, max);
              }
              else if (item is BaseHat)
              {
                  ApplyAttributesTo((BaseHat)item, attributeCount, min, max);
              }
              else if (item is BaseJewel)
              {
                  ApplyAttributesTo((BaseJewel)item, attributeCount, min, max);
              }
              else if (item is Spellbook)
              {
                  ApplyAttributesTo((Spellbook)item, attributeCount, min, max);
              }
          }
  
          public static void ApplyAttributesTo(
              Item item,
              bool isRunicTool,
              int luckChance,
              int attributeCount,
              int min,
              int max)
          {
              if (item is FishingPole)
              {
                  ApplyAttributesTo((FishingPole)item, isRunicTool, luckChance, attributeCount, min, max);
              }
              else if (item is BaseWeapon)
              {
                  ApplyAttributesTo((BaseWeapon)item, isRunicTool, luckChance, attributeCount, min, max);
              }
              else if (item is BaseArmor)
              {
                  ApplyAttributesTo((BaseArmor)item, isRunicTool, luckChance, attributeCount, min, max);
              }
              else if (item is BaseHat)
              {
                  ApplyAttributesTo((BaseHat)item, isRunicTool, luckChance, attributeCount, min, max);
              }
              else if (item is BaseJewel)
              {
                  ApplyAttributesTo((BaseJewel)item, isRunicTool, luckChance, attributeCount, min, max);
              }
              else if (item is Spellbook)
              {
                  ApplyAttributesTo((Spellbook)item, isRunicTool, luckChance, attributeCount, min, max);
              }
          }
  
          #region High Seas
          public void ApplyAttributesTo(FishingPole pole)
          {
              CraftResourceInfo resInfo = CraftResources.GetInfo(Resource);
  
              if (resInfo == null)
                  return;
  
              CraftAttributeInfo attrs = resInfo.AttributeInfo;
  
              int attributeCount = Utility.RandomMinMax(attrs.RunicMinAttributes, attrs.RunicMaxAttributes);
              int min = attrs.RunicMinIntensity;
              int max = attrs.RunicMaxIntensity;
  
              ApplyAttributesTo(pole, true, 0, attributeCount, min, max);
          }
  
          public static void ApplyAttributesTo(FishingPole pole, bool playerMade, int luckChance, int attributeCount, int min, int max)
          {
              int delta;
  
              if (min > max)
              {
                  delta = min;
                  min = max;
                  max = delta;
              }
  
              m_PlayerMade = playerMade;
              m_LuckChance = luckChance;
  
              AosAttributes primary = pole.Attributes;
              AosSkillBonuses skills = pole.SkillBonuses;
  
              m_Props.SetAll(false);
  
              for (int i = 0; i < attributeCount; ++i)
              {
                  int random = GetUniqueRandom(21);
  
                  switch (random)
                  {
                      case 0: ApplyAttribute(primary, min, max, AosAttribute.DefendChance, 1, 15); break;
                      case 1: ApplyAttribute(primary, min, max, AosAttribute.CastSpeed, 1, 1); break;
                      case 2: ApplyAttribute(primary, min, max, AosAttribute.CastRecovery, 1, 1); break;
                      case 3: ApplyAttribute(primary, min, max, AosAttribute.AttackChance, 1, 15); break;
                      case 4: ApplyAttribute(primary, min, max, AosAttribute.Luck, 1, 100); break;
                      case 5: ApplyAttribute(primary, min, max, AosAttribute.SpellChanneling, 1, 1); break;
                      case 6: ApplyAttribute(primary, min, max, AosAttribute.RegenHits, 1, 2); break;
                      case 7: ApplyAttribute(primary, min, max, AosAttribute.RegenMana, 1, 2); break;
                      case 8: ApplyAttribute(primary, min, max, AosAttribute.RegenStam, 1, 3); break;
                      case 9: ApplyAttribute(primary, min, max, AosAttribute.BonusHits, 1, 8); break;
                      case 10: ApplyAttribute(primary, min, max, AosAttribute.BonusMana, 1, 8); break;
                      case 11: ApplyAttribute(primary, min, max, AosAttribute.BonusStam, 1, 8); break;
                      case 12: ApplyAttribute(primary, min, max, AosAttribute.BonusStr, 1, 8); break;
                      case 13: ApplyAttribute(primary, min, max, AosAttribute.BonusDex, 1, 8); break;
                      case 14: ApplyAttribute(primary, min, max, AosAttribute.BonusInt, 1, 8); break;
                      case 15: ApplyAttribute(primary, min, max, AosAttribute.SpellDamage, 1, 12); break;
                      case 16: ApplySkillBonus(skills, min, max, 0, 1, 15); break;
                      case 17: ApplySkillBonus(skills, min, max, 1, 1, 15); break;
                      case 18: ApplySkillBonus(skills, min, max, 2, 1, 15); break;
                      case 19: ApplySkillBonus(skills, min, max, 3, 1, 15); break;
                      case 20: ApplySkillBonus(skills, min, max, 4, 1, 15); break;
                  }
              }
          }
          #endregion
  
          public static void ApplyAttributesTo(BaseWeapon weapon, int attributeCount, int min, int max)
          {
              ApplyAttributesTo(weapon, false, 0, attributeCount, min, max);
          }
  
          public static void ApplyAttributesTo(BaseWeapon weapon, bool playerMade, int luckChance, int attributeCount, int min, int max)
          {
              int delta;
  
              if (min > max)
              {
                  delta = min;
                  min = max;
                  max = delta;
              }
  
              if (!playerMade && RandomItemGenerator.Enabled)
              {
                  RandomItemGenerator.GenerateRandomItem(weapon, luckChance, attributeCount, min, max);
                  return;
              }
  
              m_PlayerMade = playerMade;
              m_LuckChance = luckChance;
  
              AosAttributes primary = weapon.Attributes;
              AosWeaponAttributes secondary = weapon.WeaponAttributes;
  
              m_Props.SetAll(false);
  
              if (weapon is BaseRanged)
              {
                  m_Props.Set(2, true); // ranged weapons cannot be ubws or mageweapon
              }
              else
              {
                  m_Props.Set(25, true); // Only bows can be Balanced
                  m_Props.Set(26, true); // Only bows have Velocity
              }
  
              for (int i = 0; i < attributeCount; ++i)
              {
                  int random = GetUniqueRandom(27);
  
                  if (random == -1)
                      break;
  
                  switch (random)
                  {
                      case 0:
                          {
                              switch (Utility.Random(5))
                              {
                                  case 0:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitPhysicalArea, 2, 50, 2);
                                      break;
                                  case 1:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitFireArea, 2, 50, 2);
                                      break;
                                  case 2:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitColdArea, 2, 50, 2);
                                      break;
                                  case 3:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitPoisonArea, 2, 50, 2);
                                      break;
                                  case 4:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitEnergyArea, 2, 50, 2);
                                      break;
                              }
  
                              break;
                          }
                      case 1:
                          {
                              switch (Utility.Random(4))
                              {
                                  case 0:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitMagicArrow, 2, 50, 2);
                                      break;
                                  case 1:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitHarm, 2, 50, 2);
                                      break;
                                  case 2:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitFireball, 2, 50, 2);
                                      break;
                                  case 3:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.HitLightning, 2, 50, 2);
                                      break;
                              }
  
                              break;
                          }
                      case 2:
                          {
                              switch (Utility.Random(2))
                              {
                                  case 0:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.UseBestSkill, 1, 1);
                                      break;
                                  case 1:
                                      ApplyAttribute(secondary, min, max, AosWeaponAttribute.MageWeapon, 1, 10);
                                      break;
                              }
  
                              break;
                          }
                      case 3:
                          int dmgMin = primary.WeaponDamage;
  
gt:                         int dmgMax = Math.Max(dmgMin, 50);