// Archontia Anna Tsapanidou, NTU - MSc Computer Games Systems 2008-2009

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using TankAttack;

namespace TankAttack
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
	public class TankAttackGame : Microsoft.Xna.Framework.Game
	{

		#region Class member variables

		public GraphicsDeviceManager graphics;

		public enum GameState { Idle, Intro, DefineKeys, Instructions, Loading, Playing, LevelComplete, GameOver, GameComplete, Transition }
		public GameState currentState = GameState.Idle;
		public GameState targetState = GameState.Idle;
		public SpriteBatch spriteBatch;
		public Camera camera;
		Matrix world = Matrix.Identity;
		bool twoPlayerMode;
		private Vector2 centreVector;
		private int viewportWidth;
		private int viewportHeight;
		Ground ground;
		Level currentLevel;
		Model groundModel;
		Model steelWallModel;
		Model brickWallModel;
		Model borderWallModel;
		Model baseModel;
		Model base2Model;
		Model heroModel;
		Model hero2Model;
		Model enemyModel;
		Model sphereModel;
		Model bulletModel;
		Model bonusModel;

		Texture2D reinforced;
		Texture2D introTexture;
		Texture2D instructionsTexture;
		Texture2D groundTexture;
		Texture2D steelWallTexture;
		Texture2D brickWallTexture;
		Texture2D borderWallTexture;
		Texture2D baseTexture;
		Texture2D gameOverTexture;
		Texture2D lvlCompleteTexture;
		Texture2D stateTransitionTexture;
		Texture2D enemyBaseTexture;

		List<Texture2D> bonusTextures;
		List<Wall> wallList;
		List<Tank> tanks;
		List<BonusItem> bonusItems;
		TankHuman p1 = null;
		TankHuman p2 = null;
		int lvlCounter;

		public int[,] lvl1map;
		public int[,] lvl2map;
		public int[,] lvl3map;
		public int[] lvl1enemy;
		public int[] lvl2enemy;
		public int[] lvl3enemy;
		List<Model> tankModels;
		SpriteFont arial, arialBig;

		ParticleSystem WallExplosionParticles;
		ParticleSystem BaseExplosionParticles;
		ParticleSystem TankExplosionParticles;
		ParticleSystem BulletExplosionParticles;
		ParticleSystem ShieldExplosionParticles;
		ParticleSystem explosionParticles;
		ParticleSystem explosionSmokeParticles;
		ParticleSystem projectileTrailParticles;

		List<Projectile> projectiles;
		TimeSpan timeToNextProjectile = TimeSpan.Zero;

		GameMenu menu;
		GameMenu submenu;
		KeyboardControl p1Key;
		KeyboardControl p2Key;
		GamePadControl p1Pad;
		GamePadControl p2Pad;
		InputControl p1Ctrl;
		InputControl p2Ctrl;

		int enemySpawnTime = 0;
		int bonusSpawnTime = 0;
		int p1ShieldOnTime = 0;
		int p2ShieldOnTime = 0;
		int p1SpawnTime = GameConstants.heroSpawnTime;
		int p2SpawnTime = GameConstants.heroSpawnTime;
		int menuDelayTime = 0;
		int freezeCPUTime = GameConstants.freezeCPUTime;
		int friendlyFreezeTime = GameConstants.friendlyFreezeTime;
		int stateTransitionDelay = 0;
		int overlayBaseHeroTextSpawn = GameConstants.overlayTextDuration;
		int overlayBaseEnemyTextSpawn = GameConstants.overlayTextDuration;
		int overlayBonusTextSpawn = GameConstants.overlayTextDuration;

		bool frozenCPU = false;
		bool friendlyFrozen = false;
		
		bool stateTransitionDelayActive = false;
		bool stateTransitionFadeActive = false;
		bool showBaseHeroOverlay = false;
		bool showBaseEnemyOverlay = false;
		bool showBonusOverlay = false;
		
		bool cleanAll = false;
		bool cleanLvl = false;

		List<Vector3> spawnPointList;
		Vector2 baseHeroPos;
		Vector2 baseEnemyPos;

		private static Random random = new Random();
		int p1Health, p2Health;
		int p1Lives, p2Lives;
		int p1Points, p2Points;
		int enemiesRemaining;
		int baseHeroHealth;
		int baseEnemyHealth;
		string bonusText;


		AudioEngine audioEngine;
		public WaveBank waveBank;
		public SoundBank soundBank;
		public Cue introCue, implosionCue, weaponCue, ingameCue, plopCue, explosionCue, gameCompleteCue;

		#endregion

		#region Constructor
		public TankAttackGame()
		{
			graphics = new GraphicsDeviceManager(this);
			graphics.PreferMultiSampling = true;
			Content.RootDirectory = "Content";
			viewportWidth = 1024;
			viewportHeight = 768;
		}

		#endregion

		#region Initialise
		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		protected override void Initialize()
		{
			// TODO: Add your initialization logic here
			InitGraphicsMode(viewportWidth, viewportHeight, false);

			camera = new Camera(this, new Vector3(0, 185, 180), new Vector3(0, 0, 0), Vector3.Up);
			//	camera = new Camera(this, new Vector3(0, 2, 300), new Vector3(0, 0, 0), Vector3.Up);
			//	camera = new Camera(this, new Vector3(0, 185, 0), new Vector3(0, 0, 50), Vector3.Up);
			Components.Add(camera);
			tankModels = new List<Model>();
			tanks = new List<Tank>();
			spawnPointList = new List<Vector3>();
			bonusItems = new List<BonusItem>();
			bonusTextures = new List<Texture2D>();
			wallList = new List<Wall>();
            

			WallExplosionParticles = new WallExplosionParticleSystem(this, Content);
			BaseExplosionParticles = new BaseExplosionParticleSystem(this, Content);
			TankExplosionParticles = new TankExplosionParticleSystem(this, Content);
			BulletExplosionParticles = new BulletExplosionParticleSystem(this, Content);
			ShieldExplosionParticles = new ShieldExplosionParticleSystem(this, Content);

			explosionParticles = new ExplosionParticleSystem(this, Content);
			explosionSmokeParticles = new ExplosionSmokeParticleSystem(this, Content);
			projectileTrailParticles = new ProjectileTrailParticleSystem(this, Content);

			// Set the draw order so the explosions and fire
			// will appear over the top of the smoke.
			WallExplosionParticles.DrawOrder = 100;
			BaseExplosionParticles.DrawOrder = 200;
			TankExplosionParticles.DrawOrder = 300;
			BulletExplosionParticles.DrawOrder = 400;
			ShieldExplosionParticles.DrawOrder = 500;

			// Register the particle system components.
			Components.Add(WallExplosionParticles);
			Components.Add(BaseExplosionParticles);
			Components.Add(TankExplosionParticles);
			Components.Add(BulletExplosionParticles);
			Components.Add(ShieldExplosionParticles);
			Components.Add(explosionParticles);
			Components.Add(explosionSmokeParticles);
			Components.Add(projectileTrailParticles);


			projectiles = new List<Projectile>();

            // TODO: put the maps and enemy lists into files and 
            // load them from there
			lvl1map = new int[15, 15] {
										{ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 },
										{ 9, 6, 1, 0, 0, 0, 6, 4, 6, 0, 0, 0, 1, 6, 9 }, 
										{ 9, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 9 },
										{ 9, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 9 },
										{ 9, 0, 1, 1, 1, 0, 1, 0, 1, 0, 2, 0, 0, 0, 9 },
										{ 9, 0, 1, 0, 0, 0, 1, 0, 1, 0, 2, 0, 1, 0, 9 },
										{ 9, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 9 },
										{ 9, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 9 },
										{ 9, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 9 },
										{ 9, 2, 2, 2, 2, 0, 1, 0, 1, 0, 1, 0, 2, 2, 9 },
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 9 },
										{ 9, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 9 },
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 9 },
										{ 9, 0, 0, 0, 0, 0, 7, 5, 8, 0, 0, 0, 0, 0, 9 },
										{ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }
									};
			
			lvl2map = new int[15, 15] { 
										{ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 2, 4, 2, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 2, 5, 2, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 7, 0, 8, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }
									};

			lvl3map = new int[15, 15] { 
										{ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }, 
										{ 9, 4, 1, 1, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 9 }, 
										{ 9, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 0, 1, 1, 2, 1, 1, 0, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 0, 1, 7, 2, 8, 1, 0, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 0, 1, 1, 2, 1, 1, 0, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 9 }, 
										{ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 5, 9 }, 
										{ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 } 
									};
			
			lvl1enemy = new int[] { 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 3, 1, 1, 1, 3 };
			lvl2enemy = new int[] { 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 3, 2, 2, 3, 3 };
			lvl3enemy = new int[] { 2, 2, 2, 1, 1, 2, 3, 3, 1, 2, 3, 2, 2, 2, 1, 3, 3, 2, 3, 3 };

			menu = new GameMenu(Color.YellowGreen, Color.Red, Content.Load<SpriteFont>(@"Fonts/Arial"));
			menu.addMenuItem("Single Player Mode", new Vector2(50, 500));
			menu.addMenuItem("Two Player Co-op Mode", new Vector2(50, 540));
			menu.addMenuItem("Define Controls", new Vector2(50, 580));
			menu.addMenuItem("Instructions", new Vector2(50, 620));
			menu.addMenuItem("Exit", new Vector2(50, 660));
			submenu = new GameMenu(Color.YellowGreen, Color.Red, Content.Load<SpriteFont>(@"Fonts/Arial"));
			submenu.addMenuItem("Keyboard", new Vector2(60, 460));
			submenu.addMenuItem("Joypad", new Vector2(60, 500));
			submenu.addMenuItem("Keyboard", new Vector2(60, 580));
			submenu.addMenuItem("Joypad", new Vector2(60, 620));
			p1Key = new KeyboardControl();
			p2Key = new KeyboardControl();
			p1Pad = new GamePadControl();
			p2Pad = new GamePadControl();
			p1Ctrl = p1Key;
			p2Ctrl = p2Key;

			p2Key.Up = Keys.Up;
			p2Key.Down = Keys.Down;
			p2Key.Left = Keys.Left;
			p2Key.Right = Keys.Right;
			p2Key.Fire = Keys.RightControl;
			p2Key.Menu = Keys.Escape;

			base.Initialize();
		}
		#endregion

		#region LoadContent
		/// <summary>
		/// LoadContent will be called once per game and is the place to load
		/// all of your content.
		/// </summary>
		protected override void LoadContent()
		{
			// Create a new SpriteBatch, which can be used to draw textures.
			spriteBatch = new SpriteBatch(GraphicsDevice);
			// TODO: use this.Content to load your game content here
			introTexture = Content.Load<Texture2D>(@"Textures\intro");
			gameOverTexture = Content.Load<Texture2D>(@"Textures\gameover");
			reinforced = Content.Load<Texture2D>(@"Textures\phoenixSteel");
			groundTexture = Content.Load<Texture2D>(@"Textures\ground2");
			steelWallTexture = Content.Load<Texture2D>(@"Textures\steel2");
			instructionsTexture = Content.Load<Texture2D>(@"Textures\Instructions");
			brickWallTexture = Content.Load<Texture2D>(@"Textures\brick");
			//brickWallTexture.GenerateMipMaps(TextureFilter.Anisotropic);
			borderWallTexture = Content.Load<Texture2D>(@"Textures\sand");
			baseTexture = Content.Load<Texture2D>(@"Textures\phoenix");
			enemyBaseTexture = Content.Load<Texture2D>(@"Textures\enemyBase");
			lvlCompleteTexture = Content.Load<Texture2D>(@"Textures\LevelComplete");
			groundModel = Content.Load<Model>(@"Models\ground");
			steelWallModel = Content.Load<Model>(@"Models\steelwall");
			brickWallModel = Content.Load<Model>(@"Models\brickwall");
			borderWallModel = Content.Load<Model>(@"Models\borderwall");
			bulletModel = Content.Load<Model>(@"Models\bullet");
			baseModel = Content.Load<Model>(@"Models\basewall");
			base2Model = Content.Load<Model>(@"Models\basewall");
			heroModel = Content.Load<Model>(@"Models\olifantRed"); //olifantRed2");
			hero2Model = Content.Load<Model>(@"Models\olifantBlue"); //olifantRed2");
			enemyModel = Content.Load<Model>(@"Models\olifantPurple"); //olifantRed2");
			sphereModel = Content.Load<Model>(@"Models\sphere");
			bonusModel = Content.Load<Model>(@"Models\bonusbox");

			for (int i = 0; i < 8; i++)
			{
				bonusTextures.Add(new Texture2D(GraphicsDevice, 128, 128));
			}
			bonusTextures[1] = Content.Load<Texture2D>(@"Textures\shield");
			bonusTextures[2] = Content.Load<Texture2D>(@"Textures\freeze");
			bonusTextures[3] = Content.Load<Texture2D>(@"Textures\health");
			bonusTextures[4] = Content.Load<Texture2D>(@"Textures\upgrade");
			bonusTextures[5] = Content.Load<Texture2D>(@"Textures\protectbase");
			bonusTextures[6] = Content.Load<Texture2D>(@"Textures\bomb");
			bonusTextures[7] = Content.Load<Texture2D>(@"Textures\extralife");

			arial = Content.Load<SpriteFont>(@"Fonts\Arial");
			arialBig = Content.Load<SpriteFont>(@"Fonts\ArialBig");

			ground = new Ground(groundTexture);
			ground.game = this;

			audioEngine = new AudioEngine(@"Content\Audio\TankAttackSound.xgs");
			waveBank = new WaveBank(audioEngine, @"Content\Audio\Wave Bank.xwb");
			soundBank = new SoundBank(audioEngine, @"Content\Audio\Sound Bank.xsb");
						
			centreVector = new Vector2((viewportWidth - introTexture.Width) / 2, (viewportHeight - introTexture.Height) / 2);
		}
		#endregion

		#region UnloadContent
		/// <summary>
		/// UnloadContent will be called once per game and is the place to unload
		/// all content.
		/// </summary>       
		protected override void UnloadContent()
		{
			// TODO: Unload any non ContentManager content here
		}
		#endregion

		#region Main Update
		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input, and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Update(GameTime gameTime)
		{
			// TODO: Add your update logic here
			
			if (Keyboard.GetState().IsKeyDown(Keys.F1))
			{
				graphics.ToggleFullScreen();
			}
					
			switch (currentState)
			{
				case GameState.Idle:
					currentState = GameState.Intro;
					introCue = soundBank.GetCue("intro");
					introCue.Play();
					menuDelayTime = 0;
					break;

				case GameState.Intro:
					menu.update(gameTime);
					UpdateIntro(gameTime);
					break;

				case GameState.DefineKeys:
					submenu.update(gameTime);
					UpdateDefineKeys(gameTime);
					break;

				case GameState.Instructions:
					UpdateInstructions(gameTime);
					break;

				case GameState.Loading:
					UpdateLoading();
					currentState = GameState.Playing;
					ingameCue = soundBank.GetCue("ingame");
					ingameCue.Play();
					break;

				case GameState.Playing:
					UpdatePlaying(gameTime);
					break;

				case GameState.LevelComplete:
					UpdateLvlComplete(gameTime);
					break;

				case GameState.GameOver:
					UpdateGameOver(gameTime);
					break;

				case GameState.GameComplete:
					UpdateGameComplete(gameTime);
					UpdateExplosions(gameTime);
					UpdateProjectiles(gameTime);
					break;
				case GameState.Transition:
					if (stateTransitionDelayActive == false)
					{
						stateTransitionDelay = GameConstants.transitionDelay;
						stateTransitionDelayActive = true;
					}
					else if (stateTransitionDelay <= 0)
					{
						stateTransitionDelayActive = false;
						if (cleanLvl == true)
						{
							cleanLvl = false;
							cleanUp(cleanAll);
							cleanAll = false;
						}
						currentState = targetState;
					}
					break;
			}
			if (stateTransitionDelayActive == true)
			{
				stateTransitionDelay -= gameTime.ElapsedGameTime.Milliseconds;
			}
			audioEngine.Update();
			base.Update(gameTime);
		}
		#endregion

		#region ValidateMove
		private bool ValidateMove(Tank movingObj, Vector3 moveVector)
		{
			BoundingBox newBox;
			if (moveVector.X == 0)
			{
				newBox = new BoundingBox(movingObj.Position + moveVector - new Vector3(2.2f, 0.0f, 4.8f), movingObj.Position + moveVector + new Vector3(2.2f, 0.0f, 4.8f));
				if (moveVector.Z > 0)
				{
					movingObj.Direction = new Vector3(0, 0, 1);
				}
				else
				{
					movingObj.Direction = new Vector3(0, 0, -1);
				}
			}
			else if (moveVector.Z == 0)
			{
				newBox = new BoundingBox(movingObj.Position + moveVector - new Vector3(4.8f, 0.0f, 2.2f), movingObj.Position + moveVector + new Vector3(4.8f, 0.0f, 2.2f));
				if (moveVector.X > 0)
				{
					movingObj.Direction = new Vector3(1, 0, 0);
				}
				else
				{
					movingObj.Direction = new Vector3(-1, 0, 0);
				}
			}
			else
				return false;

			foreach (Wall wObj in wallList)
			{
				if (wObj.alive)
				{
					if (wObj.Intersect(newBox))
						return false;

				}
			}
			foreach (Tank tank in tanks)
			{
				if (tank != movingObj)
				{
					if (tank.boundingBox.Intersects(newBox))
						return false;
				}
			}

			foreach (BonusItem bonusItem in bonusItems)
			{
				if (movingObj.ttype == GameConstants.TankType.HUMAN)
				{
					if (bonusItem.boundingBox.Intersects(newBox))
					{
						bonusItem.alive = false;
						plopCue = soundBank.GetCue("plop");
						plopCue.Play();

						switch (bonusItem.type)
						{
							case GameConstants.BonusType.BOMB:
								foreach (Tank tank in tanks)
								{
									if (tank.ttype == GameConstants.TankType.CPU)
									{
										for (int i = 0; i < 50; i++)
										{
											TankExplosionParticles.AddParticle(tank.Position, Vector3.Zero);
										}
										tank.alive = false;
										movingObj.scoring(tank.points);
									}
								}
								showBonusOverlay = true;
								bonusText = "Bomb!";
								break;
							case GameConstants.BonusType.EXTRALIFE:
								movingObj.lives++;
								showBonusOverlay = true;
								bonusText = "Extra Life!";
								break;
							case GameConstants.BonusType.FREEZE:
								freezeCPUTime = GameConstants.freezeCPUTime;
								frozenCPU = true;
								showBonusOverlay = true;
								bonusText = "Enemy Freeze!";
								break;
							case GameConstants.BonusType.HEALTH:
								movingObj.gainHealth(15);
								showBonusOverlay = true;
								bonusText = "Extra Health!";
								break;
							case GameConstants.BonusType.PROTECTBASE:
								foreach (Wall w in wallList)
								{
									if (w.type == GameConstants.ObjectType.BASEHERO)
									{
										w.reinforced = true;
										w.texture = reinforced;
										break;
									}
								}
								showBonusOverlay = true;
								bonusText = "Base Reinforced!";
								break;
							case GameConstants.BonusType.SHIELD:
								movingObj.shieldOn = true;
								if (movingObj == p1)
								{
									p1ShieldOnTime = GameConstants.shieldOnTime;
								}
								else
								{
									p2ShieldOnTime = GameConstants.shieldOnTime;
								}
								showBonusOverlay = true;
								bonusText = "Shield!";
								break;
							case GameConstants.BonusType.UPGRADE:
								movingObj.upgrade();
								showBonusOverlay = true;
								bonusText = "Tank Upgrade!";
								break;
						}

					}
				}
			}
			movingObj.boundingBox = newBox;
			movingObj.Position += moveVector;
			return true;
		}
		#endregion

		#region Draw
		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Draw(GameTime gameTime)
		{
			GraphicsDevice.Clear(Color.Black);
			// TODO: Add your drawing code here

			string temp;

			BulletExplosionParticles.SetCamera(camera.view, camera.projection);
			WallExplosionParticles.SetCamera(camera.view, camera.projection);
			BaseExplosionParticles.SetCamera(camera.view, camera.projection);
			TankExplosionParticles.SetCamera(camera.view, camera.projection);
			ShieldExplosionParticles.SetCamera(camera.view, camera.projection);
			TankExplosionParticles.SetCamera(camera.view, camera.projection);

			explosionParticles.SetCamera(camera.view, camera.projection);
			explosionSmokeParticles.SetCamera(camera.view, camera.projection);
			projectileTrailParticles.SetCamera(camera.view, camera.projection);


			GraphicsDevice.RenderState.DepthBufferEnable = true;
			GraphicsDevice.RenderState.AlphaBlendEnable = false;
			GraphicsDevice.RenderState.AlphaTestEnable = false;

			switch (currentState)
			{
				case GameState.Intro:
					spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
					DrawIntro();
					spriteBatch.End();
					menu.Draw(spriteBatch);
					break;

				case GameState.DefineKeys:
					spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
					DrawIntro();
					spriteBatch.DrawString(arial, "Player 1 > ", new Vector2(30, 420), Color.YellowGreen);
					spriteBatch.DrawString(arial, "Player 2 > ", new Vector2(30, 540), Color.YellowGreen);
					spriteBatch.End();
					submenu.Draw(spriteBatch);
					break;

				case GameState.Instructions:
					DrawInstructions();
					break;

				case GameState.Loading:
					temp = "Loading...";
					GraphicsDevice.Clear(Color.Black);
					spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
					spriteBatch.DrawString(arialBig, temp, new Vector2(viewportWidth / 2, viewportHeight / 2), Color.YellowGreen, 0, arialBig.MeasureString(temp) / 2, 1.0f, SpriteEffects.None, 1);
					spriteBatch.End();
					break;

				case GameState.Playing:
					DrawPlaying();
					spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
					spriteBatch.DrawString(arial, (1.0f / (gameTime.ElapsedGameTime.Milliseconds * 0.001f)).ToString() + " FPS", new Vector2(10, 10), Color.White);
					temp = "Level " + currentLevel.levelnum;
					spriteBatch.DrawString(arialBig, temp, new Vector2(viewportWidth / 2, 60), Color.YellowGreen, 0, arialBig.MeasureString(temp) / 2, 0.5f, SpriteEffects.None, 1);
					temp = "Player 1> Points: " + p1Points.ToString("00000") + " Lives: " + p1Lives.ToString("00") + "  Health: " + p1Health.ToString("000") + "%";
					spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, 680), Color.Red, 0, arial.MeasureString(temp) / 2, 1.5f, SpriteEffects.None, 1);
					temp = "Player 2> Points: " + p2Points.ToString("00000") + " Lives: " + p2Lives.ToString("00") + "  Health: " + p2Health.ToString("000") + "%";
					spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, 720), Color.Blue, 0, arial.MeasureString(temp) / 2, 1.5f, SpriteEffects.None, 1);
					temp = "Enemies Remaining: " + enemiesRemaining.ToString("00");
					spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, 120), Color.Purple, 0, arial.MeasureString(temp) / 2, 1.2f, SpriteEffects.None, 1);
					if (showBaseHeroOverlay == true)
					{
						temp = baseHeroHealth.ToString() + "%";
						spriteBatch.DrawString(arial, temp, baseHeroPos, Color.Red, 0, arial.MeasureString(temp) / 2, 3.5f, SpriteEffects.None, 1);
					}
					if (showBaseEnemyOverlay == true)
					{
						temp = baseEnemyHealth.ToString() + "%";
						spriteBatch.DrawString(arial, temp, baseEnemyPos, Color.Red, 0, arial.MeasureString(temp) / 2, 3.5f, SpriteEffects.None, 1);
					}
					if (showBonusOverlay == true)
					{
						temp = bonusText;
						spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth/5+40, viewportHeight/5+10), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 3.0f, SpriteEffects.None, 1);
					}
					spriteBatch.End();
					break;

				case GameState.LevelComplete:
					DrawLvlComplete();
					break;

				case GameState.GameOver:
					DrawGameOver();
					break;

				case GameState.GameComplete:
					DrawGameComplete();
					break;

				case GameState.Transition:
					if (stateTransitionTexture != null)
					{
						spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
						spriteBatch.Draw(stateTransitionTexture, new Vector2(0.0f, -128.0f), new Color(255, 255, 255, 255));
						spriteBatch.End();
					}
					break;
			}
			base.Draw(gameTime);

		}
		#endregion

		#region Initialise Window
		private bool InitGraphicsMode(int iWidth, int iHeight, bool bFullScreen)
		{
			// If we aren't using a full screen mode, the height and width of the window can
			// be set to anything equal to or smaller than the actual screen size.
			if (bFullScreen == false)
			{
				if ((iWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
					&& (iHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
				{
					graphics.PreferredBackBufferWidth = iWidth;
					graphics.PreferredBackBufferHeight = iHeight;
					graphics.IsFullScreen = bFullScreen;
					graphics.ApplyChanges();
					return true;
				}
			}
			else
			{
				// If we are using full screen mode, we should check to make sure that the display
				// adapter can handle the video mode we are trying to set.  To do this, we will
				// iterate thorugh the display modes supported by the adapter and check them against
				// the mode we want to set.
				foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
				{
					// Check the width and height of each mode against the passed values
					if ((dm.Width == iWidth) && (dm.Height == iHeight))
					{
						// The mode is supported, so set the buffer formats, apply changes and return
						graphics.PreferredBackBufferWidth = iWidth;
						graphics.PreferredBackBufferHeight = iHeight;
						graphics.IsFullScreen = bFullScreen;
						graphics.ApplyChanges();
						return true;
					}
				}
			}
			return false;
		}
		#endregion

		#region Introduction Screen Update
		private void UpdateIntro(GameTime gameTime)
		{
			if (menuDelayTime > 0)
			{
				menuDelayTime -= gameTime.ElapsedGameTime.Milliseconds;
			}
			else
			{
				List<InputControl.Actions> tempActions = new List<InputControl.Actions>();
				tempActions.AddRange(p1Key.GetAction());
				tempActions.AddRange(p1Pad.GetAction());
				tempActions.AddRange(p2Key.GetAction());
				tempActions.AddRange(p2Pad.GetAction());

				if (tempActions.Count > 0)
				{
					if (tempActions.First() == InputControl.Actions.UP && menu.menuUp == true)
					{
						menu.selectPrev();
						menu.menuUp = false;
						menuDelayTime = 50;
					}
					else if (tempActions.First() == InputControl.Actions.UP)
					{
						menu.menuUp = true;
						menuDelayTime = 50;
					}

					if (tempActions.First() == InputControl.Actions.DOWN && menu.menuDown == true)
					{
						menu.selectNext();
						menu.menuDown = false;
						menuDelayTime = 50;
					}
					else if (tempActions.First() == InputControl.Actions.DOWN)
					{
						menu.menuDown = true;
						menuDelayTime = 50;
					}

					if (tempActions.First() == InputControl.Actions.FIRE)
					{
						switch (menu.getSelectedNum())
						{
							case 0:
								twoPlayerMode = false;
								lvlCounter = 1;
								stateTransitionTexture = introTexture;
								stateTransitionFadeActive = true;
								targetState = GameState.Loading;
								currentState = GameState.Transition;
								introCue.Stop(AudioStopOptions.Immediate);
								break;
							case 1:
								twoPlayerMode = true;
								lvlCounter = 1;
								stateTransitionTexture = introTexture;
								stateTransitionFadeActive = true;
								targetState = GameState.Loading;
								currentState = GameState.Transition;
								introCue.Stop(AudioStopOptions.Immediate);
								break;
							case 2:
								submenu.menuDown = false;
								submenu.menuUp = false;
								stateTransitionTexture = introTexture;
								stateTransitionFadeActive = true;
								targetState = GameState.DefineKeys;
								currentState = GameState.Transition;
								break;
							case 3:
								stateTransitionTexture = introTexture;
								stateTransitionFadeActive = true;
								targetState = GameState.Instructions;
								currentState = GameState.Transition;
								break;
							case 4:
								Exit();
								break;
						}
					}
				}
			}

		}
		#endregion

		#region Define Keys Screen Update
		private void UpdateDefineKeys(GameTime gameTime)
		{
			if (menuDelayTime > 0)
			{
				menuDelayTime -= gameTime.ElapsedGameTime.Milliseconds;
			}
			else
			{
				List<InputControl.Actions> tempActions = new List<InputControl.Actions>();
				tempActions.AddRange(p1Key.GetAction());
				tempActions.AddRange(p1Pad.GetAction());
				tempActions.AddRange(p2Key.GetAction());
				tempActions.AddRange(p2Pad.GetAction());

				if (tempActions.Count > 0)
				{
					if (tempActions.First() == InputControl.Actions.UP && submenu.menuUp == true)
					{
						submenu.selectPrev();
						submenu.menuUp = false;
						menuDelayTime = 50;
					}
					else if (tempActions.First() == InputControl.Actions.UP)
					{
						submenu.menuUp = true;
						menuDelayTime = 50;
					}

					if (tempActions.First() == InputControl.Actions.DOWN && submenu.menuDown == true)
					{
						submenu.selectNext();
						submenu.menuDown = false;
						menuDelayTime = 50;
					}
					else if (tempActions.First() == InputControl.Actions.DOWN)
					{
						submenu.menuDown = true;
						menuDelayTime = 50;
					}

					if (tempActions.First() == InputControl.Actions.FIRE)
					{
						switch (submenu.getSelectedNum())
						{
							case 0:
								p1Ctrl = p1Key;
								break;
							case 1:
								p1Ctrl = p1Pad;
								p1Pad.playerIndex = PlayerIndex.One;
								if (p2Pad.playerIndex == PlayerIndex.One)
									p2Pad.playerIndex = PlayerIndex.Two;
								break;
							case 2:
								p2Ctrl = p2Key;
								break;
							case 3:
								p2Ctrl = p2Pad;
								if (p1Ctrl == p1Pad)
									p2Pad.playerIndex = PlayerIndex.Two;
								else
									p2Pad.playerIndex = PlayerIndex.One;
								break;
						}
					}
					if (tempActions.First() == InputControl.Actions.MENU)
					{
						menu.menuDown = false;
						menu.menuUp = false;
						stateTransitionTexture = introTexture;
						targetState = GameState.Intro;
						currentState = GameState.Transition;
					}
				}
			}
		}

		#endregion

		#region Instruction Screen State Update
		private void UpdateInstructions(GameTime gameTime)
		{
			List<InputControl.Actions> tempActions = p1Ctrl.GetAction();
			if (tempActions.Count > 0)
			{
				if (tempActions.First() == InputControl.Actions.MENU)
				{
					stateTransitionTexture = introTexture;
					targetState = GameState.Intro;
					currentState = GameState.Transition;
				}
			}
		}
		#endregion

		#region Loading State Update
		private void UpdateLoading()
		{
			Vector3 temp;

			switch (lvlCounter)
			{ 
				case 1:
					currentLevel = new Level(lvlCounter, lvl1map, lvl1enemy);
					break;
				case 2:
					currentLevel = new Level(lvlCounter, lvl2map, lvl2enemy);
					break;
				//case 3:
				//	currentLevel = new Level(lvlCounter, lvl3map, lvl3enemy);
				//	break;
			}

			bonusSpawnTime = GameConstants.bonusSpawnTime;
			enemySpawnTime = 0;
			enemiesRemaining = currentLevel.enemyList.Count;
			for (int i = 0; i < GameConstants.BOARDWIDTH; i++)
				for (int j = 0; j < GameConstants.BOARDHEIGHT; j++)
				{
					switch ((GameConstants.ObjectType)currentLevel.levelmap[j, i])
					{

						case GameConstants.ObjectType.EMPTY:
							break;
						case GameConstants.ObjectType.BORDER:
							wallList.Add(new Border(borderWallModel, new Vector3(i * 10 - 70, 0, j * 10 - 70), borderWallTexture, Color.White));
							wallList.Last().game = this;
							break;
						case GameConstants.ObjectType.STEEL:
							wallList.Add(new SteelWall(steelWallModel, new Vector3(i * 10 - 70, 0, j * 10 - 70), steelWallTexture, Color.White));
							wallList.Last().game = this;
							break;
						case GameConstants.ObjectType.BRICK:
							wallList.Add(new BrickWall(brickWallModel, new Vector3(i * 10 - 70, 0, j * 10 - 70), brickWallTexture, Color.Red));
							wallList.Last().game = this;
							break;
						case GameConstants.ObjectType.BASEENEMY:
							wallList.Add(new Base(base2Model, new Vector3(i * 10 - 70, 0, j * 10 - 70), GameConstants.ObjectType.BASEENEMY, enemyBaseTexture, Color.White));
							wallList.Last().health = 200;							
							wallList.Last().game = this;
							temp = GraphicsDevice.Viewport.Project(new Vector3(0, 0, 0), camera.projection, camera.view, Matrix.CreateTranslation(new Vector3(i * 10 - 70, 20, j * 10 - 70)));
							baseEnemyPos = new Vector2(temp.X, temp.Y);
							break;
						case GameConstants.ObjectType.BASEHERO:
							wallList.Add(new Base(baseModel, new Vector3(i * 10 - 70, 0, j * 10 - 70), GameConstants.ObjectType.BASEHERO, baseTexture, Color.White));
							wallList.Last().game = this;
							temp = GraphicsDevice.Viewport.Project( Vector3.Zero, camera.projection, camera.view, Matrix.CreateTranslation(new Vector3(i * 10 - 70, 20, j * 10 - 70)));
							baseHeroPos = new Vector2(temp.X, temp.Y);
							break;
						case GameConstants.ObjectType.HERO1:
							if (p1 == null)
							{
								tanks.Add(new TankHuman(heroModel,
												  GameConstants.FirePowerType.WEAK,
												  new Vector3(i * 10 - 70, 0, j * 10 - 70), 3, 1, p1Ctrl));
								p1 = (TankHuman)tanks.Last(); // pointer to player 1 object in the list
								tanks.Last().sphereModel = sphereModel;
								tanks.Last().bulletModel = bulletModel;
								tanks.Last().game = this;
								p1Health = 0;
								p1Lives = 0;
								p1Points = 0;
								p2Health = 0;
								p2Lives = 0;
								p2Points = 0;
							}
							else
							{
								tanks.Add(p1);
								p1.spawnPosition = new Vector3(i * 10 - 70, 0, j * 10 - 70);
								p1.Position = p1.spawnPosition;
							}
							break;

						case GameConstants.ObjectType.HERO2:
							if (twoPlayerMode == true)
							{
								if (p2 == null)
								{
									tanks.Add(new TankHuman(hero2Model,
													  GameConstants.FirePowerType.WEAK,
													  new Vector3(i * 10 - 70, 0, j * 10 - 70), 3, 2, p2Ctrl));
									p2 = (TankHuman)tanks.Last();
									//tanks.Last().sphereModel = sphereModel;
									tanks.Last().bulletModel = bulletModel;
									tanks.Last().game = this;
								}
								else
								{
									tanks.Add(p2);
									p2.spawnPosition = new Vector3(i * 10 - 70, 0, j * 10 - 70);
									p2.Position = p2.spawnPosition;
								}
							}
							break;
						case GameConstants.ObjectType.ENEMY:
							spawnPointList.Add(new Vector3(i * 10 - 70, 0, j * 10 - 70));
							break;
					}
				}
		}
		#endregion

		#region Playing State Update
		private void UpdatePlaying(GameTime gameTime)
		{
			foreach (Tank tank in tanks)
			{
				if (tank.alive == true && tank.frozen == false)
				{
					if (tank.ttype == GameConstants.TankType.CPU && frozenCPU == true)
						continue;
					if (tank.ttype == GameConstants.TankType.CPU)
						tank.control();
					List<InputControl.Actions> tempActions = tank.Ctrl.GetAction();
					foreach (InputControl.Actions action in tempActions)
					{
						switch (action)
						{
							case InputControl.Actions.MENU:
								stateTransitionTexture = null;
								cleanLvl = true;
								cleanAll = true;
								targetState = GameState.Idle;
								currentState = GameState.Transition;
								ingameCue.Stop(AudioStopOptions.Immediate);
								break;
							case InputControl.Actions.UP:
								if ( tank.ttype == GameConstants.TankType.CPU )
									((TankCPU)tank).bumped = !ValidateMove(tank, new Vector3(0, 0, -GameConstants.step));
								else
									ValidateMove(tank, new Vector3(0, 0, -GameConstants.step));
								break;
							case InputControl.Actions.DOWN:
								if (tank.ttype == GameConstants.TankType.CPU)
									((TankCPU)tank).bumped = !ValidateMove(tank, new Vector3(0, 0, GameConstants.step));
								else
									ValidateMove(tank, new Vector3(0, 0, GameConstants.step));
								break;
							case InputControl.Actions.LEFT:
								if (tank.ttype == GameConstants.TankType.CPU)
									((TankCPU)tank).bumped = !ValidateMove(tank, new Vector3(-GameConstants.step, 0, 0));
								else
									ValidateMove(tank, new Vector3(-GameConstants.step, 0, 0));
								break;
							case InputControl.Actions.RIGHT:
								if (tank.ttype == GameConstants.TankType.CPU)
									((TankCPU)tank).bumped = !ValidateMove(tank, new Vector3(GameConstants.step, 0, 0));
								else
									ValidateMove(tank, new Vector3(GameConstants.step, 0, 0));
								break;

							case InputControl.Actions.FIRE:
								//if (tank.ttype == GameConstants.TankType.HUMAN)
								//{
							//		weaponCue = soundBank.GetCue("weapon");
						//			weaponCue.Play();
					//			}
								tank.shoot();
                                if (tank.cueFlag == true)
                                {
                                    weaponCue = soundBank.GetCue("weapon");
                                    weaponCue.Play();
                                    tank.cueFlag = false;
                                }
                                break;
						}
					}
				}
				if (tank.ttype == GameConstants.TankType.HUMAN)
				{
					if (tank.playerIndex == 1) //(tank == p1)
					{
						p1Health = tank.health;
						p1Points = tank.getScore();
						p1Lives = tank.lives;
					}

					if (tank.playerIndex == 2)
					{
						p2Health = tank.health;
						p2Points = tank.getScore();
						p2Lives = tank.lives;

					}
					if (tank.shieldOn == true)
					{
						for (int i = 0; i < 10; i++)
						{
							ShieldExplosionParticles.AddParticle(tank.Position + new Vector3(0.0f, 0.0f, (i - 5) * 2), Vector3.Zero);
						}
					}
				}
			}

			UpdateBullets();
			BuryTheDead(gameTime);
			SpawnEnemies(gameTime);
			SpawnBonus(gameTime);
			CounterUpdate(gameTime);
		}
		#endregion

		#region Level Complete State Update
		private void UpdateLvlComplete(GameTime gameTime)
		{
			List<InputControl.Actions> tempActions = p1Ctrl.GetAction();
			if (tempActions.Count > 0)
			{
				if (tempActions.First() == InputControl.Actions.FIRE)
				{
					if (lvlCounter >= GameConstants.MAXLEVELS)
					{
						stateTransitionTexture = null;
						cleanLvl = true;
						cleanAll = true;
						targetState = GameState.GameComplete;
						currentState = GameState.Transition;
						ingameCue.Stop(AudioStopOptions.Immediate);
						gameCompleteCue = soundBank.GetCue("gameComplete");
						gameCompleteCue.Play();
					}
					else
					{
						lvlCounter++;
						stateTransitionTexture = null;
						cleanLvl = true;
						cleanAll = false;
						targetState = GameState.Loading;
						currentState = GameState.Transition;
					}
				}
			}
		}
		#endregion

		#region Game Complete State Update
		private void UpdateGameComplete(GameTime gameTime)
		{
			List<InputControl.Actions> tempActions = p1Ctrl.GetAction();
			if (tempActions.Count > 0)
			{
				if (tempActions.First() == InputControl.Actions.MENU)
				{
					stateTransitionTexture = null;
					targetState = GameState.Intro;
					currentState = GameState.Transition;
					gameCompleteCue.Stop(AudioStopOptions.Immediate);
					introCue = soundBank.GetCue("intro");
					introCue.Play();
				}
			}
		}
		#endregion

		#region Game Over State Update
		private void UpdateGameOver(GameTime gameTime)
		{
			List<InputControl.Actions> tempActions = p1Ctrl.GetAction();
			if (tempActions.Count > 0)
			{
				if (tempActions.First() == InputControl.Actions.MENU)
				{
					stateTransitionTexture = null;
					cleanAll = true;
					cleanLvl = true;
					targetState = GameState.Intro;
					currentState = GameState.Transition;
					ingameCue.Stop(AudioStopOptions.Immediate);
					introCue = soundBank.GetCue("intro");
					introCue.Play();
				}
			}
		}
		#endregion

		#region Counter Update
		private void CounterUpdate(GameTime gameTime)
		{
			if (p1 != null && p1.shieldOn == true)
			{
				p1ShieldOnTime -= gameTime.ElapsedGameTime.Milliseconds;
				if (p1ShieldOnTime > 0)
				{
					return;
				}
				else
				{
					p1ShieldOnTime = GameConstants.shieldOnTime;
					p1.shieldOn = false;
				}
			}

			if (p2 != null && p2.shieldOn == true)
			{
				p2ShieldOnTime -= gameTime.ElapsedGameTime.Milliseconds;
				if (p2ShieldOnTime > 0)
				{
					return;
				}
				else
				{
					p2ShieldOnTime = GameConstants.shieldOnTime;
					p2.shieldOn = false;
				}
			}
			if (frozenCPU == true)
			{
				freezeCPUTime -= gameTime.ElapsedGameTime.Milliseconds;
				if (freezeCPUTime < 0)
				{
					frozenCPU = false;
					freezeCPUTime = GameConstants.freezeCPUTime;
				}
			}
			if (friendlyFrozen == true)
			{
				friendlyFreezeTime -= gameTime.ElapsedGameTime.Milliseconds;
				if (friendlyFreezeTime < 0)
				{
					p1.frozen = false;
					p2.frozen = false;
					friendlyFrozen = false;
					friendlyFreezeTime = GameConstants.friendlyFreezeTime;
				}
			}
			if (showBaseHeroOverlay == true)
			{
				overlayBaseHeroTextSpawn -= gameTime.ElapsedGameTime.Milliseconds;
				if (overlayBaseHeroTextSpawn < 0)
				{
					showBaseHeroOverlay = false;
					overlayBaseHeroTextSpawn = GameConstants.overlayTextDuration;
				}
			}
			if (showBaseEnemyOverlay == true)
			{
				overlayBaseEnemyTextSpawn -= gameTime.ElapsedGameTime.Milliseconds;
				if (overlayBaseEnemyTextSpawn < 0)
				{
					showBaseEnemyOverlay = false;
					overlayBaseEnemyTextSpawn = GameConstants.overlayTextDuration;
				}
			}
			
			if (showBonusOverlay == true)
			{
				overlayBonusTextSpawn -= gameTime.ElapsedGameTime.Milliseconds;
				if (overlayBonusTextSpawn < 0)
				{
					showBonusOverlay = false;
					overlayBonusTextSpawn = GameConstants.overlayTextDuration;
				}
			}
		}
		#endregion

		#region Spawn enemies
		private void SpawnEnemies(GameTime gameTime)
		{
			int tempPoints;
			enemySpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
			if (enemySpawnTime > 0)
			{
				return;
			}
			enemySpawnTime = GameConstants.enemySpawnTime;
			GameConstants.FirePowerType fpType;
			if (currentLevel.enemyList.Count > 0)
			{
				Random random = new Random();
				int spawnPointIndex = random.Next(spawnPointList.Count);
				BoundingBox bBox = new BoundingBox(spawnPointList[spawnPointIndex] - new Vector3(10, 0, 10),
													spawnPointList[spawnPointIndex] + new Vector3(10, 0, 10));

				foreach (Tank tank in tanks)
				{
					if (tank.boundingBox.Intersects(bBox))
						return;
				}

				switch (currentLevel.enemyList.First())
				{
					case 1:
						fpType = GameConstants.FirePowerType.WEAK;
						tempPoints = 10;
						break;
					case 2:
						fpType = GameConstants.FirePowerType.NORMAL;
						tempPoints = 50;
						break;
					case 3:
						fpType = GameConstants.FirePowerType.STRONG;
						tempPoints = 150;
						break;
					default:
						fpType = GameConstants.FirePowerType.NONE;
						tempPoints = -1;
						break;
				}

				currentLevel.enemyList.RemoveAt(0);
				tanks.Add(new TankCPU(enemyModel, fpType, spawnPointList[spawnPointIndex], 0, new AIControl(), tempPoints));
				//tanks.Last().sphereModel = sphereModel;
				tanks.Last().bulletModel = bulletModel;
				tanks.Last().game = this;
			}
		}
		#endregion

		#region Spawn Bonus Items
		private void SpawnBonus(GameTime gameTime)
		{
			bonusSpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
			if (bonusSpawnTime > 0)
				return;
			bonusSpawnTime = GameConstants.bonusSpawnTime;
			Random random = new Random();
			int bonusSpawnX = 0;
			int bonusSpawnZ = 0;
			bool valid = false;

			while (!valid)
			{
				bonusSpawnX = random.Next(GameConstants.BOARDWIDTH);
				bonusSpawnZ = random.Next(GameConstants.BOARDHEIGHT);

				BoundingBox bBox = new BoundingBox(new Vector3(bonusSpawnX * 10 - 70, 0, bonusSpawnZ * 10 - 70) - new Vector3(10, 0, 10),
													new Vector3(bonusSpawnX * 10 - 70, 0, bonusSpawnZ * 10 - 70) + new Vector3(10, 0, 10));

				foreach (Tank tank in tanks)
				{
					if (!tank.boundingBox.Intersects(bBox))
						valid = true;
				}
				foreach (Wall wall in wallList)
				{
					if (!wall.boundingBox.Intersects(bBox))
						valid = true;
				}
			}
			int bonusIndex = random.Next(7) + 1;

			bonusItems.Add(new BonusItem(bonusModel, (GameConstants.BonusType)bonusIndex, new Vector3(bonusSpawnX * 10 - 70, 0, bonusSpawnZ * 10 - 70), bonusTextures[bonusIndex], Color.WhiteSmoke));
		}
		#endregion

		#region Remove all the dead objects (aka BuryTheDead)
		private void BuryTheDead(GameTime gameTime)
		{
			List<Wall> wObjDelete = new List<Wall>();
			List<Tank> TankDelete = new List<Tank>();
			List<BonusItem> bonusItemDelete = new List<BonusItem>();

			foreach (Wall wObj in wallList)
			{
				if (wObj.alive == false)
				{
					wObjDelete.Add(wObj);

					if(wObj.type == GameConstants.ObjectType.BRICK || wObj.type == GameConstants.ObjectType.STEEL)
					{
						implosionCue = soundBank.GetCue("implosion");
						implosionCue.Play();
					}
					if (wObj.type == GameConstants.ObjectType.BASEHERO)
					{
						explosionCue = soundBank.GetCue("explosion");
						explosionCue.Play();
						stateTransitionTexture = null;
						targetState = GameState.GameOver;
						currentState = GameState.Transition;
					}
					if (wObj.type == GameConstants.ObjectType.BASEENEMY)
					{	
						explosionCue = soundBank.GetCue("explosion");
						explosionCue.Play();

						foreach (Tank tank in tanks)
						{
							if (tank.ttype == GameConstants.TankType.HUMAN && tank.alive == true || tank.lives > 0)
							{
								tank.scoring(1000);
								if (tank.playerIndex == 1)
								{
									p1Points = tank.getScore();
								}
								else
								{
									p2Points = tank.getScore();
								}
							}
						}
						stateTransitionTexture = null;
						targetState = GameState.LevelComplete;
						currentState = GameState.Transition;
						ingameCue.Stop(AudioStopOptions.Immediate);
					}
				}
			}
			foreach (Tank tank in tanks)
			{
				if (tank.alive == false)
				{
					if (tank.ttype == GameConstants.TankType.CPU)
					{
						TankDelete.Add(tank);
						enemiesRemaining--;
						if (enemiesRemaining == 0)
						{
							stateTransitionTexture = null;
							targetState = GameState.LevelComplete;
							currentState = GameState.Transition;
							ingameCue.Stop(AudioStopOptions.Immediate);
						}
					}
					if (tank.ttype == GameConstants.TankType.HUMAN)
					{
						if (twoPlayerMode == false)
						{
							tank.boundingBox.Max = new Vector3(0f, -100f, 0f);
							tank.boundingBox.Min = new Vector3(0f, -100f, 0f);
							
							p1SpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
							if (p1SpawnTime < 0)
							{
								if (tank.lives > 0)
								{
									tank.Position = ((TankHuman)tank).spawnPosition;
									tank.boundingBox.Min = tank.Position - new Vector3(0.0f, 0.0f, 2.0f);
									tank.boundingBox.Max = tank.Position + new Vector3(2.0f, 0.0f, 2.0f);
									tank.alive = true;
									p1SpawnTime = GameConstants.heroSpawnTime;
								}
								else
								{
									p1Health = 0;
									stateTransitionTexture = null;
									targetState = GameState.GameOver;
									currentState = GameState.Transition;
									ingameCue.Stop(AudioStopOptions.Immediate);
								}
							}
						}
						else
						{
							if (tank.playerIndex == 1)
							{
								p1SpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
								tank.boundingBox.Max = new Vector3(0f, -100f, 0f);
								tank.boundingBox.Min = new Vector3(0f, -100f, 0f);
								
								if (p1SpawnTime < 0)
								{
									if (tank.lives > 0)
									{
										tank.Position = ((TankHuman)tank).spawnPosition;
										tank.boundingBox.Min = tank.Position - new Vector3(0.0f, 0.0f, 2.0f);
										tank.boundingBox.Max = tank.Position + new Vector3(2.0f, 0.0f, 2.0f);
										tank.alive = true;
										p1SpawnTime = GameConstants.heroSpawnTime;
									}
									else
									{
										p1Health = 0;
									}
								}
							}
							else
							{
								tank.boundingBox.Max = new Vector3(0f, -100f, 0f);
								tank.boundingBox.Min = new Vector3(0f, -100f, 0f);
								p2SpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
								if (p2SpawnTime < 0)
								{
									if (tank.lives > 0)
									{
										tank.Position = ((TankHuman)tank).spawnPosition;
										tank.boundingBox.Min = tank.Position - new Vector3(0.0f, 0.0f, 2.0f);
										tank.boundingBox.Max = tank.Position + new Vector3(2.0f, 0.0f, 2.0f);
										tank.alive = true;
										p2SpawnTime = GameConstants.heroSpawnTime;
									}
									else
									{
										p2Health = 0;
									}
								}
							}
							if (p1Lives == 0 && p2Lives == 0)
							{
								stateTransitionTexture = null;
								targetState = GameState.GameOver;
								currentState = GameState.Transition;
								ingameCue.Stop(AudioStopOptions.Immediate);
							}
						}
					}
				}
			}
			foreach (BonusItem bonusItem in bonusItems)
			{
				if (bonusItem.alive == false)
				{
					bonusItemDelete.Add(bonusItem);
				}
			}
			foreach (Wall wObj in wObjDelete)
			{
				for (int i = 0; i < 25; i++)
				{
					WallExplosionParticles.AddParticle(wObj.Position, Vector3.Zero);
				}
				wallList.Remove(wObj);
			}
			foreach (Tank tank in TankDelete)
			{
				for (int i = 0; i < 50; i++)
				{
					TankExplosionParticles.AddParticle(tank.Position, Vector3.Zero);
				}
				tanks.Remove(tank);
				implosionCue = soundBank.GetCue("implosion");
				implosionCue.Play();
			}
			foreach (BonusItem bonusItem in bonusItemDelete)
			{
				bonusItems.Remove(bonusItem);
			}
		}
		#endregion

		#region Bullet Update
		private void UpdateBullets()
		{
			foreach (Tank tank in tanks)
			{
				if (tank.bullet != null)
				{
					tank.bullet.Position += tank.bullet.trajectory;
					tank.bullet.boundingBox.Max += tank.bullet.trajectory;
					tank.bullet.boundingBox.Min += tank.bullet.trajectory;
					foreach (Wall wObj in wallList)
					{
						if (wObj.alive)
						{
							if (wObj.Intersect(tank.bullet.boundingBox))
							{
								if (tank.ttype == GameConstants.TankType.CPU && wObj.type == GameConstants.ObjectType.BASEENEMY)
								{
									// nothing (friendly fire)
								}
								else
								{
									wObj.loseHealth(wObj.calculateHealth(tank.fptype));
									if (wObj.type == GameConstants.ObjectType.BASEHERO)
									{
										showBaseHeroOverlay = true;
									}
									if (wObj.type == GameConstants.ObjectType.BASEENEMY)
									{
										showBaseEnemyOverlay = true;
										/*
										int temp = (int) (tanks.Count()/3); //send some tanks to protect their base
										for (int i = 0; i < temp; i++)
										{
											foreach (Tank tank3 in tanks)
											{
												if (tank3.ttype == GameConstants.TankType.CPU)
												{
													tank3.alarm = true;
												}
											}
										}
										*/
									}
								}
								for (int i = 0; i < 10; i++)
								{
									BulletExplosionParticles.AddParticle(tank.bullet.Position, Vector3.Zero);
								}
								tank.bullet = null;
								return;
							}
						}
					}
					foreach (Tank tank2 in tanks)
					{
						if (tank != tank2)
						{
							if (tank.bullet.boundingBox.Intersects(tank2.boundingBox))
							{
								int tempLives = tank2.lives;
								if (tank2.ttype == GameConstants.TankType.HUMAN && tank.ttype == GameConstants.TankType.HUMAN)
								{
									tank.frozen = true;
									friendlyFreezeTime = GameConstants.friendlyFreezeTime;
									friendlyFrozen = true;
								}
								else if (tank2.ttype == GameConstants.TankType.CPU && tank.ttype == GameConstants.TankType.CPU)
								{
									// nothing;
								}
								else
								{
									tank2.loseHealth(tank2.calculateHealth(tank.fptype));
									for (int i = 0; i < 10; i++)
									{
										BulletExplosionParticles.AddParticle(tank.bullet.Position, Vector3.Zero);
									}
									
									if (tank2.lives < tempLives)
									{
										for (int i = 0; i < 50; i++)
										{
											TankExplosionParticles.AddParticle(tank2.Position, Vector3.Zero);
										}
										if (tank2.ttype == GameConstants.TankType.HUMAN)
										{
											implosionCue = soundBank.GetCue("implosion");
											implosionCue.Play();
										}
										if (tank.ttype == GameConstants.TankType.HUMAN && tank2.ttype == GameConstants.TankType.CPU)
										{
											tank.scoring(tank2.points);
										}
									}
								}

								tank.bullet = null;
								return;
							}
						}
					}
				}
			}
		}
		#endregion

		#region Explosion Update (for the fireworks)
		void UpdateExplosions(GameTime gameTime)
		{
			timeToNextProjectile -= gameTime.ElapsedGameTime;

			if (timeToNextProjectile <= TimeSpan.Zero)
			{
				// Create a new projectile once per second. The real work of moving
				// and creating particles is handled inside the Projectile class.
				projectiles.Add(new Projectile(explosionParticles,
											   explosionSmokeParticles,
											   projectileTrailParticles));

				timeToNextProjectile += TimeSpan.FromSeconds(1);
			}
		}
		#endregion

		#region Projectile (fireworks) Update
		void UpdateProjectiles(GameTime gameTime)
		{
			int i = 0;

			while (i < projectiles.Count)
			{
				if (!projectiles[i].Update(gameTime))
				{
					// Remove projectiles at the end of their life.
					projectiles.RemoveAt(i);
				}
				else
				{
					// Advance to the next projectile.
					i++;
				}
			}
		}
		#endregion

		#region Introduction Screen Draw
		private void DrawIntro()
		{
			spriteBatch.Draw(introTexture, centreVector, Color.White);
			spriteBatch.DrawString(arial, "Tank Attack!", new Vector2(10, 20), Color.YellowGreen, 0, Vector2.Zero, 8.5f, SpriteEffects.None, 1);
		}
		#endregion

		#region Instructions Screen Draw
		private void DrawInstructions()
		{
			string temp;

			GraphicsDevice.Clear(Color.White);
			spriteBatch.Begin();
			spriteBatch.Draw(instructionsTexture, centreVector, Color.White);
			spriteBatch.End();
		}
		#endregion

		#region Level Complete Screen Draw
		private void DrawLvlComplete()
		{

			string temp;
			GraphicsDevice.Clear(Color.Black);
			spriteBatch.Begin();
			spriteBatch.Draw(lvlCompleteTexture, centreVector, Color.White);
			temp = "Level " + currentLevel.levelnum.ToString("00") + " Complete!";
			spriteBatch.DrawString(arialBig, temp, new Vector2(viewportWidth / 2, viewportHeight / 5), Color.YellowGreen, 0, arialBig.MeasureString(temp) / 2, 1.0f, SpriteEffects.None, 1);
			temp = "P1 Points: " + p1Points.ToString("00000");
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 2.5f, SpriteEffects.None, 1);
			temp = "P2 Points: " + p2Points.ToString("00000");
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2 + 60), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 2.5f, SpriteEffects.None, 1);
			temp = "Press Fire to continue...";
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2 + 230), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 1.5f, SpriteEffects.None, 1);
			spriteBatch.End();
		}
		#endregion

		#region Game Complete Screen Draw
		private void DrawGameComplete()
		{
			string temp;
			GraphicsDevice.Clear(Color.Black);
			spriteBatch.Begin();			
			temp = "Congratulations!";
			spriteBatch.DrawString(arialBig, temp, new Vector2(viewportWidth / 2, viewportHeight / 5), Color.YellowGreen, 0, arialBig.MeasureString(temp) / 2, 1.0f, SpriteEffects.None, 1);
			temp = "You banished the hordes of evil tanks from your land";
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 3), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 2.0f, SpriteEffects.None, 1);
			temp = "Press Menu to continue...";
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2 + 230), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 1.5f, SpriteEffects.None, 1);
			spriteBatch.End();
		}
		#endregion

		#region Game Over Screen Draw
		private void DrawGameOver()
		{
			string temp;
			GraphicsDevice.Clear(Color.Black);
			temp = "Game Over!";
			spriteBatch.Begin();
			spriteBatch.Draw(gameOverTexture, centreVector, Color.White);
			spriteBatch.DrawString(arialBig, temp, new Vector2(viewportWidth / 2, viewportHeight / 5), Color.YellowGreen, 0, arialBig.MeasureString(temp) / 2, 1.5f, SpriteEffects.None, 1);
			temp = "P1 Points: " + p1Points.ToString("00000");
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 2.5f, SpriteEffects.None, 1);
			temp = "P2 Points: " + p2Points.ToString("00000");
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2 + 60), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 2.5f, SpriteEffects.None, 1);
			temp = "Level Reached: " + currentLevel.levelnum.ToString("00");
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2 + 120), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 2.5f, SpriteEffects.None, 1);
			temp = "Press Menu to continue...";
			spriteBatch.DrawString(arial, temp, new Vector2(viewportWidth / 2, viewportHeight / 2 + 230), Color.YellowGreen, 0, arial.MeasureString(temp) / 2, 1.5f, SpriteEffects.None, 1);
			spriteBatch.End();
		}
		#endregion

		#region Playing State Draw
		private void DrawPlaying()
		{
			ground.DrawTerrain(groundModel, camera);

			foreach (Wall wObj in wallList)
			{
				wObj.Draw(camera);
				if (wObj.type == GameConstants.ObjectType.BASEHERO)
				{
					baseHeroHealth = wObj.health;					
				}
				if (wObj.type == GameConstants.ObjectType.BASEENEMY)
				{
					baseEnemyHealth = (int) (wObj.health/2);
				}
			}

			foreach (Tank tank in tanks)
			{
				tank.Draw(camera);
				if (tank.bullet != null)
					tank.bullet.Draw(camera);
			}

			foreach (BonusItem bonusItem in bonusItems)
			{
				bonusItem.Draw(camera);
			}
		}
		#endregion

		#region Cleaning Up function
		private void cleanUp(bool complete)
		{
			currentLevel = null;
			wallList.Clear();
			tanks.Clear();
			bonusItems.Clear();

			enemySpawnTime = 0;
			bonusSpawnTime = 0;
			p1ShieldOnTime = 0;
			p2ShieldOnTime = 0;
			p1SpawnTime = GameConstants.heroSpawnTime;
			p2SpawnTime = GameConstants.heroSpawnTime;
			overlayBaseHeroTextSpawn = GameConstants.overlayTextDuration;
			overlayBaseEnemyTextSpawn = GameConstants.overlayTextDuration;
			overlayBonusTextSpawn = GameConstants.overlayTextDuration;

			freezeCPUTime = GameConstants.freezeCPUTime;
			friendlyFreezeTime = GameConstants.friendlyFreezeTime;

			frozenCPU = false;
			friendlyFrozen = false;
			showBaseHeroOverlay = false;
			showBaseEnemyOverlay = false;
			showBonusOverlay = false;

			spawnPointList.Clear();

			if (complete == true)
			{
				p1 = null;
				p2 = null;
				p1Health = 0;
				p2Health = 0;
				p1Lives = 0;
				p2Lives = 0;
				p1Points = 0;
				p2Points = 0;
			}
		}
		#endregion
	}

	#region Projectile Class (fireworks)
	class Projectile
	{
		#region Constants

		const float trailParticlesPerSecond = 200;
		const int numExplosionParticles = 30;
		const int numExplosionSmokeParticles = 50;
		const float projectileLifespan = 1.5f;
		const float sidewaysVelocityRange = 60;
		const float verticalVelocityRange = 40;
		const float gravity = 15;

		#endregion

		#region Fields

		ParticleSystem explosionParticles;
		ParticleSystem explosionSmokeParticles;
		ParticleEmitter trailEmitter;

		Vector3 position;
		Vector3 velocity;
		float age;

		static Random random = new Random();

		#endregion

		/// <summary>
		/// Constructs a new projectile.
		/// </summary>
		public Projectile(ParticleSystem explosionParticles,
						  ParticleSystem explosionSmokeParticles,
						  ParticleSystem projectileTrailParticles)
		{
			this.explosionParticles = explosionParticles;
			this.explosionSmokeParticles = explosionSmokeParticles;

			// Start at the origin, firing in a random (but roughly upward) direction.
			position = new Vector3(0.0f, 0.0f, 50.0f);

			velocity.X = (float)(random.NextDouble() - 0.5) * sidewaysVelocityRange;
			velocity.Y = (float)(random.NextDouble() + 0.5) * verticalVelocityRange;
			velocity.Z = (float)(random.NextDouble() - 0.5) * sidewaysVelocityRange;

			// Use the particle emitter helper to output our trail particles.
			trailEmitter = new ParticleEmitter(projectileTrailParticles,
											   trailParticlesPerSecond, position);
		}

		/// <summary>
		/// Updates the projectile.
		/// </summary>
		public bool Update(GameTime gameTime)
		{
			float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

			// Simple projectile physics.
			position += velocity * elapsedTime;
			velocity.Y -= elapsedTime * gravity;
			age += elapsedTime;

			// Update the particle emitter, which will create our particle trail.
			trailEmitter.Update(gameTime, position);

			// If enough time has passed, explode! Note how we pass our velocity
			// in to the AddParticle method: this lets the explosion be influenced
			// by the speed and direction of the projectile which created it.
			if (age > projectileLifespan)
			{
				for (int i = 0; i < numExplosionParticles; i++)
					explosionParticles.AddParticle(position, velocity);

				for (int i = 0; i < numExplosionSmokeParticles; i++)
					explosionSmokeParticles.AddParticle(position, velocity);

				return false;
			}

			return true;
		}
	}
	#endregion
}
