context: using System;
  using Server;
  using Server.Mobiles;
  using Server.Targeting;
  using Server.Network;
  
  namespace Server.Items
  {
  	public class GreenThorns : Item
  	{
  		public override int LabelNumber { get { return 1060837; } } // green thorns
  
  		[Constructable]
  		public GreenThorns()
  			: this( 1 )
  		{
  		}
  
  		[Constructable]
  		public GreenThorns( int amount )
  			: base( 0xF42 )
  		{
  			Stackable = true;
  			Weight = 1.0;
  			Hue = 0x42;
  			Amount = amount;
  		}
  
  		public GreenThorns( Serial serial )
  			: base( serial )
  		{
  		}
  
  		public override void OnDoubleClick( Mobile from )
  		{
  			if ( !IsChildOf( from.Backpack ) )
  			{
  				from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it.
  				return;
  			}
  
  			if ( !from.CanBeginAction( typeof( GreenThorns ) ) )
  			{
  				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061908 ); // * You must wait a while before planting another thorn. *
  				return;
  			}
  
  			from.Target = new InternalTarget( this );
  			from.SendLocalizedMessage( 1061906 ); // Choose a spot to plant the thorn.
  		}
  
  		private class InternalTarget : Target
  		{
  			private GreenThorns m_Thorn;
  
  			public InternalTarget( GreenThorns thorn )
  				: base( 3, true, TargetFlags.None )
  			{
  				m_Thorn = thorn;
  			}
  
  			protected override void OnTarget( Mobile from, object targeted )
  			{
  				if ( m_Thorn.Deleted )
  					return;
  
  				if ( !m_Thorn.IsChildOf( from.Backpack ) )
  				{
  					from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it.
  					return;
  				}
  
  				if ( !from.CanBeginAction( typeof( GreenThorns ) ) )
  				{
  					from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061908 ); // * You must wait a while before planting another thorn. *
  					return;
  				}
  
  				if ( from.Map != Map.Trammel && from.Map != Map.Felucca )
  				{
  					from.LocalOverheadMessage( MessageType.Regular, 0x2B2, true, "No solen lairs exist on this facet.  Try again in Trammel or Felucca." );
  					return;
  				}
  
  				LandTarget land = targeted as LandTarget;
  
  				if ( land == null )
  				{
  					from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061912 ); // * You cannot plant a green thorn there! *
  				}
  				else
  				{
  					GreenThornsEffect effect = GreenThornsEffect.Create( from, land );
  
  					if ( effect == null )
  					{
  						from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061913 ); // * You sense it would be useless to plant a green thorn there. *
  					}
  					else
  					{
  						m_Thorn.Consume();
  
  						from.LocalOverheadMessage( MessageType.Emote, 0x961, 1061914 ); // * You push the strange green thorn into the ground *
  						from.NonlocalOverheadMessage( MessageType.Emote, 0x961, 1061915, from.Name ); // * ~1_PLAYER_NAME~ pushes a strange green thorn into the ground. *
  
  						from.BeginAction( typeof( GreenThorns ) );
  						new GreenThorns.EndActionTimer( from ).Start();
  
  						effect.Start();
  					}
  				}
  			}
  
  			protected override void OnTargetOutOfRange( Mobile from, object targeted )
  			{
  				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502825 ); // That location is too far away
  			}
  		}
  
  		private class EndActionTimer : Timer
  		{
  			private Mobile m_From;
  
  			public EndActionTimer( Mobile from )
  				: base( TimeSpan.FromMinutes( 3.0 ) )
  			{
  				m_From = from;
  			}
  
  			protected override void OnTick()
  			{
  				m_From.EndAction( typeof( GreenThorns ) );
  			}
  		}
  
  		public override void Serialize( GenericWriter writer )
  		{
  			base.Serialize( writer );
  
  			writer.Write( (int) 0 ); // version
  		}
  
  		public override void Deserialize( GenericReader reader )
  		{
  			base.Deserialize( reader );
  
  			/*int version = */
  			reader.ReadInt();
  		}
  	}
  
  	public abstract class GreenThornsEffect : Timer
  	{
  		private class TilesAndEffect
  		{
  			private int[] m_Tiles;
  			private Type m_Effect;
  
  			public int[] Tiles { get { return m_Tiles; } }
  			public Type Effect { get { return m_Effect; } }
  
  			public TilesAndEffect( int[] tiles, Type effect )
  			{
  				m_Tiles = tiles;
  				m_Effect = effect;
  			}
  		}
  
  		private static TilesAndEffect[] m_Table = new TilesAndEffect[]
  			{
  				new TilesAndEffect( new int[]
  					{
  						0x71, 0x7C,
  						0x82, 0xA7,
  						0xDC, 0xE3,
  						0xE8, 0xEB,
  						0x141, 0x144,
  						0x14C, 0x14F,
  						0x169, 0x174,
  						0x1DC, 0x1E7,
  						0x1EC, 0x1EF,
  						0x272, 0x275,
  						0x27E, 0x281,
  						0x2D0, 0x2D7,
  						0x2E5, 0x2FF,
  						0x303, 0x31F,
  						0x32C, 0x32F,
  						0x33D, 0x340,
  						0x345, 0x34C,
  						0x355, 0x358,
  						0x367, 0x36E,
  						0x377, 0x37A,
  						0x38D, 0x390,
  						0x395, 0x39C,
  						0x3A5, 0x3A8,
  						0x3F6, 0x405,
  						0x547, 0x54E,
  						0x553, 0x556,
  						0x597, 0x59E,
  						0x623, 0x63A,
  						0x6F3, 0x6FA,
  						0x777, 0x791,
  						0x79A, 0x7A9,
  						0x7AE, 0x7B1,
  					},
  				typeof( DirtGreenThornsEffect ) ),
  
  				new TilesAndEffect( new int[]
  					{
  						0x9, 0x15,
  						0x150, 0x15C
  					},
  				typeof( FurrowsGreenThornsEffect ) ),
  
  				new TilesAndEffect( new int[]
  					{
  						0x9C4, 0x9EB,
  						0x3D65, 0x3D65,
  						0x3DC0, 0x3DD9,
  						0x3DDB, 0x3DDC,
  						0x3DDE, 0x3EF0,
  						0x3FF6, 0x3FF6,
  						0x3FFC, 0x3FFE,
  					},
  				typeof( SwampGreenThornsEffect ) ),
  
  				new TilesAndEffect( new int[]
  					{
  						0x10C, 0x10F,
  						0x114, 0x117,
  						0x119, 0x11D,
  						0x179, 0x18A,
  						0x385, 0x38C,
  						0x391, 0x394,
  						0x39D, 0x3A4,
  						0x3A9, 0x3AC,
  						0x5BF, 0x5D6,
  						0x5DF, 0x5E2,
  						0x745, 0x748,
  						0x751, 0x758,
  						0x75D, 0x760,
  						0x76D, 0x773
  					},
  				typeof( SnowGreenThornsEffect ) ),
  
  				new TilesAndEffect( new int[]
  					{
  						0x16, 0x3A,
  						0x44, 0x4B,
  						0x11E, 0x121,
  						0x126, 0x12D,
  						0x192, 0x192,
  						0x1A8, 0x1AB,
  						0x1B9, 0x1D1,
  						0x282, 0x285,
  						0x28A, 0x291,
  						0x335, 0x33C,
  						0x341, 0x344,
  						0x34D, 0x354,
  						0x359, 0x35C,
  						0x3B7, 0x3BE,
  						0x3C7, 0x3CA,
  						0x5A7, 0x5B2,
  						0x64B, 0x652,
  						0x657, 0x65A,
  						0x663, 0x66A,
  						0x66F, 0x672,
  						0x7BD, 0x7D0
  					},
  				typeof( SandGreenThornsEffect ) )
  			};
  
  		public static GreenThornsEffect Create( Mobile from, LandTarget land )
  		{
  			if ( !from.Map.CanSpawnMobile( land.Location ) )
  				return null;
  
  			int tileID = land.TileID;
  
  			foreach ( TilesAndEffect taep in m_Table )
  			{
  				bool contains = false;
  
  				for ( int i = 0; !contains && i < taep.Tiles.Length; i += 2 )
  					contains = ( tileID >= taep.Tiles[i] && tileID <= taep.Tiles[i + 1] );
  
  				if ( contains )
  				{
  					GreenThornsEffect effect = (GreenThornsEffect) Activator.CreateInstance( taep.Effect, new object[] { land.Location, from.Map, from } );
  					return effect;
  				}
  			}
  
  			return null;
  		}
  
  		private Point3D m_Location;
  		private Map m_Map;
  		private Mobile m_From;
  
  		public Point3D Location { get { return m_Location; } }
  		public Map Map { get { return m_Map; } }
  		public Mobile From { get { return m_From; } }
  
  		public GreenThornsEffect( Point3D location, Map map, Mobile from )
  			: base( TimeSpan.FromSeconds( 2.5 ) )
  		{
  			m_Location = location;
  			m_Map = map;
  			m_From = from;
  		}
  
  		private int m_Step;
  
  		protected override void OnTick()
  		{
  			TimeSpan nextDelay = Play( m_Step++ );
  
  			if ( nextDelay > TimeSpan.Zero )
  			{
  				Delay = nextDelay;
  
  				Start();
  			}
  		}
  
  		protected abstract TimeSpan Play( int step );
  
  		protected bool SpawnItem( Item item )
  		{
  			for ( int i = 0; i < 5; i++ ) // Try 5 times
  			{
  				int x = Location.X + Utility.RandomMinMax( -1, 1 );
  				int y = Location.Y + Utility.RandomMinMax( -1, 1 );
  				int z = Map.GetAverageZ( x, y );
  
  				if ( Map.CanFit( x, y, Location.Z, 1 ) )
  				{
  					item.MoveToWorld( new Point3D( x, y, Location.Z ), Map );
  					return true;
  				}
  				else if ( Map.CanFit( x, y, z, 1 ) )
  				{
  					item.MoveToWorld( new Point3D( x, y, z ), Map );
  					return true;
  				}
  			}
  
  			return false;
  		}
  
  		protected bool SpawnCreature( BaseCreature creature )
  		{
  			for ( int i = 0; i < 5; i++ ) // Try 5 times
  			{
  				int x = Location.X + Utility.RandomMinMax( -1, 1 );
  				int y = Location.Y + Utility.RandomMinMax( -1, 1 );
  				int z = Map.GetAverageZ( x, y );
  
  				if ( Map.CanSpawnMobile( x, y, Location.Z ) )
  				{
  					creature.MoveToWorld( new Point3D( x, y, Location.Z ), Map );
  					creature.Combatant = From;
  					return true;
  				}
  				else if ( Map.CanSpawnMobile( x, y, z ) )
  				{
  					creature.MoveToWorld( new Point3D( x, y, z ), Map );
  					creature.Combatant = From;
  					return true;
  				}
  			}
  
  			return false;
  		}
  	}
  
  	public class DirtGreenThornsEffect : GreenThornsEffect
  	{
  		public DirtGreenThornsEffect( Point3D location, Map map, Mobile from )
  			: base( location, map, from )
  		{
  		}
  
  		protected override TimeSpan Play( int step )
  		{
  			switch ( step )
  			{
  				case 0:
  					{
  						Effects.PlaySound( Location, Map, 0x106 );
  						Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 );
  
  						return TimeSpan.FromSeconds( 4.0 );
  					}
  				case 1:
  					{
  						Effects.PlaySound( Location, Map, 0x222 );
  
  						return TimeSpan.FromSeconds( 4.0 );
  					}
  				case 2:
  					{
  						Effects.PlaySound( Location, Map, 0x21F );
  
  						return TimeSpan.FromSeconds( 5.0 );
  					}
  				case 3:
  					{
  						EffectItem dummy = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 20.0 ) );
  						dummy.PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The ground erupts with chaotic growth! *" );
  
  						Effects.PlaySound( Location, Map, 0x12D );
  
  						SpawnReagents();
  						SpawnReagents();
  
  						return TimeSpan.FromSeconds( 2.0 );
  					}
  				case 4:
  					{
  						Effects.PlaySound( Location, Map, 0x12D );
  
  						SpawnReagents();
  						SpawnReagents();
  
  						return TimeSpan.FromSeconds( 2.0 );
  					}
  				case 5:
  					{
  						Effects.PlaySound( Location, Map, 0x12D );
  
  						SpawnReagents();
  						SpawnReagents();
  
  						return TimeSpan.FromSeconds( 3.0 );
  					}
  				default:
  					{
  						Effects.PlaySound( Location, Map, 0x12D );
  
  						SpawnReagents();
  						SpawnReagents();
  
  						return TimeSpan.Zero;
  					}
  			}
  		}
  
  		private void SpawnReagents()
  		{
  			Item reagents;
  			int amount = Utility.RandomMinMax( 10, 25 );
  
  			switch ( Utility.Random( 9 ) )
  			{
  				case 0:
  					reagents = new BlackPearl( amount );
  					break;
  				case 1:
  					reagents = new Bloodmoss( amount );
  					break;
  				case 2:
  					reagents = new Garlic( amount );
  					break;
  				case 3:
  					reagents = new Ginseng( amount );
  					break;
  				case 4:
  					reagents = new MandrakeRoot( amount );
  					break;
  				case 5:
  					reagents = new Nightshade( amount );
  					break;
  				case 6:
  					reagents = new SulfurousAsh( amount );
  					break;
  				case 7:
  					reagents = new SpidersSilk( amount );
  					break;
  				default:
  					reagents = new FertileDirt( amount );
  					break;
  			}
  
  			if ( !SpawnItem( reagents ) )
  				reagents.Delete();
  		}
  	}
  
  	public class FurrowsGreenThornsEffect : GreenThornsEffect
  	{
  		public FurrowsGreenThornsEffect( Point3D location, Map map, Mobile from )
  			: base( location, map, from )
  		{
  		}
  
  		protected override TimeSpan Play( int step )
  		{
  			switch ( step )
  			{
  				case 0:
  					{
  						Effects.PlaySound( Location, Map, 0x106 );
  						Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 );
  
  						return TimeSpan.FromSeconds( 4.0 );
  					}
  				case 1:
  					{
  						EffectItem hole = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 10.0 ) );
  						hole.ItemID = 0x913;
  
  						Effects.PlaySound( Location, Map, 0x222 );
  
  
gt: 						return TimeSpan.FromSeconds( 4.0 );