1106 lines
24 KiB
C++
1106 lines
24 KiB
C++
#include "gamedirector.h"
|
|
|
|
// 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(SDL_Renderer *gRenderer)
|
|
{
|
|
this->gRenderer = gRenderer;
|
|
|
|
init();
|
|
}
|
|
|
|
// Destructor
|
|
GameDirector::~GameDirector()
|
|
{
|
|
// Libera los efectos de sonido
|
|
Mix_FreeChunk(gPopBalloonFX);
|
|
Mix_FreeChunk(gBulletFX);
|
|
gPopBalloonFX = NULL;
|
|
gBulletFX = NULL;
|
|
|
|
// Libra la música
|
|
Mix_FreeMusic(gTitleMusic);
|
|
gTitleMusic = NULL;
|
|
Mix_FreeMusic(gPlayingMusic);
|
|
gPlayingMusic = NULL;
|
|
|
|
// Libera el mando
|
|
SDL_JoystickClose(gGameController);
|
|
gGameController = NULL;
|
|
|
|
// Libera texturas
|
|
gGameBackgroundTexture->free();
|
|
gTitleBackgroundTexture->free();
|
|
gWhiteFontTexture->free();
|
|
gBlackFontTexture->free();
|
|
gMiscTexture->free();
|
|
|
|
// Libera objetos
|
|
delete player;
|
|
delete menuPause;
|
|
delete menuTitle;
|
|
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i])
|
|
{
|
|
delete balloon[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < mMaxBullets; i++)
|
|
{
|
|
if (bullet[i])
|
|
{
|
|
delete bullet[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Iniciador
|
|
void GameDirector::init()
|
|
{
|
|
// Carga la música del titulo
|
|
gTitleMusic = Mix_LoadMUS("media/music/title.ogg");
|
|
if (gTitleMusic == NULL)
|
|
{
|
|
printf("Failed to load title music! SDL_mixer Error: %s\n", Mix_GetError());
|
|
}
|
|
|
|
// Carga la música del juego
|
|
gPlayingMusic = Mix_LoadMUS("media/music/playing.ogg");
|
|
if (gPlayingMusic == NULL)
|
|
{
|
|
printf("Failed to load playing music! SDL_mixer Error: %s\n", Mix_GetError());
|
|
}
|
|
|
|
// Carga los efectos de sonido para la explosión de los globos
|
|
gPopBalloonFX = Mix_LoadWAV("media/sound/balloon.wav");
|
|
if (gPopBalloonFX == NULL)
|
|
{
|
|
printf("Failed to load balloon sound effect! SDL_mixer Error: %s\n", Mix_GetError());
|
|
}
|
|
|
|
// Carga los efectos de sonido para los disparos del jugador
|
|
gBulletFX = Mix_LoadWAV("media/sound/bullet.wav");
|
|
if (gBulletFX == NULL)
|
|
{
|
|
printf("Failed to load bullet sound effect! SDL_mixer Error: %s\n", Mix_GetError());
|
|
}
|
|
|
|
gGameBackgroundTexture = new LTexture(gRenderer);
|
|
// Carga los gráficos del fondo del juego
|
|
if (!gGameBackgroundTexture->loadFromFile("media/gfx/background.png"))
|
|
{
|
|
printf("Failed to load game background texture!\n");
|
|
}
|
|
|
|
gTitleBackgroundTexture = new LTexture(gRenderer);
|
|
// Carga los gráficos del fondo de la pantalla de titulo
|
|
if (!gTitleBackgroundTexture->loadFromFile("media/gfx/title.png"))
|
|
{
|
|
printf("Failed to load title texture!\n");
|
|
}
|
|
|
|
gMiscTexture = new LTexture(gRenderer);
|
|
// Carga varios gráficos para varios propósitos
|
|
if (!gMiscTexture->loadFromFile("media/gfx/misc.png"))
|
|
{
|
|
printf("Failed to load misc texture!\n");
|
|
}
|
|
|
|
gWhiteFontTexture = new LTexture(gRenderer);
|
|
// Carga los gráficos para el texto blanco
|
|
if (!gWhiteFontTexture->loadFromFile("media/gfx/white_font.png"))
|
|
{
|
|
printf("Failed to load white font texture!\n");
|
|
}
|
|
|
|
gBlackFontTexture = new LTexture(gRenderer);
|
|
// Carga los gráficos para el texto negro
|
|
if (!gBlackFontTexture->loadFromFile("media/gfx/black_font.png"))
|
|
{
|
|
printf("Failed to load black font texture!\n");
|
|
}
|
|
|
|
// Comprueba los mandos
|
|
if (SDL_NumJoysticks() < 1)
|
|
{
|
|
printf("Warning: No joysticks connected!\n");
|
|
}
|
|
else
|
|
{
|
|
// Carga el mando
|
|
gGameController = SDL_JoystickOpen(0);
|
|
if (gGameController == 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(gGameController) << " buttons\n";
|
|
}
|
|
|
|
// Variables
|
|
mGameStatus = GAME_STATE_TITLE;
|
|
mOldTicks = 0;
|
|
mMaxBalloons = 50;
|
|
mMaxBullets = 50;
|
|
mGameSpeed = 15;
|
|
mMenaceLevel = 0;
|
|
mMenaceLevelThreshold = 7;
|
|
mScore = 0;
|
|
mHiScore = 0;
|
|
mScoreText = std::to_string(mScore);
|
|
mHiScoreText = std::to_string(mHiScore);
|
|
mGetReady = true;
|
|
|
|
// Objeto jugador
|
|
player = new Player(gRenderer);
|
|
player->init();
|
|
|
|
// Establece a cero todos los valores del vector de objetos globo
|
|
resetBalloons();
|
|
|
|
// Crea dos objetos globo y los centra en el area de juego
|
|
// balloon[0].init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0);
|
|
// balloon[0].allignTo(PLAY_AREA_WIDTH / 2);
|
|
// balloon[1].init(0, BLOCK, BALLOON_4, BALLON_VELX_NEGATIVE, 0);
|
|
// balloon[1].allignTo(PLAY_AREA_WIDTH / 2);
|
|
|
|
// Con los globos creados, calcula el nivel de amenaza
|
|
calculateMenaceLevel();
|
|
|
|
// Establece a cero todos los valores del vector de objetos bala
|
|
resetBullets();
|
|
|
|
#ifdef TEST
|
|
balloonTest.init(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, BALLOON_4, 0);
|
|
balloonTest.stop();
|
|
bulletTest.init(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 2, BULLET_UP);
|
|
#endif
|
|
|
|
// Los fondos
|
|
gameBackground.init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - (0 * BLOCK), gGameBackgroundTexture);
|
|
titleBackground.init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, gTitleBackgroundTexture);
|
|
|
|
// Objetos texto, uno de cada color
|
|
whiteText.init(gWhiteFontTexture);
|
|
blackText.init(gBlackFontTexture);
|
|
|
|
// Inicializa el objeto con el menu del titulo
|
|
menuTitle = new Menu(gRenderer);
|
|
menuTitle->init(0, 16 * BLOCK, MENU_SELECTOR_WHITE, MENU_BACKGROUND_TRANSPARENT);
|
|
menuTitle->addItem("START");
|
|
menuTitle->addItem("EXIT");
|
|
menuTitle->setBackgroundColor(0, 0, 0, 255);
|
|
menuTitle->centerMenuOnScreen();
|
|
|
|
// Inicializa el objeto con el menu de pausa
|
|
menuPause = new Menu(gRenderer);
|
|
menuPause->init(0, 12 * BLOCK, MENU_SELECTOR_WHITE, MENU_BACKGROUND_SOLID);
|
|
menuPause->addItem("CONTINUE");
|
|
menuPause->addItem("EXIT TO TITLE");
|
|
menuPause->setBackgroundColor(0x73, 0x27, 0x5c, 255);
|
|
menuPause->centerMenuOnScreen();
|
|
}
|
|
|
|
// Hace una pausa de milisegundos
|
|
void GameDirector::sleep(Uint16 time)
|
|
{
|
|
Uint32 ticks = SDL_GetTicks();
|
|
while (SDL_GetTicks() - ticks < time)
|
|
{
|
|
/* code */
|
|
}
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
if (mScore > mHiScore)
|
|
{
|
|
mHiScore = mScore;
|
|
}
|
|
}
|
|
|
|
// Transforma un valor numérico en una cadena de 6 cifras
|
|
std::string GameDirector::updateScoreText(Uint32 num)
|
|
{
|
|
switch (num)
|
|
{
|
|
case 0 ... 9:
|
|
return ("00000" + std::to_string(num));
|
|
break;
|
|
|
|
case 10 ... 99:
|
|
return ("0000" + std::to_string(num));
|
|
break;
|
|
|
|
case 100 ... 999:
|
|
return ("000" + std::to_string(num));
|
|
break;
|
|
|
|
case 1000 ... 9999:
|
|
return ("00" + std::to_string(num));
|
|
break;
|
|
|
|
case 10000 ... 99999:
|
|
return ("0" + std::to_string(num));
|
|
break;
|
|
|
|
case 100000 ... 999999:
|
|
return (std::to_string(num));
|
|
break;
|
|
default:
|
|
return (std::to_string(num));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Pinta el marcador en pantalla usando un objeto texto
|
|
void GameDirector::renderScoreBoard(Text &text)
|
|
{
|
|
mScoreText = updateScoreText(mScore);
|
|
mHiScoreText = updateScoreText(mHiScore);
|
|
text.write(SCORE_WORD_X, SCORE_WORD_Y, "SCORE");
|
|
text.write(SCORE_NUMBER_X, SCORE_NUMBER_Y, mScoreText);
|
|
text.write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE");
|
|
text.write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, mHiScoreText);
|
|
}
|
|
|
|
// Mueve todos los globos activos
|
|
void GameDirector::moveBalloons()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i]->isActive())
|
|
{
|
|
balloon[i]->move();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pinta en pantalla todos los globos activos
|
|
void GameDirector::renderBalloons()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i]->isActive())
|
|
{
|
|
balloon[i]->render();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Devuelve el primer indice no activo del vector de globos
|
|
Uint8 GameDirector::getBallonFreeIndex()
|
|
{
|
|
int index = 0;
|
|
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i]->isActive() == false)
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
// Crea un globo nuevo en el vector de globos
|
|
Uint8 GameDirector::createNewBalloon(int x, int y, Uint8 kind, float velx, Uint16 creationtimer)
|
|
{
|
|
Uint8 index = getBallonFreeIndex();
|
|
balloon[index] = new Balloon(gRenderer);
|
|
balloon[index]->init(x, y, kind, velx, creationtimer);
|
|
return index;
|
|
}
|
|
|
|
// Establece a cero todos los valores del vector de objetos globo
|
|
void GameDirector::resetBalloons()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
balloon[i]->erase();
|
|
}
|
|
}
|
|
|
|
// Explosiona un globo. Lo destruye y crea otros dos si es el caso
|
|
void GameDirector::popBalloon(Uint8 index)
|
|
{
|
|
if (balloon[index]->isActive())
|
|
{
|
|
Uint8 kind = balloon[index]->getKind();
|
|
Uint8 freeIndex = 0;
|
|
switch (kind)
|
|
{
|
|
// Si es del tipo más pequeño, simplemente elimina el globo
|
|
case BALLOON_1:
|
|
balloon[index]->erase();
|
|
break;
|
|
|
|
// En cualquier otro caso, crea dos globos de un tipo inferior
|
|
default:
|
|
freeIndex = getBallonFreeIndex();
|
|
balloon[freeIndex]->init(0, balloon[index]->getPosY(), balloon[index]->getKind() - 1, BALLON_VELX_NEGATIVE, 0);
|
|
balloon[freeIndex]->allignTo(balloon[index]->getPosX() + (balloon[index]->getWidth() / 2));
|
|
balloon[freeIndex]->setVelY(-2.5);
|
|
|
|
freeIndex = getBallonFreeIndex();
|
|
balloon[freeIndex]->init(0, balloon[index]->getPosY(), balloon[index]->getKind() - 1, BALLON_VELX_POSITIVE, 0);
|
|
balloon[freeIndex]->allignTo(balloon[index]->getPosX() + (balloon[index]->getWidth() / 2));
|
|
balloon[freeIndex]->setVelY(-2.5);
|
|
|
|
// Elimina el globo
|
|
balloon[index]->erase();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detiene todos los globos
|
|
void GameDirector::stopAllBalloons()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i]->isActive())
|
|
{
|
|
balloon[i]->setStop(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pone en marcha todos los globos
|
|
void GameDirector::startAllBalloons()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i]->isActive())
|
|
{
|
|
balloon[i]->setStop(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Obtiene el numero de globos activos
|
|
Uint8 GameDirector::countBalloons()
|
|
{
|
|
Uint8 num = 0;
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[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 < mMaxBalloons; i++)
|
|
{
|
|
if (balloon[i]->isActive())
|
|
{
|
|
if (checkCollision(player->getCollider(), balloon[i]->getCollider()))
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Comprueba y procesa la colisión entre las balas y los globos
|
|
void GameDirector::processBulletBallonCollision()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
for (Uint8 j = 0; j < mMaxBullets; j++)
|
|
{
|
|
if (balloon[i]->isActive() && !(balloon[i]->isInvulnerable()) && bullet[j]->isActive())
|
|
{
|
|
if (checkCollision(balloon[i]->getCollider(), bullet[j]->getCollider()))
|
|
{
|
|
player->addScore(balloon[i]->getScore());
|
|
setScore(player->getScore());
|
|
updateHiScore();
|
|
popBalloon(i);
|
|
Mix_PlayChannel(-1, gPopBalloonFX, 0);
|
|
bullet[j]->erase();
|
|
calculateMenaceLevel();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mueve las balas activas
|
|
void GameDirector::moveBullets()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBullets; i++)
|
|
{
|
|
if (bullet[i]->isActive())
|
|
{
|
|
bullet[i]->move();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pinta las balas activas
|
|
void GameDirector::renderBullets()
|
|
{
|
|
for (Uint8 i = 0; i < mMaxBullets; i++)
|
|
{
|
|
if (bullet[i]->isActive())
|
|
{
|
|
bullet[i]->render();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Devuelve el primer indice no activo del vector de balas
|
|
Uint8 GameDirector::getBulletFreeIndex()
|
|
{
|
|
Uint8 index = 0;
|
|
|
|
for (int i = 0; i < mMaxBullets; i++)
|
|
{
|
|
if (bullet[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 < mMaxBullets; i++)
|
|
{
|
|
bullet[i]->init(0, 0, NO_KIND);
|
|
}
|
|
}
|
|
|
|
// Crea un objeto bala
|
|
void GameDirector::createBullet(int x, int y, Uint8 kind)
|
|
{
|
|
const int index = getBulletFreeIndex();
|
|
bullet[index] = new Bullet(gRenderer);
|
|
bullet[index]->init(x, y, kind);
|
|
}
|
|
|
|
// Calcula y establece el valor de amenaza en funcion de los globos activos
|
|
void GameDirector::calculateMenaceLevel()
|
|
{
|
|
mMenaceLevel = 0;
|
|
for (Uint8 i = 0; i < mMaxBalloons; i++)
|
|
{
|
|
switch (balloon[i]->getKind())
|
|
{
|
|
case BALLOON_1:
|
|
mMenaceLevel += 1;
|
|
break;
|
|
|
|
case BALLOON_2:
|
|
mMenaceLevel += 2;
|
|
break;
|
|
|
|
case BALLOON_3:
|
|
mMenaceLevel += 4;
|
|
break;
|
|
|
|
case BALLOON_4:
|
|
mMenaceLevel += 8;
|
|
break;
|
|
|
|
default:
|
|
mMenaceLevel += 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Obtiene el valor de la variable
|
|
Uint8 GameDirector::getMenaceLevel()
|
|
{
|
|
return mMenaceLevel;
|
|
}
|
|
|
|
// 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 = player->getPosX() + (player->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);
|
|
}
|
|
else
|
|
{
|
|
index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400);
|
|
}
|
|
balloon[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
|
|
const Uint8 *keystates = SDL_GetKeyboardState(NULL);
|
|
|
|
// Si está pulsada la tecla izquierda o el mando hacia la izquierda
|
|
if ((keystates[SDL_SCANCODE_LEFT] != 0) || (SDL_JoystickGetAxis(gGameController, 0) < -JOYSTICK_DEAD_ZONE))
|
|
{
|
|
player->checkInput(INPUT_LEFT);
|
|
}
|
|
// Si está pulsada la tecla derecha o el mando hacia la derecha
|
|
else if ((keystates[SDL_SCANCODE_RIGHT] != 0) || (SDL_JoystickGetAxis(gGameController, 0) > JOYSTICK_DEAD_ZONE))
|
|
{
|
|
player->checkInput(INPUT_RIGHT);
|
|
}
|
|
// Ninguna de las dos direcciones pulsadas
|
|
else
|
|
{
|
|
player->checkInput(NO_INPUT);
|
|
}
|
|
|
|
// Comprobamos la tecla o el botón de disparo central
|
|
if ((SDL_JoystickGetButton(gGameController, BUTTON_X)) || (keystates[SDL_SCANCODE_W] != 0))
|
|
{
|
|
if (player->canFire())
|
|
{
|
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY(), BULLET_UP);
|
|
player->setFireCooldown(10);
|
|
|
|
// Reproduce el sonido de disparo
|
|
Mix_PlayChannel(-1, gBulletFX, 0);
|
|
}
|
|
}
|
|
|
|
// Comprobamos la tecla o el botón de disparo izquierdo
|
|
if ((SDL_JoystickGetButton(gGameController, BUTTON_Y)) || (keystates[SDL_SCANCODE_Q] != 0))
|
|
{
|
|
if (player->canFire())
|
|
{
|
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY(), BULLET_LEFT);
|
|
player->setFireCooldown(10);
|
|
|
|
// Reproduce el sonido de disparo
|
|
Mix_PlayChannel(-1, gBulletFX, 0);
|
|
}
|
|
}
|
|
|
|
// Comprobamos la tecla o el botón de disparo derecho
|
|
if ((SDL_JoystickGetButton(gGameController, BUTTON_A)) || (keystates[SDL_SCANCODE_E] != 0))
|
|
{
|
|
if (player->canFire())
|
|
{
|
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY(), BULLET_RIGHT);
|
|
player->setFireCooldown(10);
|
|
|
|
// Reproduce el sonido de disparo
|
|
Mix_PlayChannel(-1, gBulletFX, 0);
|
|
}
|
|
}
|
|
|
|
// Comprobamos la tecla o el botón de pausa/menu
|
|
if ((SDL_JoystickGetButton(gGameController, BUTTON_START)) || (keystates[SDL_SCANCODE_ESCAPE] != 0))
|
|
{
|
|
setGameStatus(GAME_STATE_PAUSED);
|
|
|
|
// Detiene la música
|
|
Mix_HaltMusic();
|
|
}
|
|
}
|
|
|
|
// Gestiona la entrada de teclado y mando durante el menu
|
|
void GameDirector::checkMenuInput(Menu *menu)
|
|
{
|
|
// Obtiene el estado de las teclas pulsadas del teclado
|
|
const Uint8 *keystates = SDL_GetKeyboardState(NULL);
|
|
|
|
// Si está pulsada la tecla izquierda o el mando hacia la izquierda
|
|
if ((keystates[SDL_SCANCODE_UP] != 0) || (SDL_JoystickGetAxis(gGameController, 1) < -JOYSTICK_DEAD_ZONE))
|
|
{
|
|
menu->checkInput(INPUT_UP);
|
|
}
|
|
// Si está pulsada la tecla derecha o el mando hacia la derecha
|
|
else if ((keystates[SDL_SCANCODE_DOWN] != 0) || (SDL_JoystickGetAxis(gGameController, 1) > JOYSTICK_DEAD_ZONE))
|
|
{
|
|
menu->checkInput(INPUT_DOWN);
|
|
}
|
|
// Comprobamos la tecla o el botón de menu/pausa
|
|
else if (keystates[SDL_SCANCODE_RETURN] != 0 || (SDL_JoystickGetButton(gGameController, BUTTON_A)))
|
|
{
|
|
menu->checkInput(INPUT_FIRE);
|
|
}
|
|
|
|
#ifdef TEST
|
|
if (SDL_JoystickGetButton(gGameController, 1))
|
|
{
|
|
std::cout << "button1\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 1))
|
|
{
|
|
std::cout << "button1\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 2))
|
|
{
|
|
std::cout << "button2\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 3))
|
|
{
|
|
std::cout << "button3\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 4))
|
|
{
|
|
std::cout << "button4\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 5))
|
|
{
|
|
std::cout << "button5\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 6))
|
|
{
|
|
std::cout << "button6\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 7))
|
|
{
|
|
std::cout << "button7\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 8))
|
|
{
|
|
std::cout << "button8\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 9))
|
|
{
|
|
std::cout << "button9\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 10))
|
|
{
|
|
std::cout << "button10\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 11))
|
|
{
|
|
std::cout << "button11\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 12))
|
|
{
|
|
std::cout << "button12\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 13))
|
|
{
|
|
std::cout << "button13\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 14))
|
|
{
|
|
std::cout << "button14\n";
|
|
}
|
|
|
|
if (SDL_JoystickGetButton(gGameController, 15))
|
|
{
|
|
std::cout << "button15\n";
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// 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(gRenderer);
|
|
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
|
|
|
|
for (Uint16 i = 0; i < SCREEN_HEIGHT; i = i + 2)
|
|
{
|
|
rect.h = i;
|
|
SDL_RenderFillRect(gRenderer, &rect);
|
|
SDL_RenderPresent(gRenderer);
|
|
}
|
|
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(gRenderer);
|
|
SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
|
|
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 64);
|
|
|
|
for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
|
|
{
|
|
rect1.h = i;
|
|
SDL_RenderFillRect(gRenderer, &rect1);
|
|
|
|
rect2.h = i;
|
|
rect2.y = SCREEN_HEIGHT - (i);
|
|
SDL_RenderFillRect(gRenderer, &rect2);
|
|
|
|
SDL_RenderPresent(gRenderer);
|
|
}
|
|
|
|
rect1.x = 0;
|
|
rect1.y = 0;
|
|
rect1.w = SCREEN_WIDTH;
|
|
rect1.h = SCREEN_HEIGHT;
|
|
|
|
for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
|
|
{
|
|
SDL_RenderFillRect(gRenderer, &rect1);
|
|
SDL_RenderPresent(gRenderer);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Pinta el texto GetReady en pantalla
|
|
void GameDirector::renderGetReady()
|
|
{
|
|
if (mGetReady)
|
|
{
|
|
Sprite sprite;
|
|
sprite.setTexture(*gMiscTexture);
|
|
sprite.setWidth(53);
|
|
sprite.setHeight(10);
|
|
sprite.setPosX((PLAY_AREA_WIDTH / 2) - (sprite.getWidth() / 2));
|
|
sprite.setPosY((PLAY_AREA_HEIGHT / 2) - (sprite.getHeight() / 2));
|
|
sprite.setSpriteClip(0, 0, sprite.getWidth(), sprite.getHeight());
|
|
for (Uint8 i = 0; i < 1; i++)
|
|
{
|
|
sprite.render();
|
|
SDL_RenderPresent(gRenderer);
|
|
SDL_Delay(1500);
|
|
}
|
|
mGetReady = false;
|
|
}
|
|
}
|
|
|
|
// Bucle para el titulo del juego
|
|
void GameDirector::runTitle()
|
|
{
|
|
// Si la música no está sonando
|
|
if (Mix_PlayingMusic() == 0)
|
|
{
|
|
// Reproduce la música
|
|
Mix_PlayMusic(gTitleMusic, -1);
|
|
}
|
|
|
|
// Comprueba los eventos que hay en la cola
|
|
while (SDL_PollEvent(&eventHandler) != 0)
|
|
{
|
|
// Evento de salida de la aplicación
|
|
if (eventHandler.type == SDL_QUIT)
|
|
{
|
|
setGameStatus(GAME_STATE_QUIT);
|
|
}
|
|
}
|
|
|
|
// Limpia la pantalla
|
|
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
|
|
SDL_RenderClear(gRenderer);
|
|
|
|
// Dibuja los objetos
|
|
titleBackground.render();
|
|
menuTitle->render(whiteText);
|
|
|
|
// Actualiza la pantalla
|
|
SDL_RenderPresent(gRenderer);
|
|
|
|
// Comprueba las entradas para el menu
|
|
checkMenuInput(menuTitle);
|
|
|
|
// Comprueba si se ha seleccionado algún item del menú
|
|
switch (menuTitle->getItemSelected())
|
|
{
|
|
case 0:
|
|
setGameStatus(GAME_STATE_PLAYING);
|
|
menuTitle->resetMenu();
|
|
renderTransition(1);
|
|
Mix_HaltMusic();
|
|
SDL_Delay(1200);
|
|
break;
|
|
case 1:
|
|
setGameStatus(GAME_STATE_QUIT);
|
|
menuTitle->resetMenu();
|
|
renderTransition(1);
|
|
Mix_HaltMusic();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Bucle para el juego
|
|
void GameDirector::runGame()
|
|
{
|
|
// Si la música no está sonando
|
|
if (Mix_PlayingMusic() == 0)
|
|
{
|
|
// Reproduce la música
|
|
Mix_PlayMusic(gPlayingMusic, -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 el teclado/mando
|
|
checkGameInput();
|
|
|
|
// Comprueba los eventos que hay en la cola
|
|
while (SDL_PollEvent(&eventHandler) != 0)
|
|
{
|
|
// Evento de salida de la aplicación
|
|
if (eventHandler.type == SDL_QUIT)
|
|
{
|
|
setGameStatus(GAME_STATE_QUIT);
|
|
}
|
|
|
|
// Tecla T pulsada
|
|
if (eventHandler.key.keysym.sym == SDLK_t)
|
|
{
|
|
// startAllBalloons();
|
|
popBalloon(0);
|
|
break;
|
|
}
|
|
#ifdef TEST
|
|
// W key pressed
|
|
if (eventHandler.key.keysym.sym == SDLK_w)
|
|
{
|
|
bulletTest.setPosY(bulletTest.getPosY() - 1);
|
|
bulletTest.testMove();
|
|
break;
|
|
}
|
|
|
|
// S key pressed
|
|
if (eventHandler.key.keysym.sym == SDLK_s)
|
|
{
|
|
bulletTest.setPosY(bulletTest.getPosY() + 1);
|
|
bulletTest.testMove();
|
|
break;
|
|
}
|
|
|
|
// A key pressed
|
|
if (eventHandler.key.keysym.sym == SDLK_a)
|
|
{
|
|
bulletTest.setPosX(bulletTest.getPosX() - 1);
|
|
bulletTest.testMove();
|
|
break;
|
|
}
|
|
|
|
// D key pressed
|
|
if (eventHandler.key.keysym.sym == SDLK_d)
|
|
{
|
|
bulletTest.setPosX(bulletTest.getPosX() + 1);
|
|
bulletTest.testMove();
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Actualiza el jugador
|
|
player->update();
|
|
|
|
// Mueve los globos
|
|
moveBalloons();
|
|
|
|
#ifdef TEST
|
|
balloonTest.move();
|
|
#endif
|
|
|
|
// Mueve las balas
|
|
moveBullets();
|
|
|
|
// Procesa las colisiones entre globos y balas
|
|
processBulletBallonCollision();
|
|
|
|
// Comprueba el nivel de amenaza
|
|
checkMenaceLevel();
|
|
|
|
// Comprueba la colisión entre el jugador y los globos
|
|
if (checkPlayerBallonCollision())
|
|
{
|
|
// stopAllBalloons();
|
|
}
|
|
}
|
|
|
|
// Limpia la pantalla
|
|
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
|
|
SDL_RenderClear(gRenderer);
|
|
|
|
// Dibuja los objetos
|
|
gameBackground.render();
|
|
renderBalloons();
|
|
#ifdef TEST
|
|
balloonTest.render();
|
|
bulletTest.render();
|
|
if (checkCollision(balloonTest.getCollider(), bulletTest.getCollider()))
|
|
{
|
|
whiteText.write(0, 0, "X");
|
|
}
|
|
#endif
|
|
// whiteText.write(0, 0, std::to_string(mMenaceLevelThreshold));
|
|
// whiteText.write(0, BLOCK, std::to_string(player->getPosX() + player->getWidth()));
|
|
renderBullets();
|
|
player->render();
|
|
renderScoreBoard(whiteText);
|
|
renderGetReady();
|
|
|
|
// Actualiza la pantalla
|
|
SDL_RenderPresent(gRenderer);
|
|
}
|
|
|
|
// Bucle para el menu de pausa del juego
|
|
void GameDirector::runPausedGame()
|
|
{
|
|
// Comprueba los eventos que hay en la cola
|
|
while (SDL_PollEvent(&eventHandler) != 0)
|
|
{
|
|
// Evento de salida de la aplicación
|
|
if (eventHandler.type == SDL_QUIT)
|
|
{
|
|
setGameStatus(GAME_STATE_QUIT);
|
|
}
|
|
}
|
|
|
|
// Dibuja los objetos
|
|
gameBackground.render();
|
|
renderBalloons();
|
|
renderBullets();
|
|
player->render();
|
|
renderScoreBoard(whiteText);
|
|
menuPause->render(whiteText);
|
|
|
|
// Limpia la pantalla
|
|
SDL_RenderPresent(gRenderer);
|
|
|
|
// Comprueba las entradas para el menu
|
|
checkMenuInput(menuPause);
|
|
|
|
// Comprueba si se ha seleccionado algún item del menú
|
|
switch (menuPause->getItemSelected())
|
|
{
|
|
case 0:
|
|
setGameStatus(GAME_STATE_PLAYING);
|
|
menuPause->resetMenu();
|
|
break;
|
|
case 1:
|
|
setGameStatus(GAME_STATE_TITLE);
|
|
menuPause->resetMenu();
|
|
renderTransition(1);
|
|
init();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} |