using System;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

using DarkStrideToolbox;


namespace MetalMarines
{
	public class Game
	{
		#region Debug Properties
		private bool m_cINSTANTBUILD = false;
		private bool m_cRENDERGRID = false;
		private bool m_cMMNEVERRETURNHOME = false;
		private bool m_cAUTOWINFORONEPLAYER = false;
		private bool m_cCANATTACKOURSELVES = false;
		private bool m_cALLOWZOOM = true;
		private bool m_cSHOWFPS = false;
		private bool m_cFULLVISION = false;
		#endregion

		#region Properties
		#region Campaign Table Constants
		public static string m_cGOB_CAMPAIGN_TABLE = "Campaign";

		public static string m_cGOB_CMPGSTATS_LEVELID_CLMN = "LevelID";
		public static string m_cGOB_CMPGSTATS_PLAYERISLANDNAME_CLMN = "Player Island Name";
		public static string m_cGOB_CMPGSTATS_COMPISLANDNAME_CLMN = "Computer Island Name";
		public static string m_cGOB_CMPGSTATS_AILEVEL_CLMN = "AI Level";
		public static string m_cGOB_CMPGSTATS_AINAME_CLMN = "AI Name";
		#endregion

		#region Building Table Constants
		public static string m_cGOB_BLDSTATS_TABLE		= "Building Stats";

		public static string m_cGOB_BLDSTATS_NAME_CLMN							= "Name";
		public static string m_cGOB_BLDSTATS_FIRESMISSLES_CLMN					= "Fires Missles";
		public static string m_cGOB_BLDSTATS_BLDKEY_CLMN						= "Building Key";
		public static string m_cGOB_BLDSTATS_NUMSHOTS_CLMN						= "Number of Shots";
		public static string m_cGOB_BLDSTATS_FIRESAA_CLMN						= "Fires AA";
		public static string m_cGOB_BLDSTATS_RECHARGEPRCNT_CLMN					= "Recharge Percent Per Sec";
		public static string m_cGOB_BLDSTATS_EMPTYGRAPHIC_CLMN					= "Empty Graphic Building Key";

		public static string m_cGOB_BLDSTATS_DIR0GRAPHICINDX_CLMN				= "Image Index";
		//Don't use these anymore
		public static string m_cGOB_BLDSTATS_DIR1GRAPHICINDX_CLMN				= "Dir 1 Building";
		//Don't use these anymore
		public static string m_cGOB_BLDSTATS_DIR2GRAPHICINDX_CLMN				= "Dir 2 Building";
		//Don't use these anymore
		public static string m_cGOB_BLDSTATS_DIR3GRAPHICINDX_CLMN				= "Dir 3 Building";

		public static string m_cGOB_BLDSTATS_COST_CLMN							= "Build Cost";
		public static string m_cGOB_BLDSTATS_CANCAMO_CLMN						= "Can Camo Base";
		public static string m_cGOB_BLDSTATS_CANBECAMOD_CLMN					= "Can Be Camoed";
		public static string m_cGOB_BLDSTATS_BLDTIME_CLMN						= "Build Time";
		public static string m_cGOB_BLDSTATS_BLDINCOME_CLMN						= "Build Income";
		public static string m_cGOB_BLDSTATS_MONEYINCOME_CLMN					= "Money Income";
		public static string m_cGOB_BLDSTATS_FUELINCOM_CLMN						= "Fuel Income";
		public static string m_cGOB_BLDSTATS_FIRESMM_CLMN							= "Fires MM";
		public static string m_cGOB_BLDSTATS_FUELTOFIRE_CLMN						= "Fuel To Fire";
		public static string m_cGOB_BLDSTATS_STRENGTH_CLMN							= "Strength";
		public static string m_cGOB_BLDSTATS_OFFENSIVE_CLMN							= "OffensivePower";
		public static string m_cGOB_BLDSTATS_ANIMNUMCELLS_CLMN						= "NumberAnimCells";
		public static string m_cGOB_BLDSTATS_ANIMCELLDELAY_CLMN						= "AnimationCellDelay";
		public static string m_cGOB_BLDSTATS_MMOFFENSE_CLMN							= "MMOffense";
		public static string m_cGOB_BLDSTATS_MMOFFENSERANGE_CLMN					= "Offensive Square Range";
		public static string m_cGOB_BLDSTATS_MMSTRENGTH_CLMN						= "MMStrength";
		public static string m_cGOB_BLDSTATS_MMGRAPHICINDEX_CLMN					= "MM Graphic Index";
		public static string m_cGOB_BLDSTATS_INVISIBLE_CLMN							= "Invisible To Others";
		public static string m_cGOB_BLDSTATS_UPGRADETO_CLMN							= "BuiltUpTo";
		public static string m_cGOB_BLDSTATS_BUILDUPCOST_CLMN						= "BuildUpCost";
		public static string m_cGOB_BLDSTATS_REPLACECOST_CLMN						= "Replacement Cost";
		public static string m_cGOB_BLDSTATS_CANBEBUILT_CLMN						= "Can Be Built";
		public static string m_cGOB_BLDSTATS_SHOWINMAPEDITOR_CLMN					= "Show In MapEditor";
		public static string m_cGOB_BLDSTATS_ISARROW_CLMN							= "Is Arrow";
		public static string m_cGOB_BLDSTATS_AIATTACKS_CLMN							= "AI Will Attack";
		public static string m_cGOB_BLDSTATS_VISIBLEINFOW_CLMN						= "Visible Through FOW";
		public static string m_cGOB_BLDSTATS_CANROTTONOTHING_CLMN					= "Can Rot To Nothing";
		public static string m_cGOB_BLDSTATS_MMOFFENSEFIRESONCEPERROUNDONLY_CLMN	= "MM Offense Fires Only Once Per Round";
		public static string m_cGOB_BLDSTATS_TOOLTIP_CLMN							= "Tooltip";

		public static string m_cGOB_BLDSTATS_AARANGE_CLMN			= "AA Range";
		public static string m_cGOB_BLDSTATS_AABASEBOOST_CLMN		= "AA Base Boost";
		public static string m_cGOB_BLDSTATS_AALOSSPERSQUARE_CLMN	= "AA Loss Per Square";
		public static string m_cGOB_BLDSTATS_RADAREFFECT_CLMN		= "Radar Effect";

		public static string m_cGOB_BLDSTATS_GT1SQAR_PRNTBLDKEY_TABLE	= "GT 1 Square Parent Building Key";
		public static string m_cGOB_BLDSTATS_GT1SQR_PRNTDELTAX_TABLE	= "GT 1 Square Parent Delta X";
		public static string m_cGOB_BLDSTATS_GT1SQR_PRNTDELTAY_TABLE	= "GT 1 Square Parent Delta Y";
		public static string m_cGOB_BLDSTATS_FIRESANTIMATTER			= "Fires Antimatter";
		public static string m_cGOB_BLDSTATS_EXPLOSIONRADIUS			= "Explosion Radius";
		#endregion

		#region Terrain Table Constants
		public static string m_cGOB_TRNSTATS_TABLE				= "Terrain Stats";

		public static string m_cGOB_TRNSTATS_TERRAINKEY_CLMN	= "Terrain Key";
		public static string m_cGOB_TRNSTATS_GRAPHICINDEX_DIR0_CLMN	= "Graphics Index";
		public static string m_cGOB_TRNSTATS_GRAPHICINDEX_DIR1_CLMN	= "Dir 1 Terrain Key";
		public static string m_cGOB_TRNSTATS_GRAPHICINDEX_DIR2_CLMN	= "Dir 2 Terrain Key";
		public static string m_cGOB_TRNSTATS_GRAPHICINDEX_DIR3_CLMN	= "Dir 3 Terrain Key";		
		public static string m_cGOB_TRNSTATS_CANBEBUILTON_CLMN	= "Can Be Built On";
		public static string m_cGOB_TRNSTATS_CANBEDAMAGED_CLMN	= "Can Be Damaged";
		public static int m_cDEFAULTTERRAINKEY = 14;
		public static int m_cGRIDTERRAINKEY = 0;
		#endregion

		#region Gob File Constants
		public static string m_cGOB_BUILDINGIMAGE_A_TABLE = "Buildings Image A";
		public static string m_cGOB_BUILDINGIMAGE_B_TABLE = "Buildings Image B";
		public static string m_cGOB_FLATTILEIMG_A_TABLE	= "Flat File Image A";
		public static string m_cGOB_FLATTILEIMG_B_TABLE	= "Flat File Image B";
		
		public static string m_cGOB_FORMS_TABLE			= "Forms File";
		public static string m_cGOB_MISSLEIMG_TABLE		= "Missles Image";
		public static string m_cGOB_AAFIREIMG_TABLE		= "AAFire Image";
		public static string m_cGOB_SHIP_TABLE			= "MMShip Image";
		public static string m_cGOB_METALMARINE_TABLE	= "MetalMarine Image";
		public static string m_cGOB_BLDEXPLOSIONS_TABLE = "Building Explosions Image";
		public static string m_cGOB_MMEXPLOSIONS_TABLE	= "Metal Marine Explosions Image";
		public static string m_cGOB_AAEXPLOSIONS_TABLE	= "AA Explosions Image";
		public static string m_cGOB_TEAMBORDER_TABLE	= "TeamBorder";
		public static string m_cGOB_BULLETPOINT_TABLE	= "BulletPoint";
		public static string m_cGOB_MENUBORDER_UPPERLEFT_TABLE	= "Menu Border - Upper Left";
		public static string m_cGOB_MENUBORDER_LOWERLEFT_TABLE	= "Menu Border - Lower Left";
		public static string m_cGOB_MENUBORDER_UPPERRIGHT_TABLE	= "Menu Border - Upper Right";
		public static string m_cGOB_MENUBUTTONS_TABLE			= "Menu Buttons - Upper Left";
		public static string m_cGOB_SMOKEPARTICLES_TABLE		= "Smoke Particles";
		public static string m_cGOB_ANTIMATERIMGUP_TABLE		= "Anti-Matter Missle";
		public static string m_cGOB_ANTIMATERIMGDOWN_TABLE		= "Anti-Matter Missle Dropping";
		public static string m_cGOB_MMLAUNCHBAR_TABLE			= "MM Launch Bar";

		public static string m_cGOB_BAISHBORDER_UPPERLEFT_TABLE		= "BaishBorder_UpperLeft";
		public static string m_cGOB_BAISHBORDER_UPPERMIDDLE_TABLE	= "BaishBorder_UpperMiddle";
		public static string m_cGOB_BAISHBORDER_UPPERRIGHT_TABLE	= "BaishBorder_UpperRight";
		public static string m_cGOB_BAISHBORDER_MIDDLELEFT_TABLE	= "BaishBorder_MiddleLeft";
		public static string m_cGOB_BAISHBORDER_MIDDLERIGHT_TABLE	= "BaishBorder_MiddleRight";
		public static string m_cGOB_BAISHBORDER_LOWERLEFT_TABLE		= "BaishBorder_LowerLeft";
		public static string m_cGOB_BAISHBORDER_LOWERMIDDLE_TABLE	= "BaishBorder_LowerMiddle";
		public static string m_cGOB_BAISHBORDER_LOWERRIGHT_TABLE	= "BaishBorder_LowerRight";

		public static string m_cGOB_GRAYBORDER_UPPERLEFT_TABLE		= "GrayBorder_UpperLeft";
		public static string m_cGOB_GRAYBORDER_UPPERMIDDLE_TABLE	= "GrayBorder_UpperMiddle";
		public static string m_cGOB_GRAYBORDER_UPPERRIGHT_TABLE		= "GrayBorder_UpperRight";
		public static string m_cGOB_GRAYBORDER_MIDDLELEFT_TABLE		= "GrayBorder_MiddleLeft";
		public static string m_cGOB_GRAYBORDER_MIDDLERIGHT_TABLE	= "GrayBorder_MiddleRight";
		public static string m_cGOB_GRAYBORDER_LOWERLEFT_TABLE		= "GrayBorder_LowerLeft";
		public static string m_cGOB_GRAYBORDER_LOWERMIDDLE_TABLE	= "GrayBorder_LowerMiddle";
		public static string m_cGOB_GRAYBORDER_LOWERRIGHT_TABLE		= "GrayBorder_LowerRight";

		public static string m_cGOB_BACKGROUND_BLUECIRCUIT_TABLE	= "Background_BlueCircuit";
		public static string m_cGOB_BACKGROUND_INTRO_TABLE			= "Menu Background";
		public static string m_cGOB_BACKGROUND_WATERTILE_TABLE		= "Menu Water Tile";
		#endregion

		#region Sound Constants
		public static string m_cGOB_SOUND_EXPLOSION		= "Explosion";
		public static string m_cGOB_SOUND_CLICK			= "Click";
		public static string m_cGOB_SOUND_CANTBUILD		= "Can't Build";
		public static string m_cGOB_SOUND_AALAUNCH		= "AA Intercept Launch";
		public static string m_cGOB_SOUND_AAEXPLOSION	= "AA Intercept Explosion";
		public static string m_cGOB_SOUND_MISLAUNCH		= "Missle Launch";
		public static string m_cGOB_SOUND_BULLDOZE		= "Bulldoze";
		public static string m_cGOB_SOUND_ANTIMATLAUNCH	= "Anti-Matter Launch";
		public static string[] m_cGOB_SOUND_CONSTRUCTION= {"Construction 01","Construction 02","Construction 03","Construction 04","Construction 05","Construction 06"};
		public static string[] m_cGOB_SOUND_MMFIRING	= {"MM Firing 01","MM Firing 02"};
		public static string m_cGOB_SOUND_MMLAUNCH		= "MM Launch";
		public static string m_cGOB_SOUND_BASEPLACE		= "Base Place";
		public static string m_cGOB_SOUND_TYPING		= "Typing";
		public static string m_cGOB_SOUND_CHEATING		= "Cheating";
		public static string m_cGOB_SOUND_WALKONMINES	= "Walking On Mines";
		public static string m_cGOB_SOUND_TIPOPEN		= "Tip Window Opening";
		public static string m_cGOB_SOUND_TIPCLOSE		= "Tip Window Closing";		
		#endregion
						
		#region Other Constants
		public static string m_cALERT_INFO		= "i -  ";
		public static string m_cALERT_MONEY		= "$ -  ";
		public static string m_cALERT_ACTION	= "A -  ";

		public static long m_cBORDER_GRAY_LEFTWIDTH		= 15;
		public static long m_cBORDER_GRAY_RIGHTWIDTH	= 16;
		public static long m_cBORDER_GRAY_TOPWIDTH		= 10;
		public static long m_cBORDER_GRAY_BOTTOMWIDTH	= 9;

		public static long m_cBORDER_BEISH_COLORSTRIP_LEFT		= 5;
		public static long m_cBORDER_BEISH_COLORSTRIP_TOP		= 31;
		public static long m_cBORDER_BEISH_COLORSTRIP_WIDTH		= 11;
		public static long m_cBORDER_BEISH_COLORSTRIP_BOTTOM	= 27;

		public static long m_cGOB_BASE_KEY = 11;
		public static long m_cGOB_MINEFIELD_KEY = 4;
		public static long m_cGOB_UNDRCONSTR_LVL1_KEY = 15;
		public static long m_cGOB_UNDRCONSTR_LVL2_KEY = 20;
		public static long m_cGOB_UNDRCONSTR_LVL3_KEY = 21;

		public static double m_cBLDEXPLO_NUMFRAMES = 4;
		public static double m_cBLDEXPLO_TIMEPERFRAMES = .25;
		public static double m_cMMEXPLO_NUMFRAMES = 7;
		public static double m_cMMEXPLO_TIMEPERFRAMES = .25;
		public static double m_cAAEXPLO_NUMFRAMES = 7;
		public static double m_cAAEXPLO_TIMEPERFRAMES = .15;
		public static double m_cMAXMISSLEHEIGHT = 200.0;

		public static long m_cCAMO_NOCAMOBUILT = -1;
		public static long m_cCAMO_CAMODESTROYED = -10;

		public static long m_cSQRW_TRN = 62;
		public static long m_cSQRH_TRN = 32;
		public static long m_cSQRW_BLD = 62;
		public static long m_cSQRH_BLD = 48;
		public static long m_cSQRW_MM = 33;
		public static long m_cSQRH_MM = 37;
		public static long m_cSQRW_BLDEXPLO = 159;
		public static long m_cSQRH_BLDEXPLO = 127;
		public static long m_cSQRW_MMEXPLO = 64;
		public static long m_cSQRH_MMEXPLO = 62;
		public static long m_cSQRW_AAEXPLO = 82;
		public static long m_cSQRH_AAEXPLO = 84;
		public static int m_cSQRW_SMOKE = 25;
		public static int m_cSQRH_SMOKE = 30;
		public static int m_cSQRW_ANTIMATTER = 33;
		public static int m_cSQRH_ANTIMATTER = 136;
		public static int m_cSQRW_ANTIMATTERDOWN = 32;
		public static int m_cSQRH_ANTIMATTERDOWN = 48;

		public static double m_nSMOKETRAILDIFFERENCE = .03;
		public static double m_nSMOKETRAILINIT = .15;
		public static long m_nLANDCOMBAT_MAXRANGE = 4;
		public static double m_nMMDAMAGEMODIFIER = 1.0;

		public static string m_cFRM_BASEPLACE = "BasePlacementForm";
		public static string m_cFRM_PURCHASE = "PurchaseForm";
		public static string m_cFRM_TERRAIN = "TerrainForm";
		public static string m_cFRM_CONSOLE = "ConsoleForm";
		public static string m_cFRM_POPUP = "PopupForm";
		public static string m_cFRM_TEAM = "frmTeam";
		public static string m_cFRM_TEAMDERIVED = "frmTeamDerived";
		public static string m_cFRM_CREATETEAM = "frmNewTeam";
		public static string m_cFRM_GIVERES = "frmGiveResources";		
		
		public static string m_cDRAGDROP_BASE			= "DragAndDrop_PlaceBase";
		public static string m_cDRAGDROP_MISSLE			= "DragAndDrop_AttackWithMissle";
		public static string m_cDRAGDROP_MMARINE		= "DragAndDrop_AttackWithMetalMarine";
		public static string m_cDRAGDROP_METALMARINE	= "DragAndDrop_AttackWithMetalMarine";
		public static string m_cDRAGDROP_ANTIMATTER		= "DragAndDrop_AttackWithAntiMatter";
		public static string m_cDRAGDROP_TERRAIN		= "DragAndDrop_Terrain";
		public static string m_cISLANDFORMNAME			= "mnuPlayer_";
		public static string m_cEDITISLANDFORMNAME		= "mnuEditPlayer_";

		private static string m_cISLAND_SEP = "[GIS]";
		private static string m_cGAME_SEP = "[GS]";
		#endregion

		private DSSortedList m_oIslands = new DSSortedList();
		private DSSortedList m_oMissleAttacks = new DSSortedList();
		private DSSortedList m_oMMAttacks = new DSSortedList();
		private DSSortedList m_oExplosions = new DSSortedList();
		private DSSortedList m_oAIs = new DSSortedList();

		private static MetalMarines m_oApp = null;
		private static DSGameEngine m_oGameEngine = null;
		private static DSGobFile m_oGameGobFile = null;		

		private bool m_bUseBuildingRot = true;
		private string m_sIslandFileName = "";
		//private long m_nNumberOfConstructionSoundsPlaying = 0;

		//Victory conditions
		private DateTime m_dtGameEndTime = DateTime.MinValue;
		private Island m_oWinningTeam = null;

		private AlertManager m_oAlertManager = null;

		private int m_nCurrentSinglePlayerLevelID = 0;
		private int m_nNextFreeColor = 0;
		#endregion


		public Game()
		{
		}


		public void Render2DAfterForms()
		{
			string sGraphic = "";
			bool bGetsHit = false;
			long nPercentHitAt = 0;
			double nAngle = 0;
			double nPercToTarget = 0;
			Island oIsland = null;
			Island oSourceIsland = null;
			Island oMyIsland = null;
			System.Drawing.Rectangle[] oaSmokePositions = null;
			System.Drawing.Rectangle oMissleTargetPos;
			System.Drawing.Rectangle oShipTargetPos;
			System.Drawing.Rectangle oAACurPos;
			System.Drawing.Rectangle oSrcRect;
			MissleAttack oLoopAttack = null;
			structAAFire oLoopAAFire;
			LoadedTexture oShipTexture = null;
			MMAttack oLoopMMAttack = null;
			AI oAI = null;

int nTest = 0;
try{

			//Draw all our missles flying around
			for( int nLoopMissleAttack=0 ; nLoopMissleAttack<m_oMissleAttacks.Count ; nLoopMissleAttack++ )
			{
try{
				if(	nLoopMissleAttack >= m_oMissleAttacks.Count ){ break; }
				oLoopAttack = (MissleAttack)m_oMissleAttacks.GetByIndex( nLoopMissleAttack );
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}

				if( oLoopAttack.IsAntiMatter == true )
				{
					//Render our anti-matter missle
					oMissleTargetPos = GetAntiMatterMissleScreenPos( oLoopAttack,out sGraphic );
					if( oLoopAttack.PercentAlongPath < 66 )
					{
						oSrcRect = GetSrcRectForAntiMatter( DSMisc.GetRnd( 0,1 ) );
						//Make sure our height isn't to much
						oSrcRect.Height = oMissleTargetPos.Height;
					}
					else
					{
						oSrcRect = System.Drawing.Rectangle.Empty;
					}

					if( sGraphic.Length > 0 )
					{
						m_oGameEngine.RenderTexture2D( sGraphic,oSrcRect,
													oMissleTargetPos,new Vector2(),0,0,false,
													System.Drawing.Color.White.ToArgb() ); 
					}
				}
				else
				{
					oMissleTargetPos = GetMissleScreenPos( oLoopAttack,(double)oLoopAttack.PercentAlongPath / 100,ref nAngle,ref oaSmokePositions );

					//If this isn't the first missle mark it over to the side some
					oMissleTargetPos = new Rectangle( (int)( oMissleTargetPos.X - (oLoopAttack.ShotNumber-1)*10 ),
													  (int)( oMissleTargetPos.Y - (oLoopAttack.ShotNumber-1)*10 ),
													  oMissleTargetPos.Width,oMissleTargetPos.Height );

					//Did we get hit?  If so we should probabely stop rendering
					bGetsHit = oLoopAttack.GetsHit( ref nPercentHitAt );
					//Kablooey!!
					if( bGetsHit == true && nPercentHitAt < oLoopAttack.PercentAlongPath )
					{
						if( oLoopAttack.Detonated == false )
						{
							//Add explosion here and stop drawing missle
							this.AddAAExplosion( oMissleTargetPos,oLoopAttack.TargetIslandGlobalID );
							oLoopAttack.Detonated = true;

							//Reveal some of the land that blew us up
							oIsland = GetIsland( oLoopAttack.TargetIslandGlobalID );
							oSourceIsland = GetIsland( oLoopAttack.SourceIslandGlobalID );
							//Handle AIs properly too
							if( oSourceIsland.AIOwnerGlobalID != "" )
							{
								oAI = (AI)m_oAIs.GetByKey( oSourceIsland.AIOwnerGlobalID );
							}
try
{
							//Only update the island if we fired the missle or on shared vision
							oMyIsland = m_oApp.GetMyIsland().Island;
							if( oIsland != null && 
								(
									HaveSharedVision( oSourceIsland,oMyIsland ) == true ||
									(
										oSourceIsland.AIOwnerGlobalID != "" &&
										oAI != null &&
										oAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID
									)
								)
							  )
							{								
								for( int nLoopAAFireIndex=0 ; nLoopAAFireIndex<oLoopAttack.AAFire.Count ; nLoopAAFireIndex++ )
								{
									oLoopAAFire = (structAAFire)oLoopAttack.AAFire.GetByIndex( nLoopAAFireIndex );
									oIsland.UpdateSquaresICanSeeFromRefresh( oLoopAAFire.m_nSourceX,
																			 oLoopAAFire.m_nSourceY,2,
																			 oMyIsland );
								}
							}
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}
						}
					}
					else
					{
						//Render our missiles colored
						Game.RenderMissile( m_oGameEngine,oMissleTargetPos,nAngle,oLoopAttack.Color );
					}

					//Now render the smoke trails for the missle
					RenderSmokeTrails( oaSmokePositions );
nTest = 1;
					//Now render any AA fire heading our way
					for( int nLoopAAFireIndex=0 ; nLoopAAFireIndex<oLoopAttack.AAFire.Count ; nLoopAAFireIndex++ )
					{
						if(	nLoopAAFireIndex >= oLoopAttack.AAFire.Count ){ break; }
						oLoopAAFire = (structAAFire)oLoopAttack.AAFire.GetByIndex( nLoopAAFireIndex );

						//If we are past the missle hit spot, then fire our AA fire!
						if( oLoopAttack.PercentAlongPath >= oLoopAAFire.m_nPercToHitAt - 20 && 
							(
								(
									oLoopAttack.PercentAlongPath <= oLoopAAFire.m_nPercToHitAt 
										&&
									oLoopAAFire.m_bHits == true 
								)
									||
								( 
									oLoopAttack.PercentAlongPath <= oLoopAAFire.m_nPercToHitAt + 20 
										&&
									oLoopAAFire.m_bHits == false 
								) 
							)
						)
						{
							//Calculate our location
							nPercToTarget = 1 - ( ( oLoopAAFire.m_nPercToHitAt - oLoopAttack.PercentAlongPath ) / 20 );
							oAACurPos = GetAAScreenPos( oLoopAAFire,oLoopAttack,nPercToTarget,ref nAngle,ref oaSmokePositions );

							m_oGameEngine.RenderTexture2D( Game.m_cGOB_AAFIREIMG_TABLE,System.Drawing.Rectangle.Empty,
														oAACurPos,new Vector2(),nAngle,0,false,
														System.Drawing.Color.White.ToArgb() ); 
						
							//Now render the smoke trails for the AA shot
							RenderSmokeTrails( oaSmokePositions );
						}
					}
				}
			}

}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}
try
{
nTest = 2;
			//Draw all our MM
			for( int nLoopMMAttackIndex=0 ; nLoopMMAttackIndex<m_oMMAttacks.Count ; nLoopMMAttackIndex++ )
			{
try
{
				if(	nLoopMMAttackIndex >= m_oMMAttacks.Count ){ break; }
				oLoopMMAttack = (MMAttack)m_oMMAttacks.GetByIndex( nLoopMMAttackIndex );
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}

				if( oLoopMMAttack.Landed == false && oLoopMMAttack.Launched == true )
				{
					oShipTargetPos = GetMMShipScreenPos( oLoopMMAttack,(double)oLoopMMAttack.PercentAlongPath / 100,ref nAngle );

					//Did we get hit?  If so we should probabely stop rendering
					bGetsHit = oLoopMMAttack.GetsHit( ref nPercentHitAt );
					//Kablooey!!
					if( bGetsHit == true && nPercentHitAt < oLoopMMAttack.PercentAlongPath )
					{
						//Add explosion here and stop drawing ship
						MMAttackIsOver( oLoopMMAttack.GlobalID,false );
					}
					else
					{
						/*m_oGameEngine.RenderTexture2D( Game.m_cGOB_SHIP_TABLE,System.Drawing.Rectangle.Empty,
														oShipTargetPos,new Vector2(),nAngle,0,false,
														System.Drawing.Color.White.ToArgb() ); */
						//Lets try this without angles
						oShipTexture = DSResourceManager.GetGlobalInstance().GetLoadedTexture( Game.m_cGOB_SHIP_TABLE );

						oSrcRect = new Rectangle( 0,0,(int)oShipTexture.Size.X,(int)(oShipTexture.Size.Y/2.0) );
						m_oGameEngine.RenderTexture2D( Game.m_cGOB_SHIP_TABLE,oSrcRect,
														oShipTargetPos,new Vector2(),nAngle,0,false,
														System.Drawing.Color.White.ToArgb() ); 

						oSrcRect = new Rectangle( 0,(int)(oShipTexture.Size.Y/2.0),(int)oShipTexture.Size.X,(int)(oShipTexture.Size.Y/2.0) );
						oSourceIsland = GetIsland( oLoopMMAttack.SourceIslandGlobalID  );
						m_oGameEngine.RenderTexture2D( Game.m_cGOB_SHIP_TABLE,oSrcRect,
														oShipTargetPos,new Vector2(),nAngle,0,false,
														oSourceIsland.Color ); 
					}


					//Now render any AA fire heading our way
					for( int nLoopAAFireIndex=0 ; nLoopAAFireIndex<oLoopMMAttack.AAFire.Count ; nLoopAAFireIndex++ )
					{
oLoopAAFire = new structAAFire();
try{
						if(	nLoopAAFireIndex >= oLoopMMAttack.AAFire.Count ){ break; }
						oLoopAAFire = (structAAFire)oLoopMMAttack.AAFire.GetByIndex( nLoopAAFireIndex );

}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}

						//If we are past the missle hit spot, then fire our AA fire!
						if( oLoopMMAttack.PercentAlongPath >= oLoopAAFire.m_nPercToHitAt - 20 && 
							(
								oLoopMMAttack.PercentAlongPath <= oLoopAAFire.m_nPercToHitAt ||
								( 
									oLoopMMAttack.PercentAlongPath <= oLoopAAFire.m_nPercToHitAt - 20 
									&&
									oLoopAAFire.m_bHits == false 
								) 
							)
						  )
						{
							//Calculate our location
							nPercToTarget = 1 - ( ( oLoopAAFire.m_nPercToHitAt - oLoopMMAttack.PercentAlongPath ) / 20 );
							oAACurPos = GetAAScreenPos( oLoopAAFire,oLoopMMAttack,nPercToTarget,ref nAngle,ref oaSmokePositions );

							m_oGameEngine.RenderTexture2D( Game.m_cGOB_AAFIREIMG_TABLE,System.Drawing.Rectangle.Empty,
															oAACurPos,new Vector2(),nAngle,0,false,
															System.Drawing.Color.White.ToArgb() ); 

							//Now render the smoke trails for the AA shot
							RenderSmokeTrails( oaSmokePositions );
						}
					}
				}
			}

			RenderExplosions();
			m_oAlertManager.RenderAfterForms();
}
catch( System.Exception oEx )
{
	DSMisc.ShowErrors( oEx );
}
		}
		private void RenderExplosions()
		{
			long nFrame = 0;
			string sImage = "";
			System.Drawing.Rectangle oPos = new Rectangle();
			System.Drawing.Rectangle oClipRegion = System.Drawing.Rectangle.Empty;
			System.Drawing.Rectangle oExploSrc = new Rectangle();
			Explosion oLoopExplosion = null;


			//Walk through all our attacks in progress
			for( int nLoopExploIndex=0 ; nLoopExploIndex<m_oExplosions.Count ; nLoopExploIndex++ )
			{
				oLoopExplosion = (Explosion)m_oExplosions.GetByIndex( nLoopExploIndex );

				//Is this the island its on and has it landed?
				if( oLoopExplosion.ExplosionType == enumExplosionType.AAShot )
				{
					nFrame = (long)( oLoopExplosion.TimeIntoExplosion / Game.m_cAAEXPLO_TIMEPERFRAMES );
					sImage = Game.m_cGOB_AAEXPLOSIONS_TABLE;
					//Get our graphic source size
					oExploSrc = Game.GetSrcRectForAAExplosion( nFrame );
				}
				else
				{
					continue;
				}


				//Get our explosion pos
				oPos = new Rectangle( (int)oLoopExplosion.PosX - (int)((double)Game.m_cSQRW_AAEXPLO/2.0),
									  (int)oLoopExplosion.PosY - (int)((double)Game.m_cSQRH_AAEXPLO/2.0),0,0 );
				//Adjust for our location
				oPos.Width = oExploSrc.Width;
				oPos.Height = oExploSrc.Height;

				//Now draw the explosion
				m_oGameEngine.RenderTexture2D( sImage, oExploSrc, 
								oPos, new Vector2( 0,0 ), 0, 0, false, 
								System.Drawing.Color.White.ToArgb() );
			}		
		}
		private void RenderSmokeTrails( System.Drawing.Rectangle[] oaSmokePosition )
		{
			System.Drawing.Rectangle oSmokeTargetPos = System.Drawing.Rectangle.Empty;
			System.Drawing.Rectangle oSrcRect = System.Drawing.Rectangle.Empty;
			long nPos = 0;
			double nAngle = 0;


			for( int i=0 ; i<8 ; i++ )
			{
				if( oaSmokePosition[ i ] != System.Drawing.Rectangle.Empty )
				{
					nAngle = DSMisc.GetRnd() * Math.PI*2;
					if( i == 0 )	 { nPos = 0; }
					else if( i == 1 ){ nPos = 1; }
					else			 { nPos = 2; }
					oSrcRect = new System.Drawing.Rectangle( 1,(int)( 1+nPos*31 ),
															 m_cSQRW_SMOKE,m_cSQRH_SMOKE );

					oSmokeTargetPos = new Rectangle( (int)( oaSmokePosition[ i ].X ),
													 (int)( oaSmokePosition[ i ].Y ),
													 m_cSQRW_SMOKE,m_cSQRH_SMOKE );

					//Adjust it to line up with the missle
					oSmokeTargetPos.X -= m_cSQRW_SMOKE / 2;
					oSmokeTargetPos.Y -= m_cSQRH_SMOKE / 2;

					m_oGameEngine.RenderTexture2D( Game.m_cGOB_SMOKEPARTICLES_TABLE,oSrcRect,oSmokeTargetPos,
										new Vector2( m_cSQRW_SMOKE/2,m_cSQRH_SMOKE/2 ),
										nAngle,0,false,System.Drawing.Color.White.ToArgb() ); 
				}
			}
		}

		private Point GetIslandFormLocation( string sIslandGlobalID,long nX,long nY )
		{
			Point oPos;
			System.Drawing.Rectangle oScrPos;
			System.Drawing.Rectangle oFormPos;
			MMIslandForm oIslandForm = null;


			oIslandForm = m_oApp.GetIsland( sIslandGlobalID );
			oScrPos = oIslandForm.GetScrTerrainPosFromSquare( (int)nX,(int)nY ); 
			oFormPos = oIslandForm.GetWindowRenderArea();

			//Ok here is where the missle came from... 
			oPos = new Point( (int)( oScrPos.X + ( oScrPos.Width / 2.0 ) ),
							  (int)( oScrPos.Y + ( oScrPos.Height / 2.0 ) ) );
			//Is it off screen?
			oPos = new Point( DSMisc.Between( oFormPos.X,oPos.X,oFormPos.Right ),
							  DSMisc.Between( oFormPos.Y,oPos.Y,oFormPos.Bottom ) );

			return( oPos );
		}
		private Point GetIslandFormAttackLocation( string sIslandGlobalID,long nX,long nY )
		{
			Point oPos;
			System.Drawing.Rectangle oScrPos;
			System.Drawing.Rectangle oFormPos;
			MMIslandForm oIslandForm = null;


			oIslandForm = m_oApp.GetIsland( sIslandGlobalID );
			oScrPos = oIslandForm.GetScrTerrainPosFromSquare( (int)nX,(int)nY ); 
			oFormPos = oIslandForm.GetWindowRenderArea();

			//Ok here is where the missle came from... 
			oPos = new Point( (int)( oScrPos.X + ( oScrPos.Width / 2.0 ) ),
							  (int)( oScrPos.Y + ( oScrPos.Height / 2.0 ) ) );
			//Is it off screen?
			oPos = new Point( DSMisc.Between( oFormPos.X,oPos.X,oFormPos.Right ),
							  DSMisc.Between( oFormPos.Y,oPos.Y,oFormPos.Bottom ) );

			return( oPos );
		}


		//Missle is pointing to the right at 90 degrees and up at zero degrees
		public System.Drawing.Rectangle GetMissleScreenPos( MissleAttack oMissleAttack,double nPercToTarget,ref double nAngleOfMissle,ref System.Drawing.Rectangle[] oaSmokePositions )
		{
			/*System.Drawing.Rectangle oRetVal;
			System.Drawing.Point oStartPos;
			System.Drawing.Point oStopPos;
			double nTempAngle = 0;
			double nTemp = 0;


			oStartPos = GetIslandFormLocation( oMissleAttack.SourceIslandGlobalID,oMissleAttack.SourceX,oMissleAttack.SourceY );
			oStopPos = GetIslandFormLocation( oMissleAttack.TargetIslandGlobalID,oMissleAttack.TargetX,oMissleAttack.TargetY );
			oRetVal = GetScreenPos( oStartPos,oStopPos,nPercToTarget,ref nAngleOfMissle );

			//Return the smoke locations
			oaSmokePositions = new System.Drawing.Rectangle[ 8 ];
			for( int i=0 ; i<8 ; i++ )
			{
				nTemp = nPercToTarget - i * m_nSMOKETRAILDIFFERENCE - m_nSMOKETRAILINIT;
				if( nTemp > 0 )
				{
					oaSmokePositions[ i ] = GetScreenPos( oStartPos,oStopPos,nTemp,ref nTempAngle );
				}
				else
				{
					oaSmokePositions[ i ] = System.Drawing.Rectangle.Empty;
				}
			}

			return( oRetVal );*/

			System.Drawing.Rectangle oRetVal = System.Drawing.Rectangle.Empty;
			System.Drawing.Rectangle oFormPos = System.Drawing.Rectangle.Empty;
			System.Drawing.Point oStartPos = System.Drawing.Point.Empty;
			System.Drawing.Point oMidPos = System.Drawing.Point.Empty;
			System.Drawing.Point oStopPos = System.Drawing.Point.Empty;
			double nTempAngle = 0;
			double nTemp = 0;
			double nTempPerc = 0;
			Island oTargetIsland = null;
			MMIslandForm oIslandForm = null;


			oStartPos = GetIslandFormLocation( oMissleAttack.SourceIslandGlobalID,oMissleAttack.SourceX,oMissleAttack.SourceY );
			oTargetIsland = GetIsland( oMissleAttack.TargetIslandGlobalID );
			oStopPos = GetIslandFormLocation( oMissleAttack.TargetIslandGlobalID,oMissleAttack.TargetX,oMissleAttack.TargetY );


			//Find our island and locate the lower right corner
			oIslandForm = m_oApp.GetIsland( oMissleAttack.TargetIslandGlobalID );
			oFormPos = oIslandForm.GetWindowRenderArea();
			//Now find out where our arrows are and set our end point to that
			oMidPos = oTargetIsland.GetArrowCorner();
			if( oMidPos.X != 0 ){ oMidPos.X = 1; }
			if( oMidPos.Y != 0 ){ oMidPos.Y = 1; }
			oMidPos = new Point( oMidPos.X * oFormPos.Width + oFormPos.Left,
								oMidPos.Y * oFormPos.Height + oFormPos.Top );
			

			//Now, the first 60% is getting to the island
			if( nPercToTarget < .6 )
			{
				nTempPerc = nPercToTarget / .6;
				oRetVal = GetScreenPos( oStartPos,oMidPos,nTempPerc,ref nAngleOfMissle );
			}
			else
			{
				nTempPerc = ( nPercToTarget - .6 ) / .4;
				oRetVal = GetScreenPos( oMidPos,oStopPos,nTempPerc,ref nAngleOfMissle );
			}

			//Return the smoke locations
			oaSmokePositions = new System.Drawing.Rectangle[ 8 ];
			for( int i=0 ; i<8 ; i++ )
			{
				oaSmokePositions[ i ] = System.Drawing.Rectangle.Empty;
				if( nPercToTarget < .6 )
				{
					nTemp = nTempPerc - i * m_nSMOKETRAILDIFFERENCE - m_nSMOKETRAILINIT;
					if( nTemp > 0 )
					{
						oaSmokePositions[ i ] = GetScreenPos( oStartPos,oMidPos,nTemp,ref nTempAngle );
					}
				}
				else
				{
					nTemp = nTempPerc - i * m_nSMOKETRAILDIFFERENCE - m_nSMOKETRAILINIT;
					if( nTemp > 0 )
					{
						oaSmokePositions[ i ] = GetScreenPos( oMidPos,oStopPos,nTemp,ref nTempAngle );
					}
				}
			}

			return( oRetVal );
		}
		//Missle is pointing to the right at 90 degrees and up at zero degrees
		public System.Drawing.Rectangle GetAntiMatterMissleScreenPos( MissleAttack oMissleAttack,out string sGraphic )
		{
			double nHeightToTravel = 0;
			System.Drawing.Rectangle oRetVal = System.Drawing.Rectangle.Empty;
			System.Drawing.Point oStartPos;
			System.Drawing.Point oStopPos;


			sGraphic = "";
			oStartPos = GetIslandFormLocation( oMissleAttack.SourceIslandGlobalID,oMissleAttack.SourceX,oMissleAttack.SourceY );
			oStopPos = GetIslandFormLocation( oMissleAttack.TargetIslandGlobalID,oMissleAttack.TargetX,oMissleAttack.TargetY );
	
			//If the % is less than 33 than we are traveling up
			if( oMissleAttack.PercentAlongPath < 33 )
			{
				sGraphic = Game.m_cGOB_ANTIMATERIMGUP_TABLE;

				nHeightToTravel = oStartPos.Y + Game.m_cSQRH_ANTIMATTER;
				
				oRetVal = new Rectangle( (int)( oStartPos.X - Game.m_cSQRW_ANTIMATTER / 2.0 ),
										 (int)( oStartPos.Y - nHeightToTravel * ( oMissleAttack.PercentAlongPath / 33.0 ) ),
										 (int)( Game.m_cSQRW_ANTIMATTER ),0 );

				oRetVal.Height = DSMisc.Min( oStartPos.Y - oRetVal.Y,Game.m_cSQRH_ANTIMATTER );
			}
			else if( oMissleAttack.PercentAlongPath > 66 )
			{
				sGraphic = Game.m_cGOB_ANTIMATERIMGDOWN_TABLE;

				nHeightToTravel = oStopPos.Y + Game.m_cSQRH_ANTIMATTERDOWN;
				
				oRetVal = new Rectangle( (int)( oStopPos.X - Game.m_cSQRW_ANTIMATTERDOWN / 2.0 ),
										 (int)( nHeightToTravel * ( ( oMissleAttack.PercentAlongPath - 66.0 ) / 33.0 ) - Game.m_cSQRH_ANTIMATTERDOWN ),
										 Game.m_cSQRW_ANTIMATTERDOWN,Game.m_cSQRH_ANTIMATTERDOWN );
			}
			else
			{
				//Render nothing... the missle isn't visible
				oRetVal = new Rectangle( 0,0,0,0 );
			}

			return( oRetVal );
		}

		private System.Drawing.Rectangle GetMMShipScreenPos( MMAttack oMMAttack,double nPercToTarget,ref double nAngleOfShip )
		{
			System.Drawing.Rectangle oRetVal;
			System.Drawing.Point oStartPos;
			System.Drawing.Point oStopPos;


			oStartPos = GetIslandFormLocation( oMMAttack.SourceIslandGlobalID,oMMAttack.SourceX,oMMAttack.SourceY );
			oStopPos = GetIslandFormLocation( oMMAttack.TargetIslandGlobalID,oMMAttack.TargetX,oMMAttack.TargetY );
			oRetVal = GetScreenPos( oStartPos,oStopPos,nPercToTarget,ref nAngleOfShip );


			return( oRetVal );
		}
		private System.Drawing.Rectangle GetScreenPos( System.Drawing.Point oStartPos,System.Drawing.Point oStopPos,
													   double nPercToTarget,ref double nAngleOfItem )
		{
			System.Drawing.Rectangle oRetVal;
			double nX = 0, nY = 0;
			double nA = 0, nB = 0;
			double nAngle = 0;
			double nStartX = 0, nStartY = 0;


			nA = Math.Abs( oStartPos.X - oStopPos.X );
			nB = Math.Abs( oStartPos.Y - oStopPos.Y );

			//Lower left firing at upper right
			if( oStartPos.X <= oStopPos.X && oStartPos.Y >= oStopPos.Y )
			{
				nAngle = Math.PI - ( Math.PI / 2 ) * nPercToTarget;
				nAngleOfItem = Math.PI - nAngle;
				nStartX = oStopPos.X;
				nStartY = oStartPos.Y;
			}
			//Lower right firing at upper left
			else if( oStartPos.X >= oStopPos.X && oStartPos.Y >= oStopPos.Y )
			{
				nAngle = ( Math.PI / 2 ) * nPercToTarget;
				nAngleOfItem = Math.PI * 2 - nAngle;
				nStartX = oStopPos.X;
				nStartY = oStartPos.Y;
			}
			//Upper left firing at lower right
			else if( oStartPos.X <= oStopPos.X && oStartPos.Y <= oStopPos.Y )
			{
				nAngle = ( Math.PI / 2 ) - ( Math.PI / 2 ) * nPercToTarget;
				nAngleOfItem = Math.PI - nAngle;
				nStartX = oStartPos.X;
				nStartY = oStopPos.Y;
			}
			//Upper Right firing at lower left
			else if( oStartPos.X >= oStopPos.X && oStartPos.Y <= oStopPos.Y )
			{
				nAngle = ( Math.PI / 2 ) + ( Math.PI / 2 ) * nPercToTarget;
				nAngleOfItem = Math.PI * 1.5 - ( nAngle - ( Math.PI / 2 ) );
				nStartX = oStartPos.X;
				nStartY = oStopPos.Y;
			}

			nX = nStartX + nA * Math.Cos( nAngle );
			nY = nStartY - nB * Math.Sin( nAngle );
			oRetVal = new System.Drawing.Rectangle( (int)nX,(int)nY,0,0 );


			return( oRetVal );
		}
		private System.Drawing.Rectangle GetAAScreenPos( structAAFire oAAFire,MissleAttack oMissleAttack,double nPercToTarget,ref double nAngleOfAA,ref System.Drawing.Rectangle[] oaSmokePositions )
		{
			System.Drawing.Rectangle oRetVal;
			System.Drawing.Point oStartPos;
			System.Drawing.Point oStopPos;
			System.Drawing.Rectangle oMissleTargetPos;
			double nAngle = 0;
			double nTemp = 0;
			double nTempAngle = 0;


			//Calculate our start location
			oStartPos = GetIslandFormLocation( oMissleAttack.TargetIslandGlobalID,oAAFire.m_nSourceX,oAAFire.m_nSourceY );

			//Calculate our end position
			oMissleTargetPos = GetMissleScreenPos( oMissleAttack,(double)oAAFire.m_nPercToHitAt / 100,ref nAngle,ref oaSmokePositions );
			//Vary it some
			oStopPos = new Point( (int)( oMissleTargetPos.X + oAAFire.m_nXDeviation ), 
								  (int)( oMissleTargetPos.Y + oAAFire.m_nYDeviation ) );

			//Get our final render location
			oRetVal = new System.Drawing.Rectangle( (int)( ( oStopPos.X - oStartPos.X ) * nPercToTarget + oStartPos.X ),
													(int)( ( oStopPos.Y - oStartPos.Y ) * nPercToTarget + oStartPos.Y ), 0,0 );

			//And finally calculate our angle
			nAngleOfAA = DSMath.CalculateRadAngle( oStartPos.X,oStartPos.Y,oStopPos.X,oStopPos.Y ) + ( Math.PI / 2.0 );

			
			//Return the smoke locations
			oaSmokePositions = new System.Drawing.Rectangle[ 8 ];
			for( int i=0 ; i<8 ; i++ )
			{
				nTemp = nPercToTarget - i * m_nSMOKETRAILDIFFERENCE - m_nSMOKETRAILINIT;
				if( nTemp > 0 )
				{
					oaSmokePositions[ i ] = GetScreenPos( oStartPos,oStopPos,nTemp,ref nTempAngle );
				}
				else
				{
					oaSmokePositions[ i ] = GetScreenPos( oStartPos,oStopPos,0,ref nTempAngle );
				}
			}

			return( oRetVal );
		}
		private System.Drawing.Rectangle GetAAScreenPos( structAAFire oAAFire,MMAttack oMMAttack,double nPercToTarget,ref double nAngleOfAA,ref System.Drawing.Rectangle[] oaSmokePositions )
		{
			System.Drawing.Rectangle oRetVal;
			System.Drawing.Point oStartPos;
			System.Drawing.Point oStopPos;
			System.Drawing.Rectangle oMMShipTargetPos;
			double nAngle = 0;
			double nTemp = 0;
			double nTempAngle = 0;


			//Calculate our start location
			oStartPos = GetIslandFormLocation( oMMAttack.TargetIslandGlobalID,oAAFire.m_nSourceX,oAAFire.m_nSourceY );

			//Calculate our end position
			oMMShipTargetPos = GetMMShipScreenPos( oMMAttack,(double)oAAFire.m_nPercToHitAt / 100,ref nAngle );
			//Vary it some
			oStopPos = new Point(	(int)( oMMShipTargetPos.X + oAAFire.m_nXDeviation ), 
									(int)( oMMShipTargetPos.Y + oAAFire.m_nYDeviation ) );

			//Get our final render location
			oRetVal = new System.Drawing.Rectangle( (int)( ( oStopPos.X - oStartPos.X ) * nPercToTarget + oStartPos.X ),
													(int)( ( oStopPos.Y - oStartPos.Y ) * nPercToTarget + oStartPos.Y ), 0,0 );

			//And finally calculate our angle
			nAngleOfAA = DSMath.CalculateRadAngle( oStartPos.X,oStartPos.Y,oStopPos.X,oStopPos.Y );
			
			//Return the smoke locations
			oaSmokePositions = new System.Drawing.Rectangle[ 8 ];
			for( int i=0 ; i<8 ; i++ )
			{
				nTemp = nPercToTarget - i * m_nSMOKETRAILDIFFERENCE - m_nSMOKETRAILINIT;
				if( nTemp > 0 )
				{
					oaSmokePositions[ i ] = GetScreenPos( oStartPos,oStopPos,nTemp,ref nTempAngle );
				}
				else
				{
					oaSmokePositions[ i ] = GetScreenPos( oStartPos,oStopPos,0,ref nTempAngle );
				}
			}

			return( oRetVal );
		}


		public void Advance( double nElapsedTime )
		{
			DSSortedList oBuildingsDamaged = null;
			DSSortedList oCompositeStruct = null;
			structAAFire oLoopAAFire;
			MissleAttack oLoopMisAttack = null;
			MMAttack oLoopMMAttack = null;
			Island oIsland = null;
			Island oLoopIsland = null;
			Island oTargetIsland = null;
			Island oSourceIsland = null;
			Island oMyIsland = null;
			Explosion oLoopExplosion = null;
			Square oSquare = null;
			System.Drawing.Point[] oaMissleRadius = null;
			string sGlobalID = "";
			string sExplosionGlobalID = "";
			long nPercentHitAt = -1;
			double nEploLen = 0;
			AI oSourceAI = null;
			AI oTargetAI = null;
			AI oAI = null;
			bool bFound = false;
			int nKey = 0;
			Point oEndPt = Point.Empty;


			//Advance our messages
			m_oAlertManager.Advance( nElapsedTime );

			//Now write all the island information
			for( int nLoopIslandIndex=0 ; nLoopIslandIndex<m_oIslands.Count ; nLoopIslandIndex++ )
			{
				oLoopIsland = (Island)m_oIslands.GetByIndex( nLoopIslandIndex );
				oLoopIsland.Advance( nElapsedTime );

				//Process this AI
				if( oLoopIsland.AIOwnerGlobalID != "" && oLoopIsland.ImDead == false )
				{
					//Am I the owner of this AI?
					oAI = (AI)m_oAIs.GetByKey( oLoopIsland.AIOwnerGlobalID );
					if( oAI != null &&
						(
							oAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID ||
							m_oGameEngine.DirectPlay.IAmTheHost == true 
						)
					  )
					{
						//Ok process the AI
						oAI.ManagerGlobalID = m_oApp.MyIslandsGlobalID;
						oAI.Advance( nElapsedTime,oLoopIsland );
					}
				}
			}

			//Go through and advance all the missle attacks
			for( int i=0 ; i<m_oMissleAttacks.Count ; i++ )
			{
				if( i > m_oMissleAttacks.Count )
				{
					break;
				}
				oLoopMisAttack = (MissleAttack)m_oMissleAttacks.GetByIndex( i );


				if( oLoopMisAttack.SoundIndex == -1 )
				{
					if( oLoopMisAttack.IsAntiMatter == false )
					{
						oLoopMisAttack.SoundIndex = m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_MISLAUNCH );
					}
					else
					{
						oLoopMisAttack.SoundIndex = m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_ANTIMATLAUNCH );
					}

					//If we tried to play a sound and coulden't then don't try again in the future
					if( oLoopMisAttack.SoundIndex == -1 )
					{
						oLoopMisAttack.SoundIndex = -2;
					}
				}

				oLoopMisAttack.Advance( nElapsedTime );
				if( oLoopMisAttack.PercentAlongPath > 100 )
				{
					//Damage everything at the target location if its my island and a hit
					if( //oLoopMisAttack.TargetIslandSocketID == m_oGameEngine.DirectPlay.Me.SocketID &&
						oLoopMisAttack.GetsHit( ref nPercentHitAt ) == false )
					{
						oIsland = GetIsland( oLoopMisAttack.TargetIslandGlobalID );
						oaMissleRadius = GetSquaresInRadius( oLoopMisAttack.ExplosionRadius,oLoopMisAttack.TargetX,oLoopMisAttack.TargetY );

						//Add one explosion for the missle itself, if its not anti-matter
						if( oLoopMisAttack.IsAntiMatter == false )
						{
							sExplosionGlobalID = AddExplosion( "",oLoopMisAttack.TargetIslandGlobalID,oLoopMisAttack.TargetX,oLoopMisAttack.TargetY,enumExplosionType.Building,false,true );
						}

						/////////////////////////////////////////////////////////////////////////////
						//Damage our building... but only if it hasn't been damaged yet
						/////////////////////////////////////////////////////////////////////////////
						oBuildingsDamaged = new DSSortedList();
						nKey = 0;
						for( long nSqrNdx=0 ; nSqrNdx<oaMissleRadius.Length ; nSqrNdx++ )
						{
							oSquare = oIsland.GetSquare( oaMissleRadius[ nSqrNdx ].X,oaMissleRadius[ nSqrNdx ].Y );
							if( oSquare != null )
							{
								//Is this a composite structure and has it already been damaged?
								bFound = false;
								for( int nDmgdSqrsIdx=0 ; nDmgdSqrsIdx<oBuildingsDamaged.Count ; nDmgdSqrsIdx++ )
								{
									oEndPt = (Point)oBuildingsDamaged.GetByIndex( nDmgdSqrsIdx );
									if( oSquare.X == oEndPt.X && oSquare.Y == oEndPt.Y )
									{
										bFound = true;
									}
								}

								//Did we find it?  Have we damaged it yet?
								if( bFound == false )
								{
									oCompositeStruct = oIsland.GetCompositeBuilding( oSquare.X,oSquare.Y );
									for( int nIndex=0 ; nIndex<oCompositeStruct.Count ; nIndex++ )
									{
										oBuildingsDamaged.Add( nKey.ToString(),oCompositeStruct.GetByIndex( nIndex ) );
										nKey++;
									}
									oSquare.DamageSquare( oLoopMisAttack.Offensive,oLoopMisAttack.TargetIslandGlobalID,sExplosionGlobalID,oLoopMisAttack.IsAntiMatter );
								}
							}
						}
						/////////////////////////////////////////////////////////////////////////////

						//Get the AIs
						oSourceIsland = GetIsland( oLoopMisAttack.SourceIslandGlobalID );
						if( oIsland.AIOwnerGlobalID != "" )
						{
							oTargetAI = (AI)m_oAIs.GetByKey( oIsland.AIOwnerGlobalID );
						}
						if( oSourceIsland.AIOwnerGlobalID != "" )
						{
							oSourceAI = (AI)m_oAIs.GetByKey( oSourceIsland.AIOwnerGlobalID );
						}

						//If the source and target are both controled by me update the source
						oMyIsland = m_oApp.GetMyIsland().Island;
						if( 
							(
								HaveSharedVision( oSourceIsland,oMyIsland ) == true ||
								//oSourceIsland.GlobalID == m_oApp.MyIslandsGlobalID ||
								(
									oSourceAI != null && oSourceAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID 
								)
							)
							&&
							(
								oIsland.GlobalID == m_oApp.MyIslandsGlobalID ||
								(
									oTargetAI != null && oTargetAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID 
								)
							)
						  )
						{
							oIsland.UpdateSquaresICanSeeFromRefresh( oLoopMisAttack,oMyIsland );
							oIsland.UpdateSquaresICanSeeFromRefresh( oLoopMisAttack,oSourceIsland );
						}
						//Send update if the target island is me or an AI I control
						if( oLoopMisAttack.TargetIslandGlobalID == m_oApp.MyIslandsGlobalID ||
							(
								oTargetAI != null &&
								oTargetAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID
							)
						  )
						{
							m_oApp.SendIslandUpdate( oIsland,oLoopMisAttack );							
						}
					}

					m_oMissleAttacks.RemoveAt( i );
				}
				else
				{
					for( int nLoopAAFireIndex=0 ; nLoopAAFireIndex<oLoopMisAttack.AAFire.Count ; nLoopAAFireIndex++ )
					{
						oLoopAAFire = (structAAFire)oLoopMisAttack.AAFire.GetByIndex( nLoopAAFireIndex );

						if( oLoopAAFire.m_bLaunchSoundFired == false &&
							oLoopMisAttack.PercentAlongPath >= oLoopAAFire.m_nPercToHitAt - 20 )
						{
							oLoopAAFire.m_bLaunchSoundFired = true;
							sGlobalID = (string)oLoopMisAttack.AAFire.GetKey( nLoopAAFireIndex );
							oLoopMisAttack.AAFire.RemoveAt( nLoopAAFireIndex );
							oLoopMisAttack.AAFire.Add( sGlobalID,oLoopAAFire );

							m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_AALAUNCH );
						}
					}
				}
			}

			//Go through and advance all the MM attacks that are on our island 
			for( int i=0 ; i<m_oMMAttacks.Count ; i++ )
			{
				if( i > m_oMMAttacks.Count )
				{
					break;
				}
				oLoopMMAttack = (MMAttack)m_oMMAttacks.GetByIndex( i );

				//Move us along our attack route
				oTargetIsland = GetIsland( oLoopMMAttack.TargetIslandGlobalID );
				if( oTargetIsland == null )
				{
					MMAttackIsOver( oLoopMMAttack.GlobalID,true );
					continue;
				}
				oLoopMMAttack.Advance( nElapsedTime,oTargetIsland );

				//Get our AI if this is an AI controlled island
				if( oTargetIsland.AIOwnerGlobalID != "" )
				{
					oAI = (AI)m_oAIs.GetByKey( oTargetIsland.AIOwnerGlobalID );
				}
				//Check for a dead MM
				if( 
					oLoopMMAttack.TargetIslandGlobalID == m_oApp.MyIslandsGlobalID ||
					(
						oTargetIsland.AIOwnerGlobalID != "" &&
						oAI != null &&
						oAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID
					)
				   )
				{
					//Transmit the attack?
					if( oLoopMMAttack.LastSent + 2 <= m_oGameEngine.ElapsedTime ||
						//oLoopMMAttack.Landed == true && oLoopMMAttack.PercentAlongPath > 100 ||
						oLoopMMAttack.MMStrength <= 0 )
					{
						//Send an update of this MM attack back to the sender
						oLoopMMAttack.LastSent = m_oGameEngine.ElapsedTime;
						m_oApp.SendMMAttackUpdate( oLoopMMAttack );					
						m_oApp.SendIslandUpdate( oTargetIsland );
					}

					//Cancel our attack if neccessary
					if( oLoopMMAttack.Landed == true && 
						(
							oLoopMMAttack.PercentAlongPath > 100 ||
							oLoopMMAttack.PercSinceLastActed > 10 
						)
					  )
					{
						m_oApp.SendMMHome( oLoopMMAttack,true );
						MMAttackIsOver( oLoopMMAttack.GlobalID,true );
					}
					else if( oLoopMMAttack.MMStrength <= 0 )
					{
						m_oApp.SendMMHome( oLoopMMAttack,false );
						MMAttackIsOver( oLoopMMAttack.GlobalID,false );
					}
				}


				oSourceIsland = GetIsland( oLoopMMAttack.SourceIslandGlobalID );
				if( oSourceIsland.AIOwnerGlobalID != "" )
				{
					oAI = (AI)m_oAIs.GetByKey( oSourceIsland.AIOwnerGlobalID );
				}
				oMyIsland = m_oApp.GetMyIsland().Island;
                if( HaveSharedVision( oSourceIsland,oMyIsland ) == true ||
					(
						oSourceIsland.AIOwnerGlobalID != "" &&
						oAI != null &&
						oAI.ManagerGlobalID == m_oApp.MyIslandsGlobalID 
					)
				  )
				{
					oIsland = GetIsland( oLoopMMAttack.TargetIslandGlobalID );
					if( oIsland != null )
					{
						oIsland.UpdateSquaresICanSeeFromRefresh( oLoopMMAttack,oMyIsland );
						oIsland.UpdateSquaresICanSeeFromRefresh( oLoopMMAttack,oSourceIsland );
					}
				}
			}

			//Advance our explosions
			for( int nExploIndx=0 ; nExploIndx<m_oExplosions.Count ; nExploIndx++ )
			{
				if( nExploIndx > m_oExplosions.Count )
				{
					break;
				}

				oLoopExplosion = (Explosion)m_oExplosions.GetByIndex( nExploIndx );
				oLoopExplosion.Advance( nElapsedTime,m_oExplosions,m_oGameEngine );

				if( oLoopExplosion.ExplosionType == enumExplosionType.Building )
				{
					nEploLen = m_cBLDEXPLO_NUMFRAMES * m_cBLDEXPLO_TIMEPERFRAMES;
				}
				else
				{
					nEploLen = m_cMMEXPLO_NUMFRAMES * m_cMMEXPLO_TIMEPERFRAMES;
				}

				if( oLoopExplosion.TimeIntoExplosion > nEploLen )
				{
					m_oExplosions.RemoveAt( nExploIndx );
				}
			}
		}

		public bool GameIsOver()
		{
			bool bGameIsOver = true;
			Island oWinners = null;
			Island oLoopIsland = null;
			Island oLoopEnemySearch = null;


			//Walk every player and see if he has any enemies left
			for( int nLoopIslandIndex=0 ; nLoopIslandIndex<m_oIslands.Count ; nLoopIslandIndex++ )
			{
				oLoopIsland = (Island)m_oIslands.GetByIndex( nLoopIslandIndex );

				if( oLoopIsland.ImDead == false )
				{
					oWinners = oLoopIsland;

					//So is the game over?
					if( m_cAUTOWINFORONEPLAYER == false && m_oIslands.Count == 1 )
					{
						bGameIsOver = false;
					}

					//Now check, does this guy have any enemies left?
					for( int nLoopEnemySearchIndex=0 ; nLoopEnemySearchIndex<m_oIslands.Count ; nLoopEnemySearchIndex++ )
					{
						oLoopEnemySearch = (Island)m_oIslands.GetByIndex( nLoopEnemySearchIndex );
						//Is he my ally? 
						if( OnSameTeam( oLoopIsland,oLoopEnemySearch ) == true )
						{
							//If he is we don't care, all we care about is enemies
						}
						//This means we have an enemy left, so the game is not over
						else if( oLoopEnemySearch.ImDead == false )
						{
							bGameIsOver = false;
						}
					}
				}
				if( oLoopIsland.BasesPlaced == false )
				{
					bGameIsOver = false;
				}
			}

			//So is the game over?
			if( bGameIsOver == true && m_dtGameEndTime == DateTime.MinValue )
			{
				m_dtGameEndTime = DateTime.Now;
				m_oWinningTeam = oWinners;
			}

			return( bGameIsOver );
		}


		public void MMAttackIsOver( string sMMGlobalID,bool bMMSurvived )
		{
			System.Drawing.Rectangle oMMPos = System.Drawing.Rectangle.Empty;
			MMAttack oMMAttack = null;
			MMIslandForm oTargetMMIslandForm = null;
			Island oIsland = null;
			Square oSquare = null;
			long nPercentHitAt = 0;
			double nAngle = 0;


			oMMAttack = (MMAttack)this.MetalMarineAttacks.GetByKey( sMMGlobalID );
			if( oMMAttack != null )
			{
				//Play the sound and draw our explosion
				if( oMMAttack.Launched == true && oMMAttack.Landed == false )
				{
					oMMAttack.GetsHit( ref nPercentHitAt );
					oMMPos = GetMMShipScreenPos( oMMAttack,(double)nPercentHitAt / 100,ref nAngle );
					oMMPos = new Rectangle( (int)( oMMPos.X - 20 ),
										    (int)( oMMPos.Y + 20 ),
										    oMMPos.Width,oMMPos.Height );
				}
				else
				{
					//We need our island!
					oTargetMMIslandForm = m_oApp.GetIsland( oMMAttack.TargetIslandGlobalID );

					//Get our MM pos
					oMMPos = oTargetMMIslandForm.GetScrTerrainPosFromSquare( oMMAttack.TargetX,oMMAttack.TargetY );

					//Adjust for the graphic location & centering
					oMMPos.X += (int)( (double)Game.m_cSQRW_TRN /*/ 2.0*/ - (double)Game.m_cSQRW_MM / 2.0 );
					oMMPos.Y += (int)( (double)Game.m_cSQRH_TRN * 2.0 - (double)Game.m_cSQRH_MM );

					oMMPos.X += (int)( (double)Game.m_cSQRW_MM / 2.0 - (double)Game.m_cSQRW_AAEXPLO / 2.0 );
					oMMPos.Y += (int)( (double)Game.m_cSQRH_MM / 2.0 - (double)Game.m_cSQRH_AAEXPLO / 2.0 );

					oMMPos.X += (int)oMMAttack.LandedPosOffsetX;
					oMMPos.Y += (int)oMMAttack.LandedPosOffsetY;
				}

				if( bMMSurvived == false )
				{
					if( oMMAttack.Launched == true && oMMAttack.Landed == false )
					{
						AddAAExplosion( oMMPos,oMMAttack.TargetIslandGlobalID );
					}
					else
					{
						AddMMExplosion( oMMAttack.TargetX,oMMAttack.TargetY,oMMAttack.TargetIslandGlobalID );
					}
				}

				//Find the island this is from
				oIsland = this.GetIsland( oMMAttack.SourceIslandGlobalID );
				//Find our square
				oSquare = oIsland.GetSquare( oMMAttack.SourceX,oMMAttack.SourceY );
				//Return the MM
				oSquare.MMIsOverseas = false;
				oSquare.Armed = bMMSurvived;
				//Clean up after ourselves
				this.MetalMarineAttacks.Remove( sMMGlobalID );
			}
			else
			{
				throw new System.Exception( "Unable to find attack with GlobalID of <" + sMMGlobalID + ">." );
			}
		}


		public bool CanIAffordFuel( Island oMyIsland,double nFuelNeeded,ref string sFailureMessage )
		{
			Island oLoopIsland = null;
			double nFuelFoundSoFar = 0;
			bool bCanAfford = false;


			sFailureMessage = "";

			//Can I afford this on my own?
			if( oMyIsland.Fuel >= nFuelNeeded )
			{
				bCanAfford = true;
			}
			else
			{
				nFuelFoundSoFar = oMyIsland.Fuel;

				//First of all build a collection of just my team
				for( int nIndex=0 ; nIndex<=m_oIslands.Count-1 ; nIndex++ )
				{
					oLoopIsland = (Island)m_oIslands.GetByIndex( nIndex );
					if( oLoopIsland != oMyIsland &&
						OnSameTeam( oMyIsland,oLoopIsland ) == true &&
						oLoopIsland.TeamJoinApproved == true )
					{
						nFuelFoundSoFar += DSMisc.Min( oLoopIsland.FuelSharedPerMinute - oLoopIsland.FuelSharedUsed,oLoopIsland.Fuel );
					}
				}

				if( nFuelFoundSoFar >= nFuelNeeded )
				{
					bCanAfford = true;
				}
				else
				{
					sFailureMessage = "You need " + ((long)nFuelNeeded).ToString() + " fuel but only have access to " + ((long)nFuelFoundSoFar).ToString() + " fuel";
				}
			}


			return( bCanAfford );
		}
		public string PayFuel( Island oMyIsland,double nFuelCost )
		{
			DSSortedList oMyTeam = new DSSortedList();
			Island oLoopIsland = null;
			string sShareMessage = "";
			double nTemp = 0;
			double nFuelNeeded = nFuelCost;
			double nFuelNeededPerTeamMember = 0;
			double nTeamHelpAmount = 0;


			//First of all can I afford this building myself?
			if( oMyIsland.Fuel >= nFuelCost )
			{
				oMyIsland.Fuel -= nFuelCost;
			}
			else
			{
				//Take what Fuel we can from ourselves
				nFuelNeeded -= oMyIsland.Fuel;
				//If we got the Fuel for this, but we didn't have enough then we should be at 0
				oMyIsland.Fuel = 0;

				//Now assemble the list of the teams members
				for( int nIndex=0 ; nIndex<=m_oIslands.Count-1 ; nIndex++ )
				{
					oLoopIsland = (Island)m_oIslands.GetByIndex( nIndex );
					if( oLoopIsland != oMyIsland &&
						OnSameTeam( oMyIsland,oLoopIsland ) == true &&
						oLoopIsland.TeamJoinApproved == true )
					{
						oMyTeam.Add( oLoopIsland.GlobalID,oLoopIsland );
					}
				}

				//This is how much each person must contribute
				nFuelNeededPerTeamMember = nFuelNeeded / oMyTeam.Count;

				//Now find out if everyone can pay that amount
				for( int nIndex=0 ; nIndex<=oMyTeam.Count-1 ; nIndex++ )
				{
					oLoopIsland = (Island)oMyTeam.GetByIndex( nIndex );

					//This guy can't help me out enough
					if( oLoopIsland.FuelSharedPerMinute - oLoopIsland.FuelSharedUsed < nFuelNeededPerTeamMember )
					{
						nTemp = nFuelNeededPerTeamMember - ( oLoopIsland.FuelSharedPerMinute - oLoopIsland.FuelSharedUsed );
					}
						//This guy can foot his own bill
					else
					{
						nTemp = nFuelNeededPerTeamMember;
					}

					oLoopIsland.FuelSharedUsed += nTemp;
					oLoopIsland.Fuel -= nTemp;
					nFuelNeeded -= nTemp;
					nTeamHelpAmount += nTemp;
					m_oApp.SendTakingResources( oLoopIsland,0,nTemp );
				}

				//Did we get help?
				if( nTeamHelpAmount > 0 )
				{
					sShareMessage = "Your team pitched in " + ((long)Math.Ceiling( nTeamHelpAmount ) ).ToString() + " fuel";
				}

				//Well at this point any amount we don't have yet we have to pay for
				oMyIsland.Fuel -= nFuelNeeded;
			}


			return( sShareMessage );
		}

		public bool CanIAffordCredits( Island oMyIsland,double nMoneyNeeded,ref string sFailureMessage )
		{
			Island oLoopIsland = null;
			double nMoneyFoundSoFar = 0;
			bool bCanAfford = false;


			sFailureMessage = "";

			//Can I afford this on my own?
			if( oMyIsland.Credits >= nMoneyNeeded )
			{
				bCanAfford = true;
			}
			else
			{
				nMoneyFoundSoFar = oMyIsland.Credits;

				//First of all build a collection of just my team
				for( int nIndex=0 ; nIndex<=m_oIslands.Count-1 ; nIndex++ )
				{
					oLoopIsland = (Island)m_oIslands.GetByIndex( nIndex );
					if( oLoopIsland != oMyIsland &&
						OnSameTeam( oMyIsland,oLoopIsland ) == true &&
						oLoopIsland.TeamJoinApproved == true )
					{
						nMoneyFoundSoFar += DSMisc.Min( oLoopIsland.CreditsSharedPerMinute - oLoopIsland.CreditsSharedUsed,oLoopIsland.Credits );
					}
				}

				if( nMoneyFoundSoFar >= nMoneyNeeded )
				{
					bCanAfford = true;
				}
				else
				{
					sFailureMessage = "You need " + ((long)nMoneyNeeded).ToString() + " credits but only have access to " + ((long)nMoneyFoundSoFar).ToString() + " credits";
				}
			}


			return( bCanAfford );
		}
		public string PayCredits( Island oMyIsland,double nCost )
		{
			DSSortedList oMyTeam = new DSSortedList();
			Island oLoopIsland = null;
			string sShareMessage = "";
			double nTemp = 0;
			double nMoneyNeeded = nCost;
            double nMoneyNeededPerTeamMember = 0;
			double nTeamHelpAmount = 0;


			//First of all can I afford this building myself?
			if( oMyIsland.Credits >= nCost )
			{
				oMyIsland.Credits -= nCost;
			}
			else
			{
				//Take what money we can from ourselves
				nMoneyNeeded -= oMyIsland.Credits;
				//If we got the money for this, but we didn't have enough then we should be at 0
				oMyIsland.Credits = 0;

				//Now assemble the list of the teams members
				for( int nIndex=0 ; nIndex<=m_oIslands.Count-1 ; nIndex++ )
				{
					oLoopIsland = (Island)m_oIslands.GetByIndex( nIndex );
					if( oLoopIsland != oMyIsland &&
						OnSameTeam( oMyIsland,oLoopIsland ) == true &&
						oLoopIsland.TeamJoinApproved == true )
					{
						oMyTeam.Add( oLoopIsland.GlobalID,oLoopIsland );
					}
				}

				//This is how much each person must contribute
				nMoneyNeededPerTeamMember = nMoneyNeeded / oMyTeam.Count;

				//Now find out if everyone can pay that amount
				for( int nIndex=0 ; nIndex<=oMyTeam.Count-1 ; nIndex++ )
				{
					oLoopIsland = (Island)oMyTeam.GetByIndex( nIndex );

					//This guy can't help me out enough
					if( oLoopIsland.CreditsSharedPerMinute - oLoopIsland.CreditsSharedUsed < nMoneyNeededPerTeamMember )
					{
						nTemp = nMoneyNeededPerTeamMember - ( oLoopIsland.CreditsSharedPerMinute - oLoopIsland.CreditsSharedUsed );
					}
					//This guy can foot his own bill
					else
					{
						nTemp = nMoneyNeededPerTeamMember;
					}

					oLoopIsland.CreditsSharedUsed  += nTemp;
					oLoopIsland.Credits -= nTemp;
					nMoneyNeeded -= nTemp;
					nTeamHelpAmount += nTemp;
					m_oApp.SendTakingResources( oLoopIsland,nTemp,0 );
				}

				//Did we get help?
				if( nTeamHelpAmount > 0 )
				{
					sShareMessage = "Your team pitched in " + ((long)Math.Ceiling( nTeamHelpAmount ) ).ToString() + " credits for that purchase";
				}

				//Well at this point any amount we don't have yet we have to pay for
				oMyIsland.Credits -= nMoneyNeeded;
			}


			return( sShareMessage );
		}


		public void AddIsland( Island oIsland )
		{
			if( m_oIslands.ContainsKey( oIsland.GlobalID ) == true )
			{
				m_oIslands.Remove( oIsland.GlobalID );
			}
			m_oIslands.Add( oIsland.GlobalID,oIsland );
		}

		public void RemoveIsland( string sIslandGlobalID )
		{
			if( m_oIslands.ContainsKey( sIslandGlobalID ) == true )
			{
				m_oIslands.Remove( sIslandGlobalID );
			}
		}


		public bool HaveSharedVision( Island oPlayer1,Island oPlayer2 )
		{
			Island oTeamIsland = null;
			bool bSharedVision = false;


			//Is he my ally? 
			if( OnSameTeam( oPlayer1,oPlayer2 ) == true )
			{
				if( oPlayer1.GlobalIDOfPlayerWhoseTeamImOn.Length > 0 )
				{
					oTeamIsland = GetIsland( oPlayer1.GlobalIDOfPlayerWhoseTeamImOn );
				}
				else
				{
					oTeamIsland = oPlayer1;
				}

				//Ok were on the same team... which team?
				bSharedVision = ( oPlayer1 == oPlayer2 ||
								  (
									oTeamIsland != null && oTeamIsland.TeamSetup != null && 
									oTeamIsland.TeamSetup.SharedVision == true 
								  )
								);
			}
			else
			{
				bSharedVision = false;
			}


			return( bSharedVision );
		}
		public bool OnSameTeam( Island oPlayer1,Island oPlayer2 )
		{
			bool bAlly = false;


			//Is he my ally? 
			if( oPlayer1.GlobalID == oPlayer2.GlobalIDOfPlayerWhoseTeamImOn ||
				oPlayer2.GlobalID == oPlayer1.GlobalIDOfPlayerWhoseTeamImOn ||
				oPlayer1.GlobalID == oPlayer2.GlobalID ||
				( oPlayer1.GlobalIDOfPlayerWhoseTeamImOn == oPlayer2.GlobalIDOfPlayerWhoseTeamImOn && 
				  oPlayer2.GlobalIDOfPlayerWhoseTeamImOn != "" ) )
			{
				bAlly = true;
			}
			else
			{
				bAlly = false;
			}

			
			return( bAlly );
		}

		public Island GetTeamsIsland( string sGlobalID )
		{
			Island oIsland = null;
			Island oTeamIsland = null;


			//Get this guys island
			oIsland = GetIsland( sGlobalID );
			//Find his team socket
			if( oIsland.TeamSetup != null )
			{
				oTeamIsland = oIsland;
			}
			else if( oIsland.GlobalIDOfPlayerWhoseTeamImOn != "" )
			{
				oTeamIsland = GetIsland( oIsland.GlobalIDOfPlayerWhoseTeamImOn );
			}


			return( oTeamIsland );
		}

		public int GetNewIslandColor()
		{
			int nColor = 0;	

			nColor = Game.GetIndexedColor( m_nNextFreeColor );

			m_nNextFreeColor++;

			return( nColor );
		}
		public static int GetIndexedColor( int nIndex )
		{
			int nColor = 0;
			int nNewIndex = 0;

			System.Drawing.Color[] naColors = new System.Drawing.Color[]
				{
					System.Drawing.Color.Red,
					System.Drawing.Color.Blue,
					System.Drawing.Color.Green,
					System.Drawing.Color.Purple,
					//System.Drawing.Color.Yellow, 
					System.Drawing.Color.Orange,
					System.Drawing.Color.Gold,
					System.Drawing.Color.Pink,
					System.Drawing.Color.Silver,
					System.Drawing.Color.Teal,

					System.Drawing.Color.White,
					System.Drawing.Color.Brown,
					System.Drawing.Color.Aqua,

					System.Drawing.Color.Beige,
					System.Drawing.Color.Cyan,
					System.Drawing.Color.DarkBlue,
					System.Drawing.Color.DarkGreen,
					System.Drawing.Color.DarkRed,
					System.Drawing.Color.HotPink,
					System.Drawing.Color.LightBlue,
					System.Drawing.Color.LightSkyBlue,					
					System.Drawing.Color.DeepPink,	
					System.Drawing.Color.PaleGreen,
					System.Drawing.Color.Black
				};

			nNewIndex = ( nIndex % naColors.Length );
			nColor = naColors[ nNewIndex ].ToArgb();

			return( nColor );
		}



		public void CalculateAAFireForMissles( DSSortedList oMissles )
		{
			long nNumAAShots = 0;
			long nMissleIndex = 0;
			long nTempIndex = 0;
			double nChanceToHit = 0;
			Island oTheirIsland = null;
			DSSortedList oAAGuns = null;
			MissleAttack oMissle = null;
			Square oSquare = null;


			//Ok, now we need to go through any of the Anti-Air guns our opponet has and see if they 
			//are going to impact our missles.
			oTheirIsland = GetIsland( ((MissleAttack)oMissles.GetByIndex( 0 ) ).TargetIslandGlobalID );
			//Walk a list of all the AA guns they have and calculate percentages
			oAAGuns = oTheirIsland.GetAAShots();
			//Now calculate their chances to stop the missles
			for( int nSquareIndex=0 ; nSquareIndex<oAAGuns.Count ; nSquareIndex++ )
			{
				oSquare = (Square)oAAGuns.GetByIndex( nSquareIndex );

				//If its a double we get two shots.
				nNumAAShots = (long)GetBldStat( oSquare.GobBuildingKey,m_cGOB_BLDSTATS_NUMSHOTS_CLMN );

				for( int i=0 ; i<nNumAAShots ; i++ )
				{
					//This means we are out of missles to shoot down... so pick one we've already shot
					//down for graphical effect
					if( nMissleIndex >= oMissles.Count )
					{
						nTempIndex = DSMisc.GetRnd( 0,oMissles.Count-1 );
					}
					else
					{
						nTempIndex = nMissleIndex;
					}
					oMissle = (MissleAttack)oMissles.GetByIndex( (int)nTempIndex );


					//Calculate the REAL distance for this AA to fire
					nChanceToHit = ChanceAAFireWillHit( oSquare,oTheirIsland,oMissle.TargetX,oMissle.TargetY );

					//Hit or miss we show a missle graphic if it was close.
					if( nChanceToHit > 0 )
					{
						//We hit
						if( DSMisc.GetRnd() <= nChanceToHit ) 
						{
							oMissle.AddAAFire( oSquare.X,oSquare.Y,true,DSMisc.GetRnd( 70,85 ),0,0 );
							nMissleIndex++;
						}
						//We miss
						else
						{
							oMissle.AddAAFire( oSquare.X,oSquare.Y,false,
											DSMisc.GetRnd( 60,100 ),
											DSMisc.GetRnd( -20,20 ),
											DSMisc.GetRnd( -20,20 ) );
						}
					}
				}
			}
		}
		public void CalculateAAFireForMetalMarines( MMAttack oMetalMarinesAttack )
		{
			long nNumAAShots = 0;
			double nChanceToHit = 0;
			Island oTheirIsland = null;
			DSSortedList oAAGuns = null;
			Square oSquare = null;


			//Ok, now we need to go through any of the Anti-Air guns our opponet has and see if they 
			//are going to impact our missles.
			oTheirIsland = GetIsland( oMetalMarinesAttack.TargetIslandGlobalID );
			//Walk a list of all the AA guns they have and calculate percentages
			oAAGuns = oTheirIsland.GetAAShots();
			//Now calculate their chances to stop the missles
			for( int nSquareIndex=0 ; nSquareIndex<oAAGuns.Count ; nSquareIndex++ )
			{
				oSquare = (Square)oAAGuns.GetByIndex( nSquareIndex );

				//If its a double we get two shots.
				nNumAAShots = (long)GetBldStat( oSquare.GobBuildingKey,m_cGOB_BLDSTATS_NUMSHOTS_CLMN );

				for( int i=0 ; i<nNumAAShots ; i++ )
				{
					nChanceToHit = ChanceAAFireWillHit( oSquare,oTheirIsland,oMetalMarinesAttack.TargetX,oMetalMarinesAttack.TargetY );

					//Hit or miss we show a missle graphic if it was close.
					if( nChanceToHit > 0 )
					{
						//We hit
						if( DSMisc.GetRnd() <= nChanceToHit ) 
						{
							oMetalMarinesAttack.AddAAFire( oSquare.X,oSquare.Y,true,DSMisc.GetRnd( 75,100 ),0,0 );
						}
						//We miss
						else
						{
							oMetalMarinesAttack.AddAAFire(  oSquare.X,oSquare.Y,false,
															DSMisc.GetRnd( 60,100 ),
															DSMisc.GetRnd( -20,20 ),
															DSMisc.GetRnd( -20,20 ) );
						}
					}
				}
			}


			//Now add our MM into the attack
			m_oMMAttacks.Add( oMetalMarinesAttack.GlobalID,oMetalMarinesAttack );
		}

		public double ChanceAAFireWillHit( Square oAASquare,Island oTargetIsland,long nTargetX,long nTargetY )
		{
			double nChanceToHit = 0;
			double nPos1X = 0, nPos1Y = 0;
			double nPos2X = 0, nPos2Y = 0;
			double nYModifier = 0;
			double nAngle = 0;
			double nAngleDiff = 0;
			double nDist = 0;
			double nHowFar = 0;
			int nAdjustedDist = 0;


			//Calculate the REAL distance for this AA to fire
			//First convert our fake locations into something we can do math on
			nYModifier = 0;
			if( nTargetX % 2 != 0 )
			{
				nYModifier = (float)(Game.m_cSQRH_TRN / 2.0);
			}
			nPos1X = ( Game.m_cSQRW_TRN / 2.0f ) * nTargetX;
			nPos1Y = ( Game.m_cSQRH_TRN * nTargetY ) + nYModifier;

			nYModifier = 0;
			if( oAASquare.X % 2 != 0 )
			{
				nYModifier = (float)(Game.m_cSQRH_TRN / 2.0);
			}
			nPos2X = ( Game.m_cSQRW_TRN / 2.0f ) * oAASquare.X;
			nPos2Y = ( Game.m_cSQRH_TRN * oAASquare.Y ) + nYModifier;

			//Now get our angle of attack
			nAngle = DSMath.CalculateRadAngle( nPos1X,nPos2Y,nPos2X,nPos1Y );
			if( oTargetIsland.Direction == 0 ){ nAngle += 0.30893071691261031; }
			if( oTargetIsland.Direction == 2 ){ nAngle += 0.30893071691261031; }
			if( oTargetIsland.Direction == 3 ){ nAngle -= 0.53284392158651439; }
					
			nAngle = DSMath.NormalizeRadAngle( nAngle );
			nAngleDiff = DSMath.AngleDegDiff( nAngle,Math.PI/4 + (Math.PI/2) * oTargetIsland.Direction );
			//And finally calculate our distance off of target
			nDist = Game.GetDistance( oAASquare.X,oAASquare.Y,nTargetX,nTargetY );
			nHowFar = Math.Sin( nAngleDiff ) * nDist;
			if( nHowFar - Math.Floor( nHowFar ) < .1 )
			{
				nAdjustedDist = (int)Math.Floor( nHowFar );
			}
			else
			{
				nAdjustedDist = (int)Math.Ceiling( nHowFar );
			}


			if( nDist <= (int)Game.GetBldStat( oAASquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_AARANGE_CLMN ) ||
				(
					nAdjustedDist < (int)Game.GetBldStat( oAASquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_AARANGE_CLMN ) &&
					nAngleDiff < Math.PI / 2
				)
			  )
			{
				nChanceToHit = oTargetIsland.CalculateChanceToHit( oAASquare.X,oAASquare.Y,nTargetX,nTargetY );
			}
			else
			{
				nChanceToHit = 0;
			}


			return( nChanceToHit );
		}


		public DSSortedList AddMissleAttacks( string sSourceGlobalID, long nSourceX, long nSourceY,
										string sTargetGlobalID, long nTargetX, long nTargetY,long nNumAttacks,
										bool bAddAAFire,bool bIsAntiMatter,long nExplosionRadius,
										out bool bFiredMissles,out string sFailureMessage )
		{
			DSSortedList oMissles = new DSSortedList();
			MissleAttack oAttack = null;
			Island oMyIsland = null;
			Square oMySquare = null;
			double nFuelRequired = 0;
			double nOffensive = 0;
			string sShareMessage = "";
			sFailureMessage = "";
            

			//Get our source location
			oMyIsland = GetIsland( sSourceGlobalID );
			oMySquare = oMyIsland.GetSquare( nSourceX,nSourceY );

			//Calculate each missles damage
			nOffensive = (double)Game.GetBldStat( oMySquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_OFFENSIVE_CLMN ) / nNumAttacks;

			//Make sure we can afford this first
			nFuelRequired = (double)Game.GetBldStat( oMySquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_FUELTOFIRE_CLMN );
			//if( oMyIsland.Fuel >= nFuelRequired )
			if( CanIAffordFuel( oMyIsland,nFuelRequired,ref sFailureMessage ) == true )
			{
				sShareMessage = PayFuel( oMyIsland,nFuelRequired );
				//oMyIsland.Fuel -= nFuelRequired;
				bFiredMissles = true;

				for( int i=0 ; i<nNumAttacks ; i++ )
				{
					oAttack = new MissleAttack();
					oAttack.SourceIslandGlobalID = sSourceGlobalID;
					oAttack.SourceX = nSourceX;
					oAttack.SourceY = nSourceY;
					oAttack.TargetIslandGlobalID = sTargetGlobalID;
					oAttack.TargetX = nTargetX;
					oAttack.TargetY = nTargetY;
					oAttack.ShotNumber = i+1;
					oAttack.TotalNumberOfShots = nNumAttacks;
					oAttack.Offensive = nOffensive;
					oAttack.IsAntiMatter = bIsAntiMatter;
					oAttack.ExplosionRadius = nExplosionRadius;
					oAttack.Color = oMyIsland.Color;
		
					oMissles.Add( oAttack.GlobalID,oAttack );
				}

				if( bAddAAFire == true )
				{
					CalculateAAFireForMissles( oMissles );
				}
				if( sShareMessage.Length > 0 )
				{
					m_oApp.UpdateStatus( Game.m_cALERT_MONEY + sShareMessage );
				}
			}
			else
			{
				bFiredMissles = false;
				//sFailureMessage = nFuelRequired.ToString() + " fuel is required to launch but you only have " + ((int)oMyIsland.Fuel).ToString() + " fuel";
			}

			return( oMissles );
		}


		public MMAttack AttackWithMetalMarine( string sSourceGlobalID, long nSourceX, long nSourceY,
											  string sTargetGlobalID, long nTargetX, long nTargetY,
											  double nMMStrength,double nMMOffense, 
											  out bool bFiredMetalMarine,out string sFailureMessage )
		{
			MMAttack oAttack = null;
			Island oMyIsland = null;
			Island oTargetIsland = null;
			Square oMySquare = null;
			Square oTargetSquare = null;
			double nFuelRequired = 0;
			string sShareMessage = "";
			sFailureMessage = "";
            

			//Make sure we can afford this first
			oMyIsland = GetIsland( sSourceGlobalID );
			oMySquare = oMyIsland.GetSquare( nSourceX,nSourceY );
			oTargetIsland = GetIsland( sTargetGlobalID );
			oTargetSquare = oTargetIsland.GetSquareICanSee( nTargetX,nTargetY,oMyIsland );
			nFuelRequired = (double)Game.GetBldStat( oMySquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_FUELTOFIRE_CLMN );

			if( (bool)Game.GetTrnStat( oTargetSquare.SquareType,Game.m_cGOB_TRNSTATS_CANBEDAMAGED_CLMN )  == false )
			{
				bFiredMetalMarine = false;
				sFailureMessage = "You cannot land Metal Marines in the water";
				return( oAttack );
			}
			else if( CanIAffordFuel( oMyIsland,nFuelRequired,ref sFailureMessage ) == true )
			{
				sShareMessage = PayFuel( oMyIsland,nFuelRequired );
				//oMyIsland.Fuel -= nFuelRequired;
				bFiredMetalMarine = true;

				oAttack = new MMAttack();
				oAttack.Game = this;
				oAttack.GameEngine = m_oGameEngine;
				oAttack.SourceIslandGlobalID = sSourceGlobalID;
				oAttack.SourceX = nSourceX;
				oAttack.SourceY = nSourceY;
				oAttack.TargetIslandGlobalID = sTargetGlobalID;
				oAttack.TargetX = nTargetX;
				oAttack.TargetY = nTargetY;
				oAttack.LandedPosOffsetX = 0;
				oAttack.LandedPosOffsetY = 0;
				oAttack.MMStrength = nMMStrength;
				oAttack.MMOffense = nMMOffense;
				oAttack.MMGraphicType = (long)Game.GetBldStat( oMySquare.GobBuildingKey,Game.m_cGOB_BLDSTATS_MMGRAPHICINDEX_CLMN );
				oAttack.Color = oMyIsland.Color;
	
				CalculateAAFireForMetalMarines( oAttack );

				if( sShareMessage.Length > 0 )
				{
					m_oApp.UpdateStatus( Game.m_cALERT_MONEY + sShareMessage );
				}
			}
			else
			{
				bFiredMetalMarine = false;
				//sFailureMessage = ((long)nFuelRequired).ToString() + " fuel is required but you only have " + ((long)oMyIsland.Fuel).ToString() + " fuel";
			}

			return( oAttack );
		}

		public string AddExplosion( string sDelayTillExplosionDone,string sIslandGlobalID,long nSquareX,long nSquareY,enumExplosionType nExplosionType,bool bIsAntiMatter,bool bPlaySound )
		{
			Explosion oExplosion = new Explosion();

			oExplosion.DelayTillThisExploIsDone = sDelayTillExplosionDone;
			oExplosion.IslandGlobalID = sIslandGlobalID;
			oExplosion.SquareX = nSquareX;
			oExplosion.SquareY = nSquareY;
			oExplosion.ExplosionType = nExplosionType;
			oExplosion.IsAntiMatter = bIsAntiMatter;
			oExplosion.PlaySound = bPlaySound;

			/*if( bIsAntiMatter == false && bPlaySound == true )
			{
				if( nExplosionType == enumExplosionType.Building )
				{
					m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_EXPLOSION );					
				}
				else
				{
					m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_AAEXPLOSION );					
				}
			}*/

			m_oExplosions.Add( oExplosion.GlobalID,oExplosion );

			//m_oApp.SendExplosion( oExplosion );

			return( oExplosion.GlobalID );
		}
		public string AddMMExplosion( long nSquareX,long nSquareY,string sIslandGlobalID )
		{
			Explosion oExplosion = new Explosion();

			oExplosion.SquareX = nSquareX;
			oExplosion.SquareY = nSquareY;
			oExplosion.ExplosionType = enumExplosionType.MetalMarine;
			oExplosion.IslandGlobalID = sIslandGlobalID;
			m_oExplosions.Add( oExplosion.GlobalID,oExplosion );

			m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_AAEXPLOSION );

			return( oExplosion.GlobalID );
		}
		public string AddAAExplosion( System.Drawing.Rectangle oMisslePos,string sIslandGlobalID )
		{
			Explosion oExplosion = new Explosion();

			oExplosion.PosX = oMisslePos.X;
			oExplosion.PosY = oMisslePos.Y;
			oExplosion.ExplosionType = enumExplosionType.AAShot;
			oExplosion.IslandGlobalID = sIslandGlobalID;
			m_oExplosions.Add( oExplosion.GlobalID,oExplosion );

			m_oGameEngine.DirectSound.PlaySound( Game.m_cGOB_SOUND_AAEXPLOSION );

			return( oExplosion.GlobalID );
		}

		
		public MMAttack FindClosestMMAttack( string sIslandGlobalID,long nDefendingMMX,long nDefendingMMY,
											 double DefendingMMOffsetX,double DefendingMMOffsetY )
		{
			MMAttack oLoopMMAttack = null;
			MMAttack oClosest = null;

			for( int nIndex=0 ; nIndex<m_oMMAttacks.Count ; nIndex++ )
			{
				oLoopMMAttack = (MMAttack)m_oMMAttacks.GetByIndex( nIndex );
				if( oLoopMMAttack.TargetIslandGlobalID == sIslandGlobalID && oLoopMMAttack.Landed == true )
				{
					if( oClosest == null )
					{
						oClosest = oLoopMMAttack;
					}
					else 
					{
						if( DSMath.Distance( oLoopMMAttack.TargetX,oLoopMMAttack.TargetY,nDefendingMMX,nDefendingMMY ) < 
							DSMath.Distance( oClosest.TargetX,oClosest.TargetY,nDefendingMMX,nDefendingMMY ) )
						{
							oClosest = oLoopMMAttack;
						}
					}
				}
			}

			return( oClosest );
		}

		
		public long GetTeamCount( Island oIsland )
		{
			long nTeamCount = 0;
			Island oLoopIsland = null;


			for( int nIslandIndex=0 ; nIslandIndex<m_oIslands.Count ; nIslandIndex++ )
			{
				oLoopIsland = (Island)m_oIslands.GetByIndex( nIslandIndex );

				if( oIsland.TeamSetup != null && oLoopIsland.GlobalIDOfPlayerWhoseTeamImOn == oIsland.GlobalID ||
					oLoopIsland.TeamSetup != null && oIsland.GlobalIDOfPlayerWhoseTeamImOn == oLoopIsland.GlobalID ||
					( oLoopIsland.GlobalIDOfPlayerWhoseTeamImOn == oIsland.GlobalIDOfPlayerWhoseTeamImOn && oIsland.GlobalIDOfPlayerWhoseTeamImOn != "" ) )
				{
					nTeamCount++;

					if( oLoopIsland.TeamJoinApproved == false )
					{
						nTeamCount++;
					}
					if( oLoopIsland.TeamQuitPending == true )
					{
						nTeamCount--;
					}
				}					
			}


			return( nTeamCount );
		}


		public string Serialize()
		{
			string sRetVal = "";
			string sIslands = "";
			Island oLoopIsland = null;


			sRetVal = m_cINSTANTBUILD.ToString() + m_cGAME_SEP +
					  //m_cRENDERGRID.ToString() + m_cGAME_SEP +
					  m_cMMNEVERRETURNHOME.ToString() + m_cGAME_SEP +
					  m_cAUTOWINFORONEPLAYER.ToString() + m_cGAME_SEP +
					  m_cCANATTACKOURSELVES.ToString() + m_cGAME_SEP +
					  m_cALLOWZOOM.ToString() + m_cGAME_SEP + 
					  m_cFULLVISION.ToString() + m_cGAME_SEP + 
					  //m_cSHOWFPS.ToString() + m_cGAME_SEP;
					  m_bUseBuildingRot.ToString() + m_cGAME_SEP;

			//Now write all the island information
			for( int nLoopIslandIndex=0 ; nLoopIslandIndex<m_oIslands.Count ; nLoopIslandIndex++ )
			{
				oLoopIsland = (Island)m_oIslands.GetByIndex( nLoopIslandIndex );

				sIslands += oLoopIsland.GlobalID + m_cISLAND_SEP + 
							oLoopIsland.Serialize( false ) + m_cISLAND_SEP;
			}

			sRetVal += sIslands + m_cGAME_SEP;


			return( sRetVal );
		}
		public void DeSerialize( string sRecord )
		{
			string[] saGameParamaters = null;
			string[] saAllIslands = null;
			Island oIsland = null;


			saGameParamaters = DSMisc.Split( sRecord,m_cGAME_SEP );

			m_cINSTANTBUILD			= Convert.ToBoolean( saGameParamaters[ 0 ] );
			//m_cRENDERGRID			= Convert.ToBoolean( saGameParamaters[ 1 ] );
			m_cMMNEVERRETURNHOME	= Convert.ToBoolean( saGameParamaters[ 1 ] );
			m_cAUTOWINFORONEPLAYER	= Convert.ToBoolean( saGameParamaters[ 2 ] );
			m_cCANATTACKOURSELVES	= Convert.ToBoolean( saGameParamaters[ 3 ] );
			m_cALLOWZOOM			= Convert.ToBoolean( saGameParamaters[ 4 ] );
			m_cFULLVISION			= Convert.ToBoolean( saGameParamaters[ 5 ] );
			//m_cSHOWFPS				= Convert.ToBoolean( saGameParamaters[ 6 ] );
			m_bUseBuildingRot		= Convert.ToBoolean( saGameParamaters[ 6 ] );

			saAllIslands = DSMisc.Split( saGameParamaters[ 7 ],m_cISLAND_SEP );

			for( int i=0 ; i<saAllIslands.Length-1 ; i+=2 )
			{
				//Find if this island exists yet
				oIsland = GetIsland( saAllIslands[ i ] );

				//If there is no island then make one
				if( oIsland == null )
				{
					oIsland = new Island( this );
				}
				else
				{
					m_oIslands.Remove( saAllIslands[ i ] );
				}
				//Add in the result
				oIsland.DeSerialize( saAllIslands[ i+1 ] );
				AddIsland( oIsland );
			}
		}


		public Island GetIsland( string sIslandGlobalID )
		{
			return( (Island)m_oIslands.GetByKey( sIslandGlobalID ) );
		}

		public Island GetIslandBySocketID( long nOwnerSocketID )
		{
			Island oLoopIsland = null;

			for( int i=0 ; i<m_oIslands.Count ; i++ )
			{
				oLoopIsland = (Island)m_oIslands.GetByIndex( i );
				if( oLoopIsland.OwnerSocketID == nOwnerSocketID )
				{
					return( oLoopIsland );
				}
			}
			return( null );
		}



		public static object GetBldStat( long nGobBuildingKey,string sColumnName )
		{
			object oRetVal = null;
		
			try
			{
				oRetVal = Game.GobFile.FindValue( Game.m_cGOB_BLDSTATS_TABLE,(int)nGobBuildingKey,sColumnName );
				/*oRetVal = Game.GobFile.FindValue( Game.m_cGOB_BLDSTATS_TABLE,
												  Game.m_cGOB_BLDSTATS_BLDKEY_CLMN,
												  nGobBuildingKey,
												  sColumnName );*/
			}
			catch( System.Exception oEx )
			{
				DSMisc.ShowErrors( oEx );
			}

			return( oRetVal );
		}

		public static object GetTrnStat( long nTerrainKey,string sColumnName )
		{
			object oRetVal = null;
		
			try
			{
				oRetVal = Game.GobFile.FindValue( Game.m_cGOB_TRNSTATS_TABLE,(int)nTerrainKey,sColumnName );
			}
			catch( System.Exception oEx )
			{
				DSMisc.ShowErrors( oEx );
			}

			return( oRetVal );
		}

		public static object GetCmpgnStat( long nLevelID,string sColumnName )
		{
			object oRetVal = null;
		
			try
			{
				oRetVal = Game.GobFile.FindValue( Game.m_cGOB_CAMPAIGN_TABLE,(int)(nLevelID-1),sColumnName );
			}
			catch( System.Exception oEx )
			{
				DSMisc.ShowErrors( oEx );
			}

			return( oRetVal );
		}

		public static System.Drawing.Rectangle GetSrcRectForTerrainType( int nSquareType,ref string sImageKey )
		{
			long nTempGraphicIndex = 0;
			System.Drawing.Rectangle oSrcRect;


			nTempGraphicIndex = (int)nSquareType;

			if( nTempGraphicIndex >= 32 )
			{
				nTempGraphicIndex -= 32;
				sImageKey = Game.m_cGOB_FLATTILEIMG_B_TABLE;
			}
			else
			{
				sImageKey = Game.m_cGOB_FLATTILEIMG_A_TABLE;
			}

			oSrcRect = new System.Drawing.Rectangle( (int)( nTempGraphicIndex * Game.m_cSQRW_TRN),0,
													 (int)Game.m_cSQRW_TRN,(int)Game.m_cSQRH_TRN );

			return( oSrcRect );
		}
		public static System.Drawing.Rectangle GetSrcRectForBuildingByBuildingKey( long nBuildingGobKey,long nAnimationFrame,ref string sImageKey )
		{
			long nGraphicIndex = 0;
			System.Drawing.Rectangle oSrcRect;

			nGraphicIndex = (long)Game.GetBldStat( nBuildingGobKey,Game.m_cGOB_BLDSTATS_DIR0GRAPHICINDX_CLMN );
			oSrcRect = GetSrcRectForBuildingByGraphicIndex( nGraphicIndex,nAnimationFrame,ref sImageKey );

			return( oSrcRect );
		}
		public static System.Drawing.Rectangle GetSrcRectForBuildingByGraphicIndex( long nGraphicIndex,long nAnimationFrame,ref string sImageKey )
		{
			long nTempGraphicIndex = nGraphicIndex;
			System.Drawing.Rectangle oSrcRect;

			if( nGraphicIndex >= 32 )
			{
				nTempGraphicIndex -= 32;
				sImageKey = Game.m_cGOB_BUILDINGIMAGE_B_TABLE;
			}
			else
			{
				sImageKey = Game.m_cGOB_BUILDINGIMAGE_A_TABLE;
			}

			oSrcRect = new System.Drawing.Rectangle( (int)( nTempGraphicIndex * Game.m_cSQRW_BLD + 1 + nTempGraphicIndex ),
													 (int)( ( nAnimationFrame + 4 ) * Game.m_cSQRH_BLD + 1 + ( nAnimationFrame + 4 ) ),
													 (int)Game.m_cSQRW_BLD,(int)Game.m_cSQRH_BLD );

			return( oSrcRect );
		}

		//Direction corisponding
		//5 6 7
		//4<->0
		//3 2 1
		//Add 8 if firing
		public static System.Drawing.Rectangle GetSrcRectForMetalMarine( long nDirection,long nAnimationFrame,bool bFiring,long nMMGraphicType )
		{
			long nIndex = 0;
			System.Drawing.Rectangle oSrcRect;


			nIndex = nDirection;

			oSrcRect = new System.Drawing.Rectangle( 
							(int)(nIndex * ( Game.m_cSQRW_MM + 1 ) + 1 + 
								  nMMGraphicType * ( Game.m_cSQRW_MM + 1 ) * 8 ),//Adjust for our graphic index
							(int)(nAnimationFrame * ( Game.m_cSQRH_MM + 1 ) + 1 ),
							(int)Game.m_cSQRW_MM,(int)Game.m_cSQRH_MM );

			return( oSrcRect );
		}
		public static System.Drawing.Rectangle GetSrcRectForAntiMatter( long nAnimationFrame )
		{
			System.Drawing.Rectangle oSrcRect;

			oSrcRect = new System.Drawing.Rectangle( 1,
							(int)( nAnimationFrame * ( Game.m_cSQRH_ANTIMATTER + 1 ) + 1 ),
							(int)Game.m_cSQRW_ANTIMATTER,(int)Game.m_cSQRH_ANTIMATTER );

			return( oSrcRect );
		}
		public static System.Drawing.Rectangle GetSrcRectForBuildingExplosion( long nFrame )
		{
			System.Drawing.Rectangle oSrcRect;


			oSrcRect = new System.Drawing.Rectangle( 1,(int)(nFrame * ( Game.m_cSQRH_BLDEXPLO +1 ) + 1 ),
													   (int)Game.m_cSQRW_BLDEXPLO,
													   (int)Game.m_cSQRH_BLDEXPLO );

			return( oSrcRect );
		}
		public static System.Drawing.Rectangle GetSrcRectForMMExplosion( long nFrame )
		{
			System.Drawing.Rectangle oSrcRect;


			oSrcRect = new System.Drawing.Rectangle( 1,(int)(nFrame * ( Game.m_cSQRH_MMEXPLO +1 ) + 1 ),
													   (int)Game.m_cSQRW_MMEXPLO,
													   (int)Game.m_cSQRH_MMEXPLO );

			return( oSrcRect );
		}
		public static System.Drawing.Rectangle GetSrcRectForAAExplosion( long nFrame )
		{
			System.Drawing.Rectangle oSrcRect;


			oSrcRect = new System.Drawing.Rectangle( 1,(int)(nFrame * ( Game.m_cSQRH_AAEXPLO +1 ) + 1 ),
													   (int)Game.m_cSQRW_AAEXPLO,
													   (int)Game.m_cSQRH_AAEXPLO );

			return( oSrcRect );
		}

		public static System.Drawing.Rectangle GetSrcRectForMenuButton( long nButton,bool bPressed )
		{
			System.Drawing.Rectangle oSrcRect;
			long nY = 1;


			if( bPressed == true )
			{
				nY += 31;
			}
			oSrcRect = new System.Drawing.Rectangle( (int)( 1+31*nButton ),(int)nY,30,30 );


			return( oSrcRect );
		}


		public static long GetPriceOfBuilding( long nGobBuildingKey )
		{
			long nRetVal = 0;

			nRetVal = Convert.ToInt64( GetBldStat( nGobBuildingKey,m_cGOB_BLDSTATS_COST_CLMN ) );

			return( nRetVal );
		}

		public static System.Drawing.Point[] GetSquaresInRadius( long nRadius,long nSquareX,long nSquareY )
		{
			System.Drawing.Point[] oPoints = new System.Drawing.Point[ 0 ];
			System.Drawing.Point oTempPos = new System.Drawing.Point();
			long nSquareCount = 0;
			long nIndex = 0;


			if( nRadius == 1 )
			{
				oPoints = new System.Drawing.Point[ 9 ];

				//Center Col
				oPoints[ 0 ] = new Point( (int)nSquareX,(int)nSquareY );
				oPoints[ 1 ] = new Point( (int)nSquareX,(int)nSquareY-1 );
				oPoints[ 2 ] = new Point( (int)nSquareX,(int)nSquareY+1 );

				//Left Middle Col
				if( nSquareX % 2 == 0 )
				{
					oPoints[ 3 ] = new Point( (int)nSquareX-1,(int)nSquareY-1 );
				}
				else
				{
					oPoints[ 3 ] = new Point( (int)nSquareX-1,(int)nSquareY );
				}
				oPoints[ 4 ] = new Point( oPoints[ 3 ].X,oPoints[ 3 ].Y+1 );

				//Left Middle Col
				if( nSquareX % 2 == 0 )
				{
					oPoints[ 5 ] = new Point( (int)nSquareX+1,(int)nSquareY-1 );
				}
				else
				{
					oPoints[ 5 ] = new Point( (int)nSquareX+1,(int)nSquareY );
				}
				oPoints[ 6 ] = new Point( oPoints[ 5 ].X,oPoints[ 5 ].Y+1 );

				//Left most tip
				oPoints[ 7 ] = new Point( (int)nSquareX-2,(int)nSquareY );
				oPoints[ 8 ] = new Point( (int)nSquareX+2,(int)nSquareY );
			}
			else
			{
				//1			= 0 = 1
				//5			= 1 = 1+3+1
				//8+5		= 2 = 1+3+5+3+1
				//12+8+5	= 3 = 1+3+5+7+5+3+1
				//16+12+8+5	= 4 = 1+3+5+7+9+7+5+3+1

				//Get the square count
				nSquareCount = (long)( ( Math.Pow( nRadius,2 ) + nRadius ) * 2 + 1 );
				oPoints = new System.Drawing.Point[ nSquareCount ];

				//Start with the center vein
				for( int nVein=0 ; nVein<=nRadius ; nVein++ )
				{
					oTempPos = Game.MoveDownLeft( nVein,nSquareX,nSquareY );
					oPoints[ nIndex ] = oTempPos;
					nIndex++;

					//Walk out to the end2 of the vein
					for( int nDist=1 ; nDist<nRadius-nVein+1 && nRadius>0 ; nDist++ )
					{
						oPoints[ nIndex ] = Game.MoveUpLeft( nDist,oTempPos.X,oTempPos.Y );
						nIndex++;
						oPoints[ nIndex ] = Game.MoveDownRight( nDist,oTempPos.X,oTempPos.Y );
						nIndex++;
					}

					if( nVein > 0 )
					{
						oTempPos = Game.MoveUpRight( nVein,nSquareX,nSquareY );
						oPoints[ nIndex ] = oTempPos;
						nIndex++;

						//Walk out to the left end of the vein
						for( int nDist=1 ; nDist<nRadius-nVein+1 && nRadius>0 ; nDist++ )
						{
							oPoints[ nIndex ] = Game.MoveUpLeft( nDist,oTempPos.X,oTempPos.Y );
							nIndex++;
							oPoints[ nIndex ] = Game.MoveDownRight( nDist,oTempPos.X,oTempPos.Y );
							nIndex++;
						}
					}
				}
			}

			return( oPoints );
		}
		public static DSSortedList GetSquaresInRadiusSortedList( long nRadius,long nSquareX,long nSquareY )
		{
			System.Drawing.Point[] oaPoints = new System.Drawing.Point[ 0 ];
			DSSortedList oPoints = new DSSortedList();


			oaPoints = GetSquaresInRadius( nRadius,nSquareX,nSquareY );
			for( int i=0 ; i<oaPoints.Length ; i++ )
			{
				oPoints.Add( oaPoints[i].X.ToString() + "," + oaPoints[i].Y.ToString(),
							 oaPoints[i] );
			}

			return( oPoints );
		}

		public static long GetDistance( long nSquareX1,long nSquareY1,long nSquareX2,long nSquareY2 )
		{
			long nXDistance = 0;
			long nYDistance = 0;

			
			nXDistance = Math.Abs( nSquareX1 - nSquareX2 );

			if( nSquareX1 % 2 == 0 )
			{
				if( nSquareY2 < nSquareY1 )
				{
					nYDistance = ( nSquareY1 - nSquareY2 ) * 2;
				}
				else
				{
					nYDistance = Math.Abs( nSquareY1 - nSquareY2 ) * 2;
					if( ( nSquareX1 % 2 ) != ( nSquareX2 % 2 ) )
					{
						nYDistance++;
					}
				}
			}
			else
			{
				if( nSquareY2 > nSquareY1 )
				{
					nYDistance = ( nSquareY2 - nSquareY1 ) * 2;
				}
				else
				{
					nYDistance = Math.Abs( nSquareY2 - nSquareY1 ) * 2;
					if( ( nSquareX1 % 2 ) != ( nSquareX2 % 2 ) )
					{
						nYDistance++;
					}
				}
			}
			
			return( DSMisc.Max( nXDistance,nYDistance ) );
		}


		public static System.Drawing.Point MoveUpLeft( long nNumSquares,long nSquareX,long nSquareY )
		{
			long nYChg = 0;
			System.Drawing.Point oPoint;
			
			nYChg = (long)( nNumSquares / 2 );
			if( nNumSquares % 2 != 0 && nSquareX % 2 == 0 && nNumSquares > 0 )
			{
				nYChg++;
			}
			else if( nNumSquares % 2 != 0 && nSquareX % 2 != 0 && nNumSquares < 0 )
			{
				nYChg--;
			}

			oPoint = new System.Drawing.Point( (int)( nSquareX - nNumSquares ),(int)( nSquareY - nYChg ) );

			return( oPoint );
		}

		public static System.Drawing.Point MoveUpRight( long nNumSquares,long nSquareX,long nSquareY )
		{
			long nYChg = 0;
			System.Drawing.Point oPoint;
			
			nYChg = (long)( nNumSquares / 2 );
			if( nNumSquares % 2 != 0 && nSquareX % 2 == 0 && nNumSquares > 0 )
			{
				nYChg++;
			}
			else if( nNumSquares % 2 != 0 && nSquareX % 2 != 0 && nNumSquares < 0 )
			{
				nYChg--;
			}

			oPoint = new System.Drawing.Point( (int)( nSquareX + nNumSquares ),(int)( nSquareY - nYChg ) );

			return( oPoint );
		}

		public static System.Drawing.Point MoveDownRight( long nNumSquares,long nSquareX,long nSquareY )
		{
			System.Drawing.Point oPoint = MoveUpLeft( nNumSquares * -1,nSquareX,nSquareY );
			return( oPoint );
		}
		public static System.Drawing.Point MoveDownLeft( long nNumSquares,long nSquareX,long nSquareY )
		{
			System.Drawing.Point oPoint = MoveUpRight( nNumSquares*-1,nSquareX,nSquareY );
			return( oPoint );
		}


		public static System.Drawing.Rectangle GetAbsoluteTerrainPosFromSquare( Island oIsland,long nX,long nY )
		{
			System.Drawing.Rectangle oTrgRect = new System.Drawing.Rectangle( 0,0,0,0 );
			float nYModifier = 0;
			double nTotalWidth, nTotalHeight;


			//Setup the location based upon the progress bar
			nTotalWidth = oIsland.Width * ( Game.m_cSQRW_TRN / 2.0f );
			nTotalHeight = oIsland.Height * Game.m_cSQRH_TRN + Game.m_cSQRH_TRN / 2.0f;

			//Find our offset based on our X and Y.  Every other x is half its high farther down.
			nYModifier = 0;
			if( nX % 2 != 0 )
			{
				nYModifier = Game.m_cSQRH_TRN / 2.0f;
			}

			//Calculate the drawing locations
			oTrgRect.X = (int)( ( Game.m_cSQRW_TRN / 2.0f ) * nX );
			oTrgRect.Y = (int)( ( Game.m_cSQRH_TRN * nY ) + nYModifier );

			oTrgRect.Width = (int)Game.m_cSQRW_TRN;
			oTrgRect.Height = (int)Game.m_cSQRH_TRN;


			return( oTrgRect );
		}
		public static void GetSquareFromScrPos( Island oIsland,Vector2 vScreenPos,ref long nScreenX,ref long nScreenY )
		{
			System.Drawing.Rectangle oTrgRect = new System.Drawing.Rectangle( 0,0,0,0 );
			System.Drawing.Rectangle oSrcRect = new System.Drawing.Rectangle( 0,0,0,0 );
			float nYModifier = 0;
			double nTotalWidth, nTotalHeight;
			double nLeftOffset, nTopOffset;
			double nTempPosX = 0;
			double nTempPosY = 0;
			double nGamePosX = 0;
			double nGamePosY = 0;
			double nWRadius = ( Game.m_cSQRW_TRN / 2.0f );
			double nHRadius = ( Game.m_cSQRH_TRN / 2.0f );
			double nM = 0, nB = 0;
			double nXIntercept1 = 0, nXIntercept2 = 0;
			long nUpperRightToLowerLeftVane = 0;
			long nUpperLeftToLowerRightVane = 0;


			//Setup the location based upon the progress bar
			nTotalWidth = oIsland.Width * ( Game.m_cSQRW_TRN / 2.0f );
			nLeftOffset = Game.m_cSQRW_TRN / 2.0f;

			nTotalHeight = oIsland.Height * Game.m_cSQRH_TRN + Game.m_cSQRH_TRN / 2.0f;
			nTopOffset = Game.m_cSQRH_TRN / 2.0f;


			//First of all find out the X.  
			//Remove the graphical adjustments
			nTempPosX = vScreenPos.X;
			//Add in however much were off screen
			nTempPosX += nLeftOffset;
			nGamePosX = nTempPosX;

			//Now find the Y since we can adjust for the X position 
			//Remove the graphical adjustments
			nTempPosY = vScreenPos.Y;
			//Add in however much were off screen
			nTempPosY += nTopOffset;
			//Add in our Y offset
			nTempPosY -= nYModifier;
			nGamePosY = nTempPosY;

			//Ok new plan... this isn't work.  Lets try intersecting the line and figuring out the diagonal
			//lines so we can tell which "vane" we are in.
			//Step 1.)  Get m
			nM = (double)Game.m_cSQRH_TRN / (double)Game.m_cSQRW_TRN;
			//Step 2.)  Get b.... y-Mx = b
			nB = nGamePosY - nM * nGamePosX;
			//Step 3.)  Get the x-Intercept, -b / m = x
			nXIntercept1 = -nB / nM;
			//This tells us where it hits the X axis, now translate that to our "vane"
			nUpperLeftToLowerRightVane = (long)( nXIntercept1 / nWRadius );
			//Now convert the two sub-vanes into a single vein
			if( nUpperLeftToLowerRightVane % 2 != 0 )
			{
				if( nUpperLeftToLowerRightVane > 0 )
				{
					nUpperLeftToLowerRightVane++;
				}
				else
				{
					nUpperLeftToLowerRightVane--;
				}
			}
			nUpperLeftToLowerRightVane /= 2;


			//Step 1.)  Get m
			nM = (double)Game.m_cSQRH_TRN / -(double)Game.m_cSQRW_TRN;
			//Step 2.)  Get b.... y-Mx = b
			nB = nGamePosY - nM * nGamePosX;
			//Step 3.)  Get the x-Intercept, -b / m = x
			nXIntercept2 = -nB / nM;
			//This tells us where it hits the X axis, now translate that to our "vane"
			nUpperRightToLowerLeftVane = (long)( nXIntercept2 / nWRadius ) - 1;
			nUpperRightToLowerLeftVane /= 2;


			nScreenX = nUpperLeftToLowerRightVane*2 + Math.Abs(nUpperLeftToLowerRightVane-nUpperRightToLowerLeftVane);
			nScreenY = Math.Abs( nUpperLeftToLowerRightVane-nUpperRightToLowerLeftVane ) / 2;
		}


		public static void MoveMMTowordsGoal( Island oIsland,double nElapsedTime,
											  long nCurX,long nCurY,double nCurXOffset,double nCurYOffset,
											  long nTrgtX,long nTrgtY,double nTrgtXOffset,double nTrgtYOffset,
											  ref long nNewX,ref long nNewY,ref double nNewXOffset,ref double nNewYOffset,ref long nNewDirection )
		{
			double nAngle = 0;
			System.Drawing.Rectangle oScreenCoords;
			System.Drawing.Rectangle oNewScreenCoords;
			System.Drawing.Rectangle oCurPos;
			System.Drawing.Rectangle oTrgtPos;
			long nTempX = 0,nTempY = 0;


			//Setup our initial location
			nNewX = nCurX;
			nNewY = nCurY;
			nNewXOffset = nCurXOffset;
			nNewYOffset = nCurYOffset;
			oCurPos = GetAbsoluteTerrainPosFromSquare( oIsland,nNewX,nNewY );
			oTrgtPos = GetAbsoluteTerrainPosFromSquare( oIsland,nTrgtX,nTrgtY );

			//Move towords our target
			nAngle = DSMath.CalculateRadAngle( (double)oCurPos.X + ( nNewXOffset / Game.m_cSQRW_TRN ), 
											   (double)oCurPos.Y + ( nNewYOffset / Game.m_cSQRH_TRN ),
											   (double)oTrgtPos.X + ( nTrgtXOffset / Game.m_cSQRW_TRN ), 
											   (double)oTrgtPos.Y + ( nTrgtYOffset / Game.m_cSQRH_TRN ) ); 

			//Calculate our new direction
			nNewDirection = (long)Math.Floor( ( nAngle + ( (double)Math.PI / 8 ) ) / ( (double)Math.PI / 4 ) );
			if( nAngle > ( (double)Math.PI * 2 ) - ( (double)Math.PI / 8 ) )
			{
				nNewDirection = 0;
			}


			if( nNewX != nTrgtX || nNewY != nTrgtY )
			{
				nNewXOffset += Math.Cos( nAngle ) * 10 * nElapsedTime;
				nNewYOffset += Math.Sin( nAngle ) * 10 * nElapsedTime;
					
				//Have we moved over a square?
				oScreenCoords = Game.GetAbsoluteTerrainPosFromSquare( oIsland,nNewX,nNewY );
				Game.GetSquareFromScrPos( oIsland,
										  new Vector2( (float)(oScreenCoords.X+nNewXOffset),
													   (float)(oScreenCoords.Y+nNewYOffset) ),
										  ref nTempX, ref nTempY );
				oNewScreenCoords = Game.GetAbsoluteTerrainPosFromSquare( oIsland,nTempX,nTempY );

				if( nNewX != nTempX || nNewY != nTempY )
				{
					nNewX = nTempX;
					nNewY = nTempY;
					nNewXOffset = oScreenCoords.X + nNewXOffset - oNewScreenCoords.X;
					nNewYOffset = oScreenCoords.Y + nNewYOffset - oNewScreenCoords.Y;
				}
			}
			else
			{
				if( Math.Abs( nNewXOffset ) < Math.Abs( Math.Cos( nAngle ) * 10 * nElapsedTime ) )
				{
					nNewXOffset = 0;
				}
				else
				{
					nNewXOffset += Math.Cos( nAngle ) * 10 * nElapsedTime;
				}
				if( Math.Abs( nNewYOffset ) < Math.Abs( Math.Sin( nAngle ) * 10 * nElapsedTime ) )
				{
					nNewYOffset = 0;
				}
				else
				{
					nNewYOffset += Math.Sin( nAngle ) * 10 * nElapsedTime;
				}
			}

		}


		public static void RenderMissile( DSGameEngine oGameEngine, System.Drawing.Rectangle oMissleTargetPos, double nAngle, long nColor )
		{
			System.Drawing.Rectangle oSource = Rectangle.Empty;


			oSource = new Rectangle( 0,0,16,64 );
			oGameEngine.RenderTexture2D( Game.m_cGOB_MISSLEIMG_TABLE,oSource,
										oMissleTargetPos,new Vector2(),nAngle,0,false,
										System.Drawing.Color.White.ToArgb() ); 

			oSource = new Rectangle( 16,0,16,64 );
			oGameEngine.RenderTexture2D( Game.m_cGOB_MISSLEIMG_TABLE,oSource,
										oMissleTargetPos,new Vector2(),nAngle,0,false,
										(int)nColor ); 
		}


		#region Properties
		public DSSortedList Islands
		{
			get
			{
				return( m_oIslands );
			}
		}

		public MetalMarines App
		{
			get
			{
				return( m_oApp );
			}
			set
			{
				m_oApp = value;
			}
		}
		public DSGameEngine GameEngine
		{
			get
			{
				return( m_oGameEngine );
			}
			set
			{
				m_oGameEngine = value;

				if( m_oAlertManager == null )
				{
					m_oAlertManager = new AlertManager( value );
				}
			}
		}
		public static DSGobFile GobFile
		{
			get
			{
				return( m_oGameGobFile );
			}
			set
			{
				m_oGameGobFile = value;
			}
		}
		public DSSortedList MissleAttacks
		{
			get
			{
				return( m_oMissleAttacks );
			}
			set
			{
				m_oMissleAttacks = value;
			}
		}
		public DSSortedList MetalMarineAttacks
		{
			get
			{
				return( m_oMMAttacks );
			}
			set
			{
				m_oMMAttacks = value;
			}
		}
		public DSSortedList Explosions
		{
			get
			{
				return( m_oExplosions );
			}
			set
			{
				m_oExplosions = value;
			}
		}
		public DSSortedList AIs
		{
			get
			{
				return( m_oAIs );
			}
			set
			{
				m_oAIs = value;
			}
		}
		public DateTime GameEndTime
		{
			get
			{
				return( m_dtGameEndTime );
			}
		}
		public Island Winners
		{
			get
			{
				return( m_oWinningTeam );
			}
		}
		/*public long NumberOfConstructionSoundsPlaying
		{
			get
			{
				return( m_nNumberOfConstructionSoundsPlaying );
			}
			set
			{
				m_nNumberOfConstructionSoundsPlaying = value;
			}
		}*/
		public string IslandFileName
		{
			get
			{
				return( m_sIslandFileName );
			}
			set
			{
				m_sIslandFileName = value;
			}
		}

		public AlertManager AlertManager
		{
			get
			{
				return( m_oAlertManager );
			}
			set
			{
				m_oAlertManager = value;
			}
		}

		public bool UseBuildingRot
		{
			get
			{
				return( m_bUseBuildingRot );
			}
			set
			{
				m_bUseBuildingRot = value;
			}
		}

		public int CurrentSinglePlayerLevelID
		{
			get
			{
				return( m_nCurrentSinglePlayerLevelID );
			}
			set
			{
				m_nCurrentSinglePlayerLevelID = value;
			}
		}


		public bool Debug_InstantBuild
		{
			get
			{
				return( m_cINSTANTBUILD );
			}
			set
			{
				m_cINSTANTBUILD = value;
			}
		}
		public bool Debug_RenderGrid
		{
			get
			{
				return( m_cRENDERGRID );
			}
			set
			{
				m_cRENDERGRID = value;
			}
		}
		public bool Debug_MMNeverReturnHome
		{
			get
			{
				return( m_cMMNEVERRETURNHOME );
			}
			set
			{
				m_cMMNEVERRETURNHOME = value;
			}
		}
		public bool Debug_AutoWinForOnePlayer
		{
			get
			{
				return( m_cAUTOWINFORONEPLAYER );
			}
			set
			{
				m_cAUTOWINFORONEPLAYER = value;
			}
		}
		public bool Debug_CanAttackOurselves
		{
			get
			{
				return( m_cCANATTACKOURSELVES );
			}
			set
			{
				m_cCANATTACKOURSELVES = value;
			}
		}
		public bool Debug_AllowZoom
		{
			get
			{
				return( m_cALLOWZOOM );
			}
			set
			{
				m_cALLOWZOOM = value;
			}
		}
		public bool Debug_FPS
		{
			get
			{
				return( m_cSHOWFPS );
			}
			set
			{
				m_cSHOWFPS = value;

				if( m_oGameEngine.DirectPlay != null )
				{
					if( m_cSHOWFPS == true )
					{
						m_oGameEngine.DirectPlay.SecondsBetweenPings = 5;
					}
					else
					{
						m_oGameEngine.DirectPlay.SecondsBetweenPings = 0;
					}
				}
			}
		}
		public bool Debug_FullVision
		{
			get
			{
				return( m_cFULLVISION );
			}
			set
			{
				m_cFULLVISION = value;
			}
		}		
		#endregion
	}
}	