context: using System;
  using Server;
  using Server.Items;
  using Server.Mobiles;
  using Server.Engines.Quests;
  using Server.Engines.Quests.Collector;
  using System.Collections.Generic;
  
  namespace Server.Engines.Harvest
  {
  	public class Fishing : HarvestSystem
  	{
  		private static Fishing m_System;
  
  		public static Fishing System
  		{
  			get
  			{
  				if ( m_System == null )
  					m_System = new Fishing();
  
  				return m_System;
  			}
  		}
  
  		private HarvestDefinition m_Definition;
  
  		public HarvestDefinition Definition
  		{
  			get{ return m_Definition; }
  		}
  
  		private Fishing()
  		{
  			HarvestResource[] res;
  			HarvestVein[] veins;
  
  			#region Fishing
  			HarvestDefinition fish = new HarvestDefinition();
  
  			// Resource banks are every 8x8 tiles
  			fish.BankWidth = 8;
  			fish.BankHeight = 8;
  
  			// Every bank holds from 5 to 15 fish
  			fish.MinTotal = 5;
  			fish.MaxTotal = 15;
  
  			// A resource bank will respawn its content every 10 to 20 minutes
  			fish.MinRespawn = TimeSpan.FromMinutes( 10.0 );
  			fish.MaxRespawn = TimeSpan.FromMinutes( 20.0 );
  
  			// Skill checking is done on the Fishing skill
  			fish.Skill = SkillName.Fishing;
  
  			// Set the list of harvestable tiles
  			fish.Tiles = m_WaterTiles;
  			fish.RangedTiles = true;
  
  			// Players must be within 4 tiles to harvest
  			fish.MaxRange = 4;
  
  			// One fish per harvest action
  			fish.ConsumedPerHarvest = 1;
  			fish.ConsumedPerFeluccaHarvest = 1;
  
  			// The fishing
  			fish.EffectActions = new int[]{ 12 };
  			fish.EffectSounds = new int[0];
  			fish.EffectCounts = new int[]{ 1 };
  			fish.EffectDelay = TimeSpan.Zero;
  			fish.EffectSoundDelay = TimeSpan.FromSeconds( 8.0 );
  
  			fish.NoResourcesMessage = 503172; // The fish don't seem to be biting here.
  			fish.FailMessage = 503171; // You fish a while, but fail to catch anything.
  			fish.TimedOutOfRangeMessage = 500976; // You need to be closer to the water to fish!
  			fish.OutOfRangeMessage = 500976; // You need to be closer to the water to fish!
  			fish.PackFullMessage = 503176; // You do not have room in your backpack for a fish.
  			fish.ToolBrokeMessage = 503174; // You broke your fishing pole.
  
  			res = new HarvestResource[]
  				{
  					new HarvestResource( 00.0, 00.0, 100.0, 1043297, typeof( Fish ) )
  				};
  
  			veins = new HarvestVein[]
  				{
  					new HarvestVein( 100.0, 0.0, res[0], null )
  				};
  
  			fish.Resources = res;
  			fish.Veins = veins;
  
  			if ( Core.ML )
  			{
  				fish.BonusResources = new BonusHarvestResource[]
  				{
  					new BonusHarvestResource( 0, 99.4, null, null ), //set to same chance as mining ml gems
  					new BonusHarvestResource( 80.0, .6, 1072597, typeof( WhitePearl ) )
  				};
  			}
  
  			m_Definition = fish;
  			Definitions.Add( fish );
  			#endregion
  		}
  
  		public override void OnConcurrentHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest )
  		{
  			from.SendLocalizedMessage( 500972 ); // You are already fishing.
  		}
  
  		private class MutateEntry
  		{
  			public double m_ReqSkill, m_MinSkill, m_MaxSkill;
  			public bool m_DeepWater;
  			public Type[] m_Types;
  
  			public MutateEntry( double reqSkill, double minSkill, double maxSkill, bool deepWater, params Type[] types )
  			{
  				m_ReqSkill = reqSkill;
  				m_MinSkill = minSkill;
  				m_MaxSkill = maxSkill;
  				m_DeepWater = deepWater;
  				m_Types = types;
  			}
  		}
  
  		private static MutateEntry[] m_MutateTable = new MutateEntry[]
  			{
  				new MutateEntry(  80.0,  80.0,  4080.0,  true, typeof( SpecialFishingNet ) ),
  				new MutateEntry(  80.0,  80.0,  4080.0,  true, typeof( BigFish ) ),
  				new MutateEntry(  90.0,  80.0,  4080.0,  true, typeof( TreasureMap ) ),
  				new MutateEntry( 100.0,  80.0,  4080.0,  true, typeof( MessageInABottle ) ),
  				new MutateEntry(   0.0, 125.0, -2375.0, false, typeof( PrizedFish ), typeof( WondrousFish ), typeof( TrulyRareFish ), typeof( PeculiarFish ) ),
  				new MutateEntry(   0.0, 105.0,  -420.0, false, typeof( Boots ), typeof( Shoes ), typeof( Sandals ), typeof( ThighBoots ) ),
  				new MutateEntry(   0.0, 200.0,  -200.0, false, new Type[1]{ null } )
  			};
  
  		public override bool SpecialHarvest( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc )
  		{
  			PlayerMobile player = from as PlayerMobile;
  
  			if ( player != null )
  			{
  				QuestSystem qs = player.Quest;
  
  				if ( qs is CollectorQuest )
  				{
  					QuestObjective obj = qs.FindObjective( typeof( FishPearlsObjective ) );
  
  					if ( obj != null && !obj.Completed )
  					{
  						if ( Utility.RandomDouble() < 0.5 )
  						{
  							player.SendLocalizedMessage( 1055086, "", 0x59 ); // You pull a shellfish out of the water, and find a rainbow pearl inside of it.
  
  							obj.CurProgress++;
  						}
  						else
  						{
  							player.SendLocalizedMessage( 1055087, "", 0x2C ); // You pull a shellfish out of the water, but it doesn't have a rainbow pearl.
  						}
  
  						return true;
  					}
  				}
  			}
  
  			return false;
  		}
  
  		public override Type MutateType( Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource )
  		{
  			bool deepWater = SpecialFishingNet.FullValidation( map, loc.X, loc.Y );
  
  			double skillBase = from.Skills[SkillName.Fishing].Base;
  			double skillValue = from.Skills[SkillName.Fishing].Value;
  
  			for ( int i = 0; i < m_MutateTable.Length; ++i )
  			{
  				MutateEntry entry = m_MutateTable[i];
  
  				if ( !deepWater && entry.m_DeepWater )
  					continue;
  
  				if ( skillBase >= entry.m_ReqSkill )
  				{
  					double chance = (skillValue - entry.m_MinSkill) / (entry.m_MaxSkill - entry.m_MinSkill);
  
  					if ( chance > Utility.RandomDouble() )
  						return entry.m_Types[Utility.Random( entry.m_Types.Length )];
  				}
  			}
  
  			return type;
  		}
  
  		private static Map SafeMap( Map map )
  		{
  			if ( map == null || map == Map.Internal )
  				return Map.Trammel;
  
  			return map;
  		}
  
  		public override bool CheckResources( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed )
  		{
  			Container pack = from.Backpack;
  
  			if ( pack != null )
  			{
  				List<SOS> messages = pack.FindItemsByType<SOS>();
  
  				for ( int i = 0; i < messages.Count; ++i )
  				{
  					SOS sos = messages[i];
  
  					if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) )
  						return true;
  				}
  			}
  
  			return base.CheckResources( from, tool, def, map, loc, timed );
  		}
  
  		public override Item Construct( Type type, Mobile from )
  		{
  			if ( type == typeof( TreasureMap ) )
  			{
  				int level;
  				if ( from is PlayerMobile && ((PlayerMobile)from).Young && from.Map == Map.Trammel && TreasureMap.IsInHavenIsland( from ) )
  					level = 0;
  				else
  					level = 1;
  
  				return new TreasureMap( level, from.Map == Map.Felucca ? Map.Felucca : Map.Trammel );
  			}
  			else if ( type == typeof( MessageInABottle ) )
  			{
  				return new MessageInABottle( from.Map == Map.Felucca ? Map.Felucca : Map.Trammel );
  			}
  
  			Container pack = from.Backpack;
  
  			if ( pack != null )
  			{
  				List<SOS> messages = pack.FindItemsByType<SOS>();
  
  				for ( int i = 0; i < messages.Count; ++i )
  				{
  					SOS sos = messages[i];
  
  					if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) )
  					{
  						Item preLoot = null;
  
  						switch ( Utility.Random( 8 ) )
  						{
  							case 0: // Body parts
  							{
  								int[] list = new int[]
  									{
  										0x1CDD, 0x1CE5, // arm
  										0x1CE0, 0x1CE8, // torso
  										0x1CE1, 0x1CE9, // head
  										0x1CE2, 0x1CEC // leg
  									};
  
  								preLoot = new ShipwreckedItem( Utility.RandomList( list ) );
  								break;
  							}
  							case 1: // Bone parts
  							{
  								int[] list = new int[]
  									{
  										0x1AE0, 0x1AE1, 0x1AE2, 0x1AE3, 0x1AE4, // skulls
  										0x1B09, 0x1B0A, 0x1B0B, 0x1B0C, 0x1B0D, 0x1B0E, 0x1B0F, 0x1B10, // bone piles
  										0x1B15, 0x1B16 // pelvis bones
  									};
  
  								preLoot = new ShipwreckedItem( Utility.RandomList( list ) );
  								break;
  							}
  							case 2: // Paintings and portraits
  							{
  								preLoot = new ShipwreckedItem( Utility.Random( 0xE9F, 10 ) );
  								break;
  							}
  							case 3: // Pillows
  							{
  								preLoot = new ShipwreckedItem( Utility.Random( 0x13A4, 11 ) );
  								break;
  							}
  							case 4: // Shells
  							{
  								preLoot = new ShipwreckedItem( Utility.Random( 0xFC4, 9 ) );
  								break;
  							}
  							case 5:	//Hats
  							{
  								if ( Utility.RandomBool() )
  
gt: 									preLoot = new SkullCap();