context: using System;
  using System.Collections;
  using Server.Engines.Craft;
  using Server.Mobiles;
  using Server.Network;
  using Server.Targeting;
  
  namespace Server.Items
  {
      public delegate void InstrumentPickedCallback(Mobile from, BaseInstrument instrument);
  
      public abstract class BaseInstrument : Item, ICraftable, ISlayer, IQuality
      {
          private int m_WellSound, m_BadlySound;
          private SlayerName m_Slayer, m_Slayer2;
          private ItemQuality m_Quality;
          private Mobile m_Crafter;
          private int m_UsesRemaining;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int SuccessSound
          {
              get
              {
                  return m_WellSound;
              }
              set
              {
                  m_WellSound = value;
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int FailureSound
          {
              get
              {
                  return m_BadlySound;
              }
              set
              {
                  m_BadlySound = value;
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public SlayerName Slayer
          {
              get
              {
                  return m_Slayer;
              }
              set
              {
                  m_Slayer = value;
                  InvalidateProperties();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public SlayerName Slayer2
          {
              get
              {
                  return m_Slayer2;
              }
              set
              {
                  m_Slayer2 = value;
                  InvalidateProperties();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public ItemQuality Quality
          {
              get
              {
                  return m_Quality;
              }
              set
              {
                  UnscaleUses();
                  m_Quality = value;
                  InvalidateProperties();
                  ScaleUses();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool PlayerConstructed { get { return m_Crafter != null; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Mobile Crafter
          {
              get
              {
                  return m_Crafter;
              }
              set
              {
                  m_Crafter = value;
                  InvalidateProperties();
              }
          }
  
          public virtual int InitMinUses
          {
              get
              {
                  return 350;
              }
          }
          public virtual int InitMaxUses
          {
              get
              {
                  return 450;
              }
          }
  
          public virtual TimeSpan ChargeReplenishRate
          {
              get
              {
                  return TimeSpan.FromMinutes(5.0);
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int UsesRemaining
          {
              get
              {
                  CheckReplenishUses();
                  return m_UsesRemaining;
              }
              set
              {
                  m_UsesRemaining = value;
                  InvalidateProperties();
              }
          }
  
          private DateTime m_LastReplenished;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public DateTime LastReplenished
          {
              get
              {
                  return m_LastReplenished;
              }
              set
              {
                  m_LastReplenished = value;
                  CheckReplenishUses();
              }
          }
  
          private bool m_ReplenishesCharges;
          [CommandProperty(AccessLevel.GameMaster)]
          public bool ReplenishesCharges
          {
              get
              {
                  return m_ReplenishesCharges;
              }
              set 
              {
                  if (value != m_ReplenishesCharges && value)
                      m_LastReplenished = DateTime.UtcNow;
  
                  m_ReplenishesCharges = value; 
              }
          }
  
          public void RandomInstrument()
          {
              switch (Utility.Random(3))
              {
                  case 0:
                      {
                          ItemID = 0xEB2;
                          SuccessSound = 0x45;
                          FailureSound = 0x46;
                          break;
                      }
                  case 1:
                      {
                          ItemID = 0xEB3;
                          SuccessSound = 0x4C;
                          FailureSound = 0x4D;
                          break;
                      }
                  default:
                      {
                          ItemID = 0xE9C;
                          SuccessSound = 0x38;
                          FailureSound = 0x39;
                          break;
                      }
              }
          }
  
          public void CheckReplenishUses()
          {
              CheckReplenishUses(true);
          }
  
          public void CheckReplenishUses(bool invalidate)
          {
              if (!m_ReplenishesCharges || m_UsesRemaining >= InitMaxUses)
                  return;
  
              if (m_LastReplenished + ChargeReplenishRate < DateTime.UtcNow)
              {
                  TimeSpan timeDifference = DateTime.UtcNow - m_LastReplenished;
  
                  m_UsesRemaining = Math.Min(m_UsesRemaining + (int)(timeDifference.Ticks / ChargeReplenishRate.Ticks), InitMaxUses);	//How rude of TimeSpan to not allow timespan division.
                  m_LastReplenished = DateTime.UtcNow;
  
                  if (invalidate)
                      InvalidateProperties();
              }
          }
  
          public void ScaleUses()
          {
              UsesRemaining = (UsesRemaining * GetUsesScalar()) / 100;
              //InvalidateProperties();
          }
  
          public void UnscaleUses()
          {
              UsesRemaining = (UsesRemaining * 100) / GetUsesScalar();
          }
  
          public int GetUsesScalar()
          {
              if (m_Quality == ItemQuality.Exceptional)
                  return 200;
  
              return 100;
          }
  
          public void ConsumeUse(Mobile from)
          {
              // TODO: Confirm what must happen here?
              if (UsesRemaining > 1)
              {
                  --UsesRemaining;
              }
              else
              {
                  if (from != null)
                      from.SendLocalizedMessage(502079); // The instrument played its last tune.
  
                  Delete();
              }
          }
  
          private static readonly Hashtable m_Instruments = new Hashtable();
  
          public static BaseInstrument GetInstrument(Mobile from)
          {
              BaseInstrument item = m_Instruments[from] as BaseInstrument;
  
              if (item == null)
                  return null;
  
              if (!item.IsChildOf(from.Backpack))
              {
                  m_Instruments.Remove(from);
                  return null;
              }
  
              return item;
          }
  
          public static int GetBardRange(Mobile bard, SkillName skill)
          {
              return 8 + (int)(bard.Skills[skill].Value / 15);
          }
  
          public static void PickInstrument(Mobile from, InstrumentPickedCallback callback)
          {
              BaseInstrument instrument = GetInstrument(from);
  
              if (instrument != null)
              {
                  if (callback != null)
                      callback(from, instrument);
              }
              else
              {
                  from.SendLocalizedMessage(500617); // What instrument shall you play?
                  from.BeginTarget(1, false, TargetFlags.None, new TargetStateCallback(OnPickedInstrument), callback);
              }
          }
  
          public static void OnPickedInstrument(Mobile from, object targeted, object state)
          {
              BaseInstrument instrument = targeted as BaseInstrument;
  
              if (instrument == null)
              {
                  from.SendLocalizedMessage(500619); // That is not a musical instrument.
              }
              else
              {
                  SetInstrument(from, instrument);
  
                  InstrumentPickedCallback callback = state as InstrumentPickedCallback;
  
                  if (callback != null)
                      callback(from, instrument);
              }
          }
  
          public static bool IsMageryCreature(BaseCreature bc)
          {
              return (bc != null && bc.AI == AIType.AI_Mage && bc.Skills[SkillName.Magery].Base > 5.0);
          }
  
          public static bool IsFireBreathingCreature(BaseCreature bc)
          {
              if (bc == null)
                  return false;
  
              return bc.HasBreath;
          }
  
          public static bool IsPoisonImmune(BaseCreature bc)
          {
              return (bc != null && bc.PoisonImmune != null);
          }
  
          public static int GetPoisonLevel(BaseCreature bc)
          {
  
gt:             if (bc == null)