//	GamePlayState.cpp - The main gameplay state.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "sound/GenericFileSounder.h"
#include "TankYouGlobalData.h"
#include "PickupGenerator.h"
#include "ShockwaveEntity.h"
#include "drawing/Drawer.h"
#include "MineModifiers.h"
#include "GamePlayState.h"
#include "OSCDispatcher.h"
#include "Application.h"
#include "MiscHelper.h"
#include "MADButton.h"
#include "Turret.h"
#include "Zombie.h"
#include "Puddle.h"
#include "Random.h"
#include "Bullet.h"
#include "Pickup.h"
#include "Tank.h"
#include "Wall.h"
#include "Mine.h"
#include "Bee.h"

#include <cwctype>

//------------------------------------------------------------------------------
GameStateCreator<GamePlayState> GamePlayState::creator;

//------------------------------------------------------------------------------
GamePlayState::GamePlayState(Application& app, SounderPtr& sound):
GameState(app, sound),
backgroundColour(0.125f),
backgroundTweener(backgroundColour, 0.125f, 0.2f),
borderColour(0.1875f),
borderTweener(borderColour, 0.1875f, 0.2f),
shake(0.0f),
shakeIndex(0.0f),
recursiveShotProperties(false),
bulletPickupCollection(false),
bulletsTriggerMines(false),
camouflageMines(false),
turnBasedMode(false),
turnCounter(0),
turnPauseCounter(0),
turnTank(0),
turnBounce(0.0f),
lineLeft(1.0f, 1.0f, 0.33f),
lineLeftTweener(lineLeft, ThreeFloats(0.33f, 0.33f, 1.0f), 0.005f),
lineRight(1.0f, 0.33f, 0.33f),
lineRightTweener(lineRight, ThreeFloats(0.33f, 1.0f, 0.33f), 0.005f),
backgroundColLimit(1),
gameOver(false),
gameOverTweener(gameOverPos, gameOverPos, 0.01f),
console(false),
consoleReady(true),
sounder(dynamic_pointer_cast<GenericFileSounder>(sound))
{
	unsigned int i;
	float xPos = 96.0f;
	float yPos = 64.0f;
	const TwoFloats drawSize(getDrawingAreaSize());

	globalData = dynamic_pointer_cast<TankYouGlobalData>(getGlobalData());
	globalData->getModifierFactory().reset();

	if(snowflakes.getDecember())
		snowflakes.setNumSnowflakes(globalData->getSnowflakes());

	//Set up game over position.
	gameOverPos = TwoFloats(-1000.0f, (getDrawingAreaSize().y*0.5f)+32.0f);
	gameOverTweener.reset(TwoFloats(0.0f, (getDrawingAreaSize().y*0.5f)+32.0f));

	//Set up gradient lines.
	linePos[0].push_back(TwoFloats(32.0f+10.0f, 64.0f));
	linePos[0].push_back(TwoFloats(32.0f+10.0f, 64.0f));
	linePos[0].push_back(TwoFloats(32.0f+10.0f, 64.0f));
	linePos[0].push_back(TwoFloats(drawSize.x-(32.0f+12.0f), 64.0f));
	linePos[0].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), 64.0f));
	linePos[0].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), 64.0f));

	linePos[1].push_back(TwoFloats(32.0f+10.0f, drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(32.0f+10.0f, drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(32.0f+10.0f, drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(drawSize.x-(32.0f+12.0f), drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), drawSize.y-32.0f));

	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	
	lineCol.push_back(lineLeft);
	lineCol.push_back(lineLeft);
	lineCol.push_back(lineLeft);
	lineCol.push_back(lineRight);
	lineCol.push_back(lineRight);
	lineCol.push_back(lineRight);

	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);

	//Set up walls.
	xPos = 192.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));

	xPos += (drawSize.x-(2.0f*192.0f))/3;
	yPos = drawSize.y-(6.0f*32.0f);
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));

	xPos += (drawSize.x-(2.0f*192.0f))/3;
	yPos = 64.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));

	xPos += (drawSize.x-(2.0f*192.0f))/3;
	yPos = drawSize.y-(6.0f*32.0f);
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	yPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));

	xPos = 320.0f;
	yPos = 64.0f + ((drawSize.y-96.0f)*0.5f);
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos += 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));

	xPos = drawSize.x-320.0f;
	yPos = 64.0f + ((drawSize.y-96.0f)*0.5f);
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos -= 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos -= 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos -= 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos -= 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));
	xPos -= 32.0f;
	walls.push_back(shared_ptr<Wall>(new Wall(TwoFloats(xPos, yPos))));

	for(i=0;i<walls.size();++i)
		collidables.push_back(shared_ptr<Collidable>(walls[i]));

	//Create the Collidable we use to represent the arena walls.
	arenaWall = shared_ptr<Wall>(new Wall(0.0f));

	//Set up tanks.
	xPos = 96.0f;
	tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, drawSize.y-96.0f),
											  ThreeFloats(1.0f, 0.95f, 0.45f),
											  0)));
	deathMessages.push_back(DeathMessage(TwoFloats(46.0f, 96.0f)));
	switch(globalData->getNumPlayers())
	{
		case 2:
			xPos = drawSize.x-96.0f;
			tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, 128.0f),
													  ThreeFloats(1.0f, 0.45f, 0.45f),
													  0)));
			deathMessages.push_back(DeathMessage(TwoFloats(362.0f, 96.0f)));
			break;
		case 3:
			xPos = drawSize.x*0.5f;
			tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, 128.0f),
													  ThreeFloats(1.0f, 0.45f, 0.45f),
													  0)));
			deathMessages.push_back(DeathMessage(TwoFloats(362.0f, 96.0f)));
			xPos = drawSize.x-96.0f;
			tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, drawSize.y-96.0f),
													  ThreeFloats(0.53f, 0.45f, 1.0f),
													  0)));
			deathMessages.push_back(DeathMessage(TwoFloats(683.0f, 96.0f)));
			break;
		case 4:
			xPos += (drawSize.x-192.0f)/3.0f;
			tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, 128.0f),
													  ThreeFloats(1.0f, 0.45f, 0.45f),
													  1)));
			deathMessages.push_back(DeathMessage(TwoFloats(362.0f, 96.0f)));
			xPos += (drawSize.x-192.0f)/3.0f;
			tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, drawSize.y-96.0f),
													  ThreeFloats(0.53f, 0.45f, 1.0f),
													  2)));
			deathMessages.push_back(DeathMessage(TwoFloats(683.0f, 96.0f)));
			xPos += (drawSize.x-192.0f)/3.0f;
			tanks.push_back(shared_ptr<Tank>(new Tank(TwoFloats(xPos, 128.0f),
													  ThreeFloats(0.45f, 0.97f, 1.0f),
													  3)));
			deathMessages.push_back(DeathMessage(TwoFloats(1002.0f, 96.0f)));
			break;
	}

	for(i=0;i<tanks.size();++i)
		collidables.push_back(weak_ptr<Collidable>(tanks[i]));

	//Set up pickups.
	pickupGenerator = globalData->getPickupGenerator();
	pickupGenerator->addStartingPickups(*this, globalData->getMaxPickups());

	if(!sounder->getMusicIsPlaying() && globalData->getMusic())
		sounder->playMusic(L"Music");
}

//------------------------------------------------------------------------------
GamePlayState::~GamePlayState()
{

}

//------------------------------------------------------------------------------
void GamePlayState::update()
{
	int i, j;
	int numTanks = 0;
	const TwoFloats arenaTopLeft(32.0f, 64.0f);
	const TwoFloats arenaBottomRight(getDrawingAreaSize().x-32.0f,
									 getDrawingAreaSize().y-32.0f);

	globalData->getOSCDispatcher().update(getApplication().getTimeSinceStart());

	consoleReady = true;

	if(snowflakes.getDecember())
		snowflakes.update();

	if(bullets.size() < 200)
		bulletLimit = 32;
	else if(bullets.size() < 400)
		bulletLimit = 16;
	else if(bullets.size() < 450)
		bulletLimit = 8;
	else
		bulletLimit = 1;

	if(shockwaves.size() < 50)
		shockwaveLimit = 16;
	else if(shockwaves.size() < 100)
		shockwaveLimit = 8;
	else if(shockwaves.size() < 200)
		shockwaveLimit = 1;

	backgroundColLimit = 1;

	//Update the particles.
	particles.update();

	//Update any tweeners.
	if(!backgroundTweener.getFinished())
		backgroundTweener.tick();
	if(!borderTweener.getFinished())
		borderTweener.tick();
	if(!lineRightTweener.getFinished())
		lineRightTweener.tick();
	else
	{
		ThreeFloats tempCol;

		while((tempCol == 0.0f) || (tempCol == 1.0f) || (tempCol == lineRight))
		{
			tempCol.x = (Random::getGlobal().getRandomFloat() > 0.5f) ? 1.0f : 0.33f;
			tempCol.y = (Random::getGlobal().getRandomFloat() > 0.5f) ? 1.0f : 0.33f;
			tempCol.z = (Random::getGlobal().getRandomFloat() > 0.5f) ? 1.0f : 0.33f;
		}
		lineRightTweener.reset(tempCol);
	}
	if(!lineLeftTweener.getFinished())
		lineLeftTweener.tick();
	else
		lineLeftTweener.reset(lineRight);
	if(gameOver && !gameOverTweener.getFinished())
		gameOverTweener.tick();

	//Update screen shake.
	if(shake > 0.0f)
	{
		shake -= 0.05f;
		shakeIndex += 0.1f;
		if(shakeIndex > 1.0f)
			shakeIndex -= 1.0f;
	}

	//Update pickup announcer.
	announcer.update();


	//If it's game over, don't do any more updating.
	if(gameOver)
		return;


	if(!turnBasedMode || (turnBasedMode && (turnCounter > 0)))
	{
		for(i=(shockwaves.size()-1);i>=0;--i)
		{
			if(shockwaves[i]->update())
				shockwaves.erase(shockwaves.begin()+i);
		}

		for(i=0;i<(int)bullets.size();++i)
			bullets[i]->update(arenaTopLeft, arenaBottomRight, *this);

		for(i=0;i<(int)puddles.size();++i)
			puddles[i]->update();

		for(i=(mines.size()-1);i>=0;--i)
		{
			if(mines[i]->update())
			{
				mineTriggered(mines[i]);
				mines.erase(mines.begin()+i);

				if(globalData->getOSCOutput())
					globalData->getOSCDispatcher().sendNote(L"/mine", getRandomMIDINote());
			}
		}

		for(i=0;i<(int)npcs.size();++i)
			npcs[i]->update(*this);
	}

	for(i=0;i<(int)tanks.size();++i)
	{
		if(!tanks[i]->getDead())
		{
			if(turnBasedMode)
			{
				if(turnCounter > 0)
				{
					if(i == turnTank)
						tanks[i]->updatePlayerInput(*this, arenaTopLeft, arenaBottomRight);
					tanks[i]->updateCounters(*this, arenaTopLeft, arenaBottomRight);
				}
			}
			else
			{
				tanks[i]->updatePlayerInput(*this, arenaTopLeft, arenaBottomRight);
				tanks[i]->updateCounters(*this, arenaTopLeft, arenaBottomRight);
			}
		}
	}

	if(madButton)
		madButton->update(arenaTopLeft, arenaBottomRight);

	if(!turnBasedMode || (turnBasedMode && (turnCounter > 0)))
	{
		collisionDetection();

		//Check for dead tanks.
		for(i=0;i<(int)tanks.size();++i)
		{
			if(!tanks[i]->getDead())
			{
				if(tanks[i]->getArmour() <= 0.0f)
				{
					shared_ptr<Collidable> tankCol(tanks[i]);

					addShockwave(tanks[i]->getPosition(), tanks[i]->getColour());
					tanks[i]->setDead();

					triggerCollisionEffects(tanks[i]->getPosition());

					for(j=0;j<(int)collidables.size();++j)
					{
						if(tankCol == collidables[j].lock())
						{
							collidables.erase(collidables.begin() + j);
							break;
						}
					}
				}
				else
					++numTanks;
			}
		}

		if(gameOverText == L"")
		{
			if(numTanks <= 1)
			{
				gameOver = true;

				if(numTanks == 1)
				{
					int winner;
					std::wstringstream tempstr;

					for(i=0;i<(int)tanks.size();++i)
					{
						if(!tanks[i]->getDead())
						{
							winner = i+1;
							break;
						}
					}
					tempstr << L"Player" << winner << L" Wins!";
					gameOverText = tempstr.str();
					gameOverColour = tanks[winner-1]->getColour();
				}
				else
				{
					gameOverText = L"Humiliating Draw!";
					gameOverColour = ThreeFloats(0.66f, 0.2f, 1.0f);
				}
			}
		}

		//Check for collected pickups.
		for(i=(pickups.size()-1);i>=0;--i)
		{
			if(pickups[i]->getCollected())
			{
				std::wstring tempstr;
				ThreeFloats announceCol(pickups[i]->getCollectorColour());

				tempstr = globalData->getModifierFactory().pickupCollected(*this, pickups[i]->getType());
				pickupGenerator->addNewPickup(*this);

				pickups.erase(pickups.begin()+i);

				announcer.trigger(tempstr, announceCol);

				if(globalData->getOSCOutput())
					globalData->getOSCDispatcher().sendNote(L"/pickupCollected", getRandomMIDINote(), 0.5f);
			}
		}

		//Check if the MADButton's been collected.
		if(madButton)
		{
			if(madButton->getCollected())
			{
				pickupGenerator->madButtonCollected(*this);
				announcer.trigger(L"Chaos Pickups Enabled!",
								  madButton->getCollectorColour());
				madButton.reset();
			}
		}

		//Clear any dead Bullets.
		for(i=(bullets.size()-1);i>=0;--i)
		{
			if(bullets[i]->getDead())
			{
				triggerCollisionEffects(bullets[i]->getPosition());

				bullets[i]->deathAction(*this);
				bullets.erase(bullets.begin()+i);

				if(globalData->getOSCOutput())
					globalData->getOSCDispatcher().sendNote(L"/bulletDeath", getRandomMIDINote());
			}
		}

		//Clear any dead npcs.
		for(i=(npcs.size()-1);i>=0;--i)
		{
			if(npcs[i]->getDead())
			{
				triggerCollisionEffects(npcs[i]->getPosition());
				npcs.erase(npcs.begin()+i);
			}
		}

		//Clear any Collidables we no longer need.
		for(i=(collidables.size()-1);i>=0;--i)
		{
			if(collidables[i].expired())
				collidables.erase(collidables.begin()+i);
		}
	}

	if(turnBasedMode)
	{
		if(turnCounter > 0)
		{
			--turnCounter;
			if(!turnCounter)
				turnPauseCounter = PauseCounterMax;
		}
		else if(turnPauseCounter > 0)
		{
			--turnPauseCounter;
			if(!turnPauseCounter)
			{
				++turnTank;
				turnTank %= tanks.size();
				while(tanks[turnTank]->getDead())
				{
					++turnTank;
					turnTank %= tanks.size();
				}
				tanks[turnTank]->startTurn();
			}
		}
		else if(tanks[turnTank]->getPlayerInput())
			turnCounter = TurnCounterMax;

		turnBounce += 0.01f;
		if(turnBounce > 1.0f)
			turnBounce -= 1.0f;
	}
}

//------------------------------------------------------------------------------
void GamePlayState::draw(DrawerPtr& d, float timestepInterp)
{
	unsigned int i;
	TwoFloats shakeVal;
	const TwoFloats& drawSize(d->getDrawingAreaSize());

	//--Arena background--------------------------------------------------------
	d->fillRect(0.0f, drawSize, backgroundColour);

	if(shake > 0.0f)
	{
		shakeVal.x = sinf(shakeIndex*2.0f*NIALL_PI) * 16.0f * shake * 0.25f * globalData->getScreenShake();
		d->translate(shakeVal);
		d->rotate(shakeVal.x * 0.001f, TwoFloats(640.0f, 360.0f));
	}

	//--Entities----------------------------------------------------------------
	//Tank tracks should be drawn in the background.
	for(i=0;i<tanks.size();++i)
	{
		if(!tanks[i]->getDead())
			tanks[i]->drawTracks(d, timestepInterp);
	}

	if(madButton)
	{
		madButton->draw(d,
						timestepInterp,
						lineLeft,
						lineRight,
						borderColour,
						backgroundColour);
	}

	for(i=0;i<puddles.size();++i)
		puddles[i]->draw(d, timestepInterp);

	for(i=0;i<shockwaves.size();++i)
		shockwaves[i]->draw(d, timestepInterp);

	for(i=0;i<mines.size();++i)
		mines[i]->draw(d, timestepInterp);

	for(i=0;i<pickups.size();++i)
	{
		pickups[i]->draw(d,
						 timestepInterp,
						 lineLeft,
						 lineRight,
						 borderColour);
	}

	for(i=0;i<walls.size();++i)
		walls[i]->drawBack(d, timestepInterp, lineLeft, lineRight);

	for(i=0;i<bullets.size();++i)
		bullets[i]->drawTrail(d, timestepInterp);

	for(i=0;i<bullets.size();++i)
		bullets[i]->draw(d, timestepInterp);

	for(i=0;i<npcs.size();++i)
		npcs[i]->draw(d, timestepInterp);

	for(i=0;i<tanks.size();++i)
	{
		if(!tanks[i]->getDead())
			tanks[i]->draw(d, timestepInterp);
	}

	particles.draw(d, timestepInterp);

	//--Border background-------------------------------------------------------
	//Top bar.
	d->fillRect(-64.0f, TwoFloats(drawSize.x+128.0f, 64.0f+64.0f), borderColour);
	//Bottom bar.
	d->fillRect(TwoFloats(-64.0f, drawSize.y-32.0f),
				TwoFloats(drawSize.x+128.0f, 32.0f+64.0f),
				borderColour);
	//Left bar.
	d->fillRect(-64.0f, TwoFloats(32.0f+64.0f, drawSize.y+128.0f), borderColour);
	//Right bar.
	d->fillRect(TwoFloats(drawSize.x-32.0f, -64.0f),
				TwoFloats(32.0f+64.0f, drawSize.y+128.0f),
				borderColour);

	//Border line.
	//Top.
	lineCol[0] = lineLeft;
	lineCol[1] = lineLeft;
	lineCol[2] = lineLeft;
	lineCol[3] = lineRight;
	lineCol[4] = lineRight;
	lineCol[5] = lineRight;
	d->drawConnectedLine(L"",
						 linePos[0],
						 lineSize,
						 lineCol,
						 lineAlpha);
	//Bottom.
	d->drawConnectedLine(L"",
						 linePos[1],
						 lineSize,
						 lineCol,
						 lineAlpha);
	//Left.
	d->fillRect(TwoFloats(30.0f, 64.0f+10.0f),
				TwoFloats(4.0f, drawSize.y-(64.0f+32.0f+20.0f)),
				lineLeft);
	//Right.
	d->fillRect(TwoFloats(drawSize.x-34.0f, 64.0f+10.0f),
				TwoFloats(4.0f, drawSize.y-(64.0f+32.0f+20.0f)),
				lineRight);

	//Top-left Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(32.0f-4.0f, 64.0f-4.0f),
				 1.0f,
				 borderColour);
	//Top-right Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(drawSize.x-(32.0f-4.0f), 64.0f-4.0f),
				 TwoFloats(-1.0f, 1.0f),
				 borderColour);
	//Bottom-left Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(32.0f-4.0f, drawSize.y-(32.0f-4.0f)),
				 TwoFloats(1.0f, -1.0f),
				 borderColour);
	//Bottom-right Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(drawSize.x-(32.0f-4.0f), drawSize.y-(32.0f-4.0f)),
				 TwoFloats(-1.0f, -1.0f),
				 borderColour);

	//Top-left Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(32.0f-2.0f, 64.0f-2.0f),
				 1.0f,
				 lineLeft);
	//Top-right Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(drawSize.x-(32.0f-2.0f), 64.0f-2.0f),
				 TwoFloats(-1.0f, 1.0f),
				 lineRight);
	//Bottom-left Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(32.0f-2.0f, drawSize.y-(32.0f-2.0f)),
				 TwoFloats(1.0f, -1.0f),
				 lineLeft);
	//Bottom-right Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(drawSize.x-(32.0f-2.0f), drawSize.y-(32.0f-2.0f)),
				 TwoFloats(-1.0f, -1.0f),
				 lineRight);

	//Wall tops.
	for(i=0;i<walls.size();++i)
		walls[i]->drawTop(d, timestepInterp, borderColour);

	//Player info.
	//Names.
	d->drawImage(L"Player1Text",
				 TwoFloats(46.0f, 14.0f));
	d->drawImage(L"Player2Text",
				 TwoFloats(362.0f, 14.0f));
	d->drawImage(L"Player3Text",
				 TwoFloats(683.0f, 14.0f));
	d->drawImage(L"Player4Text",
				 TwoFloats(1002.0f, 14.0f));

	//Bars.
	//Player 1.
	d->drawImage(L"ArmourSecondaryBars",
				 TwoFloats(134.0f, 10.0f),
				 1.0f,
				 ThreeFloats(1.0f, 0.95f, 0.45f));
	d->fillRect(TwoFloats(163.0f, 16.0f),
				TwoFloats(108.0f*tanks[0]->getArmour(), 8.0f),
				ThreeFloats(1.0f, 0.45f, 0.45f));
	d->fillRect(TwoFloats(163.0f, 38.0f),
				TwoFloats(108.0f*tanks[0]->getSecondaryCharge(), 8.0f),
				ThreeFloats(0.43f, 0.76f, 0.41f));
	//Player 2.
	d->drawImage(L"ArmourSecondaryBars",
				 TwoFloats(454.0f, 10.0f),
				 1.0f,
				 ThreeFloats(1.0f, 0.45f, 0.45f));
	d->fillRect(TwoFloats(483.0f, 16.0f),
				TwoFloats(108.0f*tanks[1]->getArmour(), 8.0f),
				ThreeFloats(1.0f, 0.45f, 0.45f));
	d->fillRect(TwoFloats(483.0f, 38.0f),
				TwoFloats(108.0f*tanks[1]->getSecondaryCharge(), 8.0f),
				ThreeFloats(0.43f, 0.76f, 0.41f));
	//Player 3.
	d->drawImage(L"ArmourSecondaryBars",
					TwoFloats(773.0f, 10.0f),
					1.0f,
					ThreeFloats(0.53f, 0.45f, 1.0f));
	if(tanks.size() > 2)
	{
		d->fillRect(TwoFloats(802.0f, 16.0f),
					TwoFloats(108.0f*tanks[2]->getArmour(), 8.0f),
					ThreeFloats(1.0f, 0.45f, 0.45f));
		d->fillRect(TwoFloats(802.0f, 38.0f),
					TwoFloats(108.0f*tanks[2]->getSecondaryCharge(), 8.0f),
					ThreeFloats(0.43f, 0.76f, 0.41f));
	}
	//Player 4.
	d->drawImage(L"ArmourSecondaryBars",
					TwoFloats(1092.0f, 10.0f),
					1.0f,
					ThreeFloats(0.45f, 0.97f, 1.0f));
	if(tanks.size() > 3)
	{
		d->fillRect(TwoFloats(1121.0f, 16.0f),
					TwoFloats(108.0f*tanks[3]->getArmour(), 8.0f),
					ThreeFloats(1.0f, 0.45f, 0.45f));
		d->fillRect(TwoFloats(1121.0f, 38.0f),
					TwoFloats(108.0f*tanks[3]->getSecondaryCharge(), 8.0f),
					ThreeFloats(0.43f, 0.76f, 0.41f));
	}

	//--Turn marker-------------------------------------------------------------
	if(turnBasedMode)
	{
		TwoFloats tempPos(tanks[turnTank]->getPosition());

		tempPos.x -= 18.5f;
		tempPos.y -= tanks[turnTank]->getRadius() + 47.0f;
		tempPos.y -= (sinf(turnBounce * 2.0f * NIALL_PI)+1.0f) * 8.0f;

		tempPos.x += 5.0f;
		tempPos.y += 4.0f;
		d->drawImage(L"TurnMarkerBottom", tempPos, 1.0f, 0.1875f);
		tempPos.x -= 5.0f;
		tempPos.y -= 4.0f;
		d->drawImage(L"TurnMarkerTop", tempPos, 1.0f, tanks[turnTank]->getColour());
	}

	//--Death messages----------------------------------------------------------
	for(i=0;i<deathMessages.size();++i)
	{
		if(deathMessages[i].message != L"")
		{
			d->drawText(deathMessages[i].message,
						L"KilledByFont",
						deathMessages[i].layout,
						deathMessages[i].position+2.0f,
						240.0f,
						TextLayout::Centre);
			d->drawText(deathMessages[i].message,
						L"KilledByFont",
						deathMessages[i].layout,
						deathMessages[i].position,
						240.0f,
						TextLayout::Centre,
						1.0f,
						deathMessages[i].colour);
		}
	}

	//--Pickup announcer--------------------------------------------------------
	announcer.draw(d);

	//--Game over text----------------------------------------------------------
	if(gameOver)
	{
		d->drawText(gameOverText,
					L"BigFont",
					gameOverLayout,
					gameOverPos+8.0f,//TwoFloats(8.0f, (d->getDrawingAreaSize().y*0.5f)+8.0f+32.0f),
					d->getDrawingAreaSize().x,
					TextLayout::Centre);
		d->drawText(gameOverText,
					L"BigFont",
					gameOverLayout,
					gameOverPos,//TwoFloats(0.0f, (d->getDrawingAreaSize().y*0.5f)+32.0f),
					d->getDrawingAreaSize().x,
					TextLayout::Centre,
					1.0f,
					gameOverColour);
	}

	//--Reset screen shake------------------------------------------------------
	if(shake > 0.0f)
	{
		shakeVal.x = -shakeVal.x;
		d->translate(shakeVal);
		d->rotate(-shakeVal.x * 0.001f, TwoFloats(640.0f, 360.0f));
	}

	//--Draw console------------------------------------------------------------
	if(console)
	{
		d->drawText(consoleText.str(),
					L"KilledByFont",
					TwoFloats(64.0f, 128.0f),
					512.0f,
					TextLayout::Left,
					1.0f,
					1.0f);
	}


	//temp.
	if(0)
	{
		std::wstringstream tempstr2;

		tempstr2 << L"Num Bullets: " << bullets.size();
		d->drawText(tempstr2.str(),
					L"KilledByFont",
					128.0f,
					512.0f,
					TextLayout::Left,
					1.0f,
					1.0f);
	}

	if(snowflakes.getDecember())
		snowflakes.draw(d);
}

//------------------------------------------------------------------------------
void GamePlayState::keyDown(const std::wstring& action,
							const std::wstring& keyPressed,
							int keyModifier)
{
	std::wstring tempstr;

	if((action == L"Escape") && (!console))
		nextState(L"OptionsScreen");
	else if(gameOver && (action == L"Restart"))
		nextState(L"GamePlayState");
	else if(console)
	{
		if(consoleReady)
		{
			if(keyPressed == L"Backspace")
			{
				tempstr = consoleText.str();
				if(tempstr.length() > 1)
				{
					tempstr = consoleText.str().substr(0, tempstr.length()-1);
					consoleText.str(L"");
					consoleText << tempstr;
				}
			}
			else if(keyPressed == L"Return")
			{
				tempstr = consoleText.str();
				tempstr = tempstr.substr(1);
				tempstr = globalData->getModifierFactory().pickupCollected(*this, tempstr);
				if(tempstr != L"")
					announcer.trigger(tempstr, 1.0f);
				console = false;
			}
			else if(keyPressed == L"Escape")
				console = false;
			else if(keyPressed != L"")
			{
				if(keyPressed == L"Space")
					tempstr = L" ";
				else
					tempstr = keyPressed;

				if(keyModifier == 2)
				{
					if(tempstr == L"1")
						tempstr = L"!";
					else
						tempstr[0] = std::towupper(tempstr[0]);
				}

				consoleText << tempstr;
			}
			consoleReady = false;
		}
	}
	else if(action == L"Console")
	{
		console = !console;
		if(console)
		{
			consoleText.str(L"");
			consoleText << L">";
		}
	}
	else if(action != L"")
	{
		if(action[1] == L'1')
		{
			tempstr = action.substr(2);

			/*if(tempstr == L"Primary")
				firePrimary(tanks[0]);
			else*/
				tanks[0]->keyDown(tempstr);
		}
		else if(action[1] == L'2')
		{
			tempstr = action.substr(2);

			/*if(tempstr == L"Primary")
				firePrimary(tanks[1]);
			else*/
				tanks[1]->keyDown(tempstr);
		}
		else if((action[1] == L'3') && (tanks.size() > 2))
		{
			tempstr = action.substr(2);

			/*if(tempstr == L"Primary")
				firePrimary(tanks[2]);
			else*/
				tanks[2]->keyDown(tempstr);
		}
		else if((action[1] == L'4') && (tanks.size() > 3))
		{
			tempstr = action.substr(2);

			/*if(tempstr == L"Primary")
				firePrimary(tanks[3]);
			else*/
				tanks[3]->keyDown(tempstr);
		}
	}
}

//------------------------------------------------------------------------------
void GamePlayState::keyUp(const std::wstring& action,
						  const std::wstring& keyPressed,
						  int keyModifier)
{
	std::wstring tempstr;

	if(action != L"")
	{
		if(action[1] == L'1')
		{
			tempstr = action.substr(2);

			tanks[0]->keyUp(tempstr);
		}
		else if(action[1] == L'2')
		{
			tempstr = action.substr(2);

			tanks[1]->keyUp(tempstr);
		}
		else if((action[1] == L'3') && (tanks.size() > 2))
		{
			tempstr = action.substr(2);

			tanks[2]->keyUp(tempstr);
		}
		else if((action[1] == L'4') && (tanks.size() > 3))
		{
			tempstr = action.substr(2);

			tanks[3]->keyUp(tempstr);
		}
	}
}

//------------------------------------------------------------------------------
void GamePlayState::joyMove(const std::wstring& action,
							float value,
							int joystick,
							int axis)
{
	std::wstring tempstr;

	if(action != L"")
	{
		if(action[1] == L'1')
		{
			tempstr = action.substr(2);

			tanks[0]->move(tempstr, value);
		}
		else if(action[1] == L'2')
		{
			tempstr = action.substr(2);

			tanks[1]->move(tempstr, value);
		}
		else if((action[1] == L'3') && (tanks.size() > 2))
		{
			tempstr = action.substr(2);

			tanks[2]->move(tempstr, value);
		}
		else if((action[1] == L'4') && (tanks.size() > 3))
		{
			tempstr = action.substr(2);

			tanks[3]->move(tempstr, value);
		}
	}
}

//------------------------------------------------------------------------------
void GamePlayState::firePrimary(const TwoFloats& pos,
								const TwoFloats& dir,
								const ThreeFloats& col,
								const std::wstring& parent)
{
	bullets.push_back(bulletCreator.getBullet(pos, dir, col, parent));
	collidables.push_back(weak_ptr<Collidable>(bullets.back()));

	if(globalData->getOSCOutput())
		globalData->getOSCDispatcher().sendNote(L"/primaryFire", getRandomMIDINote(), 0.25f);
}

//------------------------------------------------------------------------------
void GamePlayState::fireSecondary(const Tank& tank)
{
	
}

//------------------------------------------------------------------------------
void GamePlayState::firePlainBullet(const TwoFloats& pos,
									const TwoFloats& dir,
									const ThreeFloats& col,
									const std::wstring& parent)
{
	if(bulletLimit > 0)
	{
		if(recursiveShotProperties)
			bullets.push_back(bulletCreator.getBullet(pos, dir, col, parent));
		else
			bullets.push_back(bulletCreator.getPlainBullet(pos, dir, col, parent));
		collidables.push_back(weak_ptr<Collidable>(bullets.back()));

		--bulletLimit;
	}
}

//------------------------------------------------------------------------------
void GamePlayState::addMine(const TwoFloats& pos,
							const std::wstring& owner,
							const ThreeFloats& colour)
{
	if(mines.size() < 256)
	{
		mines.push_back(shared_ptr<Mine>(new Mine(pos, owner, colour)));
		collidables.push_back(mines.back());

		if(bulletsTriggerMines)
			mines.back()->setTriggeredByBullets();
		if(camouflageMines)
			mines.back()->setCamouflage(snowflakes.getDecember());

		if(globalData->getOSCOutput())
			globalData->getOSCDispatcher().sendNote(L"/mine", getRandomMIDINote());
	}
}

//------------------------------------------------------------------------------
void GamePlayState::setRecursiveShotProperties()
{
	recursiveShotProperties = true;
}

//------------------------------------------------------------------------------
void GamePlayState::setBulletPickupCollection()
{
	unsigned int i;

	bulletPickupCollection = true;

	for(i=0;i<pickups.size();++i)
		pickups[i]->setBulletCollection();

	if(madButton)
		madButton->setBulletCollection();
}

//------------------------------------------------------------------------------
void GamePlayState::setBulletsTriggerMines()
{
	unsigned int i;

	bulletsTriggerMines = true;

	for(i=0;i<mines.size();++i)
		mines[i]->setTriggeredByBullets();
}

//------------------------------------------------------------------------------
void GamePlayState::setCamouflageMines()
{
	unsigned int i;

	camouflageMines = true;

	for(i=0;i<mines.size();++i)
		mines[i]->setCamouflage(snowflakes.getDecember());
}

//------------------------------------------------------------------------------
void GamePlayState::setTurnBasedMode()
{
	unsigned int i = Random::getGlobal().getRandomInt(tanks.size());

	turnBasedMode = true;

	while(1)
	{
		if(!tanks[i]->getDead())
			break;
		++i;
		i %= tanks.size();
	}
	turnCounter = 0;
	turnPauseCounter = PauseCounterMax;
	turnTank = i;
	tanks[turnTank]->startTurn();
}

//------------------------------------------------------------------------------
void GamePlayState::setDeathMessage(int index,
									const std::wstring& message,
									const ThreeFloats& colour)
{
	deathMessages[index].message = message;
	deathMessages[index].colour = colour;
}

//------------------------------------------------------------------------------
void GamePlayState::addPickup(const std::wstring& pickupType)
{
	TwoFloats pos;
	unsigned int i;
	bool found = false;
	const TwoFloats arenaSize(getDrawingAreaSize().x-64.0f,
							  getDrawingAreaSize().y-96.0f);

	pos.x = 32.0f + 24.0f + (Random::getGlobal().getRandomFloat() * (arenaSize.x-48.0f));
	pos.y = 64.0f + 24.0f + (Random::getGlobal().getRandomFloat() * (arenaSize.y-48.0f));

	while(!found)
	{
		found = true;
		for(i=0;i<collidables.size();++i)
		{
			shared_ptr<Collidable> col(collidables[i].lock());

			if(col)
			{
				if(pos.getDistance(col->getPosition()) < (64.0f+col->getRadius()))
				{
					found = false;
					break;
				}
			}
		}

		if(!found)
		{
			pos.x = 32.0f + 24.0f + (Random::getGlobal().getRandomFloat() * (arenaSize.x-48.0f));
			pos.y = 64.0f + 24.0f + (Random::getGlobal().getRandomFloat() * (arenaSize.y-48.0f));
		}
	}

	pickups.push_back(shared_ptr<Pickup>(new Pickup(pos, pickupType)));
	collidables.push_back(pickups.back());

	if(bulletPickupCollection)
		pickups.back()->setBulletCollection();
	if(snowflakes.getDecember())
		pickups.back()->setDecember();
}

//------------------------------------------------------------------------------
void GamePlayState::addBee(const TwoFloats& pos, const ThreeFloats& col)
{
	npcs.push_back(shared_ptr<Bee>(new Bee(pos, col)));
	collidables.push_back(npcs.back());
}

//------------------------------------------------------------------------------
void GamePlayState::addZombie(const TwoFloats& pos, const ThreeFloats& col)
{
	npcs.push_back(shared_ptr<Zombie>(new Zombie(pos, col)));
	collidables.push_back(npcs.back());
}

//------------------------------------------------------------------------------
void GamePlayState::addTurret(const TwoFloats& pos, const ThreeFloats& col)
{
	npcs.push_back(shared_ptr<Turret>(new Turret(pos,
												 col,
												 snowflakes.getDecember())));
	collidables.push_back(npcs.back());
}

//------------------------------------------------------------------------------
void GamePlayState::addNpc(shared_ptr<NPC>& npc)
{
	npcs.push_back(npc);
	collidables.push_back(npcs.back());
}

//------------------------------------------------------------------------------
void GamePlayState::addMADButton()
{
	if(!madButton)
	{
		madButton = shared_ptr<MADButton>(new MADButton(getDrawingAreaSize()*0.5f));
		collidables.push_back(madButton);
	}
}

//------------------------------------------------------------------------------
void GamePlayState::addWall(const TwoFloats& pos)
{
	walls.push_back(shared_ptr<Wall>(new Wall(pos)));
	collidables.push_back(walls.back());
}

//------------------------------------------------------------------------------
void GamePlayState::addShockwave(const TwoFloats& pos, const ThreeFloats& col)
{
	if(shockwaveLimit > 0)
	{
		shockwaves.push_back(shared_ptr<ShockwaveEntity>(new ShockwaveEntity(pos, col)));
		collidables.push_back(shockwaves.back());

		--shockwaveLimit;
	}
}

//------------------------------------------------------------------------------
void GamePlayState::registerMineTrigger(shared_ptr<MineTrigger> trigger)
{
	mineTriggers.push_back(trigger);
}

//------------------------------------------------------------------------------
void GamePlayState::registerPuddleCollisionMod(PuddleCollisionCreatorBase *mod)
{
	puddleCollisionModifiers.push_back(mod);
}

//------------------------------------------------------------------------------
void GamePlayState::collisionDetection()
{
	unsigned int i, j;
	bool useSquared = false;

	//610 seems about where the frame rate starts to drop on my machine.
	if(collidables.size() > 610)
		useSquared = true;

	for(i=0;i<collidables.size();++i)
	{
		shared_ptr<Collidable> colI(collidables[i].lock());

		if(colI)
		{
			const TwoFloats& iPos = colI->getPosition();
			const float iRad = colI->getRadius();
			const float iRadSquared = iRad*iRad;

			arenaWallCheck(colI);

			for(j=(i+1);j<collidables.size();++j)
			{
				shared_ptr<Collidable> colJ(collidables[j].lock());

				if(colJ)
				{
					const TwoFloats& jPos = colJ->getPosition();
					const float jRad = colJ->getRadius();
					const float jRadSquared = jRad*jRad;

					if(colI->interestedInCollisionsWith(colJ->getName()))
					{
						if(!useSquared)
						{
							if(iPos.getDistance(jPos) < (iRad+jRad))
							{
								if(!colJ->getIncorporeal())
									colI->collide(colJ, *this);

								if(!colI->getIncorporeal() &&
								   (colJ->interestedInCollisionsWith(colI->getName())))
								{
									if(colJ->collide(colI, *this))
										triggerCollisionEffects(iPos + ((jPos-iPos)*0.5f));
								}
							}
						}
						else
						{
							if(iPos.getDistanceSquared(jPos) < (iRadSquared+jRadSquared))
							{
								if(!colJ->getIncorporeal())
									colI->collide(colJ, *this);

								if(!colI->getIncorporeal() &&
								   (colJ->interestedInCollisionsWith(colI->getName())))
								{
									if(colJ->collide(colI, *this))
										triggerCollisionEffects(iPos + ((jPos-iPos)*0.5f));
								}
							}
						}
					}
				}
			}
		}
	}
}

//------------------------------------------------------------------------------
void GamePlayState::arenaWallCheck(shared_ptr<Collidable>& collidable)
{
	TwoFloats wallPos;
	const float wallRad = arenaWall->getRadius();
	const float colRad = collidable->getRadius();
	const TwoFloats& colPos(collidable->getPosition());
	const TwoFloats arenaTopLeft(32.0f, 64.0f);
	const TwoFloats arenaBottomRight(getDrawingAreaSize().x-32.0f,
									 getDrawingAreaSize().y-32.0f);

	if((colPos.x-colRad) < arenaTopLeft.x)
	{
		wallPos.x = arenaTopLeft.x - wallRad;
		wallPos.y = colPos.y;
	}
	else if((colPos.x+colRad) > arenaBottomRight.x)
	{
		wallPos.x = arenaBottomRight.x + wallRad;
		wallPos.y = colPos.y;
	}
	if((colPos.y-colRad) < arenaTopLeft.y)
	{
		wallPos.y = arenaTopLeft.y - wallRad;
		if(wallPos.x == 0.0f)
			wallPos.x = colPos.x;
	}
	else if((colPos.y+colRad) > arenaBottomRight.y)
	{
		wallPos.y = arenaBottomRight.y + wallRad;
		if(wallPos.x == 0.0f)
			wallPos.x = colPos.x;
	}

	if(wallPos != 0.0f)
	{
		arenaWall->setPosition(wallPos);
		collidable->collide(arenaWall, *this);
	}
}

//------------------------------------------------------------------------------
void GamePlayState::triggerCollisionEffects(const TwoFloats& pos)
{
	int colourIndex;
	const float colInc = 1.0f/8.0f;
	const ThreeFloats lastBack(backgroundColour);
	const ThreeFloats lastBorder(borderColour);

	particles.trigger(pos);

	if(backgroundColLimit > 0)
	{
		colourIndex = Random::getGlobal().getRandomInt(3);
		switch(colourIndex)
		{
			case 0:
				backgroundColour.x += colInc;
				borderColour.y += colInc;
				borderColour.z += colInc;
				break;
			case 1:
				backgroundColour.y += colInc;
				borderColour.x += colInc;
				borderColour.z += colInc;
				break;
			case 2:
				backgroundColour.z += colInc;
				borderColour.x += colInc;
				borderColour.y += colInc;
				break;
			default:
				backgroundColour.x += colInc;
				borderColour.y += colInc;
				borderColour.z += colInc;
		}
		backgroundColour = lastBack + (globalData->getBackgroundFlash() * (backgroundColour-lastBack));
		borderColour = lastBorder + (globalData->getBorderFlash() * (borderColour-lastBorder));

		backgroundTweener.reset(ThreeFloats(0.125f));
		borderTweener.reset(ThreeFloats(0.1875f));

		--backgroundColLimit;
	}

	shake += 1.0f;
	if(shake > 2.0f)
		shake = 2.0f;
}

//------------------------------------------------------------------------------
void GamePlayState::mineTriggered(const shared_ptr<Mine>& mine)
{
	int i;

	for(i=0;i<(int)mineTriggers.size();++i)
		mineTriggers[i]->trigger(mine, *this);

	if(puddleCollisionModifiers.size() > 0)
	{
		puddles.push_back(shared_ptr<Puddle>(new Puddle(mine->getPosition())));
		collidables.push_back(puddles.back());

		for(i=0;i<(int)puddleCollisionModifiers.size();++i)
			puddles.back()->addCollisionModifier(puddleCollisionModifiers[i]->createModifier(mine));
	}
}

//------------------------------------------------------------------------------
int GamePlayState::getRandomMIDINote()
{
	int availableNotes[] = {0, 2, 4, 5, 7, 9, 11};

	return 48 + (availableNotes[Random::getGlobal().getRandomInt(7)]*4);
}
