context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using Server;
  using Server.Items;
  using Server.Mobiles;
  using Server.Targeting;
  using Server.Factions.AI;
  using Server.Spells;
  using Server.Spells.First;
  using Server.Spells.Second;
  using Server.Spells.Third;
  using Server.Spells.Fourth;
  using Server.Spells.Fifth;
  using Server.Spells.Sixth;
  using Server.Spells.Seventh;
  
  namespace Server.Factions
  {
  	public enum GuardAI
  	{
  		Bless	= 0x01, // heal, cure, +stats
  		Curse	= 0x02, // poison, -stats
  		Melee	= 0x04, // weapons
  		Magic	= 0x08, // damage spells
  		Smart	= 0x10  // smart weapons/damage spells
  	}
  
  	public class ComboEntry
  	{
  		private Type m_Spell;
  		private TimeSpan m_Hold;
  		private int m_Chance;
  
  		public Type Spell{ get{ return m_Spell; } }
  		public TimeSpan Hold{ get{ return m_Hold; } }
  		public int Chance{ get{ return m_Chance; } }
  
  		public ComboEntry( Type spell ) : this( spell, 100, TimeSpan.Zero )
  		{
  		}
  
  		public ComboEntry( Type spell, int chance ) : this( spell, chance, TimeSpan.Zero )
  		{
  		}
  
  		public ComboEntry( Type spell, int chance, TimeSpan hold )
  		{
  			m_Spell = spell;
  			m_Chance = chance;
  			m_Hold = hold;
  		}
  	}
  
  	public class SpellCombo
  	{
  		private int m_Mana;
  		private ComboEntry[] m_Entries;
  
  		public int Mana{ get{ return m_Mana; } }
  		public ComboEntry[] Entries{ get{ return m_Entries; } }
  
  		public SpellCombo( int mana, params ComboEntry[] entries )
  		{
  			m_Mana = mana;
  			m_Entries = entries;
  		}
  
  		public static readonly SpellCombo Simple = new SpellCombo( 50,
  			new ComboEntry( typeof( ParalyzeSpell ), 20 ),
  			new ComboEntry( typeof( ExplosionSpell ), 100, TimeSpan.FromSeconds( 2.8 ) ),
  			new ComboEntry( typeof( PoisonSpell ), 30 ),
  			new ComboEntry( typeof( EnergyBoltSpell ) )
  		);
  
  		public static readonly SpellCombo Strong = new SpellCombo( 90,
  			new ComboEntry( typeof( ParalyzeSpell ), 20 ),
  			new ComboEntry( typeof( ExplosionSpell ), 50, TimeSpan.FromSeconds( 2.8 ) ),
  			new ComboEntry( typeof( PoisonSpell ), 30 ),
  			new ComboEntry( typeof( ExplosionSpell ), 100, TimeSpan.FromSeconds( 2.8 ) ),
  			new ComboEntry( typeof( EnergyBoltSpell ) ),
  			new ComboEntry( typeof( PoisonSpell ), 30 ),
  			new ComboEntry( typeof( EnergyBoltSpell ) )
  		);
  
  		public static Spell Process( Mobile mob, Mobile targ, ref SpellCombo combo, ref int index, ref DateTime releaseTime )
  		{
  			while ( ++index < combo.m_Entries.Length )
  			{
  				ComboEntry entry = combo.m_Entries[index];
  
  				if ( entry.Spell == typeof( PoisonSpell ) && targ.Poisoned )
  					continue;
  
  				if ( entry.Chance > Utility.Random( 100 ) )
  				{
  					releaseTime = DateTime.UtcNow + entry.Hold;
  					return (Spell) Activator.CreateInstance( entry.Spell, new object[]{ mob, null } );
  				}
  			}
  
  			combo = null;
  			index = -1;
  			return null;
  		}
  	}
  
  	public class FactionGuardAI : BaseAI
  	{
  		private BaseFactionGuard m_Guard;
  
  		private BandageContext m_Bandage;
  		private DateTime m_BandageStart;
  
  		private SpellCombo m_Combo;
  		private int m_ComboIndex = -1;
  		private DateTime m_ReleaseTarget;
  
  		private const int ManaReserve = 30;
  
  		public bool IsAllowed( GuardAI flag )
  		{
  			return ( ( m_Guard.GuardAI & flag ) == flag );
  		}
  
  		public bool IsDamaged
  		{
  			get{ return ( m_Guard.Hits < m_Guard.HitsMax ); }
  		}
  
  		public bool IsPoisoned
  		{
  			get{ return m_Guard.Poisoned; }
  		}
  
  		public TimeSpan TimeUntilBandage
  		{
  			get
  			{
  				if ( m_Bandage != null && m_Bandage.Timer == null )
  					m_Bandage = null;
  
  				if ( m_Bandage == null )
  					return TimeSpan.MaxValue;
  
  				TimeSpan ts = ( m_BandageStart + m_Bandage.Timer.Delay ) - DateTime.UtcNow;
  
  				if ( ts < TimeSpan.FromSeconds( -1.0 ) )
  				{
  					m_Bandage = null;
  					return TimeSpan.MaxValue;
  				}
  
  				if ( ts < TimeSpan.Zero )
  					ts = TimeSpan.Zero;
  
  				return ts;
  			}
  		}
  
  		public bool DequipWeapon()
  		{
  			Container pack = m_Guard.Backpack;
  
  			if ( pack == null )
  				return false;
  
  			Item weapon = m_Guard.Weapon as Item;
  
  			if ( weapon != null && weapon.Parent == m_Guard && !(weapon is Fists) )
  			{
  				pack.DropItem( weapon );
  				return true;
  			}
  
  			return false;
  		}
  
  		public bool EquipWeapon()
  		{
  			Container pack = m_Guard.Backpack;
  
  			if ( pack == null )
  				return false;
  
  			Item weapon = pack.FindItemByType( typeof( BaseWeapon ) );
  
  			if ( weapon == null )
  				return false;
  
  			return m_Guard.EquipItem( weapon );
  		}
  
  		public bool StartBandage()
  		{
  			m_Bandage = null;
  
  			Container pack = m_Guard.Backpack;
  
  			if ( pack == null )
  				return false;
  
  			Item bandage = pack.FindItemByType( typeof( Bandage ) );
  
  			if ( bandage == null )
  				return false;
  
  			m_Bandage = BandageContext.BeginHeal( m_Guard, m_Guard );
  			m_BandageStart = DateTime.UtcNow;
  			return ( m_Bandage != null );
  		}
  
  		public bool UseItemByType( Type type )
  		{
  			Container pack = m_Guard.Backpack;
  
  			if ( pack == null )
  				return false;
  
  			Item item = pack.FindItemByType( type );
  
  			if ( item == null )
  				return false;
  
  			bool requip = DequipWeapon();
  
  			item.OnDoubleClick( m_Guard );
  
  			if ( requip )
  				EquipWeapon();
  
  			return true;
  		}
  
  		public int GetStatMod( Mobile mob, StatType type )
  		{
  			StatMod mod = mob.GetStatMod( String.Format( "[Magic] {0} Offset", type ) );
  
  			if ( mod == null )
  				return 0;
  
  			return mod.Offset;
  		}
  
  		public Spell RandomOffenseSpell()
  		{
  			int maxCircle = (int)((m_Guard.Skills.Magery.Value + 20.0) / (100.0 / 7.0));
  
  			if ( maxCircle < 1 )
  				maxCircle = 1;
  
  			switch ( Utility.Random( maxCircle*2 ) )
  			{
  				case  0: case  1: return new MagicArrowSpell( m_Guard, null );
  				case  2: case  3: return new HarmSpell( m_Guard, null );
  				case  4: case  5: return new FireballSpell( m_Guard, null );
  				case  6: case  7: return new LightningSpell( m_Guard, null );
  				case  8: return new MindBlastSpell( m_Guard, null );
  				case  9: return new ParalyzeSpell( m_Guard, null );
  				case 10: return new EnergyBoltSpell( m_Guard, null );
  				case 11: return new ExplosionSpell( m_Guard, null );
  				default: return new FlameStrikeSpell( m_Guard, null );
  			}
  		}
  
  		public Mobile FindDispelTarget( bool activeOnly )
  		{
  			if ( m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel( m_Mobile ) || m_Mobile.AutoDispel )
  				return null;
  
  			if ( activeOnly )
  			{
  				List<AggressorInfo> aggressed = m_Mobile.Aggressed;
  				List<AggressorInfo> aggressors = m_Mobile.Aggressors;
  
  				Mobile active = null;
  				double activePrio = 0.0;
  
  				Mobile comb = m_Mobile.Combatant;
  
  				if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange( comb, 12 ) && CanDispel( comb ) )
  				{
  					active = comb;
  					activePrio = m_Mobile.GetDistanceToSqrt( comb );
  
  					if ( activePrio <= 2 )
  						return active;
  				}
  
  				for ( int i = 0; i < aggressed.Count; ++i )
  				{
  					AggressorInfo info = aggressed[i];
  					Mobile m = info.Defender;
  
  					if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) )
  					{
  						double prio = m_Mobile.GetDistanceToSqrt( m );
  
  						if ( active == null || prio < activePrio )
  						{
  							active = m;
  							activePrio = prio;
  
  							if ( activePrio <= 2 )
  								return active;
  						}
  					}
  				}
  
  				for ( int i = 0; i < aggressors.Count; ++i )
  				{
  					AggressorInfo info = aggressors[i];
  					Mobile m = info.Attacker;
  
  					if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) )
  					{
  						double prio = m_Mobile.GetDistanceToSqrt( m );
  
  						if ( active == null || prio < activePrio )
  						{
  							active = m;
  							activePrio = prio;
  
  							if ( activePrio <= 2 )
  								return active;
  						}
  					}
  				}
  
  				return active;
  			}
  			else
  			{
  				Map map = m_Mobile.Map;
  
  				if ( map != null )
  				{
  					Mobile active = null, inactive = null;
  					double actPrio = 0.0, inactPrio = 0.0;
  
  					Mobile comb = m_Mobile.Combatant;
  
  					if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && CanDispel( comb ) )
  					{
  						active = inactive = comb;
  						actPrio = inactPrio = m_Mobile.GetDistanceToSqrt( comb );
  					}
  
  					foreach ( Mobile m in m_Mobile.GetMobilesInRange( 12 ) )
  					{
  						if ( m != m_Mobile && CanDispel( m ) )
  						{
  							double prio = m_Mobile.GetDistanceToSqrt( m );
  
  							if ( !activeOnly && (inactive == null || prio < inactPrio) )
  							{
  								inactive = m;
  								inactPrio = prio;
  							}
  
  							if ( (m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio) )
  							{
  								active = m;
  								actPrio = prio;
  							}
  						}
  					}
  
  					return active != null ? active : inactive;
  				}
  			}
  
  			return null;
  		}
  
  		public bool CanDispel( Mobile m )
  		{
  			return ( m is BaseCreature && ((BaseCreature)m).Summoned && m_Mobile.CanBeHarmful( m, false ) && !((BaseCreature)m).IsAnimatedDead );
  		}
  
  		public void RunTo( Mobile m )
  		{
  			/*if ( m.Paralyzed || m.Frozen )
  			{
  				if ( m_Mobile.InRange( m, 1 ) )
  					RunFrom( m );
  				else if ( !m_Mobile.InRange( m, m_Mobile.RangeFight > 2 ? m_Mobile.RangeFight : 2 ) && !MoveTo( m, true, 1 ) )
  					OnFailedMove();
  			}
  			else
  			{*/
  				if ( !m_Mobile.InRange( m, m_Mobile.RangeFight ) )
  				{
  					if ( !MoveTo( m, true, 1 ) )
  						OnFailedMove();
  				}
  				else if ( m_Mobile.InRange( m, m_Mobile.RangeFight - 1 ) )
  				{
  					RunFrom( m );
  				}
  			/*}*/
  		}
  
  		public void RunFrom( Mobile m )
  		{
  			Run( (m_Mobile.GetDirectionTo( m ) - 4) & Direction.Mask );
  		}
  
  		public void OnFailedMove()
  		{
  			/*if ( !m_Mobile.DisallowAllMoves && 20 > Utility.Random( 100 ) && IsAllowed( GuardAI.Magic ) )
  			{
  				if ( m_Mobile.Target != null )
  					m_Mobile.Target.Cancel( m_Mobile, TargetCancelType.Canceled );
  
  				new TeleportSpell( m_Mobile, null ).Cast();
  
  				m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" );
  			}
  			else*/ if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
  			{
  				if ( m_Mobile.Debug )
  					m_Mobile.DebugSay( "My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name );
  
  				m_Mobile.Combatant = m_Mobile.FocusMob;
  				Action = ActionType.Combat;
  			}
  			else
  			{
  				m_Mobile.DebugSay( "I am stuck" );
  			}
  		}
  
  		public void Run( Direction d )
  		{
  			if ( (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting) || m_Mobile.Paralyzed || m_Mobile.Frozen || m_Mobile.DisallowAllMoves )
  				return;
  
  			m_Mobile.Direction = d | Direction.Running;
  
  			if ( !DoMove( m_Mobile.Direction, true ) )
  				OnFailedMove();
  		}
  
  		public FactionGuardAI( BaseFactionGuard guard ) : base( guard )
  		{
  			m_Guard = guard;
  		}
  
  		public override bool Think()
  		{
  			if ( m_Mobile.Deleted )
  				return false;
  
  			Mobile combatant = m_Guard.Combatant;
  
  			if ( combatant == null || combatant.Deleted || !combatant.Alive || combatant.IsDeadBondedPet || !m_Mobile.CanSee( combatant ) || !m_Mobile.CanBeHarmful( combatant, false ) || combatant.Map != m_Mobile.Map )
  			{
  				// Our combatant is deleted, dead, hidden, or we cannot hurt them
  				// Try to find another combatant
  
  				if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
  				{
  					m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
  					m_Mobile.FocusMob = null;
  				}
  				else
  				{
  					m_Mobile.Combatant = combatant = null;
  				}
  			}
  
  			if ( combatant != null && (!m_Mobile.InLOS( combatant ) || !m_Mobile.InRange( combatant, 12 )) )
  			{
  				if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
  				{
  					m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
  					m_Mobile.FocusMob = null;
  				}
  				else if ( !m_Mobile.InRange( combatant, 36 ) )
  				{
  					m_Mobile.Combatant = combatant = null;
  				}
  			}
  
  			Mobile dispelTarget = FindDispelTarget( true );
  
  			if ( m_Guard.Target != null && m_ReleaseTarget == DateTime.MinValue )
  				m_ReleaseTarget = DateTime.UtcNow + TimeSpan.FromSeconds( 10.0 );
  
  			if ( m_Guard.Target != null && DateTime.UtcNow > m_ReleaseTarget )
  			{
  				Target targ = m_Guard.Target;
  
  				Mobile toHarm = ( dispelTarget == null ? combatant : dispelTarget );
  
  				if ( (targ.Flags & TargetFlags.Harmful) != 0 && toHarm != null )
  				{
  					if ( m_Guard.Map == toHarm.Map && ( targ.Range < 0 || m_Guard.InRange( toHarm, targ.Range ) ) && m_Guard.CanSee( toHarm ) && m_Guard.InLOS( toHarm ) )
  						targ.Invoke( m_Guard, toHarm );
  					else if ( targ is DispelSpell.InternalTarget )
  						targ.Cancel( m_Guard, TargetCancelType.Canceled );
  				}
  				else if ( (targ.Flags & TargetFlags.Beneficial) != 0 )
  				{
  					targ.Invoke( m_Guard, m_Guard );
  				}
  				else
  				{
  					targ.Cancel( m_Guard, TargetCancelType.Canceled );
  				}
  
  				m_ReleaseTarget = DateTime.MinValue;
  			}
  
  			if ( dispelTarget != null )
  			{
  				if ( Action != ActionType.Combat )
  					Action = ActionType.Combat;
  
  				m_Guard.Warmode = true;
  
  				RunFrom( dispelTarget );
  			}
  			else if ( combatant != null )
  			{
  				if ( Action != ActionType.Combat )
  					Action = ActionType.Combat;
  
  				m_Guard.Warmode = true;
  
  				RunTo( combatant );
  			}
  			else if ( m_Guard.Orders.Movement != MovementType.Stand )
  			{
  				Mobile toFollow = null;
  
  				if ( m_Guard.Town != null && m_Guard.Orders.Movement == MovementType.Follow )
  				{
  					toFollow = m_Guard.Orders.Follow;
  
  					if ( toFollow == null )
  						toFollow = m_Guard.Town.Sheriff;
  				}
  
  				if ( toFollow != null && toFollow.Map == m_Guard.Map && toFollow.InRange( m_Guard, m_Guard.RangePerception * 3 ) && Town.FromRegion( toFollow.Region ) == m_Guard.Town )
  				{
  					if ( Action != ActionType.Combat )
  						Action = ActionType.Combat;
  
  					if ( m_Mobile.CurrentSpeed != m_Mobile.ActiveSpeed )
  						m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed;
  
  					m_Guard.Warmode = true;
  
  					RunTo( toFollow );
  				}
  				else
  				{
  					if ( Action != ActionType.Wander )
  						Action = ActionType.Wander;
  
  					if ( m_Mobile.CurrentSpeed != m_Mobile.PassiveSpeed )
  						m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed;
  
  					m_Guard.Warmode = false;
  
  					WalkRandomInHome( 2, 2, 1 );
  				}
  			}
  			else
  			{
  				if ( Action != ActionType.Wander )
  					Action = ActionType.Wander;
  
  				m_Guard.Warmode = false;
  			}
  
  			if ( (IsDamaged || IsPoisoned) && m_Guard.Skills.Healing.Base > 20.0 )
  			{
  				TimeSpan ts = TimeUntilBandage;
  
  				if ( ts == TimeSpan.MaxValue )
  					StartBandage();
  			}
  
  			if ( m_Mobile.Spell == null && Core.TickCount - m_Mobile.NextSpellTime >= 0 )
  			{
  				Spell spell = null;
  
  				DateTime toRelease = DateTime.MinValue;
  
  				if ( IsPoisoned )
  				{
  					Poison p = m_Guard.Poison;
  
  					TimeSpan ts = TimeUntilBandage;
  
  					if ( p != Poison.Lesser || ts == TimeSpan.MaxValue || TimeUntilBandage < TimeSpan.FromSeconds( 1.5 ) || (m_Guard.HitsMax - m_Guard.Hits) > Utility.Random( 250 ) )
  					{
  						if ( IsAllowed( GuardAI.Bless ) )
  							spell = new CureSpell( m_Guard, null );
  						else
  
gt: 							UseItemByType( typeof( BaseCurePotion ) );