context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using Server;
  using System.Reflection;
  using Server.Misc;
  using Server.Spells;
  using Server.Spells.Third;
  using Server.Spells.Sixth;
  using Server.Items;
  using Server.Targeting;
  using Server.Commands;
  
  namespace Server.Mobiles
  {
  	[CorpseName( "a changeling corpse" )]
  	public class Changeling : BaseCreature
  	{
  		public virtual string DefaultName{ get{ return "a changeling"; } }
  		public virtual int DefaultHue{ get{ return 0; } }
  
  		[Constructable]
  		public Changeling()
  			: base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 )
  		{
  			Name = DefaultName;
  			Body = 264;
  			Hue = DefaultHue;
  
  			SetStr( 36, 105 );
  			SetDex( 212, 262 );
  			SetInt( 317, 399 );
  
  			SetHits( 201, 211 );
  			SetStam( 212, 262 );
  			SetMana( 317, 399 );
  
  			SetDamage( 9, 15 );
  
  			SetDamageType( ResistanceType.Physical, 100 );
  
  			SetResistance( ResistanceType.Physical, 81, 90 );
  			SetResistance( ResistanceType.Fire, 40, 50 );
  			SetResistance( ResistanceType.Cold, 40, 49 );
  			SetResistance( ResistanceType.Poison, 40, 50 );
  			SetResistance( ResistanceType.Energy, 43, 50 );
  
  			SetSkill( SkillName.Wrestling, 10.4, 12.5 );
  			SetSkill( SkillName.Tactics, 101.1, 108.3 );
  			SetSkill( SkillName.MagicResist, 121.6, 132.2 );
  			SetSkill( SkillName.Magery, 91.6, 99.5 );
  			SetSkill( SkillName.EvalInt, 91.5, 98.8 );
  			SetSkill( SkillName.Meditation, 91.7, 98.5 );
  
  			Fame = 15000;
  			Karma = -15000;
  
  			PackScroll( 1, 7 );
  			PackItem( new Arrow( 35 ) );
  			PackItem( new Bolt( 25 ) );
  			PackGem( 2 );
  
  			PackArcaneScroll( 0, 1 );
  		}
  
  		public override bool ShowFameTitle{ get{ return false; } }
  		public override bool InitialInnocent{ get{ return ( m_MorphedInto != null ); } }
  
  		public override void GenerateLoot()
  		{
  			AddLoot( LootPack.AosRich, 3 );
  		}
  
  		public override int GetAngerSound() { return 0x46E; }
  		public override int GetIdleSound() { return 0x470; }
  		public override int GetAttackSound() { return 0x46D; }
  		public override int GetHurtSound() { return 0x471; }
  		public override int GetDeathSound() { return 0x46F; }
  
  		private Mobile m_MorphedInto;
  		private DateTime m_LastMorph;
  		private DateTime m_NextFireRing;
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Mobile MorphedInto
  		{
  			get { return m_MorphedInto; }
  			set
  			{
  				if ( value == this )
  					value = null;
  
  				if ( m_MorphedInto != value )
  				{
  					Revert();
  
  					if ( value != null )
  					{
  						Morph( value );
  						m_LastMorph = DateTime.UtcNow;
  					}
  
  					m_MorphedInto = value;
  					Delta( MobileDelta.Noto );
  				}
  			}
  		}
  
          private void RandoTarget(Mobile from)
          {
              double SwitchRate = .05;
              int PullRange = 10;
              foreach (Mobile m_target in GetMobilesInRange(PullRange))
                  if ((m_target != from) && (SpellHelper.ValidIndirectTarget(from, (Mobile)m_target) && from.CanBeHarmful((Mobile)m_target, false)))
                  {
                      if (Utility.RandomDouble() < SwitchRate)
                          from.Combatant = m_target;
                  }
          }
  
          public override void OnThink()
  		{
  			base.OnThink();
  
  			if ( Combatant != null )
  			{
  				if ( m_NextFireRing <= DateTime.UtcNow && Utility.RandomDouble() < 0.02 )
  				{
  					FireRing();
  					m_NextFireRing = DateTime.UtcNow + TimeSpan.FromMinutes( 2 );
  				}
  
  				if ( /*Combatant.Player &&*/ m_MorphedInto != Combatant && Utility.RandomDouble() < 0.05 )
  					MorphedInto = Combatant;
  
                  if (Skills.Discordance.Value >= 100.0)
                      Suppress(Combatant);
                  if (Skills.Provocation.Value >= 100.0)
                      Provoke(Combatant);
                  if (Skills.Peacemaking.Value >= 100.0)
                      Peace(Combatant);
              }
  
              RandoTarget(this);
              
  		}
  
          #region Suppress
          private static Dictionary<Mobile, Timer> m_Suppressed = new Dictionary<Mobile, Timer>();
          private DateTime m_NextSuppress;
          
          public void Suppress(Mobile target)
          {
              if (target == null || m_Suppressed.ContainsKey(target) || Deleted || !Alive || m_NextSuppress > DateTime.UtcNow || 0.1 < Utility.RandomDouble())
                  return;
  
              TimeSpan delay = TimeSpan.FromSeconds(Utility.RandomMinMax(20, 80));
  
              if (!target.Hidden && CanBeHarmful(target))
              {
                  target.SendLocalizedMessage(1072061); // You hear jarring music, suppressing your strength.
  
                  for (int i = 0; i < target.Skills.Length; i++)
                  {
                      Skill s = target.Skills[i];
  
                      target.AddSkillMod(new TimedSkillMod(s.SkillName, true, s.Base * -0.28, delay));
                  }
  
                  int count = (int)Math.Round(delay.TotalSeconds / 1.25);
                  Timer timer = new AnimateTimer(target, count);
                  m_Suppressed.Add(target, timer);
                  timer.Start();
  
                  PlaySound(0x58C);
              }
  
              m_NextSuppress = DateTime.UtcNow + TimeSpan.FromSeconds(10);
          }
  
          public static void SuppressRemove(Mobile target)
          {
              if (target != null && m_Suppressed.ContainsKey(target))
              {
                  Timer timer = m_Suppressed[target];
  
                  if (timer != null || timer.Running)
                      timer.Stop();
  
                  m_Suppressed.Remove(target);
              }
          }
  
          private class AnimateTimer : Timer
          {
              private Mobile m_Owner;
              private int m_Count;
  
              public AnimateTimer(Mobile owner, int count) : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.25))
              {
                  m_Owner = owner;
                  m_Count = count;
              }
  
              protected override void OnTick()
              {
                  if (m_Owner.Deleted || !m_Owner.Alive || m_Count-- < 0)
                  {
                      SuppressRemove(m_Owner);
                  }
                  else
                      m_Owner.FixedParticles(0x376A, 1, 32, 0x15BD, EffectLayer.Waist);
              }
          }
          #endregion
  
          #region Provoke
          private DateTime m_NextProvoke;
  
          public void Provoke(Mobile target)
          {
              if (target == null || Deleted || !Alive || m_NextProvoke > DateTime.UtcNow || 0.05 < Utility.RandomDouble())
                  return;
  
              foreach (Mobile m in GetMobilesInRange(RangePerception))
              {
                  if (m is BaseCreature)
                  {
                      BaseCreature c = (BaseCreature)m;
  
                      if (c == this || c == target || c.Unprovokable || c.IsParagon || c.BardProvoked || c.AccessLevel != AccessLevel.Player || !c.CanBeHarmful(target))
                          continue;
  
                      c.Provoke(this, target, true);
  
                      if (target.Player)
                          target.SendLocalizedMessage(1072062); // You hear angry music, and start to fight.
  
                      PlaySound(0x58A);
                      break;
                  }
              }
  
              m_NextProvoke = DateTime.UtcNow + TimeSpan.FromSeconds(10);
          }
          #endregion
  
          #region Peace
          private DateTime m_NextPeace;
  
          public void Peace(Mobile target)
          {
              if (target == null || Deleted || !Alive || m_NextPeace > DateTime.UtcNow || 0.1 < Utility.RandomDouble())
                  return;
  
              PlayerMobile p = target as PlayerMobile;
  
              if (p != null && p.PeacedUntil < DateTime.UtcNow && !p.Hidden && CanBeHarmful(p))
              {
                  p.PeacedUntil = DateTime.UtcNow + TimeSpan.FromMinutes(1);
                  p.SendLocalizedMessage(500616); // You hear lovely music, and forget to continue battling!
                  p.FixedParticles(0x376A, 1, 32, 0x15BD, EffectLayer.Waist);
                  p.Combatant = null;
  
                  PlaySound(0x58D);
              }
  
              m_NextPeace = DateTime.UtcNow + TimeSpan.FromSeconds(10);
          }
          #endregion
  
          public override bool CheckIdle()
  		{
  			bool idle = base.CheckIdle();
  
  			if ( idle && m_MorphedInto != null && DateTime.UtcNow - m_LastMorph > TimeSpan.FromSeconds( 60 ) )
  				MorphedInto = null;
  
  			return idle;
  		}
  
  		private void FireEffects( int itemID, int[] offsets )
  		{
  			for ( int i = 0; i < offsets.Length; i += 2 )
  			{
  				Point3D p = Location;
  
  				p.X += offsets[i];
  				p.Y += offsets[i + 1];
  
  				if ( SpellHelper.AdjustField( ref p, Map, 12, false ) )
  					Effects.SendLocationEffect( p, Map, itemID, 50 );
  			}
  		}
  
  		private static readonly int[] m_FireNorth = new int[]
  		{
  			-1, -1,
  			1, -1,
  			-1, 2,
  			1, 2
  		};
  
  		private static readonly int[] m_FireEast = new int[]
  		{
  			-1, 0,
  			2, 0
  		};
  
  		protected virtual void FireRing()
  		{
  			FireEffects( 0x3E27, m_FireNorth );
  			FireEffects( 0x3E31, m_FireEast );
  		}
  
  		protected virtual void Morph( Mobile m )
  		{
              
  			Body = m.Body;
  			Hue = m.Hue;
  			Female = m.Female;
  			Name = m.Name;
  			NameHue = m.NameHue;
  			Title = m.Title;
  			Kills = m.Kills;
  			HairItemID = m.HairItemID;
  			HairHue = m.HairHue;
  			FacialHairItemID = m.FacialHairItemID;
  			FacialHairHue = m.FacialHairHue;
  
              MorphSkills(m);
  
  
              foreach ( Item item in m.Items )
  			{
                  Item newItem;
                  Type t;
                  ConstructorInfo c;
                  object o;
                  if ( item.Layer != Layer.Backpack && item.Layer != Layer.Mount && item.Layer != Layer.Bank )
                  {
                      t = item.GetType();
                      c = t.GetConstructor(Type.EmptyTypes);
  
                      if (c != null)
                      {
                          o = c.Invoke(null);
                          if (o != null && o is Item)
                          {
                              //TODO: How to copy ASHammer
                              newItem = (Item)o;
                              Dupe.CopyProperties(newItem, item);
                              newItem.LootType = LootType.Newbied;
                              item.OnAfterDuped(newItem);
                              newItem.Parent = null;
                              AddItem(newItem);
                              newItem.InvalidateProperties();
                          }
                      }
                  }
              }
  
  			PlaySound( 0x511 );
  			FixedParticles( 0x376A, 1, 14, 5045, EffectLayer.Waist );
  		}
  
          protected virtual void MorphSkills( Mobile m )
          {
              if (Skills.Alchemy.Base < m.Skills.Alchemy.Base)
                  Skills.Alchemy.Base = m.Skills.Alchemy.Base;
              if (Skills.Anatomy.Base < m.Skills.Anatomy.Base)
                  Skills.Anatomy.Base = m.Skills.Anatomy.Base;
              if (Skills.ItemID.Base < m.Skills.ItemID.Base)
                  Skills.ItemID.Base = m.Skills.ItemID.Base;
              if (Skills.ArmsLore.Base < m.Skills.ArmsLore.Base)
                  Skills.ArmsLore.Base = m.Skills.ArmsLore.Base;
              if (Skills.Parry.Base < m.Skills.Parry.Base )
                  Skills.Parry.Base = m.Skills.Parry.Base;
  
gt:             if (Skills.Begging.Base < m.Skills.Begging.Base)