Files
coffee_crisis_arcade_edition/source/game.cpp
2024-09-28 10:16:35 +02:00

2828 lines
68 KiB
C++

#include "game.h"
#include "param.h"
#include "options.h"
#define GAME_OVER_COUNTER 350
// Constructor
Game::Game(int playerID, int currentStage, bool demo, Screen *screen, Asset *asset, Input *input, JA_Music_t *music)
{
// Copia los punteros
this->screen = screen;
this->asset = asset;
this->input = input;
this->music = music;
renderer = screen->getRenderer();
// Pasa variables
this->demo.enabled = demo;
this->currentStage = currentStage;
lastStageReached = currentStage;
difficulty = options.game.difficulty;
// Crea los objetos
fade = new Fade(renderer);
eventHandler = new SDL_Event();
scoreboard = new Scoreboard(renderer, asset);
background = new Background(renderer, asset);
explosions = new Explosions();
enemyFormations = new EnemyFormations();
// Carga los recursos
loadMedia();
// Inicializa los vectores con los datos para la demo
if (demo)
{ // Aleatoriza la asignación del fichero
const int index1 = rand() % 2;
const int index2 = (index1 + 1) % 2;
loadDemoFile(asset->get("demo1.bin"), &this->demo.dataFile[index1]);
loadDemoFile(asset->get("demo2.bin"), &this->demo.dataFile[index2]);
}
background->setPos(param.game.playArea.rect);
n1000Sprite = new SmartSprite(gameTextTexture);
n2500Sprite = new SmartSprite(gameTextTexture);
n5000Sprite = new SmartSprite(gameTextTexture);
explosions->addTexture(1, explosionsTextures[0], explosionsAnimations[0]);
explosions->addTexture(2, explosionsTextures[1], explosionsAnimations[1]);
explosions->addTexture(3, explosionsTextures[2], explosionsAnimations[2]);
explosions->addTexture(4, explosionsTextures[3], explosionsAnimations[3]);
canvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.playArea.rect.w, param.game.playArea.rect.h);
SDL_SetTextureBlendMode(canvas, SDL_BLENDMODE_BLEND);
// Inicializa las variables necesarias para la sección 'Game'
init(playerID);
}
Game::~Game()
{
// Guarda las puntuaciones en un fichero
ManageHiScoreTable *manager = new ManageHiScoreTable(&options.game.hiScoreTable);
manager->saveToFile(asset->get("score.bin"));
delete manager;
#ifdef RECORDING
saveDemoFile(asset->get("demo1.bin"));
#endif
// Elimina todos los objetos contenidos en vectores
deleteAllVectorObjects();
// Libera los recursos
unloadMedia();
delete scoreboard;
delete background;
delete explosions;
delete enemyFormations;
delete fade;
delete eventHandler;
delete n1000Sprite;
delete n2500Sprite;
delete n5000Sprite;
SDL_DestroyTexture(canvas);
}
// Inicializa las variables necesarias para la sección 'Game'
void Game::init(int playerID)
{
ticks = 0;
ticksSpeed = 15;
// Elimina qualquier jugador que hubiese antes de crear los nuevos
for (auto player : players)
{
if (player)
{
delete player;
}
};
players.clear();
// Crea los dos jugadores
Player *player1 = new Player(1, (param.game.playArea.firstQuarterX * ((0 * 2) + 1)) - 11, param.game.playArea.rect.h - 30, &param.game.playArea.rect, playerTextures[0], playerAnimations);
player1->setScoreBoardPanel(SCOREBOARD_LEFT_PANEL);
player1->setName(lang::getText(53));
const int controller1 = getController(player1->getId());
player1->setController(controller1);
players.push_back(player1);
Player *player2 = new Player(2, (param.game.playArea.firstQuarterX * ((1 * 2) + 1)) - 11, param.game.playArea.rect.h - 30, &param.game.playArea.rect, playerTextures[1], playerAnimations);
player2->setScoreBoardPanel(SCOREBOARD_RIGHT_PANEL);
player2->setName(lang::getText(54));
const int controller2 = getController(player2->getId());
player2->setController(controller2);
players.push_back(player2);
// Obtiene el "id" del jugador que va a jugar
Player *player = getPlayer(playerID);
// Cambia el estado del jugador seleccionado
player->setStatusPlaying(PLAYER_STATUS_PLAYING);
// Como es el principio del juego, empieza sin inmunidad
player->setInvulnerable(false);
// Variables relacionadas con la dificultad
switch (difficulty)
{
case DIFFICULTY_EASY:
defaultEnemySpeed = BALLOON_SPEED_1;
difficultyScoreMultiplier = 0.5f;
difficultyColor = difficultyEasyColor;
scoreboard->setColor(difficultyColor);
break;
case DIFFICULTY_NORMAL:
defaultEnemySpeed = BALLOON_SPEED_1;
difficultyScoreMultiplier = 1.0f;
difficultyColor = difficultyNormalColor;
scoreboard->setColor(scoreboardColor);
break;
case DIFFICULTY_HARD:
defaultEnemySpeed = BALLOON_SPEED_5;
difficultyScoreMultiplier = 1.5f;
difficultyColor = difficultyHardColor;
scoreboard->setColor(difficultyColor);
break;
default:
break;
}
// Variables para el marcador
scoreboard->setPos({param.scoreboard.x, param.scoreboard.y, param.scoreboard.w, param.scoreboard.h});
for (auto player : players)
{
scoreboard->setName(player->getScoreBoardPanel(), player->getName());
if (player->isWaiting())
{
scoreboard->setMode(player->getScoreBoardPanel(), SCOREBOARD_MODE_GAME_OVER);
}
}
scoreboard->setMode(SCOREBOARD_CENTER_PANEL, SCOREBOARD_MODE_STAGE_INFO);
// Resto de variables
hiScore.score = options.game.hiScoreTable[0].score;
hiScore.name = options.game.hiScoreTable[0].name;
paused = false;
gameCompleted = false;
gameCompletedCounter = 0;
section::name = section::NAME_GAME;
section::options = section::OPTIONS_GAME_PLAY_1P;
currentPower = 0;
menaceCurrent = 0;
menaceThreshold = 0;
hiScoreAchieved = false;
stageBitmapCounter = STAGE_COUNTER;
gameOverCounter = GAME_OVER_COUNTER;
timeStopped = false;
timeStoppedCounter = 0;
counter = 0;
lastEnemyDeploy = 0;
enemyDeployCounter = 0;
enemySpeed = defaultEnemySpeed;
helper.needCoffee = false;
helper.needCoffeeMachine = false;
helper.needPowerBall = false;
helper.counter = HELP_COUNTER;
helper.itemPoints1Odds = ITEM_POINTS_1_DISK_ODDS;
helper.itemPoints2Odds = ITEM_POINTS_2_GAVINA_ODDS;
helper.itemPoints3Odds = ITEM_POINTS_3_PACMAR_ODDS;
helper.itemClockOdds = ITEM_CLOCK_ODDS;
helper.itemCoffeeOdds = ITEM_COFFEE_ODDS;
helper.itemCoffeeMachineOdds = ITEM_COFFEE_MACHINE_ODDS;
powerBallEnabled = false;
powerBallCounter = 0;
coffeeMachineEnabled = false;
balloonsPopped = 0;
#ifdef DEBUG
autoPopBalloons = false;
#endif
// Inicializa las variables para el modo DEMO
if (demo.enabled)
{
// Selecciona una pantalla al azar
const int demos = 3;
const int demo = rand() % demos;
const int stages[demos] = {0, 3, 5};
currentStage = stages[demo];
// Actualiza el numero de globos explotados según la fase de la demo
for (int i = 0; i < currentStage; ++i)
{
balloonsPopped += enemyFormations->getStage(i).powerToComplete;
}
// Activa o no al otro jugador
if (rand() % 2 == 0)
{
const int otherPlayer = playerID == 1 ? 2 : 1;
Player *player = getPlayer(otherPlayer);
player->setStatusPlaying(PLAYER_STATUS_PLAYING);
}
for (auto player : players)
{
// Añade 0, 1 o 2 cafes al jugador
for (int i = 0; i < rand() % 3; ++i)
{
player->giveExtraHit();
}
// Empieza sin inmunidad
player->setInvulnerable(false);
}
// Deshabilita los sonidos
JA_EnableSound(false);
// Configura los marcadores
scoreboard->setMode(SCOREBOARD_LEFT_PANEL, SCOREBOARD_MODE_DEMO);
scoreboard->setMode(SCOREBOARD_RIGHT_PANEL, SCOREBOARD_MODE_DEMO);
}
initPaths();
totalPowerToCompleteGame = 0;
for (int i = 0; i < 10; ++i)
{
totalPowerToCompleteGame += enemyFormations->getStage(i).powerToComplete;
}
// Modo grabar demo
#ifdef RECORDING
demo.recording = true;
#else
demo.recording = false;
#endif
demo.counter = 0;
// Inicializa el objeto para el fundido
fade->setColor(fadeColor.r, fadeColor.g, fadeColor.b);
fade->setPost(param.fade.postDuration);
fade->setType(FADE_VENETIAN);
// Con los globos creados, calcula el nivel de amenaza
evaluateAndSetMenace();
// Inicializa el bitmap de 1000 puntos
const int height = 15;
const int sprite1Width = 35;
const int sprite2Width = 38;
const int sprite3Width = 39;
n1000Sprite->setPosX(0);
n1000Sprite->setPosY(0);
n1000Sprite->setWidth(sprite1Width);
n1000Sprite->setHeight(height);
n1000Sprite->setVelX(0.0f);
n1000Sprite->setVelY(-0.5f);
n1000Sprite->setAccelX(0.0f);
n1000Sprite->setAccelY(-0.1f);
n1000Sprite->setSpriteClip(0, 0, sprite1Width, height);
n1000Sprite->setEnabled(false);
n1000Sprite->setEnabledCounter(0);
n1000Sprite->setDestX(0);
n1000Sprite->setDestY(0);
// Inicializa el bitmap de 2500 puntos
n2500Sprite->setPosX(0);
n2500Sprite->setPosY(0);
n2500Sprite->setWidth(sprite2Width);
n2500Sprite->setHeight(height);
n2500Sprite->setVelX(0.0f);
n2500Sprite->setVelY(-0.5f);
n2500Sprite->setAccelX(0.0f);
n2500Sprite->setAccelY(-0.1f);
n2500Sprite->setSpriteClip(sprite1Width, 0, sprite2Width, height);
n2500Sprite->setEnabled(false);
n2500Sprite->setEnabledCounter(0);
n2500Sprite->setDestX(0);
n2500Sprite->setDestY(0);
// Inicializa el bitmap de 5000 puntos
n5000Sprite->setPosX(0);
n5000Sprite->setPosY(0);
n5000Sprite->setWidth(sprite3Width);
n5000Sprite->setHeight(height);
n5000Sprite->setVelX(0.0f);
n5000Sprite->setVelY(-0.5f);
n5000Sprite->setAccelX(0.0f);
n5000Sprite->setAccelY(-0.1f);
n5000Sprite->setSpriteClip(sprite1Width + sprite2Width, 0, sprite3Width, height);
n5000Sprite->setEnabled(false);
n5000Sprite->setEnabledCounter(0);
n5000Sprite->setDestX(0);
n5000Sprite->setDestY(0);
}
// Carga los recursos necesarios para la sección 'Game'
void Game::loadMedia()
{
if (options.console)
{
std::cout << std::endl
<< "** LOADING RESOURCES FOR GAME SECTION" << std::endl;
}
playerAnimations.clear();
balloonAnimations.clear();
itemAnimations.clear();
player1Textures.clear();
player2Textures.clear();
itemTextures.clear();
balloonTextures.clear();
explosionsTextures.clear();
// Texturas
bulletTexture = new Texture(renderer, asset->get("bullet.png"));
gameTextTexture = new Texture(renderer, asset->get("game_text.png"));
// Texturas - Globos
Texture *balloon1Texture = new Texture(renderer, asset->get("balloon1.png"));
balloonTextures.push_back(balloon1Texture);
Texture *balloon2Texture = new Texture(renderer, asset->get("balloon2.png"));
balloonTextures.push_back(balloon2Texture);
Texture *balloon3Texture = new Texture(renderer, asset->get("balloon3.png"));
balloonTextures.push_back(balloon3Texture);
Texture *balloon4Texture = new Texture(renderer, asset->get("balloon4.png"));
balloonTextures.push_back(balloon4Texture);
Texture *balloon5Texture = new Texture(renderer, asset->get("powerball.png"));
balloonTextures.push_back(balloon5Texture);
// Texturas - Explosiones
Texture *explosion1Texture = new Texture(renderer, asset->get("explosion1.png"));
explosionsTextures.push_back(explosion1Texture);
Texture *explosion2Texture = new Texture(renderer, asset->get("explosion2.png"));
explosionsTextures.push_back(explosion2Texture);
Texture *explosion3Texture = new Texture(renderer, asset->get("explosion3.png"));
explosionsTextures.push_back(explosion3Texture);
Texture *explosion4Texture = new Texture(renderer, asset->get("explosion4.png"));
explosionsTextures.push_back(explosion4Texture);
// Texturas - Items
Texture *item1 = new Texture(renderer, asset->get("item_points1_disk.png"));
itemTextures.push_back(item1);
Texture *item2 = new Texture(renderer, asset->get("item_points2_gavina.png"));
itemTextures.push_back(item2);
Texture *item3 = new Texture(renderer, asset->get("item_points3_pacmar.png"));
itemTextures.push_back(item3);
Texture *item4 = new Texture(renderer, asset->get("item_clock.png"));
itemTextures.push_back(item4);
Texture *item5 = new Texture(renderer, asset->get("item_coffee.png"));
itemTextures.push_back(item5);
Texture *item6 = new Texture(renderer, asset->get("item_coffee_machine.png"));
itemTextures.push_back(item6);
// Texturas - Player1
Texture *player1 = new Texture(renderer, asset->get("player1.gif"));
player1->addPalette(asset->get("player1_pal1.gif"));
player1->addPalette(asset->get("player1_pal2.gif"));
player1->addPalette(asset->get("player1_pal3.gif"));
player1Textures.push_back(player1);
Texture *player1Power = new Texture(renderer, asset->get("player_power.gif"));
player1Power->addPalette(asset->get("player_power_pal.gif"));
player1Textures.push_back(player1Power);
playerTextures.push_back(player1Textures);
// Texturas - Player2
Texture *player2 = new Texture(renderer, asset->get("player2.gif"));
player2->addPalette(asset->get("player2_pal1.gif"));
player2->addPalette(asset->get("player2_pal2.gif"));
player2->addPalette(asset->get("player2_pal3.gif"));
player2Textures.push_back(player2);
Texture *player2Power = new Texture(renderer, asset->get("player_power.gif"));
player2Power->addPalette(asset->get("player_power_pal.gif"));
player2Power->setPalette(1);
player2Textures.push_back(player2Power);
playerTextures.push_back(player2Textures);
// Animaciones -- Jugador
{
std::vector<std::string> *playerAnimation = new std::vector<std::string>;
loadAnimations(asset->get("player.ani"), playerAnimation);
playerAnimations.push_back(playerAnimation);
std::vector<std::string> *playerPowerAnimation = new std::vector<std::string>;
loadAnimations(asset->get("player_power.ani"), playerPowerAnimation);
playerAnimations.push_back(playerPowerAnimation);
}
// Animaciones -- Globos
{
std::vector<std::string> *balloon1Animation = new std::vector<std::string>;
loadAnimations(asset->get("balloon1.ani"), balloon1Animation);
balloonAnimations.push_back(balloon1Animation);
std::vector<std::string> *balloon2Animation = new std::vector<std::string>;
loadAnimations(asset->get("balloon2.ani"), balloon2Animation);
balloonAnimations.push_back(balloon2Animation);
std::vector<std::string> *balloon3Animation = new std::vector<std::string>;
loadAnimations(asset->get("balloon3.ani"), balloon3Animation);
balloonAnimations.push_back(balloon3Animation);
std::vector<std::string> *balloon4Animation = new std::vector<std::string>;
loadAnimations(asset->get("balloon4.ani"), balloon4Animation);
balloonAnimations.push_back(balloon4Animation);
std::vector<std::string> *balloon5Animation = new std::vector<std::string>;
loadAnimations(asset->get("powerball.ani"), balloon5Animation);
balloonAnimations.push_back(balloon5Animation);
}
// Animaciones -- Explosiones
std::vector<std::string> *explosions1Animation = new std::vector<std::string>;
loadAnimations(asset->get("explosion1.ani"), explosions1Animation);
explosionsAnimations.push_back(explosions1Animation);
std::vector<std::string> *explosions2Animation = new std::vector<std::string>;
loadAnimations(asset->get("explosion2.ani"), explosions2Animation);
explosionsAnimations.push_back(explosions2Animation);
std::vector<std::string> *explosions3Animation = new std::vector<std::string>;
loadAnimations(asset->get("explosion3.ani"), explosions3Animation);
explosionsAnimations.push_back(explosions3Animation);
std::vector<std::string> *explosions4Animation = new std::vector<std::string>;
loadAnimations(asset->get("explosion4.ani"), explosions4Animation);
explosionsAnimations.push_back(explosions4Animation);
// Animaciones -- Items
std::vector<std::string> *item1Animation = new std::vector<std::string>;
loadAnimations(asset->get("item_points1_disk.ani"), item1Animation);
itemAnimations.push_back(item1Animation);
std::vector<std::string> *item2Animation = new std::vector<std::string>;
loadAnimations(asset->get("item_points2_gavina.ani"), item2Animation);
itemAnimations.push_back(item2Animation);
std::vector<std::string> *item3Animation = new std::vector<std::string>;
loadAnimations(asset->get("item_points3_pacmar.ani"), item3Animation);
itemAnimations.push_back(item3Animation);
std::vector<std::string> *item4Animation = new std::vector<std::string>;
loadAnimations(asset->get("item_clock.ani"), item4Animation);
itemAnimations.push_back(item4Animation);
std::vector<std::string> *item5Animation = new std::vector<std::string>;
loadAnimations(asset->get("item_coffee.ani"), item5Animation);
itemAnimations.push_back(item5Animation);
std::vector<std::string> *item6Animation = new std::vector<std::string>;
loadAnimations(asset->get("item_coffee_machine.ani"), item6Animation);
itemAnimations.push_back(item6Animation);
// Texto
text = new Text(asset->get("smb2.gif"), asset->get("smb2.txt"), renderer);
textBig = new Text(asset->get("smb2_big.png"), asset->get("smb2_big.txt"), renderer);
textNokia2 = new Text(asset->get("nokia2.png"), asset->get("nokia2.txt"), renderer);
textNokiaBig2 = new Text(asset->get("nokia_big2.png"), asset->get("nokia_big2.txt"), renderer);
// Sonidos
balloonSound = JA_LoadSound(asset->get("balloon.wav").c_str());
bubble1Sound = JA_LoadSound(asset->get("bubble1.wav").c_str());
bubble2Sound = JA_LoadSound(asset->get("bubble2.wav").c_str());
bubble3Sound = JA_LoadSound(asset->get("bubble3.wav").c_str());
bubble4Sound = JA_LoadSound(asset->get("bubble4.wav").c_str());
bulletSound = JA_LoadSound(asset->get("bullet.wav").c_str());
clockSound = JA_LoadSound(asset->get("clock.wav").c_str());
coffeeOutSound = JA_LoadSound(asset->get("coffeeout.wav").c_str());
hiScoreSound = JA_LoadSound(asset->get("hiscore.wav").c_str());
itemDropSound = JA_LoadSound(asset->get("itemdrop.wav").c_str());
itemPickUpSound = JA_LoadSound(asset->get("itempickup.wav").c_str());
playerCollisionSound = JA_LoadSound(asset->get("player_collision.wav").c_str());
powerBallSound = JA_LoadSound(asset->get("powerball.wav").c_str());
stageChangeSound = JA_LoadSound(asset->get("stage_change.wav").c_str());
coffeeMachineSound = JA_LoadSound(asset->get("title.wav").c_str());
if (options.console)
{
std::cout << "** RESOURCES FOR GAME SECTION LOADED" << std::endl
<< std::endl;
}
}
// Libera los recursos previamente cargados
void Game::unloadMedia()
{
// Texturas
delete bulletTexture;
delete gameTextTexture;
for (auto texture : player1Textures)
{
if (texture)
{
delete texture;
}
}
player1Textures.clear();
for (auto texture : player2Textures)
{
if (texture)
{
delete texture;
}
}
player2Textures.clear();
for (auto texture : itemTextures)
{
if (texture)
{
delete texture;
}
}
itemTextures.clear();
for (auto texture : balloonTextures)
{
if (texture)
{
delete texture;
}
}
balloonTextures.clear();
for (auto texture : explosionsTextures)
{
if (texture)
{
delete texture;
}
}
explosionsTextures.clear();
// Animaciones
for (auto animation : playerAnimations)
{
if (animation)
{
delete animation;
}
}
playerAnimations.clear();
for (auto animation : balloonAnimations)
{
if (animation)
{
delete animation;
}
}
balloonAnimations.clear();
for (auto animation : explosionsAnimations)
{
if (animation)
{
delete animation;
}
}
explosionsAnimations.clear();
for (auto animation : itemAnimations)
{
if (animation)
{
delete animation;
}
}
itemAnimations.clear();
// Text
delete text;
delete textBig;
delete textNokia2;
delete textNokiaBig2;
// Sonidos
JA_DeleteSound(balloonSound);
JA_DeleteSound(bulletSound);
JA_DeleteSound(playerCollisionSound);
JA_DeleteSound(hiScoreSound);
JA_DeleteSound(itemDropSound);
JA_DeleteSound(itemPickUpSound);
JA_DeleteSound(coffeeOutSound);
JA_DeleteSound(stageChangeSound);
JA_DeleteSound(bubble1Sound);
JA_DeleteSound(bubble2Sound);
JA_DeleteSound(bubble3Sound);
JA_DeleteSound(bubble4Sound);
JA_DeleteSound(clockSound);
JA_DeleteSound(powerBallSound);
JA_DeleteSound(coffeeMachineSound);
}
// Carga el fichero de datos para la demo
bool Game::loadDemoFile(std::string filePath, demoKeys_t (*dataFile)[TOTAL_DEMO_DATA])
{
// Indicador de éxito en la carga
bool success = true;
const std::string fileName = filePath.substr(filePath.find_last_of("\\/") + 1);
SDL_RWops *file = SDL_RWFromFile(filePath.c_str(), "r+b");
if (file == nullptr)
{ // El fichero no existe
if (options.console)
{
std::cout << "Warning: Unable to open " << fileName.c_str() << " file" << std::endl;
}
// Creamos el fichero para escritura
file = SDL_RWFromFile(filePath.c_str(), "w+b");
// Si ha creado el fichero
if (file != nullptr)
{
if (options.console)
{
std::cout << "New file (" << fileName.c_str() << ") created!" << std::endl;
}
// Inicializas los datos y los guarda en el fichero
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
{
demoKeys_t tmp;
tmp.left = 0;
tmp.right = 0;
tmp.noInput = 0;
tmp.fire = 0;
tmp.fireLeft = 0;
tmp.fireRight = 0;
(*dataFile)[i] = tmp;
SDL_RWwrite(file, &tmp, sizeof(demoKeys_t), 1);
}
// Cerramos el fichero
SDL_RWclose(file);
}
else
{ // Si no puede crear el fichero
if (options.console)
{
std::cout << "Error: Unable to create file " << fileName.c_str() << std::endl;
}
success = false;
}
}
// El fichero existe
else
{
// Mensaje de proceder a la carga de los datos
if (options.console)
{
std::cout << "Reading file: " << fileName.c_str() << std::endl;
}
// Lee todos los datos del fichero y los deja en el destino
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
{
demoKeys_t tmp;
SDL_RWread(file, &tmp, sizeof(demoKeys_t), 1);
(*dataFile)[i] = tmp;
}
// Cierra el fichero
SDL_RWclose(file);
}
return success;
}
#ifdef RECORDING
// Guarda el fichero de datos para la demo
bool Game::saveDemoFile(std::string filePath)
{
bool success = true;
const std::string filename = filePath.substr(filePath.find_last_of("\\/") + 1);
SDL_RWops *file = SDL_RWFromFile(filePath.c_str(), "w+b");
if (file != nullptr)
{
// Guardamos los datos
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
{
SDL_RWwrite(file, &demo.dataFile[0][i], sizeof(demoKeys_t), 1);
}
if (options.console)
{
std::cout << "Writing file " << filename.c_str() << std::endl;
}
// Cerramos el fichero
SDL_RWclose(file);
}
else
{
if (options.console)
{
std::cout << "Error: Unable to save " << filename.c_str() << " file! " << SDL_GetError() << std::endl;
}
}
return success;
}
#endif
// Crea una formación de enemigos
void Game::deployEnemyFormation()
{
// Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última
if (enemyDeployCounter == 0)
{
// En este punto se decide entre crear una powerball o una formación enemiga
if ((rand() % 100 < 15) && (canPowerBallBeCreated()))
{
// Crea una powerball
createPowerBall();
// Da un poco de margen para que se creen mas enemigos
enemyDeployCounter = 50;
}
else
{
// Decrementa el contador de despliegues enemigos de la PowerBall
powerBallCounter > 0 ? powerBallCounter-- : powerBallCounter = 0;
// Elige una formación enemiga la azar
int set = rand() % 10;
// Evita repetir la ultima formación enemiga desplegada
if (set == lastEnemyDeploy)
{
++set %= 10;
}
lastEnemyDeploy = set;
const stage_t stage = enemyFormations->getStage(currentStage);
const int numEnemies = stage.enemyPool->set[set]->numberOfEnemies;
for (int i = 0; i < numEnemies; ++i)
{
createBalloon(stage.enemyPool->set[set]->init[i].x,
stage.enemyPool->set[set]->init[i].y,
stage.enemyPool->set[set]->init[i].kind,
stage.enemyPool->set[set]->init[i].velX,
enemySpeed,
stage.enemyPool->set[set]->init[i].creationCounter);
}
enemyDeployCounter = 300;
}
}
}
// Aumenta el poder de la fase
void Game::increaseStageCurrentPower(int power)
{
currentPower += power;
}
// Actualiza el valor de hiScore en caso necesario
void Game::updateHiScore()
{
// Si la puntuación actual es mayor que la máxima puntuación
for (auto player : players)
{
if (player->getScore() > hiScore.score)
{
// Actualiza la máxima puntuación
hiScore.score = player->getScore();
// Si se supera la máxima puntuación emite sonido
if (hiScoreAchieved == false)
{
hiScoreAchieved = true;
JA_PlaySound(hiScoreSound);
}
}
}
}
// Actualiza las variables del jugador
void Game::updatePlayers()
{
for (auto player : players)
{
player->update();
if (player->isPlaying())
{ // Comprueba la colisión entre el jugador y los globos
if (checkPlayerBalloonCollision(player))
{
killPlayer(player);
if (demo.enabled && allPlayersAreWaiting())
{
fade->setType(FADE_RANDOM_SQUARE);
fade->activate();
}
}
// Comprueba las colisiones entre el jugador y los items
checkPlayerItemCollision(player);
}
}
}
// Dibuja a los jugadores
void Game::renderPlayers()
{
for (auto player : players)
{
if (!player->isWaiting())
{
player->render();
#ifdef DEBUG
// SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// const circle_t c = player->getCollider();
// DrawCircle(renderer, c.x, c.y, c.r);
#endif
}
}
}
// Comprueba si hay cambio de fase y actualiza las variables
void Game::updateStage()
{
if (currentPower >= enemyFormations->getStage(currentStage).powerToComplete)
{
// Cambio de fase
currentStage++;
currentPower = 0;
lastStageReached = currentStage;
if (currentStage == 10)
{ // Ha llegado al final el juego
gameCompleted = true; // Marca el juego como completado
currentStage = 9; // Deja el valor dentro de los limites
destroyAllBalloons(); // Destruye a todos los enemigos
currentPower = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos lo globos
menaceCurrent = 255; // Sube el nivel de amenaza para que no cree mas globos
for (auto player : players)
{ // Añade un millon de puntos a los jugadores que queden vivos
if (player->isPlaying())
{
player->addScore(1000000);
}
}
updateHiScore();
JA_StopMusic();
}
JA_PlaySound(stageChangeSound);
stageBitmapCounter = 0;
enemySpeed = defaultEnemySpeed;
setBalloonSpeed(enemySpeed);
screen->flash(flashColor, 5);
screen->shake();
}
// Incrementa el contador del bitmap que aparece mostrando el cambio de fase
if (stageBitmapCounter < STAGE_COUNTER)
{
stageBitmapCounter++;
}
// Si el juego se ha completado, el bitmap se detiene en el centro de la pantalla
if (gameCompleted)
{
if (stageBitmapCounter > 100)
{
stageBitmapCounter = 100;
}
}
}
// Actualiza el estado de fin de la partida
void Game::updateGameOver()
{
// Comprueba si todos los jugadores estan muertos
if (allPlayersAreWaiting())
{
if (gameOverCounter > 0)
{
gameOverCounter--;
if ((gameOverCounter == 250) || (gameOverCounter == 200) || (gameOverCounter == 180) || (gameOverCounter == 120) || (gameOverCounter == 60))
{
// Hace sonar aleatoriamente uno de los 4 sonidos de burbujas
const int index = rand() % 4;
JA_Sound_t *sound[4] = {bubble1Sound, bubble2Sound, bubble3Sound, bubble4Sound};
JA_PlaySound(sound[index], 0);
}
if (gameOverCounter == 150)
{
fade->activate();
}
}
if (fade->hasEnded())
{
section::name = section::NAME_HI_SCORE_TABLE;
}
}
}
// Actualiza los globos
void Game::updateBalloons()
{
for (auto balloon : balloons)
{
balloon->update();
}
}
// Pinta en pantalla todos los globos activos
void Game::renderBalloons()
{
for (auto balloon : balloons)
{
balloon->render();
}
}
// Crea un globo nuevo en el vector de globos
int Game::createBalloon(float x, int y, int kind, float velx, float speed, int creationtimer)
{
const int index = (kind - 1) % 4;
Balloon *b = new Balloon(x, y, kind, velx, speed, creationtimer, balloonTextures[index], balloonAnimations[index]);
balloons.push_back(b);
return (int)(balloons.size() - 1);
}
// Crea una PowerBall
void Game::createPowerBall()
{
const int values = 6;
const int posY = -BLOCK;
const int left = param.game.playArea.rect.x;
const int center = param.game.playArea.centerX - (BALLOON_WIDTH_4 / 2);
const int right = param.game.playArea.rect.w - BALLOON_WIDTH_4;
const float vpos = BALLOON_VELX_POSITIVE;
const float vneg = BALLOON_VELX_NEGATIVE;
const int luck = rand() % values;
const int x[values] = {left, left, center, center, right, right};
const float vx[values] = {vpos, vpos, vpos, vneg, vneg, vneg};
Balloon *b = new Balloon(x[luck], posY, POWER_BALL, vx[luck], enemySpeed, 300, balloonTextures[4], balloonAnimations[4]);
balloons.push_back(b);
powerBallEnabled = true;
powerBallCounter = POWERBALL_COUNTER;
}
// Establece la velocidad de los globos
void Game::setBalloonSpeed(float speed)
{
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
balloon->setSpeed(speed);
}
}
}
// Incrementa la velocidad de los globos
void Game::incBalloonSpeed()
{
// La velocidad solo se incrementa en el modo normal
if (difficulty == DIFFICULTY_NORMAL)
{
if (enemySpeed == BALLOON_SPEED_1)
{
enemySpeed = BALLOON_SPEED_2;
}
else if (enemySpeed == BALLOON_SPEED_2)
{
enemySpeed = BALLOON_SPEED_3;
}
else if (enemySpeed == BALLOON_SPEED_3)
{
enemySpeed = BALLOON_SPEED_4;
}
else if (enemySpeed == BALLOON_SPEED_4)
{
enemySpeed = BALLOON_SPEED_5;
}
setBalloonSpeed(enemySpeed);
}
}
// Decrementa la velocidad de los globos
void Game::decBalloonSpeed()
{
// La velocidad solo se decrementa en el modo normal
if (difficulty == DIFFICULTY_NORMAL)
{
if (enemySpeed == BALLOON_SPEED_5)
{
enemySpeed = BALLOON_SPEED_4;
}
else if (enemySpeed == BALLOON_SPEED_4)
{
enemySpeed = BALLOON_SPEED_3;
}
else if (enemySpeed == BALLOON_SPEED_3)
{
enemySpeed = BALLOON_SPEED_2;
}
else if (enemySpeed == BALLOON_SPEED_2)
{
enemySpeed = BALLOON_SPEED_1;
}
setBalloonSpeed(enemySpeed);
}
}
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
void Game::updateBalloonSpeed()
{
const float percent = (float)currentPower / (float)enemyFormations->getStage(currentStage).powerToComplete;
if (enemySpeed == BALLOON_SPEED_1)
{
if (percent > 0.2f)
{
incBalloonSpeed();
}
}
else if (enemySpeed == BALLOON_SPEED_2)
{
if (percent > 0.4f)
{
incBalloonSpeed();
}
}
else if (enemySpeed == BALLOON_SPEED_3)
{
if (percent > 0.6f)
{
incBalloonSpeed();
}
}
else if (enemySpeed == BALLOON_SPEED_4)
{
if (percent > 0.8f)
{
incBalloonSpeed();
}
}
}
// Explosiona un globo. Lo destruye y crea otros dos si es el caso
void Game::popBalloon(Balloon *balloon)
{
// Aumenta el poder de la fase
increaseStageCurrentPower(1);
balloonsPopped++;
const int kind = balloon->getKind();
if (kind == POWER_BALL)
{
destroyAllBalloons();
powerBallEnabled = false;
enemyDeployCounter = 20;
}
else
{
const int size = balloon->getSize();
if (size == BALLOON_SIZE_1)
{ // Si es del tipo más pequeño, simplemente elimina el globo
explosions->add(balloon->getPosX(), balloon->getPosY(), size);
balloon->pop();
}
else
{ // En cualquier otro caso, crea dos globos de un tipo inferior
const int index = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, BALLOON_VELX_NEGATIVE, enemySpeed, 0);
balloons[index]->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
if (balloons[index]->getClass() == BALLOON_CLASS)
{
balloons[index]->setVelY(-2.50f);
}
else
{
balloons[index]->setVelY(BALLOON_VELX_NEGATIVE);
}
const int index2 = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, BALLOON_VELX_POSITIVE, enemySpeed, 0);
balloons[index2]->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
if (balloons[index2]->getClass() == BALLOON_CLASS)
{
balloons[index2]->setVelY(-2.50f);
}
else
{
balloons[index2]->setVelY(BALLOON_VELX_NEGATIVE);
}
// Elimina el globo
explosions->add(balloon->getPosX(), balloon->getPosY(), size);
balloon->pop();
}
}
// Recalcula el nivel de amenaza
evaluateAndSetMenace();
}
// Explosiona un globo. Lo destruye = no crea otros globos
void Game::destroyBalloon(Balloon *balloon)
{
int score = 0;
// Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos
const int size = balloon->getSize();
switch (size)
{
case BALLOON_SIZE_4:
score = BALLOON_SCORE_4 + (2 * BALLOON_SCORE_3) + (4 * BALLOON_SCORE_2) + (8 * BALLOON_SCORE_1);
break;
case BALLOON_SIZE_3:
score = BALLOON_SCORE_3 + (2 * BALLOON_SCORE_2) + (4 * BALLOON_SCORE_1);
break;
case BALLOON_SIZE_2:
score = BALLOON_SCORE_2 + (2 * BALLOON_SCORE_1);
break;
case BALLOON_SIZE_1:
score = BALLOON_SCORE_1;
break;
default:
score = 0;
break;
}
// Otorga los puntos correspondientes al globo
for (auto player : players)
{
player->addScore(Uint32(score * player->getScoreMultiplier() * difficultyScoreMultiplier));
}
updateHiScore();
// Aumenta el poder de la fase
const int power = balloon->getPower();
increaseStageCurrentPower(power);
balloonsPopped += power;
// Destruye el globo
explosions->add(balloon->getPosX(), balloon->getPosY(), size);
balloon->pop();
// Recalcula el nivel de amenaza
evaluateAndSetMenace();
}
// Explosiona todos los globos
void Game::popAllBalloons()
{
for (auto balloon : balloons)
{
if (balloon->canBePopped())
{
popBalloon(balloon);
}
}
JA_PlaySound(balloonSound);
}
// Destruye todos los globos
void Game::destroyAllBalloons()
{
for (auto balloon : balloons)
{
if (balloon->canBeDestroyed())
{
destroyBalloon(balloon);
}
}
enemyDeployCounter = 255;
JA_PlaySound(powerBallSound);
screen->flash(flashColor, 5);
screen->shake();
}
// Detiene todos los globos
void Game::stopAllBalloons(int time)
{
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
balloon->setStop(true);
balloon->setStoppedTimer(time);
}
}
}
// Pone en marcha todos los globos
void Game::startAllBalloons()
{
for (auto balloon : balloons)
{
if ((balloon->isEnabled()) && (!balloon->isBeingCreated()))
{
balloon->setStop(false);
balloon->setStoppedTimer(0);
}
}
}
// Obtiene el número de globos activos
int Game::countBalloons()
{
int num = 0;
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
num++;
}
}
return num;
}
// Vacia del vector de globos los globos que ya no sirven
void Game::freeBalloons()
{
if (balloons.empty() == false)
{
for (int i = balloons.size() - 1; i >= 0; --i)
{
if (balloons[i]->isEnabled() == false)
{
delete balloons[i];
balloons.erase(balloons.begin() + i);
}
}
}
}
// Comprueba la colisión entre el jugador y los globos activos
bool Game::checkPlayerBalloonCollision(Player *player)
{
for (auto balloon : balloons)
{
if ((balloon->isEnabled()) && !(balloon->isStopped()) && !(balloon->isInvulnerable()))
{
if (checkCollision(player->getCollider(), balloon->getCollider()))
{
return true;
}
}
}
return false;
}
// Comprueba la colisión entre el jugador y los items
void Game::checkPlayerItemCollision(Player *player)
{
if (!player->isPlaying())
{
return;
}
for (auto item : items)
{
if (item->isEnabled())
{
if (checkCollision(player->getCollider(), item->getCollider()))
{
switch (item->getClass())
{
case ITEM_POINTS_1_DISK:
player->addScore(1000);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n1000Sprite->getWidth() / 2), player->getPosY(), n1000Sprite);
JA_PlaySound(itemPickUpSound);
break;
case ITEM_POINTS_2_GAVINA:
player->addScore(2500);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n2500Sprite->getWidth() / 2), player->getPosY(), n2500Sprite);
JA_PlaySound(itemPickUpSound);
break;
case ITEM_POINTS_3_PACMAR:
player->addScore(5000);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n5000Sprite->getWidth() / 2), player->getPosY(), n5000Sprite);
JA_PlaySound(itemPickUpSound);
break;
case ITEM_CLOCK:
enableTimeStopItem();
JA_PlaySound(itemPickUpSound);
break;
case ITEM_COFFEE:
if (player->getCoffees() == 2)
{
player->addScore(5000);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n5000Sprite->getWidth() / 2), player->getPosY(), n5000Sprite);
}
player->giveExtraHit();
JA_PlaySound(itemPickUpSound);
break;
case ITEM_COFFEE_MACHINE:
player->setPowerUp();
JA_PlaySound(itemPickUpSound);
coffeeMachineEnabled = false;
break;
default:
break;
}
item->disable();
}
}
}
}
// Comprueba y procesa la colisión entre las balas y los globos
void Game::checkBulletBalloonCollision()
{
for (auto bullet : bullets)
{
for (auto balloon : balloons)
{
if (balloon->isEnabled() && (!balloon->isInvulnerable()) && bullet->isEnabled())
{
if (checkCollision(balloon->getCollider(), bullet->getCollider()))
{
// Otorga los puntos correspondientes al globo al jugador que disparó la bala
Player *player = getPlayer(bullet->getOwner());
if (!player)
{
return;
}
player->incScoreMultiplier();
player->addScore(Uint32(balloon->getScore() * player->getScoreMultiplier() * difficultyScoreMultiplier));
updateHiScore();
// Suelta el item si se da el caso
const int droppeditem = dropItem();
if ((droppeditem != ITEM_NULL) && !(demo.recording))
{
if (droppeditem != ITEM_COFFEE_MACHINE)
{
createItem(droppeditem, balloon->getPosX(), balloon->getPosY());
JA_PlaySound(itemDropSound);
}
else
{
createItem(droppeditem, player->getPosX(), 0);
coffeeMachineEnabled = true;
}
}
// Explota el globo
popBalloon(balloon);
// Sonido de explosión
JA_PlaySound(balloonSound);
// Deshabilita la bala
bullet->disable();
break;
}
}
}
}
}
// Mueve las balas activas
void Game::moveBullets()
{
for (auto bullet : bullets)
{
if (bullet->isEnabled())
{
if (bullet->move() == BULLET_MOVE_OUT)
{
Player *player = getPlayer(bullet->getOwner());
player->decScoreMultiplier();
}
}
}
}
// Pinta las balas activas
void Game::renderBullets()
{
for (auto bullet : bullets)
{
if (bullet->isEnabled())
{
bullet->render();
}
}
}
// Crea un objeto bala
void Game::createBullet(int x, int y, int kind, bool poweredUp, int owner)
{
Bullet *b = new Bullet(x, y, kind, poweredUp, owner, &(param.game.playArea.rect), bulletTexture);
bullets.push_back(b);
}
// Vacia el vector de balas
void Game::freeBullets()
{
if (bullets.empty() == false)
{
for (int i = bullets.size() - 1; i >= 0; --i)
{
if (bullets[i]->isEnabled() == false)
{
delete bullets[i];
bullets.erase(bullets.begin() + i);
}
}
}
}
// Actualiza los items
void Game::updateItems()
{
for (auto item : items)
{
if (item->isEnabled())
{
item->update();
if (item->isOnFloor())
{
JA_PlaySound(coffeeMachineSound);
screen->shake();
}
}
}
}
// Pinta los items activos
void Game::renderItems()
{
for (auto item : items)
{
item->render();
}
}
// Devuelve un item al azar y luego segun sus probabilidades
int Game::dropItem()
{
const int luckyNumber = rand() % 100;
const int item = rand() % 6;
switch (item)
{
case 0:
if (luckyNumber < helper.itemPoints1Odds)
{
return ITEM_POINTS_1_DISK;
}
break;
case 1:
if (luckyNumber < helper.itemPoints2Odds)
{
return ITEM_POINTS_2_GAVINA;
}
break;
case 2:
if (luckyNumber < helper.itemPoints3Odds)
{
return ITEM_POINTS_3_PACMAR;
}
break;
case 3:
if (luckyNumber < helper.itemClockOdds)
{
return ITEM_CLOCK;
}
break;
case 4:
if (luckyNumber < helper.itemCoffeeOdds)
{
helper.itemCoffeeOdds = ITEM_COFFEE_ODDS;
return ITEM_COFFEE;
}
else
{
if (helper.needCoffee)
{
helper.itemCoffeeOdds++;
}
}
break;
case 5:
if (luckyNumber < helper.itemCoffeeMachineOdds)
{
helper.itemCoffeeMachineOdds = ITEM_COFFEE_MACHINE_ODDS;
if ((!coffeeMachineEnabled) && (helper.needCoffeeMachine))
{
return ITEM_COFFEE_MACHINE;
}
}
else
{
if (helper.needCoffeeMachine)
{
helper.itemCoffeeMachineOdds++;
}
}
break;
default:
break;
}
return ITEM_NULL;
}
// Crea un objeto item
void Game::createItem(int kind, float x, float y)
{
Item *item = new Item(kind, x, y, &(param.game.playArea.rect), itemTextures[kind - 1], itemAnimations[kind - 1]);
items.push_back(item);
}
// Vacia el vector de items
void Game::freeItems()
{
if (items.empty() == false)
{
for (int i = items.size() - 1; i >= 0; --i)
{
if (items[i]->isEnabled() == false)
{
delete items[i];
items.erase(items.begin() + i);
}
}
}
}
// Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto
void Game::createItemScoreSprite(int x, int y, SmartSprite *sprite)
{
SmartSprite *ss = new SmartSprite(nullptr);
smartSprites.push_back(ss);
// Crea una copia del objeto
*ss = *sprite;
ss->setPosX(x);
ss->setPosY(y);
ss->setDestX(x);
ss->setDestY(y - 25);
ss->setEnabled(true);
ss->setEnabledCounter(100);
}
// Vacia el vector de smartsprites
void Game::freeSmartSprites()
{
if (smartSprites.empty() == false)
{
for (int i = smartSprites.size() - 1; i >= 0; --i)
{
if (smartSprites[i]->hasFinished())
{
delete smartSprites[i];
smartSprites.erase(smartSprites.begin() + i);
}
}
}
}
// Crea un SmartSprite para arrojar el item café al recibir un impacto
void Game::throwCoffee(int x, int y)
{
SmartSprite *ss = new SmartSprite(itemTextures[4]);
smartSprites.push_back(ss);
ss->setPosX(x - 8);
ss->setPosY(y - 8);
ss->setWidth(param.game.itemSize);
ss->setHeight(param.game.itemSize);
ss->setVelX(-1.0f + ((rand() % 5) * 0.5f));
ss->setVelY(-4.0f);
ss->setAccelX(0.0f);
ss->setAccelY(0.2f);
ss->setDestX(x + (ss->getVelX() * 50));
ss->setDestY(param.game.height + 1);
ss->setEnabled(true);
ss->setEnabledCounter(1);
ss->setSpriteClip(0, param.game.itemSize, param.game.itemSize, param.game.itemSize);
ss->setRotate(true);
ss->setRotateSpeed(10);
ss->setRotateAmount(90.0);
}
// Actualiza los SmartSprites
void Game::updateSmartSprites()
{
for (auto ss : smartSprites)
{
ss->update();
}
}
// Pinta los SmartSprites activos
void Game::renderSmartSprites()
{
for (auto ss : smartSprites)
{
ss->render();
}
}
// Acciones a realizar cuando el jugador muere
void Game::killPlayer(Player *player)
{
if (!player->isPlaying() || player->isInvulnerable())
{ // Si no está jugando o tiene inmunidad, no hace nada
return;
}
// Si tiene cafes
if (player->hasExtraHit())
{ // Lo pierde
player->removeExtraHit();
throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2));
JA_PlaySound(coffeeOutSound);
screen->shake();
}
else
{ // Si no tiene cafes, muere
if (!demo.enabled)
{
JA_PauseMusic();
}
stopAllBalloons(10);
JA_PlaySound(playerCollisionSound);
screen->shake();
JA_PlaySound(coffeeOutSound);
demo.enabled ? player->setStatusPlaying(PLAYER_STATUS_WAITING) : player->setStatusPlaying(PLAYER_STATUS_CONTINUE);
if (!demo.enabled)
{ // En el modo DEMO ni se para la musica ni se añade la puntuación a la tabla
allPlayersAreWaiting() ? JA_StopMusic() : JA_ResumeMusic();
addScoreToScoreBoard(player->getName(), player->getScore());
}
}
}
// Calcula y establece el valor de amenaza en funcion de los globos activos
void Game::evaluateAndSetMenace()
{
menaceCurrent = 0;
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
menaceCurrent += balloon->getMenace();
}
}
}
// Obtiene el valor de la variable
int Game::getMenace()
{
return menaceCurrent;
}
// Establece el valor de la variable
void Game::setTimeStopped(bool value)
{
timeStopped = value;
}
// Obtiene el valor de la variable
bool Game::isTimeStopped()
{
return timeStopped;
}
// Establece el valor de la variable
void Game::setTimeStoppedCounter(int value)
{
timeStoppedCounter = value;
}
// Incrementa el valor de la variable
void Game::incTimeStoppedCounter(int value)
{
timeStoppedCounter += value;
}
// Actualiza y comprueba el valor de la variable
void Game::updateTimeStoppedCounter()
{
if (isTimeStopped())
{
if (timeStoppedCounter > 0)
{
timeStoppedCounter--;
stopAllBalloons(TIME_STOPPED_COUNTER);
}
else
{
disableTimeStopItem();
}
}
}
// Actualiza la variable enemyDeployCounter
void Game::updateEnemyDeployCounter()
{
if (enemyDeployCounter > 0)
{
enemyDeployCounter--;
}
}
// Actualiza el juego
void Game::update()
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks > ticksSpeed)
{
// Actualiza el contador de ticks
ticks = SDL_GetTicks();
// Actualiza el contador de juego
counter++;
if (demo.enabled)
{
// Incrementa el contador de la demo
if (demo.counter < TOTAL_DEMO_DATA)
{
demo.counter++;
}
// Activa el fundido antes de acabar con los datos de la demo
if (demo.counter == TOTAL_DEMO_DATA - 200)
{
fade->setType(FADE_RANDOM_SQUARE);
fade->activate();
}
// Si ha terminado el fundido, cambia de sección
if (fade->hasEnded())
{
section::name = section::NAME_HI_SCORE_TABLE;
return;
}
}
#ifdef RECORDING
// Solo mira y guarda el input en cada update
checkInput();
// Incrementa el contador de la demo
if (demo.counter < TOTAL_DEMO_DATA)
{
demo.counter++;
}
// Si se ha llenado el vector con datos, sale del programa
else
{
section::name = section::NAME_QUIT;
return;
}
#endif
#ifdef DEBUG
if (autoPopBalloons && !gameCompleted)
{
balloonsPopped++;
increaseStageCurrentPower(1);
}
#endif
if (!paused)
{
// Actualiza el objeto fade
fade->update();
// Actualiza las variables del jugador
updatePlayers();
// Actualiza el marcador
checkPlayersStatusPlaying();
updateScoreboard();
// Actualiza el fondo
updateBackground();
// Mueve los globos
updateBalloons();
// Actualiza el objeto encargado de las explosiones
explosions->update();
// Mueve las balas
moveBullets();
// Actualiza los items
updateItems();
// Comprueba si hay cambio de fase y actualiza las variables
updateStage();
// Actualiza el estado de muerte
updateGameOver();
// Actualiza los SmartSprites
updateSmartSprites();
// Actualiza los contadores de estado y efectos
updateTimeStoppedCounter();
updateEnemyDeployCounter();
// Actualiza el ayudante
updateHelper();
// Comprueba las colisiones entre globos y balas
checkBulletBalloonCollision();
// Comprueba el nivel de amenaza para ver si se han de crear nuevos enemigos
updateMenace();
// Actualiza la velocidad de los enemigos
updateBalloonSpeed();
// Actualiza el tramo final de juego, una vez completado
updateGameCompleted();
// Vacia los vectores
freeBullets();
freeBalloons();
freeItems();
freeSmartSprites();
}
// Comprueba si la música ha de estar sonando
checkMusicStatus();
// Actualiza el objeto screen
screen->update();
// Dibuja los graficos de la zona de juego en la textura
fillCanvas();
}
}
// Actualiza el fondo
void Game::updateBackground()
{
// Si el juego está completado, se reduce la velocidad de las nubes
if (gameCompleted)
{
if (balloonsPopped > 400)
{
balloonsPopped -= 25;
}
else
{
balloonsPopped = 200;
}
}
// Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego
const float cloudsInitialSpeed = 0.05f;
const float cloudsFinalSpeed = 2.00f - cloudsInitialSpeed;
const float cloudsSpeed = (-cloudsInitialSpeed) + (-cloudsFinalSpeed * ((float)balloonsPopped / (float)totalPowerToCompleteGame));
background->setCloudsSpeed(cloudsSpeed);
// Calcula la transición de los diferentes fondos
const float gradientNumber = std::min(((float)balloonsPopped / 1250.0f), 3.0f);
const float percent = gradientNumber - (int)gradientNumber;
background->setGradientNumber((int)gradientNumber);
background->setTransition(percent);
// Actualiza el objeto
background->update();
}
// Dibuja los elementos de la zona de juego en su textura
void Game::fillCanvas()
{
// Dibujamos el contenido de la zona de juego en su textura
SDL_Texture *temp = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, canvas);
// Dibuja los objetos
background->render();
renderItems();
renderSmartSprites();
explosions->render();
renderBalloons();
renderBullets();
renderMessages();
renderPlayers();
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer, temp);
}
// Dibuja el juego
void Game::render()
{
// Prepara para empezar a dibujar en la textura de juego
screen->start();
// Copia la textura con la zona de juego a la pantalla
SDL_RenderCopy(renderer, canvas, nullptr, &param.game.playArea.rect);
// Dibuja el marcador
scoreboard->render();
// Dibuja el fade
fade->render();
// Vuelca el contenido del renderizador en pantalla
screen->blit();
}
// Gestiona el nivel de amenaza
void Game::updateMenace()
{
if (gameCompleted)
{
return;
}
const stage_t stage = enemyFormations->getStage(currentStage);
const float percent = currentPower / stage.powerToComplete;
const int difference = stage.maxMenace - stage.minMenace;
// Aumenta el nivel de amenaza en función de la puntuación
menaceThreshold = stage.minMenace + (difference * percent);
// Si el nivel de amenza es inferior al umbral
if (menaceCurrent < menaceThreshold)
{
// Crea una formación de enemigos
deployEnemyFormation();
// Recalcula el nivel de amenaza con el nuevo globo
evaluateAndSetMenace();
}
}
// Gestiona la entrada durante el juego
void Game::checkInput()
{
// Comprueba si se sale con el teclado
if (input->checkInput(input_exit, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD))
{
quit(section::OPTIONS_QUIT_NORMAL);
return;
}
for (int i = 0; i < input->getNumControllers(); ++i)
{
// Comprueba si se sale con el mando
if (input->checkModInput(input_service, input_exit, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
{
quit(section::OPTIONS_QUIT_SHUTDOWN);
return;
}
// Comprueba si se va a resetear el juego
if (input->checkModInput(input_service, input_reset, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
{
section::name = section::NAME_LOGO;
screen->showNotification("Reset");
return;
}
// Comprueba si se va a activar o desactivar el audio
if (input->checkModInput(input_service, input_mute, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
{
options.audio.sound.enabled = options.audio.music.enabled = !options.audio.music.enabled;
JA_EnableMusic(options.audio.music.enabled);
JA_EnableSound(options.audio.sound.enabled);
screen->showNotification("Audio " + boolToOnOff(options.audio.music.enabled));
return;
}
// Comprueba si se va a pausar el juego
if (input->checkModInput(input_service, input_pause, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
{
pause(!paused);
return;
}
}
// Modo Demo activo
if (demo.enabled)
{
int i = 0;
for (auto player : players)
{
if (player->isPlaying())
{
// Comprueba direcciones
if (demo.dataFile[i][demo.counter].left == 1)
{
player->setInput(input_left);
}
else if (demo.dataFile[i][demo.counter].right == 1)
{
player->setInput(input_right);
}
else if (demo.dataFile[i][demo.counter].noInput == 1)
{
player->setInput(input_null);
}
// Comprueba botones
if (demo.dataFile[i][demo.counter].fire == 1)
{
if (player->canFire())
{
player->setInput(input_fire_center);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_UP, player->isPowerUp(), player->getId());
player->setFireCooldown(10);
}
}
else if (demo.dataFile[i][demo.counter].fireLeft == 1)
{
if (player->canFire())
{
player->setInput(input_fire_left);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_LEFT, player->isPowerUp(), player->getId());
player->setFireCooldown(10);
}
}
else if (demo.dataFile[i][demo.counter].fireRight == 1)
{
if (player->canFire())
{
player->setInput(input_fire_right);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_RIGHT, player->isPowerUp(), player->getId());
player->setFireCooldown(10);
}
}
// Si se pulsa cualquier tecla, se sale del modo demo
if (input->checkAnyButtonPressed())
{
section::name = section::NAME_TITLE;
return;
}
}
i++;
}
}
// Modo Demo no activo
else
{
#ifdef RECORDING
// Resetea el teclado
demo.keys.left = 0;
demo.keys.right = 0;
demo.keys.noInput = 0;
demo.keys.fire = 0;
demo.keys.fireLeft = 0;
demo.keys.fireRight = 0;
#endif
for (auto player : players)
{
const int controllerIndex = player->getController();
const bool autofire = player->isPowerUp() || options.game.autofire;
if (player->isPlaying())
{
// Input a la izquierda
if (input->checkInput(input_left, INPUT_ALLOW_REPEAT, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index))
{
player->setInput(input_left);
#ifdef RECORDING
demo.keys.left = 1;
#endif
}
else
{
// Input a la derecha
if (input->checkInput(input_right, INPUT_ALLOW_REPEAT, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index))
{
player->setInput(input_right);
#ifdef RECORDING
demo.keys.right = 1;
#endif
}
else
{
// Ninguno de los dos inputs anteriores
player->setInput(input_null);
#ifdef RECORDING
demo.keys.noInput = 1;
#endif
}
}
// Comprueba el input de disparar al centro
if (input->checkInput(input_fire_center, autofire, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index))
{
if (player->canFire())
{
player->setInput(input_fire_center);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_UP, player->isPowerUp(), player->getId());
player->setFireCooldown(10);
// Reproduce el sonido de disparo
JA_PlaySound(bulletSound);
#ifdef RECORDING
demo.keys.fire = 1;
#endif
}
}
// Comprueba el input de disparar a la izquierda
else if (input->checkInput(input_fire_left, autofire, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index))
{
if (player->canFire())
{
player->setInput(input_fire_left);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_LEFT, player->isPowerUp(), player->getId());
player->setFireCooldown(10);
// Reproduce el sonido de disparo
JA_PlaySound(bulletSound);
#ifdef RECORDING
demo.keys.fireLeft = 1;
#endif
}
}
// Comprueba el input de disparar a la derecha
else if (input->checkInput(input_fire_right, autofire, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index))
{
if (player->canFire())
{
player->setInput(input_fire_right);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_RIGHT, player->isPowerUp(), player->getId());
player->setFireCooldown(10);
// Reproduce el sonido de disparo
JA_PlaySound(bulletSound);
#ifdef RECORDING
demo.keys.fireRight = 1;
#endif
}
}
#ifdef RECORDING
if (demo.recording)
{
if (demo.counter < TOTAL_DEMO_DATA)
{
demo.dataFile[0][demo.counter] = demo.keys;
}
}
#endif
}
else
{
// Si no está jugando, el botón de start le permite continuar jugando
if (input->checkInput(input_start, INPUT_ALLOW_REPEAT, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index))
{
// Si no ha entrado ya en el estado de game over, entonces se puede continuar
if (gameOverCounter == GAME_OVER_COUNTER)
{
player->setStatusPlaying(PLAYER_STATUS_PLAYING);
}
}
// Si está continuando, los botones de fuego hacen decrementar el contador
const bool fire1 = input->checkInput(input_fire_left, false, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index);
const bool fire2 = input->checkInput(input_fire_center, false, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index);
const bool fire3 = input->checkInput(input_fire_right, false, options.controller[controllerIndex].deviceType, options.controller[controllerIndex].index);
if (fire1 || fire2 || fire3)
{
player->decContinueCounter();
}
}
}
}
// Comprueba el input para el resto de objetos
screen->checkInput();
}
// Pinta diferentes mensajes en la pantalla
void Game::renderMessages()
{
// GetReady
if ((counter < STAGE_COUNTER) && (!demo.enabled))
{
textNokiaBig2->write((int)getReadyBitmapPath[counter], param.game.playArea.centerY - 8, lang::getText(75), -2);
}
// Time Stopped
if (timeStopped)
{
if ((timeStoppedCounter > 100) || (timeStoppedCounter % 10 > 4))
{
textNokia2->writeDX(TXT_CENTER, param.game.playArea.centerX, param.game.playArea.firstQuarterY, lang::getText(36) + std::to_string(timeStoppedCounter / 10), -1, noColor, 1, shdwTxtColor);
}
if (timeStoppedCounter > 100)
{
if (timeStoppedCounter % 30 == 0)
{
// JA_PlaySound(clockSound, false);
JA_PlaySound(clockSound);
}
}
else
{
if (timeStoppedCounter % 15 == 0)
{
// JA_PlaySound(clockSound, false);
JA_PlaySound(clockSound);
}
}
}
// STAGE NUMBER
if (stageBitmapCounter < STAGE_COUNTER)
{
const int stageNum = enemyFormations->getStage(currentStage).number;
std::string text;
if (stageNum == 10)
{ // Ultima fase
text = lang::getText(79);
}
else
{ // X fases restantes
text = std::to_string(11 - stageNum) + lang::getText(38);
}
if (!gameCompleted)
{ // Escribe el número de fases restantes
textNokiaBig2->writeDX(TXT_CENTER, param.game.playArea.centerX, stageBitmapPath[stageBitmapCounter], text, -2, noColor, 2, shdwTxtColor);
}
else
{ // Escribe el texto de juego completado
text = lang::getText(50);
textNokiaBig2->writeDX(TXT_CENTER, param.game.playArea.centerX, stageBitmapPath[stageBitmapCounter], text, -2, noColor, 1, shdwTxtColor);
textNokia2->writeDX(TXT_CENTER, param.game.playArea.centerX, stageBitmapPath[stageBitmapCounter] + textNokiaBig2->getCharacterSize() + 2, lang::getText(76), -1, noColor, 1, shdwTxtColor);
}
}
}
// Habilita el efecto del item de detener el tiempo
void Game::enableTimeStopItem()
{
stopAllBalloons(TIME_STOPPED_COUNTER);
setTimeStopped(true);
incTimeStoppedCounter(TIME_STOPPED_COUNTER);
if (JA_GetMusicState() == JA_MUSIC_PLAYING && !demo.enabled)
{
JA_PauseMusic();
}
}
// Deshabilita el efecto del item de detener el tiempo
void Game::disableTimeStopItem()
{
timeStopped = false;
setTimeStoppedCounter(0);
startAllBalloons();
if (JA_GetMusicState() == JA_MUSIC_PAUSED && !demo.enabled)
{
JA_ResumeMusic();
}
}
// Comprueba si la música ha de estar sonando
void Game::checkMusicStatus()
{
// Si la música no está sonando
if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED))
{
// Si se ha completado el juego o los jugadores han terminado, detiene la música
gameCompleted || allPlayersAreWaiting() ? JA_StopMusic() : JA_PlayMusic(music);
}
}
// Bucle para el juego
void Game::run()
{
while (section::name == section::NAME_GAME)
{
#ifndef RECORDING
checkInput();
#endif
update();
checkEvents(); // Tiene que ir antes del render
render();
}
// Vuelve a dejar el sonido como estaba
if (demo.enabled)
{
JA_EnableSound(options.audio.sound.enabled);
}
else
{
JA_StopMusic();
}
}
// Indica si se puede crear una powerball
bool Game::canPowerBallBeCreated()
{
if ((!powerBallEnabled) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (powerBallCounter == 0))
{
return true;
}
return false;
}
// Calcula el poder actual de los globos en pantalla
int Game::calculateScreenPower()
{
int power = 0;
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
power += balloon->getPower();
}
}
return power;
}
// Inicializa las variables que contienen puntos de ruta para mover objetos
void Game::initPaths()
{
// Vector con los valores del seno para 360 grados
float sin[360];
for (int i = 0; i < 360; ++i)
{
sin[i] = SDL_sinf((float)i * 3.14f / 180.0f);
}
// Letrero de STAGE #
const int firstPart = STAGE_COUNTER / 4; // 50
const int secondPart = firstPart * 3; // 150
const int centerPoint = param.game.playArea.centerY - (BLOCK * 2);
const int distance = (param.game.playArea.rect.h) - (param.game.playArea.centerY - 16);
for (int i = 0; i < STAGE_COUNTER; ++i)
{
if (i < firstPart)
{
stageBitmapPath[i] = (sin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint);
}
else if (i < secondPart)
{
stageBitmapPath[i] = (int)centerPoint;
}
else
{
stageBitmapPath[i] = (sin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17);
}
}
// Letrero de GetReady
const int size = textNokiaBig2->lenght(lang::getText(75), -2);
const float start1 = param.game.playArea.rect.x - size;
const float finish1 = param.game.playArea.centerX - (size / 2);
const float start2 = finish1;
const float finish2 = param.game.playArea.rect.w;
const float distance1 = finish1 - start1;
const float distance2 = finish2 - start2;
for (int i = 0; i < STAGE_COUNTER; ++i)
{
if (i < firstPart)
{
getReadyBitmapPath[i] = sin[(int)(i * 1.8f)];
getReadyBitmapPath[i] *= distance1;
getReadyBitmapPath[i] -= size;
}
else if (i < secondPart)
{
getReadyBitmapPath[i] = (int)finish1;
}
else
{
getReadyBitmapPath[i] = sin[(int)((i - 150) * 1.8f)];
getReadyBitmapPath[i] *= distance2;
getReadyBitmapPath[i] += finish1;
}
}
}
// Actualiza el tramo final de juego, una vez completado
void Game::updateGameCompleted()
{
if (gameCompleted)
{
gameCompletedCounter++;
}
if (gameCompletedCounter == GAME_COMPLETED_END)
{
// section::options = SUBSECTION_GAME_GAMEOVER;
section::name = section::NAME_TITLE;
section::options = section::OPTIONS_TITLE_1;
}
}
// Actualiza las variables de ayuda
void Game::updateHelper()
{
// Solo ofrece ayuda cuando la amenaza es elevada
if (menaceCurrent > 15)
{
for (auto player : players)
{
if (player->getCoffees() == 0)
{
helper.needCoffee = true;
}
else
{
helper.needCoffee = false;
}
if (!player->isPowerUp())
{
helper.needCoffeeMachine = true;
}
else
{
helper.needCoffeeMachine = false;
}
}
}
else
{
helper.needCoffee = false;
helper.needCoffeeMachine = false;
}
}
// Comprueba si todos los jugadores han terminado de jugar
bool Game::allPlayersAreWaiting()
{
bool success = true;
for (auto player : players)
{
success &= player->isWaiting();
}
return success;
}
// Comprueba los eventos que hay en cola
void Game::checkEvents()
{
while (SDL_PollEvent(eventHandler) != 0)
{
// Evento de salida de la aplicación
if (eventHandler->type == SDL_QUIT)
{
section::name = section::NAME_QUIT;
break;
}
else if (eventHandler->type == SDL_WINDOWEVENT)
{
switch (eventHandler->window.event)
{
case SDL_WINDOWEVENT_FOCUS_LOST:
if (!demo.enabled)
{
pause(true);
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
pause(false);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
reloadTextures();
break;
default:
break;
}
}
#ifdef DEBUG
else if (eventHandler->type == SDL_KEYDOWN)
{
switch (eventHandler->key.keysym.sym)
{
// CREA UN SPRITE DE 1000 PUNTOS
case SDLK_h:
createItemScoreSprite(param.game.width / 2, param.game.width / 2, n1000Sprite);
break;
// CREA UNA POWERBALL
case SDLK_1:
createPowerBall();
break;
// CREA DOS BALLON4
case SDLK_2:
{
const int set = 0;
const stage_t stage = enemyFormations->getStage(0);
const int numEnemies = stage.enemyPool->set[set]->numberOfEnemies;
for (int i = 0; i < numEnemies; ++i)
{
createBalloon(stage.enemyPool->set[set]->init[i].x,
stage.enemyPool->set[set]->init[i].y,
stage.enemyPool->set[set]->init[i].kind,
stage.enemyPool->set[set]->init[i].velX,
enemySpeed,
stage.enemyPool->set[set]->init[i].creationCounter);
}
}
break;
// ACTIVA EL MODO PARA PASAR EL JUEGO AUTOMATICAMENTE
case SDLK_3:
autoPopBalloons = !autoPopBalloons;
screen->showNotification("autoPopBalloons " + boolToString(autoPopBalloons));
break;
default:
break;
}
}
#endif
}
}
// Carga las animaciones
void Game::loadAnimations(std::string filePath, std::vector<std::string> *buffer)
{
std::ifstream file(filePath);
std::string line;
if (file)
{
if (options.console)
{
std::cout << "Animation loaded: " << filePath.substr(filePath.find_last_of("\\/") + 1).c_str() << std::endl;
}
while (std::getline(file, line))
{
buffer->push_back(line);
}
file.close();
}
}
// Elimina todos los objetos contenidos en vectores
void Game::deleteAllVectorObjects()
{
for (auto player : players)
{
delete player;
};
players.clear();
for (auto ballon : balloons)
{
delete ballon;
};
balloons.clear();
for (auto bullet : bullets)
{
delete bullet;
};
bullets.clear();
for (auto item : items)
{
delete item;
};
items.clear();
for (auto smartSprite : smartSprites)
{
delete smartSprite;
};
smartSprites.clear();
}
// Recarga las texturas
void Game::reloadTextures()
{
for (auto texture : itemTextures)
{
texture->reLoad();
}
for (auto texture : balloonTextures)
{
texture->reLoad();
}
for (auto texture : player1Textures)
{
texture->reLoad();
}
for (auto texture : player2Textures)
{
texture->reLoad();
}
bulletTexture->reLoad();
gameTextTexture->reLoad();
background->reloadTextures();
}
// Actualiza el marcador
void Game::updateScoreboard()
{
for (auto player : players)
{
scoreboard->setScore(player->getScoreBoardPanel(), player->getScore());
scoreboard->setMult(player->getScoreBoardPanel(), player->getScoreMultiplier());
}
// Resto de marcador
scoreboard->setStage(enemyFormations->getStage(currentStage).number);
scoreboard->setPower((float)currentPower / (float)enemyFormations->getStage(currentStage).powerToComplete);
scoreboard->setHiScore(hiScore.score);
scoreboard->setHiScoreName(hiScore.name);
// Lógica del marcador
scoreboard->update();
}
// Pausa el juego
void Game::pause(bool value)
{
paused = value;
screen->attenuate(paused);
}
// Añade una puntuación a la tabla de records
void Game::addScoreToScoreBoard(std::string name, int score)
{
const hiScoreEntry_t entry = {name, score};
ManageHiScoreTable *manager = new ManageHiScoreTable(&options.game.hiScoreTable);
manager->add(entry);
delete manager;
}
// Comprueba el estado de los jugadores
void Game::checkPlayersStatusPlaying()
{
if (demo.enabled)
{
return;
}
for (auto player : players)
{
switch (player->getStatusPlaying())
{
case PLAYER_STATUS_PLAYING:
scoreboard->setMode(player->getScoreBoardPanel(), SCOREBOARD_MODE_SCORE);
break;
case PLAYER_STATUS_CONTINUE:
scoreboard->setMode(player->getScoreBoardPanel(), SCOREBOARD_MODE_CONTINUE);
scoreboard->setContinue(player->getScoreBoardPanel(), player->getContinueCounter());
break;
case PLAYER_STATUS_WAITING:
scoreboard->setMode(player->getScoreBoardPanel(), SCOREBOARD_MODE_GAME_OVER);
break;
default:
break;
}
}
}
// Obtiene un jugador a partir de su "id"
Player *Game::getPlayer(int id)
{
for (auto player : players)
{
if (player->getId() == id)
{
return player;
}
}
return nullptr;
}
// Obtiene un controlador a partir del "id" del jugador
int Game::getController(int playerId)
{
for (int i = 0; i < (int)options.controller.size(); ++i)
{
if (options.controller[i].playerId == playerId)
{
return i;
}
}
return -1;
}
// Termina
void Game::quit(section::options_e code)
{
if (screen->notificationsAreActive())
{
section::name = section::NAME_QUIT;
section::options = code;
}
else
{
screen->showNotification(lang::getText(94));
}
}