context: using System;
  using System.Collections;
  using Server;
  using Server.Network;
  
  namespace Server.Items
  {
  	[FlipableAttribute( 0x100A/*East*/, 0x100B/*South*/ )]
  	public class ArcheryButte : AddonComponent
  	{
  		private double m_MinSkill;
  		private double m_MaxSkill;
  
  		private int m_Arrows, m_Bolts;
  
  		private DateTime m_LastUse;
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public double MinSkill
  		{
  			get{ return m_MinSkill; }
  			set{ m_MinSkill = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public double MaxSkill
  		{
  			get{ return m_MaxSkill; }
  			set{ m_MaxSkill = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public DateTime LastUse
  		{
  			get{ return m_LastUse; }
  			set{ m_LastUse = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool FacingEast
  		{
  			get{ return ( ItemID == 0x100A ); }
  			set{ ItemID = value ? 0x100A : 0x100B; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public int Arrows
  		{
  			get{ return m_Arrows; }
  			set{ m_Arrows = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public int Bolts
  		{
  			get{ return m_Bolts; }
  			set{ m_Bolts = value; }
  		}
  
  		[Constructable]
  		public ArcheryButte() : this( 0x100A )
  		{
  		}
  
  		public ArcheryButte( int itemID ) : base( itemID )
  		{
  			m_MinSkill = -25.0;
  			m_MaxSkill = +25.0;
  		}
  
  		public ArcheryButte( Serial serial ) : base( serial )
  		{
  		}
  
  		public override void OnDoubleClick( Mobile from )
  		{
  			if ( (m_Arrows > 0 || m_Bolts > 0) && from.InRange( GetWorldLocation(), 1 ) )
  				Gather( from );
  			else
  				Fire( from );
  		}
  
  		public void Gather( Mobile from )
  		{
  			from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500592 ); // You gather the arrows and bolts.
  
  			if ( m_Arrows > 0 )
  				from.AddToBackpack( new Arrow( m_Arrows ) );
  
  			if ( m_Bolts > 0 )
  				from.AddToBackpack( new Bolt( m_Bolts ) );
  
  			m_Arrows = 0;
  			m_Bolts = 0;
  
  			m_Entries = null;
  		}
  
  		private static TimeSpan UseDelay = TimeSpan.FromSeconds( 2.0 );
  
  		private class ScoreEntry
  		{
  			private int m_Total;
  			private int m_Count;
  
  			public int Total{ get{ return m_Total; } set{ m_Total = value; } }
  			public int Count{ get{ return m_Count; } set{ m_Count = value; } }
  
  			public void Record( int score )
  			{
  				m_Total += score;
  				m_Count += 1;
  			}
  
  			public ScoreEntry()
  			{
  			}
  		}
  
  		private Hashtable m_Entries;
  
  		private ScoreEntry GetEntryFor( Mobile from )
  		{
  			if ( m_Entries == null )
  				m_Entries = new Hashtable();
  
  			ScoreEntry e = (ScoreEntry)m_Entries[from];
  
  			if ( e == null )
  				m_Entries[from] = e = new ScoreEntry();
  
  			return e;
  		}
  
  		public void Fire( Mobile from )
  		{
  			BaseRanged bow = from.Weapon as BaseRanged;
  
  			if ( bow == null )
  			{
  				SendLocalizedMessageTo( from, 500593 ); // You must practice with ranged weapons on this.
  				return;
  			}
  
  			if ( DateTime.Now < (m_LastUse + UseDelay) )
  				return;
  
  			Point3D worldLoc = GetWorldLocation();
  
  			if ( FacingEast ? from.X <= worldLoc.X : from.Y <= worldLoc.Y )
  			{
  				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500596 ); // You would do better to stand in front of the archery butte.
  				return;
  			}
  
  			if ( FacingEast ? from.Y != worldLoc.Y : from.X != worldLoc.X )
  			{
  				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500597 ); // You aren't properly lined up with the archery butte to get an accurate shot.
  				return;
  			}
  
  			if ( !from.InRange( worldLoc, 6 ) )
  			{
  				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500598 ); // You are too far away from the archery butte to get an accurate shot.
  				return;
  			}
  			else if ( from.InRange( worldLoc, 4 ) )
  			{
  				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500599 ); // You are too close to the target.
  				return;
  			}
  
  			Container pack = from.Backpack;
  			Type ammoType = bow.AmmoType;
  
  			bool isArrow = ( ammoType == typeof( Arrow ) );
  			bool isBolt = ( ammoType == typeof( Bolt ) );
  			bool isKnown = ( isArrow || isBolt );
  
  			if ( pack == null || !pack.ConsumeTotal( ammoType, 1 ) )
  			{
  				if ( isArrow )
  					from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500594 ); // You do not have any arrows with which to practice.
  				else if ( isBolt )
  					from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500595 ); // You do not have any crossbow bolts with which to practice.
  				else
  					SendLocalizedMessageTo( from, 500593 ); // You must practice with ranged weapons on this.
  
  				return;
  			}
  
  			m_LastUse = DateTime.Now;
  
  			from.Direction = from.GetDirectionTo( GetWorldLocation() );
  			bow.PlaySwingAnimation( from );
  			from.MovingEffect( this, bow.EffectID, 18, 1, false, false );
  
  			ScoreEntry se = GetEntryFor( from );
  
  			if ( !from.CheckSkill( bow.Skill, m_MinSkill, m_MaxSkill ) )
  			{
  				from.PlaySound( bow.MissSound );
  
  				PublicOverheadMessage( MessageType.Regular, 0x3B2, 500604, from.Name ); // You miss the target altogether.
  
  				se.Record( 0 );
  
  				if ( se.Count == 1 )
  					PublicOverheadMessage( MessageType.Regular, 0x3B2, 1062719, se.Total.ToString() );
  				else
  					PublicOverheadMessage( MessageType.Regular, 0x3B2, 1042683, String.Format( "{0}\t{1}", se.Total, se.Count ) );
  
  				return;
  			}
  
  			Effects.PlaySound( Location, Map, 0x2B1 );
  
  			double rand = Utility.RandomDouble();
  
  			int area, score, splitScore;
  
  			if ( 0.10 > rand )
  			{
  				area = 0; // bullseye
  				score = 50;
  				splitScore = 100;
  			}
  			else if ( 0.25 > rand )
  			{
  				area = 1; // inner ring
  				score = 10;
  				splitScore = 20;
  			}
  			else if ( 0.50 > rand )
  			{
  				area = 2; // middle ring
  				score = 5;
  				splitScore = 15;
  			}
  			else
  			{
  				area = 3; // outer ring
  				score = 2;
  				splitScore = 5;
  			}
  
  			bool split = ( isKnown && ((m_Arrows + m_Bolts) * 0.02) > Utility.RandomDouble() );
  
  			if ( split )
  			{
  				PublicOverheadMessage( MessageType.Regular, 0x3B2, 1010027 + area, String.Format( "{0}\t{1}", from.Name, isArrow ? "arrow" : "bolt" ) );
  			}
  			else
  			{
  				PublicOverheadMessage( MessageType.Regular, 0x3B2, 1010035 + area, from.Name );
  
  				if ( isArrow )
  					++m_Arrows;
  				else if ( isBolt )
  					++m_Bolts;
  			}
  
  			se.Record( split ? splitScore : score );
  
  			if ( se.Count == 1 )
  				PublicOverheadMessage( MessageType.Regular, 0x3B2, 1062719, se.Total.ToString() );
  			else
  				PublicOverheadMessage( MessageType.Regular, 0x3B2, 1042683, String.Format( "{0}\t{1}", se.Total, se.Count ) );
  		}
  
  		public override void Serialize( GenericWriter writer )
  		{
  			base.Serialize( writer );
  
  			writer.Write( (int) 0 );
  
  
gt: 			writer.Write( m_MinSkill );