#include "const.h"
#include "struct.h"
#include "gamedirector.h"
#include <iostream>

const Uint8 *keystates;

//Calcula el cuadrado de la distancia entre dos puntos
double distanceSquared(int x1, int y1, int x2, int y2)
{
	int deltaX = x2 - x1;
	int deltaY = y2 - y1;
	return deltaX * deltaX + deltaY * deltaY;
}

//Detector de colisiones entre dos circulos
bool checkCollision(Circle &a, Circle &b)
{
	//Calcula el radio total al cuadrado
	int totalRadiusSquared = a.r + b.r;
	totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;

	//Si la distancia entre el centro de los circulos es inferior a la suma de sus radios
	if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared))
	{
		//Los circulos han colisionado
		return true;
	}

	//En caso contrario
	return false;
}

//Constructor
GameDirector::GameDirector()
{
	//Texturas
	mPlayerTexture = new LTexture();
	mGameBackgroundTexture = new LTexture();
	mTitleBackgroundTexture = new LTexture();
	mWhiteFontTexture = new LTexture();
	mNokiaFontTexture = new LTexture();
	mMenuTexture = new LTexture();
	mBalloonTexture = new LTexture();
	mBulletTexture = new LTexture();
	mMiscTexture = new LTexture();
	mIntroTexture = new LTexture();

	//Manejador de eventos
	mEventHandler = new SDL_Event();

	//Objetos para el juego
	mPlayer = new Player();
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		mBalloon[i] = new Balloon();
	}
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		mBullet[i] = new Bullet();
	}
	for (Uint8 i = 0; i < MAX_ITEMS; i++)
	{
		mItem[i] = new Item();
	}
	for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++)
	{
		mSmartSprite[i] = new SmartSprite();
	}
	mGameBackgroundFront = new Sprite();
	mGBClouds1 = new MovingSprite();
	mGBClouds1b = new MovingSprite();
	mGBClouds2 = new MovingSprite();
	mGBClouds2b = new MovingSprite();
	mTitleBackground = new Sprite();
	mWhiteText = new Text();
	mNewText = new Text();
	mMenuTitle = new Menu();
	mMenuPause = new Menu();
	mGetReadyBitmap = new SmartSprite();
	mCoffeeBitmap = new SmartSprite();
	mCrisisBitmap = new SmartSprite();
	mDustSpriteLeft = new AnimatedSprite();
	mDustSpriteRight = new AnimatedSprite();
	m1000Bitmap = new SmartSprite();
	m2500Bitmap = new SmartSprite();
	m5000Bitmap = new SmartSprite();
	for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
	{
		mIntroBitmap[i] = new SmartSprite();
	}
	for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
	{
		mIntroText[i] = new Text2();
	}
	init(false);
}

GameDirector::~GameDirector()
{
	close();

	//Destruye las texturas
	delete mPlayerTexture;
	delete mGameBackgroundTexture;
	delete mTitleBackgroundTexture;
	delete mWhiteFontTexture;
	delete mNokiaFontTexture;
	delete mMenuTexture;
	delete mBalloonTexture;
	delete mBulletTexture;
	delete mMiscTexture;
	delete mIntroTexture;

	//Destuye el manejador de eventos
	delete mEventHandler;

	//Destruye los objetos
	delete mPlayer;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		delete mBalloon[i];
	}
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		delete mBullet[i];
	}
	for (Uint8 i = 0; i < MAX_ITEMS; i++)
	{
		delete mItem[i];
	}
	for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++)
	{
		delete mSmartSprite[i];
	}
	delete mGameBackgroundFront;
	delete mGBClouds1;
	delete mGBClouds1b;
	delete mGBClouds2;
	delete mGBClouds2b;
	delete mTitleBackground;
	delete mWhiteText;
	delete mNewText;
	delete mMenuTitle;
	delete mMenuPause;
	delete mGetReadyBitmap;
	delete mCoffeeBitmap;
	delete mCrisisBitmap;
	delete mDustSpriteLeft;
	delete mDustSpriteRight;
	delete m1000Bitmap;
	delete m2500Bitmap;
	delete m5000Bitmap;
	for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
	{
		delete mIntroBitmap[i];
	}
	for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
	{
		delete mIntroText[i];
	}
}

//Iniciador
void GameDirector::init(bool reset)
{
	if (true)
	{
		//Variables
		mOldTicks = 0;
		mGameSpeed = 15;
		mMenaceLevel = 0;
		mMenaceLevelThreshold = 0;
		mScore = 0;
		mHiScoreAchieved = false;
		mPlayFieldDrawOnly = true;
		mDebug = false;
		mTitleStatus = 0;
		mExplosionTime = false;
		mRemainingExplosions = REMAINING_EXPLOSIONS;
		mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER;
		mTimeStopped = false;
		mTimeStoppedTimer = TIME_STOPPED_TIMER;
		mGameOverTimer = GAME_OVER_TIMER;
		mTitleTimer = TITLE_TIMER;
		mDemo = false;
		mDemoRecording = false;
		mDemoCounter = 0;

		//Fondo animado
		mGBClouds1->init(0, 0, 256, 52, -0.1f, 0.0f, 0.0f, 0.0f, mGameBackgroundTexture, mRenderer);
		mGBClouds1->setSpriteClip(256, 0, 256, 52);

		mGBClouds1b->init(256, 0, 256, 52, -0.1f, 0.0f, 0.0f, 0.0f, mGameBackgroundTexture, mRenderer);
		mGBClouds1b->setSpriteClip(256, 0, 256, 52);

		mGBClouds2->init(0, 52, 256, 192 - 52, -0.05f, 0.0f, 0.0f, 0.0f, mGameBackgroundTexture, mRenderer);
		mGBClouds2->setSpriteClip(256, 52, 256, 192 - 52);

		mGBClouds2b->init(256, 52, 256, 192 - 52, -0.05f, 0.0f, 0.0f, 0.0f, mGameBackgroundTexture, mRenderer);
		mGBClouds2b->setSpriteClip(256, 52, 256, 192 - 52);

		//Objeto jugador
		mPlayer->init(PLAY_AREA_CENTER_X - 12, PLAY_AREA_BOTTOM - 24, mPlayerTexture, mRenderer);

		//Establece a cero todos los valores del vector de objetos globo
		resetBalloons();

		//Crea objetos globo y los centra en el area de juego
		mBalloon[0]->init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0, mBalloonTexture, mRenderer);
		mBalloon[0]->allignTo(PLAY_AREA_CENTER_X);

		//Con los globos creados, calcula el nivel de amenaza
		calculateMenaceLevel();

		//Establece a cero todos los valores del vector de objetos bala
		resetBullets();

		//Establece a cero todos los valores del vector de objetos item
		resetItems();

		//Establece a cero todos los valores del vector de objetos SmafrtSprite
		resetSmartSprites();

		//Inicializa el bitmap de GetReady!
		mGetReadyBitmap->init(mMiscTexture, mRenderer);
		mGetReadyBitmap->setPosX(-113);
		mGetReadyBitmap->setPosY(PLAY_AREA_CENTER_Y - 10);
		mGetReadyBitmap->setWidth(109);
		mGetReadyBitmap->setHeight(20);
		mGetReadyBitmap->setVelX(2.0f);
		mGetReadyBitmap->setVelY(0.0f);
		mGetReadyBitmap->setAccelX(0.0f);
		mGetReadyBitmap->setAccelY(0.0f);
		mGetReadyBitmap->setSpriteClip(0, 0, 109, 20);
		mGetReadyBitmap->setEnabled(true);
		mGetReadyBitmap->setEnabledTimer(0);
		mGetReadyBitmap->setDestX(PLAY_AREA_RIGHT);
		mGetReadyBitmap->setDestY(PLAY_AREA_CENTER_Y - 10);

		//Inicializa el bitmap de Coffee
		mCoffeeBitmap->init(mTitleBackgroundTexture, mRenderer);
		mCoffeeBitmap->setId(0);
		mCoffeeBitmap->setIntroEvents(&mTitleEvents[0]);
		mCoffeeBitmap->setPosX(45);
		mCoffeeBitmap->setPosY(11 - 200);
		mCoffeeBitmap->setWidth(167);
		mCoffeeBitmap->setHeight(46);
		mCoffeeBitmap->setVelX(0.0f);
		mCoffeeBitmap->setVelY(2.5f);
		mCoffeeBitmap->setAccelX(0.0f);
		mCoffeeBitmap->setAccelY(0.1f);
		mCoffeeBitmap->setSpriteClip(45, 11, 167, 46);
		mCoffeeBitmap->setEnabled(true);
		mCoffeeBitmap->setEnabledTimer(0);
		mCoffeeBitmap->setDestX(45);
		mCoffeeBitmap->setDestY(11);

		//Inicializa el bitmap de Crisis
		mCrisisBitmap->init(mTitleBackgroundTexture, mRenderer);
		mCrisisBitmap->setId(1);
		mCrisisBitmap->setIntroEvents(&mTitleEvents[0]);
		mCrisisBitmap->setPosX(60);
		mCrisisBitmap->setPosY(57 + 200);
		mCrisisBitmap->setWidth(137);
		mCrisisBitmap->setHeight(46);
		mCrisisBitmap->setVelX(0.0f);
		mCrisisBitmap->setVelY(-2.5f);
		mCrisisBitmap->setAccelX(0.0f);
		mCrisisBitmap->setAccelY(-0.1f);
		mCrisisBitmap->setSpriteClip(60, 57, 137, 46);
		mCrisisBitmap->setEnabled(true);
		mCrisisBitmap->setEnabledTimer(0);
		mCrisisBitmap->setDestX(60);
		mCrisisBitmap->setDestY(57);

		//Inicializa el bitmap de DustRight
		mDustSpriteRight->init(mMiscTexture, mRenderer);
		mDustSpriteRight->setPosX(218);
		mDustSpriteRight->setPosY(47);
		mDustSpriteRight->setWidth(16);
		mDustSpriteRight->setHeight(14);
		mDustSpriteRight->setCurrentFrame(0);
		mDustSpriteRight->setAnimationCounter(0);
		mDustSpriteRight->setAnimationNumFrames(0, 7);
		mDustSpriteRight->setAnimationSpeed(0, 8);
		mDustSpriteRight->setAnimationLoop(0, false);
		mDustSpriteRight->setAnimationFrames(0, 0, mDustSpriteRight->getWidth() * 0, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());
		mDustSpriteRight->setAnimationFrames(0, 1, mDustSpriteRight->getWidth() * 1, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());
		mDustSpriteRight->setAnimationFrames(0, 2, mDustSpriteRight->getWidth() * 2, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());
		mDustSpriteRight->setAnimationFrames(0, 3, mDustSpriteRight->getWidth() * 3, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());
		mDustSpriteRight->setAnimationFrames(0, 4, mDustSpriteRight->getWidth() * 4, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());
		mDustSpriteRight->setAnimationFrames(0, 5, mDustSpriteRight->getWidth() * 5, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());
		mDustSpriteRight->setAnimationFrames(0, 6, mDustSpriteRight->getWidth() * 6, 34, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight());

		//Inicializa el bitmap de DustLeft
		mDustSpriteLeft->init(mMiscTexture, mRenderer);
		mDustSpriteLeft->setPosX(33);
		mDustSpriteLeft->setPosY(47);
		mDustSpriteLeft->setWidth(16);
		mDustSpriteLeft->setHeight(14);
		mDustSpriteLeft->setCurrentFrame(0);
		mDustSpriteLeft->setAnimationCounter(0);
		mDustSpriteLeft->setAnimationNumFrames(0, 7);
		mDustSpriteLeft->setAnimationSpeed(0, 8);
		mDustSpriteLeft->setAnimationLoop(0, false);
		mDustSpriteLeft->setAnimationFrames(0, 0, mDustSpriteLeft->getWidth() * 0, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());
		mDustSpriteLeft->setAnimationFrames(0, 1, mDustSpriteLeft->getWidth() * 1, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());
		mDustSpriteLeft->setAnimationFrames(0, 2, mDustSpriteLeft->getWidth() * 2, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());
		mDustSpriteLeft->setAnimationFrames(0, 3, mDustSpriteLeft->getWidth() * 3, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());
		mDustSpriteLeft->setAnimationFrames(0, 4, mDustSpriteLeft->getWidth() * 4, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());
		mDustSpriteLeft->setAnimationFrames(0, 5, mDustSpriteLeft->getWidth() * 5, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());
		mDustSpriteLeft->setAnimationFrames(0, 6, mDustSpriteLeft->getWidth() * 6, 20, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight());

		//Inicializa el bitmap de 1000 puntos
		m1000Bitmap->init(mMiscTexture, mRenderer);
		m1000Bitmap->setPosX(0);
		m1000Bitmap->setPosY(0);
		m1000Bitmap->setWidth(26);
		m1000Bitmap->setHeight(9);
		m1000Bitmap->setVelX(0.0f);
		m1000Bitmap->setVelY(-0.5f);
		m1000Bitmap->setAccelX(0.0f);
		m1000Bitmap->setAccelY(-0.05f);
		m1000Bitmap->setSpriteClip(0, 80, 26, 9);
		m1000Bitmap->setEnabled(false);
		m1000Bitmap->setEnabledTimer(0);
		m1000Bitmap->setDestX(0);
		m1000Bitmap->setDestY(0);

		//Inicializa el bitmap de 2500 puntos
		m2500Bitmap->init(mMiscTexture, mRenderer);
		m2500Bitmap->setPosX(0);
		m2500Bitmap->setPosY(0);
		m2500Bitmap->setWidth(28);
		m2500Bitmap->setHeight(9);
		m2500Bitmap->setVelX(0.0f);
		m2500Bitmap->setVelY(-0.5f);
		m2500Bitmap->setAccelX(0.0f);
		m2500Bitmap->setAccelY(-0.1f);
		m2500Bitmap->setSpriteClip(0, 89, 28, 9);
		m2500Bitmap->setEnabled(false);
		m2500Bitmap->setEnabledTimer(0);
		m2500Bitmap->setDestX(0);
		m2500Bitmap->setDestY(0);

		//Inicializa el bitmap de 5000 puntos
		m5000Bitmap->init(mMiscTexture, mRenderer);
		m5000Bitmap->setPosX(0);
		m5000Bitmap->setPosY(0);
		m5000Bitmap->setWidth(28);
		m5000Bitmap->setHeight(9);
		m5000Bitmap->setVelX(0.0f);
		m5000Bitmap->setVelY(-0.5f);
		m5000Bitmap->setAccelX(0.0f);
		m5000Bitmap->setAccelY(-0.1f);
		m5000Bitmap->setSpriteClip(28, 80, 28, 9);
		m5000Bitmap->setEnabled(false);
		m5000Bitmap->setEnabledTimer(0);
		m5000Bitmap->setDestX(0);
		m5000Bitmap->setDestY(0);

		//Inicializa el vector de eventos de la intro
		for (Uint8 i = 0; i < INTRO_TOTAL_EVENTS; i++)
		{
			mIntroEvents[i] = EVENT_WAITING;
		}

		//Inicializa el vector de eventos de la pantalla de titulo
		for (Uint8 i = 0; i < TITLE_TOTAL_EVENTS; i++)
		{
			mTitleEvents[i] = EVENT_WAITING;
		}

		//Inicializa los bitmaps de la intro
		for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
		{
			mIntroBitmap[i]->init(mIntroTexture, mRenderer);
			mIntroBitmap[i]->setId(i);
			mIntroBitmap[i]->setIntroEvents(&mIntroEvents[0]);
			mIntroBitmap[i]->setWidth(128);
			mIntroBitmap[i]->setHeight(96);
			mIntroBitmap[i]->setEnabled(false);
			mIntroBitmap[i]->setEnabledTimer(20);
			mIntroBitmap[i]->setDestX(SCREEN_CENTER_X - 64);
			mIntroBitmap[i]->setDestY(SCREEN_FIRST_QUARTER_Y - 24);
		}

		mIntroBitmap[0]->setPosX(-128);
		mIntroBitmap[0]->setPosY(SCREEN_FIRST_QUARTER_Y - 24);
		mIntroBitmap[0]->setVelX(0.0f);
		mIntroBitmap[0]->setVelY(0.0f);
		mIntroBitmap[0]->setAccelX(0.6f);
		mIntroBitmap[0]->setAccelY(0.0f);
		mIntroBitmap[0]->setSpriteClip(0, 0, 128, 96);

		mIntroBitmap[1]->setPosX(SCREEN_WIDTH);
		mIntroBitmap[1]->setPosY(SCREEN_FIRST_QUARTER_Y - 24);
		mIntroBitmap[1]->setVelX(-1.0f);
		mIntroBitmap[1]->setVelY(0.0f);
		mIntroBitmap[1]->setAccelX(-0.3f);
		mIntroBitmap[1]->setAccelY(0.0f);
		mIntroBitmap[1]->setSpriteClip(128, 0, 128, 96);

		mIntroBitmap[2]->setPosX(SCREEN_CENTER_X - 64);
		mIntroBitmap[2]->setPosY(-96);
		mIntroBitmap[2]->setVelX(0.0f);
		mIntroBitmap[2]->setVelY(3.0f);
		mIntroBitmap[2]->setAccelX(0.1f);
		mIntroBitmap[2]->setAccelY(0.3f);
		mIntroBitmap[2]->setSpriteClip(0, 96, 128, 96);

		mIntroBitmap[2]->setEnabledTimer(250);

		mIntroBitmap[3]->setPosX(SCREEN_CENTER_X - 64);
		mIntroBitmap[3]->setPosY(SCREEN_HEIGHT);
		mIntroBitmap[3]->setVelX(0.0f);
		mIntroBitmap[3]->setVelY(-0.7f);
		mIntroBitmap[3]->setAccelX(0.0f);
		mIntroBitmap[3]->setAccelY(0.0f);
		mIntroBitmap[3]->setSpriteClip(128, 96, 128, 96);

		mIntroBitmap[4]->setPosX(SCREEN_CENTER_X - 64);
		mIntroBitmap[4]->setPosY(-96);
		mIntroBitmap[4]->setVelX(0.0f);
		mIntroBitmap[4]->setVelY(3.0f);
		mIntroBitmap[4]->setAccelX(0.1f);
		mIntroBitmap[4]->setAccelY(0.3f);
		mIntroBitmap[4]->setSpriteClip(0, 192, 128, 96);

		mIntroBitmap[5]->setPosX(SCREEN_WIDTH);
		mIntroBitmap[5]->setPosY(SCREEN_FIRST_QUARTER_Y - 24);
		mIntroBitmap[5]->setVelX(-0.7f);
		mIntroBitmap[5]->setVelY(0.0f);
		mIntroBitmap[5]->setAccelX(0.0f);
		mIntroBitmap[5]->setAccelY(0.0f);
		mIntroBitmap[5]->setSpriteClip(128, 192, 128, 96);

		//Inicializa los textos de la intro
		for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
		{
			mIntroText[i]->init(mNokiaFontTexture, mRenderer, TEXT_VARIABLE, 10);
			mIntroText[i]->setId(6 + i);
			mIntroText[i]->setIntroEvents(&mIntroEvents[0]);
			mIntroText[i]->setPosX(BLOCK * 0);
			mIntroText[i]->setPosY(SCREEN_HEIGHT - (BLOCK * 6));
			mIntroText[i]->setKerning(-1);
			mIntroText[i]->setEnabled(false);
			mIntroText[i]->setEnabledTimer(180);
		}

		mIntroText[0]->setCaption("Un dia qualsevol de l'any 2000");
		mIntroText[0]->setWrittingSpeed(10);

		mIntroText[1]->setCaption("Tot esta tranquil a la UPV");
		mIntroText[1]->setWrittingSpeed(10);

		mIntroText[2]->setCaption("Fins que un desaprensiu...");
		mIntroText[2]->setWrittingSpeed(15);

		mIntroText[3]->setCaption("HEY! ME ANE A FERME UN CORTAET...");
		mIntroText[3]->setWrittingSpeed(10);

		mIntroText[4]->setCaption("UAAAAAAAAAAAAA!!!");
		mIntroText[4]->setWrittingSpeed(1);

		mIntroText[5]->setCaption("Espera un moment...");
		mIntroText[5]->setWrittingSpeed(20);

		mIntroText[6]->setCaption("Si resulta que no tinc solt!");
		mIntroText[6]->setWrittingSpeed(2);

		mIntroText[7]->setCaption("MERDA DE MAQUINA!");
		mIntroText[7]->setWrittingSpeed(3);

		mIntroText[8]->setCaption("Blop..blop..blop..");
		mIntroText[8]->setWrittingSpeed(25);

		for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
		{
			mIntroText[i]->center(SCREEN_CENTER_X);
		}
	}

	//Esta parte solo se ejecuta al arrancar la aplicación
	if (reset == false)
	{
		//Variables
		mGameStatus = GAME_STATE_INTRO;
		mTitleStatus = 0;
		mTiteNextGS = GAME_STATE_PLAYING;

		//Teclado
		mKeyboard.up = SDL_SCANCODE_UP;
		mKeyboard.down = SDL_SCANCODE_DOWN;
		mKeyboard.left = SDL_SCANCODE_LEFT;
		mKeyboard.right = SDL_SCANCODE_RIGHT;
		mKeyboard.accept = SDL_SCANCODE_RETURN;
		mKeyboard.cancel = SDL_SCANCODE_ESCAPE;
		mKeyboard.fire = SDL_SCANCODE_W;
		mKeyboard.fireLeft = SDL_SCANCODE_Q;
		mKeyboard.fireRight = SDL_SCANCODE_E;
		mKeyboard.start = SDL_SCANCODE_ESCAPE;
		mKeyboard.escape = SDL_SCANCODE_ESCAPE;

		//Carga la máxima puntuación a partir del fichero de datos
		if (mScoreDataFile[0] == 0)
		{
			mHiScore = 10000;
		}
		//Comprueba el checksum para ver si se ha modificado el fichero
		else if (mScoreDataFile[0] % 43 == mScoreDataFile[1])
		{
			mHiScore = mScoreDataFile[0];
		}
		else
		{
			mHiScore = 10000;
		}

		//Los fondos
		mGameBackgroundFront->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mGameBackgroundTexture, mRenderer);
		mTitleBackground->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTitleBackgroundTexture, mRenderer);

		//Objetos texto, uno de cada color
		mWhiteText->init(mWhiteFontTexture, mRenderer, TEXT_FIXED, 8);
		mNewText->init(mNokiaFontTexture, mRenderer, TEXT_VARIABLE, 10);

		//Inicializa el objeto con el menu del titulo
		mMenuTitle->init(0, 16 * BLOCK, MENU_BACKGROUND_TRANSPARENT, mMenuTexture, mRenderer, mWhiteText);
		mMenuTitle->addItem("START");
		mMenuTitle->addItem("EXIT");
		mMenuTitle->setBackgroundColor(0, 0, 0, 255);
		mMenuTitle->centerMenu(SCREEN_CENTER_X);
		mMenuTitle->centerMenuElements(SCREEN_CENTER_X);

		//Inicializa el objeto con el menu de pausa
		mMenuPause->init(0, 12 * BLOCK, MENU_BACKGROUND_SOLID, mMenuTexture, mRenderer, mWhiteText);
		mMenuPause->addItem("CONTINUE");
		mMenuPause->addItem("EXIT TO TITLE");
		//mMenuPause->setBackgroundColor(0x73, 0x27, 0x5c, 255);
		mMenuPause->setBackgroundColor(0x29, 0x39, 0x41, 255);
		mMenuPause->centerMenu(SCREEN_CENTER_X);
	}
}

//Arranca SDL y crea la ventana
bool GameDirector::initSDL()
{
	//Indicador de inicialización
	bool success = true;

	//Inicializa SDL
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0)
	{
		printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
		success = false;
	}
	else
	{
		//Establece el filtro de la textura a nearest
		if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"))
		{
			printf("Warning: Nearest texture filtering not enabled!");
		}

		//Inicializa SDL_mixer
		if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
		{
			printf("SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError());
			success = false;
		}

		//Comprueba los mandos
		if (SDL_NumJoysticks() < 1)
		{
			printf("Warning: No joysticks connected!\n");
		}
		else
		{
			//Carga el mando
			mGameController = SDL_JoystickOpen(0);
			if (mGameController == NULL)
			{
				printf("Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError());
			}
			printf("%i joysticks were found.\n", SDL_NumJoysticks());
			std::cout << SDL_JoystickNumButtons(mGameController) << " buttons\n";
		}

		//Crea la ventana
		mWindow = SDL_CreateWindow("Coffe Crisis", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, VIEW_WIDTH, VIEW_HEIGHT, SDL_WINDOW_SHOWN);
		if (mWindow == NULL)
		{
			printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
		else
		{
			//Crea un renderizador para la ventana con vsync
			mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
			if (mRenderer == NULL)
			{
				printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
				success = false;
			}
			else
			{
				//Inicializa el color de renderizado
				SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);

				//Establece el tamaño del buffer de renderizado
				SDL_RenderSetLogicalSize(mRenderer, SCREEN_WIDTH, SCREEN_HEIGHT);

				//Inicializa el cargador de PNG
				int imgFlags = IMG_INIT_PNG;
				if (!(IMG_Init(imgFlags) & imgFlags))
				{
					printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
					success = false;
				}
			}
		}
	}

	return success;
}

//Carga todos los recursos
bool GameDirector::loadMedia()
{
	//Indicador de éxito en la carga
	bool success = true;

	//Abrimos el fichero con la puntuacion para leer en binario
	SDL_RWops *file = SDL_RWFromFile("score.bin", "r+b");

	//El fichero no existe
	if (file == NULL)
	{
		printf("Warning: Unable to open score.bin file! SDL Error: %s\n", SDL_GetError());

		//Creamos el fichero para escritura
		file = SDL_RWFromFile("score.bin", "w+b");
		if (file != NULL)
		{
			printf("New file (score.bin) created!\n");

			//Inicializamos los datos
			for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
			{
				mScoreDataFile[i] = 0;
				SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1);
			}

			//Cerramos el fichero
			SDL_RWclose(file);
		}
		else
		{
			printf("Error: Unable to create file score.bin! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
	}
	//El fichero existe
	else
	{
		//Cargamos los datos
		printf("Reading file score.bin ...!\n");
		for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
		{
			SDL_RWread(file, &mScoreDataFile[i], sizeof(Uint32), 1);
		}

		//Cierra el fichero
		SDL_RWclose(file);
	}

	//Abrimos el fichero con los datos para el modo demo para leer en binario
	SDL_RWops *file2 = SDL_RWFromFile("demo.bin", "r+b");

	//El fichero no existe
	if (file2 == NULL)
	{
		printf("Warning: Unable to open demo.bin file! SDL Error: %s\n", SDL_GetError());

		//Creamos el fichero para escritura
		file2 = SDL_RWFromFile("demo.bin", "w+b");
		if (file2 != NULL)
		{
			printf("New file (demo.bin) created!\n");

			//Inicializamos los datos
			for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
			{
				mDemoKeys.left = 0;
				mDemoKeys.right = 0;
				mDemoKeys.noInput = 0;
				mDemoKeys.fire = 0;
				mDemoKeys.fireLeft = 0;
				mDemoKeys.fireRight = 0;
				mDemoDataFile[i] = mDemoKeys;
				SDL_RWwrite(file2, &mDemoDataFile[i], sizeof(DemoKeys), 1);
			}

			//Cerramos el fichero
			SDL_RWclose(file2);
		}
		else
		{
			printf("Error: Unable to create file demo.bin ! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
	}
	//El fichero existe
	else
	{
		//Cargamos los datos
		printf("Reading file demo.bin ...!\n");
		for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
		{
			SDL_RWread(file2, &mDemoDataFile[i], sizeof(DemoKeys), 1);
		}

		//Cierra el fichero
		SDL_RWclose(file2);
	}

	//Carga los gráficos del jugador
	if (!mPlayerTexture->loadFromFile("media/gfx/player.png", mRenderer))
	{
		printf("Failed to load mPlayer texture!\n");
		success = false;
	}

	//Carga los gráficos de los globos
	if (!mBalloonTexture->loadFromFile("media/gfx/balloon.png", mRenderer))
	{
		printf("Failed to load mBalloon texture!\n");
		success = false;
	}

	//Carga los gráficos de las balas
	if (!mBulletTexture->loadFromFile("media/gfx/bullet.png", mRenderer))
	{
		printf("Failed to load mBullet texture!\n");
		success = false;
	}

	//Carga los gráficos del fondo del juego
	if (!mGameBackgroundTexture->loadFromFile("media/gfx/background.png", mRenderer))
	{
		printf("Failed to load game background texture!\n");
		success = false;
	}

	//Carga los gráficos del fondo de la pantalla de titulo
	if (!mTitleBackgroundTexture->loadFromFile("media/gfx/title.png", mRenderer))
	{
		printf("Failed to load title texture!\n");
		success = false;
	}

	//Carga varios gráficos para varios propósitos
	if (!mMiscTexture->loadFromFile("media/gfx/misc.png", mRenderer))
	{
		printf("Failed to load misc texture!\n");
		success = false;
	}

	//Carga los gráficos para el menu
	if (!mMenuTexture->loadFromFile("media/gfx/menu.png", mRenderer))
	{
		printf("Failed to load menu texture!\n");
		success = false;
	}

	//Carga los gráficos para el texto blanco
	if (!mWhiteFontTexture->loadFromFile("media/gfx/whitefont2.png", mRenderer))
	{
		printf("Failed to load white font texture!\n");
		success = false;
	}

	//Carga los gráficos para el texto negro
	if (!mNokiaFontTexture->loadFromFile("media/gfx/nokiafont.png", mRenderer))
	{
		printf("Failed to load black font texture!\n");
		success = false;
	}

	//Carga los gráficos para el texto negro
	if (!mIntroTexture->loadFromFile("media/gfx/intro.png", mRenderer))
	{
		printf("Failed to load intro texture!\n");
		success = false;
	}

	//Carga la música del titulo
	mTitleMusic = Mix_LoadMUS("media/music/title.ogg");
	if (mTitleMusic == NULL)
	{
		printf("Failed to load title music! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga la música del juego
	mPlayingMusic = Mix_LoadMUS("media/music/playing.ogg");
	if (mPlayingMusic == NULL)
	{
		printf("Failed to load playing music! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga la música de la intro
	mIntroMusic = Mix_LoadMUS("media/music/intro.ogg");
	if (mIntroMusic == NULL)
	{
		printf("Failed to load intro music! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para la explosión de los globos
	mPopBalloonFX = Mix_LoadWAV("media/sound/balloon.wav");
	if (mPopBalloonFX == NULL)
	{
		printf("Failed to load mBalloon sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para los disparos del jugador
	mBulletFX = Mix_LoadWAV("media/sound/bullet.wav");
	if (mBulletFX == NULL)
	{
		printf("Failed to load mBullet sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para el menu
	mMenuSelectedFX = Mix_LoadWAV("media/sound/menu_selected.wav");
	if (mMenuSelectedFX == NULL)
	{
		printf("Failed to load menu selected sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para el titulo
	mTitleFX = Mix_LoadWAV("media/sound/title.wav");
	if (mTitleFX == NULL)
	{
		printf("Failed to load title sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para los disparos del jugador
	mPlayerCollisionFX = Mix_LoadWAV("media/sound/player_collision.wav");
	if (mPlayerCollisionFX == NULL)
	{
		printf("Failed to load mPlayer collision sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para la puntuación máxima
	mHiScoreFX = Mix_LoadWAV("media/sound/hiscore.wav");
	if (mHiScoreFX == NULL)
	{
		printf("Failed to load hiscore sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para cuando aparece un item
	mItemDropFX = Mix_LoadWAV("media/sound/itemdrop.wav");
	if (mItemDropFX == NULL)
	{
		printf("Failed to load item drop sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para cuando se recoge un item
	mItemPickUpFX = Mix_LoadWAV("media/sound/itempickup.wav");
	if (mItemPickUpFX == NULL)
	{
		printf("Failed to load item pick up sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para cuando se pierde el toque extra
	mCoffeeOutFX = Mix_LoadWAV("media/sound/coffeeout.wav");
	if (mCoffeeOutFX == NULL)
	{
		printf("Failed to load coffee out up sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	return success;
}

//Libera todos los recursos y cierra SDL
void GameDirector::close()
{
	//Abre el fichero de puntuación para escribir
	SDL_RWops *file = SDL_RWFromFile("score.bin", "w+b");
	if (file != NULL)
	{
		//Guardamos los datos
		for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
		{
			SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1);
		}

		//Cerramos el fichero
		SDL_RWclose(file);
	}
	else
	{
		printf("Error: Unable to save file! %s\n", SDL_GetError());
	}

	//Abre el fichero de demo para escribir
	if (mDemoRecording)
	{
		SDL_RWops *file2 = SDL_RWFromFile("demo.bin", "w+b");
		if (file2 != NULL)
		{
			//Guardamos los datos
			for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
			{
				SDL_RWwrite(file2, &mDemoDataFile[i], sizeof(DemoKeys), 1);
			}

			//Cerramos el fichero
			SDL_RWclose(file2);
		}
		else
		{
			printf("Error: Unable to save file! %s\n", SDL_GetError());
		}
	}

	//Libera todas las imagenes
	mPlayerTexture->free();
	mGameBackgroundTexture->free();
	mTitleBackgroundTexture->free();
	mWhiteFontTexture->free();
	mNokiaFontTexture->free();
	mMenuTexture->free();
	mBalloonTexture->free();
	mMiscTexture->free();

	//Libera los efectos de sonido
	Mix_FreeChunk(mPopBalloonFX);
	mPopBalloonFX = NULL;
	Mix_FreeChunk(mBulletFX);
	mBulletFX = NULL;
	Mix_FreeChunk(mMenuSelectedFX);
	mMenuSelectedFX = NULL;
	Mix_FreeChunk(mPlayerCollisionFX);
	mPlayerCollisionFX = NULL;
	Mix_FreeChunk(mHiScoreFX);
	mHiScoreFX = NULL;
	Mix_FreeChunk(mTitleFX);
	mTitleFX = NULL;
	Mix_FreeChunk(mItemDropFX);
	mItemDropFX = NULL;
	Mix_FreeChunk(mItemPickUpFX);
	mItemPickUpFX = NULL;
	Mix_FreeChunk(mCoffeeOutFX);
	mCoffeeOutFX = NULL;

	//Libra la música
	Mix_FreeMusic(mTitleMusic);
	mTitleMusic = NULL;
	Mix_FreeMusic(mPlayingMusic);
	mPlayingMusic = NULL;
	Mix_FreeMusic(mIntroMusic);
	mIntroMusic = NULL;

	//Libera el mando
	SDL_JoystickClose(mGameController);
	mGameController = NULL;

	//Destruye la ventana
	SDL_DestroyRenderer(mRenderer);
	SDL_DestroyWindow(mWindow);
	mWindow = NULL;
	mRenderer = NULL;

	//Sal del subsistema SDL
	Mix_Quit();
	IMG_Quit();
	SDL_Quit();
}

//Establece el valor de la variable
void GameDirector::setScore(Uint32 score)
{
	mScore = score;
}

//Establece el valor de la variable
void GameDirector::setHiScore(Uint32 score)
{
	mHiScore = score;
}

//Actualiza el valor de HiScore en caso necesario
void GameDirector::updateHiScore()
{
	//La puntuación es mayor que la máxima puntuación
	if (mScore > mHiScore)
	{
		//Actualiza la máxima puntuación
		mHiScore = mScore;

		//Almacena la máxima puntuación en el fichero junto con un checksum
		mScoreDataFile[0] = mHiScore;
		mScoreDataFile[1] = mHiScore % 43;

		//Si superamos la máxima puntuación
		if (mHiScoreAchieved == false)
		{
			mHiScoreAchieved = true;
			Mix_PlayChannel(-1, mHiScoreFX, 0);
		}
	}
}

//Transforma un valor numérico en una cadena de 6 cifras
std::string GameDirector::updateScoreText(Uint32 num)
{
	if ((num >= 0) && (num <= 9))
	{
		return ("00000" + std::to_string(num));
	}

	if ((num >= 10) && (num <= 99))
	{
		return ("0000" + std::to_string(num));
	}

	if ((num >= 100) && (num <= 999))
	{
		return ("000" + std::to_string(num));
	}

	if ((num >= 1000) && (num <= 9999))
	{
		return ("00" + std::to_string(num));
	}

	if ((num >= 010000) && (num <= 99999))
	{
		return ("0" + std::to_string(num));
	}

	if ((num >= 100000) && (num <= 999999))
	{
		return (std::to_string(num));
	}

	return (std::to_string(num));
}

//Pinta el marcador en pantalla usando un objeto texto
void GameDirector::renderScoreBoard()
{
	mWhiteText->write(SCORE_WORD_X, SCORE_WORD_Y, "SCORE", 0);
	mWhiteText->write(SCORE_NUMBER_X, SCORE_NUMBER_Y, updateScoreText(mScore), 0);

	mWhiteText->write(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y, "MULT", 0);
	mWhiteText->write(MULTIPLIER_NUMBER_X, MULTIPLIER_NUMBER_Y, std::to_string(mPlayer->getScoreMultiplier()), 0, 3);

	mWhiteText->write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE", 0);
	mWhiteText->write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, updateScoreText(mHiScore), 0);
}

//Mueve todos los globos activos
void GameDirector::moveBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->update();
		}
	}
}

//Pinta en pantalla todos los globos activos
void GameDirector::renderBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->render();
			if ((mDebug) && (mBalloon[i]->isPopping() == false))
				mWhiteText->write(mBalloon[i]->getPosX() + (mBalloon[i]->getWidth() / 2) - 4, mBalloon[i]->getPosY() - 8, std::to_string(i));
		}
	}
}

//Devuelve el primer indice no activo del vector de globos
Uint8 GameDirector::getBallonFreeIndex()
{
	int index = 0;

	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive() == false)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Crea un globo nuevo en el vector de globos
Uint8 GameDirector::createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture)
{
	Uint8 index = getBallonFreeIndex();
	mBalloon[index]->init(x, y, kind, velx, creationtimer, texture, mRenderer);
	return index;
}

//Establece a cero todos los valores del vector de objetos globo
void GameDirector::resetBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		mBalloon[i]->erase();
	}
}

//Explosiona un globo. Lo destruye y crea otros dos si es el caso
void GameDirector::popBalloon(Uint8 index)
{
	if (mBalloon[index]->isActive())
	{
		//Otorga los puntos correspondientes al globo
		mPlayer->addScore(Uint32(mBalloon[index]->getScore() * mPlayer->getScoreMultiplier()));
		setScore(mPlayer->getScore());
		updateHiScore();

		Uint8 kind = mBalloon[index]->getKind();
		Uint8 freeIndex = 0;
		switch (kind)
		{
		//Si es del tipo más pequeño, simplemente elimina el globo
		case BALLOON_1:
			mBalloon[index]->pop();
			break;

		//En cualquier otro caso, crea dos globos de un tipo inferior
		default:
			freeIndex = getBallonFreeIndex();
			mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_NEGATIVE, 0, mBalloonTexture, mRenderer);
			mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2));
			mBalloon[freeIndex]->setVelY(-2.5);
			//if (isTimeStopped()) mBalloon[freeIndex]->setStop(true);

			freeIndex = getBallonFreeIndex();
			mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_POSITIVE, 0, mBalloonTexture, mRenderer);
			mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2));
			mBalloon[freeIndex]->setVelY(-2.5);
			//if (isTimeStopped()) mBalloon[freeIndex]->setStop(true);

			//Elimina el globo
			mBalloon[index]->pop();
			break;
		}
		//Reproduce el sonido de explotar el globo
		//Mix_PlayChannel(-1, mPopBalloonFX, 0);

		//Recalcula el nivel de amenaza
		calculateMenaceLevel();
	}
}

//Explosiona todos los globos
void GameDirector::popAllBallons()
{
	int candidate[MAX_BALLOONS];
	Uint8 j = 0;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if ((mBalloon[i]->isActive()) && (mBalloon[i]->isPopping() == false) && (mBalloon[i]->isBeingCreated() == false))
		{
			candidate[j] = i;
		}
		else
		{
			candidate[j] = -1;
		}
		j += 1;
	}
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (candidate[i] >= 0)
		{
			popBalloon(i);
		}
	}
	Mix_PlayChannel(-1, mPopBalloonFX, 0);
}

//Detiene todos los globos
void GameDirector::stopAllBalloons(Uint16 time)
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->setStop(true);
			mBalloon[i]->setStoppedTimer(time);
		}
	}
}

//Pone en marcha todos los globos
void GameDirector::startAllBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if ((mBalloon[i]->isActive()) && (mBalloon[i]->isBeingCreated() == false))
		{
			mBalloon[i]->setStop(false);
			mBalloon[i]->setStoppedTimer(0);
		}
	}
}

//Obtiene el numero de globos activos
Uint8 GameDirector::countBalloons()
{
	Uint8 num = 0;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			++num;
		}
	}
	return num;
}

//Comprueba la colisión entre el jugador y los globos activos
bool GameDirector::checkPlayerBallonCollision()

{
	bool result = false;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if ((mBalloon[i]->isActive()) && (mBalloon[i]->isStopped() == false))
		{
			if (checkCollision(mPlayer->getCollider(), mBalloon[i]->getCollider()))
			{
				result = true;
				break;
			}
		}
	}
	return result;
}

//Comprueba la colisión entre el jugador y los items
void GameDirector::checkPlayerItemCollision()
{
	for (Uint8 i = 0; i < MAX_ITEMS; i++)
	{
		if (mItem[i]->isEnabled())
		{
			if (checkCollision(mPlayer->getCollider(), mItem[i]->getCollider()))
			{
				switch (mItem[i]->getClass())
				{
				case ITEM_POINTS_1_DISK:
					mPlayer->addScore(1000);
					setScore(mPlayer->getScore());
					updateHiScore();
					createSmartSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m1000Bitmap->getWidth() / 2), mItem[i]->getPosY(), m1000Bitmap);
					Mix_PlayChannel(-1, mItemPickUpFX, 0);
					break;
				case ITEM_POINTS_2_GAVINA:
					mPlayer->addScore(2500);
					setScore(mPlayer->getScore());
					updateHiScore();
					createSmartSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m2500Bitmap->getWidth() / 2), mPlayer->getPosY(), m2500Bitmap);
					Mix_PlayChannel(-1, mItemPickUpFX, 0);
					break;
				case ITEM_POINTS_3_PACMAR:
					mPlayer->addScore(5000);
					setScore(mPlayer->getScore());
					updateHiScore();
					createSmartSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), mPlayer->getPosY(), m5000Bitmap);
					Mix_PlayChannel(-1, mItemPickUpFX, 0);
					break;
				case ITEM_CLOCK:
					enableTimeStopItem();
					Mix_PlayChannel(-1, mItemPickUpFX, 0);
					break;
				case ITEM_TNT:
					popAllBallons();
					setExplosionTime(true);
					Mix_PlayChannel(-1, mTitleFX, 0);
					break;
				case ITEM_COFFEE:
					mPlayer->giveExtraHit();
					Mix_PlayChannel(-1, mItemPickUpFX, 0);
					break;

				default:
					break;
				}
				mItem[i]->erase();
			}
		}
	}
}

//Comprueba y procesa la colisión entre las balas y los globos
void GameDirector::checkBulletBallonCollision()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		for (Uint8 j = 0; j < MAX_BULLETS; j++)
		{
			if (mBalloon[i]->isActive() && !(mBalloon[i]->isInvulnerable()) && mBullet[j]->isActive())
			{
				if (checkCollision(mBalloon[i]->getCollider(), mBullet[j]->getCollider()))
				{
					/*mPlayer->addScore(Uint32(mBalloon[i]->getScore() * mPlayer->getScoreMultiplier()));
					mPlayer->incScoreMultiplier();
					setScore(mPlayer->getScore());
					updateHiScore();*/
					mPlayer->incScoreMultiplier();
					popBalloon(i);
					if (mDemo == false)
						Mix_PlayChannel(-1, mPopBalloonFX, 0);
					mBullet[j]->erase();
					calculateMenaceLevel();
					Uint8 droppeditem = dropItem();
					if ((droppeditem != NO_KIND) && (mDemo == false) && (mDemoRecording == false))
					{
						createItem(mBalloon[i]->getPosX(), mBalloon[i]->getPosY(), droppeditem);
						Mix_PlayChannel(-1, mItemDropFX, 0);
					}
					break;
				}
			}
		}
	}
}

//Mueve las balas activas
void GameDirector::moveBullets()
{
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		if (mBullet[i]->isActive())
		{
			if (mBullet[i]->move() == MSG_BULLET_OUT)
			{
				mPlayer->decScoreMultiplier();
			}
		}
	}
}

//Pinta las balas activas
void GameDirector::renderBullets()
{
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		if (mBullet[i]->isActive())
		{
			mBullet[i]->render();
		}
	}
}

//Devuelve el primer indice no activo del vector de balas
Uint8 GameDirector::getBulletFreeIndex()
{
	Uint8 index = 0;

	for (int i = 0; i < MAX_BULLETS; i++)
	{
		if (mBullet[i]->isActive() == false)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Establece a cero todos los valores del vector de objetos bala
void GameDirector::resetBullets()
{
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		mBullet[i]->erase();
	}
}

//Crea un objeto bala
void GameDirector::createBullet(int x, int y, Uint8 kind)
{
	mBullet[getBulletFreeIndex()]->init(x, y, kind, mBulletTexture, mRenderer);
}

//Actualiza los items
void GameDirector::updateItems()
{
	for (Uint8 i = 0; i < MAX_ITEMS; i++)
	{
		mItem[i]->update();
	}
}

//Pinta los items activos
void GameDirector::renderItems()
{
	for (Uint8 i = 0; i < MAX_ITEMS; i++)
	{
		mItem[i]->render();
	}
}

//Devuelve el primer indice no activo del vector de items
Uint8 GameDirector::getItemFreeIndex()
{
	Uint8 index = 0;

	for (int i = 0; i < MAX_ITEMS; i++)
	{
		if (mItem[i]->getClass() == NO_KIND)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Establece a cero todos los valores del vector de objetos item
void GameDirector::resetItems()
{
	for (Uint8 i = 0; i < MAX_ITEMS; i++)
	{
		mItem[i]->erase();
	}
}

//Devuelve un item en función del azar
Uint8 GameDirector::dropItem()
{
	//return ITEM_COFFEE;
	Uint8 luckyNumber = rand() % 99;
	Uint8 item = rand() % 6;
	switch (item)
	{
	case 0:
		if (luckyNumber < 10)
		{
			return ITEM_POINTS_1_DISK;
		}
		break;
	case 1:
		if (luckyNumber < 6)
		{
			return ITEM_POINTS_2_GAVINA;
		}
		break;
	case 2:
		if (luckyNumber < 3)
		{
			return ITEM_POINTS_3_PACMAR;
		}
		break;
	case 3:
		if (luckyNumber < 5)
		{
			return ITEM_CLOCK;
		}
		break;
	case 4:
		if (luckyNumber < 5)
		{
			return ITEM_TNT;
		}
		break;
	case 5:
		if (luckyNumber < 5)
		{
			return ITEM_COFFEE;
		}
		break;
	default:
		break;
	}
	return NO_KIND;
}

//Crea un objeto item
void GameDirector::createItem(int x, int y, Uint8 kind)
{
	mItem[getItemFreeIndex()]->init(kind, x, y, mMiscTexture, mRenderer);
}

//Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto
void GameDirector::createSmartSprite(int x, int y, SmartSprite *sprite)
{
	Uint8 index = getSmartSpriteFreeIndex();
	*mSmartSprite[index] = *sprite;
	mSmartSprite[index]->setPosX(x);
	mSmartSprite[index]->setPosY(y);
	mSmartSprite[index]->setDestX(x);
	mSmartSprite[index]->setDestY(y - 15);
	mSmartSprite[index]->setEnabled(true);
	mSmartSprite[index]->setEnabledTimer(100);
}

//Crea un SmartSprite para arrojar el item café al recibir un impacto
void GameDirector::throwCoffee(int x, int y)
{
	Uint8 index = getSmartSpriteFreeIndex();
	mSmartSprite[index]->init(mMiscTexture, mRenderer);
	mSmartSprite[index]->setPosX(x - 8);
	mSmartSprite[index]->setPosY(y - 8);
	mSmartSprite[index]->setWidth(16);
	mSmartSprite[index]->setHeight(16);
	mSmartSprite[index]->setVelX(-1.0f + ((rand() % 5) * 0.5f));
	mSmartSprite[index]->setVelY(-4.0f);
	mSmartSprite[index]->setAccelX(0.0f);
	mSmartSprite[index]->setAccelY(0.2f);
	mSmartSprite[index]->setDestX(x + (mSmartSprite[index]->getVelX() * 50));
	mSmartSprite[index]->setDestY(SCREEN_HEIGHT + 1);
	mSmartSprite[index]->setEnabled(true);
	mSmartSprite[index]->setEnabledTimer(1);
	mSmartSprite[index]->setSpriteClip(80, 64, 16, 16);
}

//Actualiza los SmartSprites
void GameDirector::updateSmartSprites()
{
	for (int i = 0; i < MAX_SMART_SPRITES; i++)
	{
		mSmartSprite[i]->update();
	}
}

//Pinta los SmartSprites activos
void GameDirector::renderSmartSprites()
{
	for (int i = 0; i < MAX_SMART_SPRITES; i++)
	{
		mSmartSprite[i]->render();
	}
}

//Devuelve el primer indice no activo del vector de SmartSprites
Uint8 GameDirector::getSmartSpriteFreeIndex()
{
	Uint8 index = 0;

	for (int i = 0; i < MAX_SMART_SPRITES; i++)
	{
		if (mSmartSprite[i]->isEnabled() == false)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Establece a cero todos los valores del vector de objetos SmafrtSprite
void GameDirector::resetSmartSprites()
{
	for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++)
	{
		mSmartSprite[i]->erase();
	}
}

//Acciones a realizar cuando el jugador muere
void GameDirector::killPlayer()
{
	if (mPlayer->hasExtraHit())
	{
		mPlayer->removeExtraHit();
		throwCoffee(mPlayer->getPosX() + (mPlayer->getWidth() / 2), mPlayer->getPosY() + (mPlayer->getHeight() / 2));
		Mix_PlayChannel(-1, mCoffeeOutFX, 0);
	}
	else if (mPlayer->isInvulnerable() == false)
	{
		stopAllBalloons(5000);
		Mix_HaltMusic();
		Mix_PlayChannel(-1, mPlayerCollisionFX, 0);
		shakeScreen();
		mPlayer->setAlive(false);
		SDL_Delay(1000);
		setGameStatus(GAME_STATE_GAME_OVER_SCREEN);
	}
}

//Calcula y establece el valor de amenaza en funcion de los globos activos
void GameDirector::calculateMenaceLevel()
{
	mMenaceLevel = 0;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		mMenaceLevel += mBalloon[i]->getMenace();
	}
}

//Obtiene el valor de la variable
Uint8 GameDirector::getMenaceLevel()
{
	return mMenaceLevel;
}

//Obtiene el valor de la variable
bool GameDirector::isPlayFieldDrawOnly()
{
	return mPlayFieldDrawOnly;
}

//Establece el valor de la variable
void GameDirector::setPlayFieldDrawOnly(bool state)
{
	mPlayFieldDrawOnly = state;
}

//Establece el valor de la variable
void GameDirector::setTimeStopped(bool value)
{
	mTimeStopped = value;
}

//Obtiene el valor de la variable
bool GameDirector::isTimeStopped()
{
	return mTimeStopped;
}

//Establece el valor de la variable
void GameDirector::setTimeStoppedTimer(Uint16 value)
{
	mTimeStoppedTimer = value;
}

//Actualiza y comprueba el valor de la variable
void GameDirector::updateTimeStoppedTimer()
{
	if (isTimeStopped())
	{
		if (mTimeStoppedTimer > 0)
		{
			mTimeStoppedTimer--;
			stopAllBalloons(TIME_STOPPED_TIMER);
		}
		else
		{
			disableTimeStopItem();
		}
	}
}

//Establece el valor de la variable
void GameDirector::setExplosionTime(bool value)
{
	mExplosionTime = value;
}

//Obtiene el valor de la variable
bool GameDirector::isExplosionTime()
{
	return mExplosionTime;
}

//Establece el valor de la variable
void GameDirector::setRemainingExplosions(Uint8 value)
{
	mRemainingExplosions = value;
}

//Actualiza y comprueba el valor de la variable
void GameDirector::updateRemainingExplosionsTimer()
{
	if (isExplosionTime())
	{
		if (mRemainingExplosionsTimer > 0)
		{
			--mRemainingExplosionsTimer;
		}
		else if (mRemainingExplosions > 0)
		{
			popAllBallons();
			--mRemainingExplosions;
			mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER;
		}
		else
		{
			mExplosionTime = false;
			mRemainingExplosions = REMAINING_EXPLOSIONS;
			mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER;
		}
	}
}

//Actualiza el campo de juego
void GameDirector::updatePlayField()
{
	if (isPlayFieldDrawOnly() == false)
	{
		//Comprueba el teclado/mando
		checkGameInput();

		//Actualiza el jugador
		mPlayer->update();

		//Mueve los globos
		moveBalloons();

		//Mueve las balas
		moveBullets();

		//Actualiza los items
		updateItems();

		//Actualiza los SmartSprites
		updateSmartSprites();

		//Actualiza los contadores de estado
		updateTimeStoppedTimer();
		updateRemainingExplosionsTimer();

		//Comprueba las colisiones entre globos y balas
		checkBulletBallonCollision();

		//Comprueba las colisiones entre elk jugador y los items
		checkPlayerItemCollision();

		//Comprueba el nivel de amenaza
		checkMenaceLevel();

		//Comprueba la colisión entre el jugador y los globos
		if (checkPlayerBallonCollision())
		{
			if (mPlayer->isAlive())
			{
				if (mDemo)
				{
					mGameStatus = GAME_STATE_TITLE;
				}
				else
				{
					killPlayer();
				}
			}
		}
	}
}

//Actualiza el fondo
void GameDirector::updateBackground()
{
	mGBClouds1->move();
	mGBClouds1b->move();
	mGBClouds2->move();
	mGBClouds2b->move();
	if (mGBClouds1->getPosX() < -mGBClouds1->getWidth())
	{
		mGBClouds1->setPosX(mGBClouds1->getWidth());
	}
	if (mGBClouds1b->getPosX() < -mGBClouds1b->getWidth())
	{
		mGBClouds1b->setPosX(mGBClouds1b->getWidth());
	}
	if (mGBClouds2->getPosX() < -mGBClouds2->getWidth())
	{
		mGBClouds2->setPosX(mGBClouds2->getWidth());
	}
	if (mGBClouds2b->getPosX() < -mGBClouds2b->getWidth())
	{
		mGBClouds2b->setPosX(mGBClouds2b->getWidth());
	}
}

//Dibuja el fondo
void GameDirector::renderBackground()
{
	updateBackground();
	mGBClouds1->render();
	mGBClouds1b->render();
	mGBClouds2->render();
	mGBClouds2b->render();
	mGameBackgroundFront->render();
}

//Dibuja el campo de juego
void GameDirector::renderPlayField()
{
	renderBackground();
	renderBalloons();
	renderBullets();
	renderItems();
	renderSmartSprites();
	mPlayer->render();
	renderScoreBoard();
	renderMessages();
	renderDebugInfo();
}

//Gestiona el nivel de amenaza
void GameDirector::checkMenaceLevel()
{
	//Aumenta el nivel de amenaza en función de la puntuación
	mMenaceLevelThreshold = 7 + (4 * (mScore / 10000));

	//Si el nivel de amenza es inferior al umbral
	if (mMenaceLevel < mMenaceLevelThreshold)
	{
		Uint8 index = 0;

		//Obtiene el centro del jugador en el eje X
		int x = mPlayer->getPosX() + (mPlayer->getWidth() / 2);

		//Crea un globo sobre el jugador en dirección hacia el centro
		if (x < (PLAY_AREA_WIDTH / 2))
		{
			index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_POSITIVE, 400, mBalloonTexture);
		}
		else
		{
			index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400, mBalloonTexture);
		}
		mBalloon[index]->allignTo(x);

		//Recalcula el nivel de amenaza con el nuevo globo
		calculateMenaceLevel();
	}
}

//Gestiona la entrada de teclado y mando durante el juego
void GameDirector::checkGameInput()
{
	//Obtiene el estado de las teclas pulsadas del teclado
	keystates = SDL_GetKeyboardState(NULL);

	mDemoKeys.left = 0;
	mDemoKeys.right = 0;
	mDemoKeys.noInput = 0;
	mDemoKeys.fire = 0;
	mDemoKeys.fireLeft = 0;
	mDemoKeys.fireRight = 0;

	if (mDemo)
	{
		if (mDemoDataFile[mDemoCounter].left == 1)
			mPlayer->checkInput(INPUT_LEFT);
		if (mDemoDataFile[mDemoCounter].right == 1)
			mPlayer->checkInput(INPUT_RIGHT);
		if (mDemoDataFile[mDemoCounter].noInput == 1)
			mPlayer->checkInput(NO_INPUT);
		if (mDemoDataFile[mDemoCounter].fire == 1)
			if (mPlayer->canFire())
			{
				createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP);
				mPlayer->setFireCooldown(10);
			}
		if (mDemoDataFile[mDemoCounter].fireLeft == 1)
			if (mPlayer->canFire())
			{
				createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP);
				mPlayer->setFireCooldown(10);
			}
		if (mDemoDataFile[mDemoCounter].fireRight == 1)
			if (mPlayer->canFire())
			{
				createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP);
				mPlayer->setFireCooldown(10);
			}

		//Comprobamos la tecla o el botón de pausa/menu
		if ((keystates[mKeyboard.accept] != 0) || (keystates[mKeyboard.cancel] != 0) || (SDL_JoystickGetButton(mGameController, BUTTON_START)))
		{
			setGameStatus(GAME_STATE_TITLE);
		}

		if (mDemoCounter < TOTAL_DEMO_DATA)
		{
			mDemoCounter++;
		}
		else
		{
			mGameStatus = GAME_STATE_TITLE;
		}
	}
	else
	{
		//Si está pulsada la tecla izquierda o el mando hacia la izquierda
		if ((keystates[mKeyboard.left] != 0) || (SDL_JoystickGetAxis(mGameController, 0) < -JOYSTICK_DEAD_ZONE))
		{
			mPlayer->checkInput(INPUT_LEFT);
			mDemoKeys.left = 1;
		}
		//Si está pulsada la tecla derecha o el mando hacia la derecha
		else if ((keystates[mKeyboard.right] != 0) || (SDL_JoystickGetAxis(mGameController, 0) > JOYSTICK_DEAD_ZONE))
		{
			mPlayer->checkInput(INPUT_RIGHT);
			mDemoKeys.right = 1;
		}
		//Ninguna de las dos direcciones pulsadas
		else
		{
			mPlayer->checkInput(NO_INPUT);
			mDemoKeys.noInput = 1;
		}

		//Comprobamos la tecla o el botón de disparo central
		if ((keystates[mKeyboard.fire] != 0) || (SDL_JoystickGetButton(mGameController, BUTTON_X)))
		{
			if (mPlayer->canFire())
			{
				createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP);
				mPlayer->setFireCooldown(10);

				//Reproduce el sonido de disparo
				Mix_PlayChannel(-1, mBulletFX, 0);

				mDemoKeys.fire = 1;
			}
		}

		//Comprobamos la tecla o el botón de disparo izquierdo
		if ((keystates[mKeyboard.fireLeft] != 0) || (SDL_JoystickGetButton(mGameController, BUTTON_Y)))
		{
			if (mPlayer->canFire())
			{
				createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_LEFT);
				mPlayer->setFireCooldown(10);

				//Reproduce el sonido de disparo
				Mix_PlayChannel(-1, mBulletFX, 0);

				mDemoKeys.fireLeft = 1;
			}
		}

		//Comprobamos la tecla o el botón de disparo derecho
		if ((keystates[mKeyboard.fireRight] != 0) || (SDL_JoystickGetButton(mGameController, BUTTON_A)))
		{
			if (mPlayer->canFire())
			{
				createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_RIGHT);
				mPlayer->setFireCooldown(10);

				//Reproduce el sonido de disparo
				Mix_PlayChannel(-1, mBulletFX, 0);

				mDemoKeys.fireRight = 1;
			}
		}

		//Comprobamos la tecla o el botón de pausa/menu
		if ((keystates[mKeyboard.escape] != 0) || (SDL_JoystickGetButton(mGameController, BUTTON_START)))
		{
			setGameStatus(GAME_STATE_PAUSED);

			if (Mix_PlayingMusic() == 1)
			{
				Mix_PauseMusic();
			}
		}

		if (mDemoCounter < TOTAL_DEMO_DATA)
		{
			mDemoDataFile[mDemoCounter] = mDemoKeys;
			mDemoCounter++;
		}
		else if (mDemoRecording)
		{
			mGameStatus = GAME_STATE_QUIT;
		}
	}
}

//Gestiona la entrada de teclado y mando durante el menu
void GameDirector::checkMenuInput(Menu *menu)
{
	//Obtiene el estado de las teclas pulsadas del teclado
	keystates = SDL_GetKeyboardState(NULL);

	//Si está pulsada la tecla izquierda o el mando hacia la izquierda
	if ((keystates[mKeyboard.up] != 0) || (SDL_JoystickGetAxis(mGameController, 1) < -JOYSTICK_DEAD_ZONE))
	{
		menu->checkInput(INPUT_UP);
	}
	//Si está pulsada la tecla derecha o el mando hacia la derecha
	else if ((keystates[mKeyboard.down] != 0) || (SDL_JoystickGetAxis(mGameController, 1) > JOYSTICK_DEAD_ZONE))
	{
		menu->checkInput(INPUT_DOWN);
	}
	//Comprobamos la tecla o el botón de menu/pausa
	else if (keystates[mKeyboard.accept] != 0 || (SDL_JoystickGetButton(mGameController, BUTTON_A)))
	{
		menu->checkInput(INPUT_ACCEPT);
	}
}

//Obtiene el valor de la variable
Uint8 GameDirector::getGameStatus()
{
	return mGameStatus;
}

//Establece el valor de la variable
void GameDirector::setGameStatus(Uint8 status)
{
	mGameStatus = status;
}

//Pinta una transición en pantalla
void GameDirector::renderTransition(Uint8 index)
{
	switch (index)
	{
	case 0:
		SDL_Rect rect;
		rect.x = 0;
		rect.y = 0;
		rect.w = SCREEN_WIDTH;
		rect.h = 0;

		SDL_RenderPresent(mRenderer);
		SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0);

		for (Uint16 i = 0; i < SCREEN_HEIGHT; i = i + 2)
		{
			rect.h = i;
			SDL_RenderFillRect(mRenderer, &rect);
			SDL_RenderPresent(mRenderer);
		}
		break;

	case 1:
		SDL_Rect rect1;
		rect1.x = 0;
		rect1.y = 0;
		rect1.w = SCREEN_WIDTH;
		rect1.h = 0;

		SDL_Rect rect2;
		rect2.x = 0;
		rect2.y = 0;
		rect2.w = SCREEN_WIDTH;
		rect2.h = 0;

		SDL_RenderPresent(mRenderer);
		SDL_SetRenderDrawBlendMode(mRenderer, SDL_BLENDMODE_BLEND);
		SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 64);

		for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
		{
			rect1.h = i;
			SDL_RenderFillRect(mRenderer, &rect1);

			rect2.h = i;
			rect2.y = SCREEN_HEIGHT - (i);
			SDL_RenderFillRect(mRenderer, &rect2);

			SDL_RenderPresent(mRenderer);
		}

		rect1.x = 0;
		rect1.y = 0;
		rect1.w = SCREEN_WIDTH;
		rect1.h = SCREEN_HEIGHT;
		SDL_SetRenderDrawColor(mRenderer, 0, 0, 0, 64);

		for (Uint16 i = 0; i < 32; i++)
		{
			SDL_RenderFillRect(mRenderer, &rect1);
			SDL_RenderPresent(mRenderer);
		}
		break;

	default:
		break;
	}
}

//Pinta diferentes mensajes en la pantalla
void GameDirector::renderMessages()
{
	mGetReadyBitmap->update();
	mGetReadyBitmap->render();
	if (mGetReadyBitmap->isEnabled() == false)
		setPlayFieldDrawOnly(false);

	if (mTimeStopped)
	{
		if ((mTimeStoppedTimer > 100) || (mTimeStoppedTimer % 10 > 4))
			mWhiteText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "Time Stopped: " + std::to_string(mTimeStoppedTimer / 10), 0);
	}

	if (mDemo)
	{
		if (mDemoCounter % 30 > 14)
			mWhiteText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "D E M O", 0);
	}
}

//Habilita el efecto del item de detener el tiempo
void GameDirector::enableTimeStopItem()
{
	stopAllBalloons(TIME_STOPPED_TIMER);
	setTimeStopped(true);
	setTimeStoppedTimer(TIME_STOPPED_TIMER);
	if (Mix_PlayingMusic() == 1)
	{
		Mix_PauseMusic();
	}
}

//Deshabilita el efecto del item de detener el tiempo
void GameDirector::disableTimeStopItem()
{
	mTimeStopped = false;
	mTimeStoppedTimer = TIME_STOPPED_TIMER;
	startAllBalloons();
	if (Mix_PlayingMusic() == 1)
	{
		Mix_ResumeMusic();
	}
}

//Agita la pantalla
void GameDirector::shakeScreen()
{
	int v[] = {-1, 1, -1, 1, -1, 1, -1, 0};
	for (Uint8 n = 0; n < 8; n++)
	{
		//Limpia la pantalla
		SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);
		SDL_RenderClear(mRenderer);

		//Dibuja los objetos
		mGameBackgroundFront->setPosX(0);
		mGameBackgroundFront->setWidth(1);
		mGameBackgroundFront->setSpriteClip(0, 0, 1, 192);
		renderBackground();

		mGameBackgroundFront->setPosX(255);
		mGameBackgroundFront->setSpriteClip(255, 0, 1, 192);
		mGameBackgroundFront->render();

		mGameBackgroundFront->setPosX(v[n]);
		mGameBackgroundFront->setWidth(256);
		mGameBackgroundFront->setSpriteClip(0, 0, 256, 192);
		mGameBackgroundFront->render();

		renderBalloons();
		renderBullets();
		renderItems();
		mPlayer->render();
		renderScoreBoard();

		//Pinta dos lineas negras a los bordes
		//SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);
		//SDL_RenderDrawLine(mRenderer, 0, 0, 0, SCREEN_HEIGHT - 1);
		//SDL_RenderDrawLine(mRenderer, SCREEN_WIDTH - 1, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);

		//Actualiza la pantalla
		SDL_RenderPresent(mRenderer);
		SDL_Delay(50);
	}
}

//Bucle para la intro del juego
void GameDirector::runIntro()
{
	//Si la música no está sonando
	if (Mix_PlayingMusic() == 0)
	{
		//Reproduce la música
		Mix_PlayMusic(mIntroMusic, 1);
	}

	if (SDL_GetTicks() - mOldTicks > mGameSpeed)
	{
		//Actualiza el contador de ticks
		mOldTicks = SDL_GetTicks();

		//Comprueba los eventos que hay en la cola
		while (SDL_PollEvent(mEventHandler) != 0)
		{
			//Evento de salida de la aplicación
			if (mEventHandler->type == SDL_QUIT)
			{
				setGameStatus(GAME_STATE_QUIT);
				break;
			}

			if (mEventHandler->type == SDL_KEYDOWN)
			{
				Mix_HaltMusic();
				setGameStatus(GAME_STATE_TITLE);
				break;
			}

			//Tecla ESPACIO, ENTER o ESC pulsada
			/*if ((mEventHandler->key.keysym.sym == SDLK_SPACE) ||
				(mEventHandler->key.keysym.sym == SDLK_ESCAPE) ||
				(mEventHandler->key.keysym.sym == SDLK_RETURN))
			{
				Mix_HaltMusic();
				setGameStatus(GAME_STATE_TITLE);
				//SDL_Delay(200);
				break;
			}*/
		}

		//Actualiza los objetos
		for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
		{
			mIntroBitmap[i]->update();
		}
		for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
		{
			mIntroText[i]->update();
		}

		//Guión de eventos
		//Primera imagen - UPV
		if (mIntroEvents[BITMAP0] == EVENT_WAITING)
		{
			mIntroBitmap[0]->setEnabled(true);
			mIntroEvents[BITMAP0] = EVENT_RUNNING;
		}

		//Primer texto de la primera imagen
		if ((mIntroEvents[BITMAP0] == EVENT_COMPLETED) && (mIntroEvents[TEXT0] == EVENT_WAITING))
		{
			mIntroText[0]->setEnabled(true);
			mIntroEvents[TEXT0] = EVENT_RUNNING;
		}

		//Segundo texto de la primera imagen
		if ((mIntroEvents[TEXT0] == EVENT_COMPLETED) && (mIntroEvents[TEXT1] == EVENT_WAITING))
		{
			mIntroText[0]->setEnabled(false);
			mIntroText[1]->setEnabled(true);
			mIntroEvents[TEXT1] = EVENT_RUNNING;
		}

		//Tercer texto de la primera imagen
		if ((mIntroEvents[TEXT1] == EVENT_COMPLETED) && (mIntroEvents[TEXT2] == EVENT_WAITING))
		{
			mIntroText[1]->setEnabled(false);
			mIntroText[2]->setEnabled(true);
			mIntroEvents[TEXT2] = EVENT_RUNNING;
		}

		//Segunda imagen - Máquina
		if ((mIntroEvents[TEXT2] == EVENT_COMPLETED) && (mIntroEvents[BITMAP1] == EVENT_WAITING))
		{
			mIntroBitmap[0]->setEnabled(false);
			mIntroText[2]->setEnabled(false);
			mIntroBitmap[1]->setEnabled(true);
			mIntroEvents[BITMAP1] = EVENT_RUNNING;
		}

		//Primer texto de la segunda imagen
		if ((mIntroEvents[BITMAP1] == EVENT_COMPLETED) && (mIntroEvents[TEXT3] == EVENT_WAITING))
		{
			mIntroText[3]->setEnabled(true);
			mIntroEvents[TEXT3] = EVENT_RUNNING;
		}

		//Tercera imagen junto con primer texto - GRITO
		if ((mIntroEvents[TEXT3] == EVENT_COMPLETED) && (mIntroEvents[BITMAP2] == EVENT_WAITING) && (mIntroEvents[TEXT4] == EVENT_WAITING))
		{
			mIntroBitmap[1]->setEnabled(false);
			mIntroText[3]->setEnabled(false);
			mIntroBitmap[2]->setEnabled(true);
			mIntroText[4]->setEnabled(true);
			mIntroEvents[BITMAP2] = EVENT_RUNNING;
			mIntroEvents[TEXT4] = EVENT_RUNNING;
		}

		//Cuarta imagen junto con primer texto - Reflexión
		if ((mIntroEvents[TEXT4] == EVENT_COMPLETED) && (mIntroEvents[BITMAP3] == EVENT_WAITING) && (mIntroEvents[TEXT5] == EVENT_WAITING))
		{
			mIntroBitmap[2]->setEnabled(false);
			mIntroText[4]->setEnabled(false);
			mIntroBitmap[3]->setEnabled(true);
			mIntroText[5]->setEnabled(true);
			mIntroEvents[BITMAP3] = EVENT_RUNNING;
			mIntroEvents[TEXT5] = EVENT_RUNNING;
		}

		//Segundo texto de la cuarta imagen
		if ((mIntroEvents[TEXT5] == EVENT_COMPLETED) && (mIntroEvents[TEXT6] == EVENT_WAITING))
		{
			mIntroText[5]->setEnabled(false);
			mIntroText[6]->setEnabled(true);
			mIntroEvents[TEXT6] = EVENT_RUNNING;
		}

		//Quinta imagen - Patada
		if ((mIntroEvents[TEXT6] == EVENT_COMPLETED) && (mIntroEvents[BITMAP4] == EVENT_WAITING))
		{
			mIntroBitmap[3]->setEnabled(false);
			mIntroText[6]->setEnabled(false);
			mIntroBitmap[4]->setEnabled(true);
			mIntroEvents[BITMAP4] = EVENT_RUNNING;
		}

		//Primer texto de la quinta imagen
		if ((mIntroEvents[BITMAP4] == EVENT_COMPLETED) && (mIntroEvents[TEXT7] == EVENT_WAITING))
		{
			mIntroText[7]->setEnabled(true);
			mIntroEvents[TEXT7] = EVENT_RUNNING;
		}

		//Sexta imagen junto con texto - Globos de café
		if ((mIntroEvents[TEXT7] == EVENT_COMPLETED) && (mIntroEvents[BITMAP5] == EVENT_WAITING) && (mIntroEvents[TEXT8] == EVENT_WAITING))
		{
			mIntroBitmap[4]->setEnabled(false);
			mIntroText[7]->setEnabled(false);
			mIntroBitmap[5]->setEnabled(true);
			mIntroText[8]->setEnabled(true);
			mIntroEvents[BITMAP5] = EVENT_RUNNING;
			mIntroEvents[TEXT8] = EVENT_RUNNING;
		}

		//Acaba el último texto
		if (mIntroEvents[TEXT8] == EVENT_COMPLETED)
		{
			mIntroText[8]->setEnabled(false);
			Mix_HaltMusic();
			mGameStatus = GAME_STATE_TITLE;
		}
	}

	//Limpia la pantalla
	SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF);
	SDL_RenderClear(mRenderer);

	//Dibuja los objetos
	for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
	{
		mIntroBitmap[i]->render();
	}
	for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
	{
		mIntroText[i]->render();
	}

	//Actualiza la pantalla
	SDL_RenderPresent(mRenderer);
}

//Bucle para el titulo del juego
void GameDirector::runTitle()
{
	//Comprueba los eventos que hay en la cola
	while (SDL_PollEvent(mEventHandler) != 0)
	{
		//Evento de salida de la aplicación
		if (mEventHandler->type == SDL_QUIT)
		{
			setGameStatus(GAME_STATE_QUIT);
		}

		if (mEventHandler->key.keysym.sym == SDLK_RETURN)
		{
			mTitleStatus = 2;
			break;
		}

		//Tecla D pulsada
		if (mEventHandler->key.keysym.sym == SDLK_d)
		{
			/*init(true);
			Mix_HaltMusic();
			setGameStatus(GAME_STATE_TITLE);
			break;*/
		}
	}

	//Estado 0 - Titulo desplazandose
	if (mTitleStatus == 0)
	{
		//Limpia la pantalla
		SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF);
		SDL_RenderClear(mRenderer);

		//Actualiza los objetos
		mCoffeeBitmap->update();
		mCrisisBitmap->update();

		//Dibuja los objetos
		mCoffeeBitmap->render();
		mCrisisBitmap->render();

		//Actualiza la pantalla
		SDL_RenderPresent(mRenderer);

		//Si los objetos han llegado a su destino, cambiamos de estado
		if ((mTitleEvents[0] == EVENT_COMPLETED) && (mTitleEvents[0] == EVENT_COMPLETED))
		{
			mTitleStatus = 1;
		}
	}

	//Estado 1 - Titulo vibrando
	if (mTitleStatus == 1)
	{
		//Reproduce el efecto sonoro
		Mix_PlayChannel(-1, mTitleFX, 0);

		//Agita la pantalla
		int v[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0};
		int a = mCoffeeBitmap->getPosX();
		int b = mCrisisBitmap->getPosX();
		for (Uint8 n = 0; n < 11 * 3; n++)
		{
			//Limpia la pantalla
			SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF);
			SDL_RenderClear(mRenderer);

			//Dibuja los objetos
			mCoffeeBitmap->setPosX(a + v[n / 3]);
			mCrisisBitmap->setPosX(b + v[n / 3]);
			mCoffeeBitmap->render();
			mCrisisBitmap->render();
			mDustSpriteRight->animate(0);
			mDustSpriteLeft->animate(0);
			mDustSpriteRight->render();
			mDustSpriteLeft->render();

			//Actualiza la pantalla
			SDL_RenderPresent(mRenderer);
			//SDL_Delay(50);
		}

		//Desactiva los bitmaps con el título
		mCoffeeBitmap->setEnabled(false);
		mCrisisBitmap->setEnabled(false);
		mTitleStatus = 2;
	}

	//Estado 2 - La pantalla de titulo con el menú y la música
	if (mTitleStatus == 2)
	{
		if (mTitleTimer > 0)
		{
			//Si la música no está sonando
			if (Mix_PlayingMusic() == 0)
			{
				//Reproduce la música
				Mix_PlayMusic(mTitleMusic, -1);
			}

			//Limpia la pantalla
			SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF);
			SDL_RenderClear(mRenderer);

			//Dibuja los objetos
			mTitleBackground->render();
			mMenuTitle->render();
			mDustSpriteRight->animate(0);
			mDustSpriteLeft->animate(0);
			mDustSpriteRight->render();
			mDustSpriteLeft->render();

			//Actualiza la pantalla
			SDL_RenderPresent(mRenderer);

			//Comprueba las entradas para el menu
			checkMenuInput(mMenuTitle);

			//Comprueba si se ha seleccionado algún item del menú
			switch (mMenuTitle->getItemSelected())
			{
			case 0:
				setGameStatus(GAME_STATE_PLAYING);
				mMenuTitle->resetMenu();
				Mix_PlayChannel(-1, mMenuSelectedFX, 0);
				renderTransition(1);
				Mix_HaltMusic();
				init(true);
				disableDemoMode();
				break;
			case 1:
				setGameStatus(GAME_STATE_QUIT);
				mMenuTitle->resetMenu();
				Mix_PlayChannel(-1, mMenuSelectedFX, 0);
				renderTransition(1);
				Mix_HaltMusic();
				break;
			default:
				break;
			}

			mTitleTimer--;
		}
		else if (mTitleTimer == 0)
		{
			mTitleTimer = TITLE_TIMER;
			mGameStatus = mTiteNextGS;
			toogleTitleNextGS();
			renderTransition(1);
			if (mGameStatus == GAME_STATE_INTRO)
			{
				Mix_HaltMusic();
			}
			init(true);
			enableDemoMode();
		}
	}
}

//Bucle para el juego
void GameDirector::runGame()
{
	//Si la música no está sonando
	if (Mix_PlayingMusic() == 0)
	{
		//Reproduce la música
		Mix_PlayMusic(mPlayingMusic, -1);
	}

	//Lógica del juego
	//Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
	if (SDL_GetTicks() - mOldTicks > mGameSpeed)
	{
		//Actualiza el contador de ticks
		mOldTicks = SDL_GetTicks();

		//Comprueba los eventos que hay en la cola
		while (SDL_PollEvent(mEventHandler) != 0)
		{
			//Evento de salida de la aplicación
			if (mEventHandler->type == SDL_QUIT)
			{
				setGameStatus(GAME_STATE_QUIT);
			}

			//Tecla D pulsada
			if (mEventHandler->key.keysym.sym == SDLK_d)
			{
				mDebug = true;
				break;
			}

			//Tecla F pulsada
			if (mEventHandler->key.keysym.sym == SDLK_f)
			{
				mDebug = false;
				break;
			}
		}

		updatePlayField();
	}

	//Limpia la pantalla
	SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);
	SDL_RenderClear(mRenderer);

	//Dibuja los objetos
	renderPlayField();

	//Actualiza la pantalla
	SDL_RenderPresent(mRenderer);
}

//Bucle para el menu de pausa del juego
void GameDirector::runPausedGame()
{
	//Comprueba los eventos que hay en la cola
	while (SDL_PollEvent(mEventHandler) != 0)
	{
		//Evento de salida de la aplicación
		if (mEventHandler->type == SDL_QUIT)
		{
			setGameStatus(GAME_STATE_QUIT);
			Mix_HaltMusic();
		}
	}

	//Obtiene el estado de las teclas pulsadas del teclado
	/*keystates = SDL_GetKeyboardState(NULL);

	//Comprobamos la tecla o el botón de cancelar
	if (keystates[mKeyboard.cancel] != 0 || (SDL_JoystickGetButton(mGameController, BUTTON_B)))
	{
		setGameStatus(GAME_STATE_PLAYING);
		mMenuPause->resetMenu();
		SDL_Delay(100);
		if (Mix_PlayingMusic() == 1)
		{
			Mix_ResumeMusic();
		}
	}*/

	//Dibuja los objetos
	renderBackground();
	renderBalloons();
	renderBullets();
	mPlayer->render();
	renderScoreBoard();
	mMenuPause->render();

	//Limpia la pantalla
	SDL_RenderPresent(mRenderer);

	//Comprueba las entradas para el menu
	checkMenuInput(mMenuPause);

	//Comprueba si se ha seleccionado algún item del menú
	switch (mMenuPause->getItemSelected())
	{
	case 0:
		setGameStatus(GAME_STATE_PLAYING);
		mMenuPause->resetMenu();
		if (Mix_PlayingMusic() == 1)
		{
			Mix_ResumeMusic();
		}
		break;
	case 1:
		setGameStatus(GAME_STATE_TITLE);
		mMenuPause->resetMenu();
		Mix_PlayChannel(-1, mMenuSelectedFX, 0);
		Mix_HaltMusic();
		renderTransition(1);
		init(true);
		break;
	default:
		break;
	}
}

//Bucle para la pantalla de game over
void GameDirector::runGameOverScreen()
{
	//Comprueba los eventos que hay en la cola
	while (SDL_PollEvent(mEventHandler) != 0)
	{
		//Evento de salida de la aplicación
		if (mEventHandler->type == SDL_QUIT)
		{
			setGameStatus(GAME_STATE_QUIT);
		}

		if (mEventHandler->type == SDL_KEYDOWN)
		{
			Mix_HaltMusic();
			setGameStatus(GAME_STATE_TITLE);
			SDL_Delay(200);
			break;
		}
	}

	if (mGameOverTimer > 50)
	{
		//Limpia la pantalla
		SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF);
		SDL_RenderClear(mRenderer);

		//Dibuja los objetos
		renderScoreBoard();
		mWhiteText->write(PLAY_AREA_CENTER_X - (mWhiteText->lenght("GAME OVER", 0) / 2), PLAY_AREA_CENTER_Y, "GAME OVER", 0);

		//Muestra la pantalla
		SDL_RenderPresent(mRenderer);
	}
	else if (mGameOverTimer > 0)
	{
		//Retiene la pantalla de 'game over' y luego limpia la pantalla y vuelve al titulo
		SDL_RenderClear(mRenderer);
		SDL_RenderPresent(mRenderer);
	}
	else if (mGameOverTimer == 0)
	{
		init(true);
		setGameStatus(GAME_STATE_TITLE);
	}

	mGameOverTimer--;
}

//Dibuja la informacion de debug en pantalla
void GameDirector::renderDebugInfo()
{
	if (mDebug)
	{
		mWhiteText->write(0, 0 * BLOCK, "menace:         " + std::to_string(mMenaceLevelThreshold) + "/" + std::to_string(mMenaceLevel));
		mWhiteText->write(0, 1 * BLOCK, "item[0].TTL:    " + std::to_string(mItem[0]->mTimeToLive));
		mWhiteText->write(0, 2 * BLOCK, "BGClouds1.x/w:  " + std::to_string(mGBClouds1->getPosX()) + "/" + std::to_string(mGBClouds1->getWidth()));
		mWhiteText->write(0, 3 * BLOCK, "BGClouds1b.x/w: " + std::to_string(mGBClouds1b->getPosX()) + "/" + std::to_string(mGBClouds1b->getWidth()));
		mWhiteText->write(0, 4 * BLOCK, ": ");
		mWhiteText->write(0, 5 * BLOCK, ": ");
		mWhiteText->write(0, 6 * BLOCK, ": ");
		mWhiteText->write(0, 7 * BLOCK, ": ");
	}
}

//Activa el modo Demo
void GameDirector::enableDemoMode()
{
	mDemo = true;
	mGetReadyBitmap->setEnabled(false);
}

//Desactiva el modo Demo
void GameDirector::disableDemoMode()
{
	mDemo = false;
	mGetReadyBitmap->setEnabled(true);
}

//Actualiza el proximo estado del juego despues del titulo
void GameDirector::toogleTitleNextGS()
{
	if (mTiteNextGS == GAME_STATE_INTRO)
	{
		mTiteNextGS = GAME_STATE_PLAYING;
	}
	else
	{
		mTiteNextGS = GAME_STATE_INTRO;
	}
}