#include "game.h" // Constructor Game::Game(int numPlayers, int currentStage, SDL_Renderer *renderer, Screen *screen, Asset *asset, Lang *lang, Input *input, bool demo, param_t *param, options_t *options, section_t *section) { // Copia los punteros this->renderer = renderer; this->screen = screen; this->asset = asset; this->lang = lang; this->input = input; this->param = param; this->options = options; this->section = section; // Pasa variables this->demo.enabled = demo; this->numPlayers = numPlayers; this->currentStage = currentStage; lastStageReached = currentStage; if (numPlayers == 1) { // Si solo juega un jugador, permite jugar tanto con teclado como con mando onePlayerControl = options->game.input[0].deviceType; options->game.input[0].deviceType = INPUT_USE_ANY; } difficulty = options->game.difficulty; // Crea los objetos fade = new Fade(renderer, param); eventHandler = new SDL_Event(); scoreboard = new Scoreboard(renderer, screen, asset, lang, options); background = new Background(renderer, screen, asset, param); // Carga los recursos loadMedia(); // Carga ficheros loadDemoFile(); // Establece la máxima puntuación desde fichero o desde las puntuaciones online setHiScore(); n1000Sprite = new SmartSprite(gameTextTexture, renderer); n2500Sprite = new SmartSprite(gameTextTexture, renderer); n5000Sprite = new SmartSprite(gameTextTexture, renderer); gameOverSprite = new Sprite(16, 80, 128, 96, gameOverTexture, renderer); gameOverEndSprite = new Sprite(PLAY_AREA_CENTER_X - gameOverEndTexture->getWidth() / 2, 80, 128, 96, gameOverEndTexture, renderer); canvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, playArea.w, playArea.h); SDL_SetTextureBlendMode(canvas, SDL_BLENDMODE_BLEND); // Inicializa las variables necesarias para la sección 'Game' init(); } Game::~Game() { saveScoreFile(); saveDemoFile(); // Restaura el metodo de control if (numPlayers == 1) { options->game.input[0].deviceType = onePlayerControl; } // Elimina todos los objetos contenidos en vectores deleteAllVectorObjects(); bulletTexture->unload(); delete bulletTexture; gameTextTexture->unload(); delete gameTextTexture; gameOverTexture->unload(); delete gameOverTexture; gameOverEndTexture->unload(); delete gameOverEndTexture; // Animaciones for (auto animation : playerAnimations) { delete animation; } playerAnimations.clear(); for (auto animation : balloonAnimations) { delete animation; } balloonAnimations.clear(); for (auto animation : itemAnimations) { delete animation; } itemAnimations.clear(); // Texturas for (auto texture : player1Textures) { texture->unload(); delete texture; } player1Textures.clear(); for (auto texture : player2Textures) { texture->unload(); delete texture; } player2Textures.clear(); for (auto texture : itemTextures) { texture->unload(); delete texture; } itemTextures.clear(); for (auto texture : balloonTextures) { texture->unload(); delete texture; } balloonTextures.clear(); delete scoreboard; delete background; delete text; delete textBig; delete textNokia2; delete textNokiaBig2; delete gameOverMenu; delete pauseMenu; delete fade; delete eventHandler; delete n1000Sprite; delete n2500Sprite; delete n5000Sprite; delete gameOverSprite; delete gameOverEndSprite; SDL_DestroyTexture(canvas); 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); JA_DeleteMusic(gameMusic); } // Inicializa las variables necesarias para la sección 'Game' void Game::init() { ticks = 0; ticksSpeed = 15; // Elimina qualquier jugador que hubiese antes de crear los nuevos for (auto player : players) { delete player; }; players.clear(); // Crea los jugadores if (numPlayers == 1) { Player *player = new Player(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, renderer, playerTextures[options->game.playerSelected], playerAnimations); players.push_back(player); } else if (numPlayers == 2) { Player *player1 = new Player((PLAY_AREA_CENTER_FIRST_QUARTER_X * ((0 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24, renderer, playerTextures[0], playerAnimations); Player *player2 = new Player((PLAY_AREA_CENTER_FIRST_QUARTER_X * ((1 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24, renderer, playerTextures[1], playerAnimations); players.push_back(player1); players.push_back(player2); } // 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; } // Colores pauseMenu->setSelectorColor(difficultyColor, 255); gameOverMenu->setSelectorColor(difficultyColor, 255); // Variables para el marcador scoreboard->setPos({param->scoreboard.x, param->scoreboard.y, param->scoreboard.w, param->scoreboard.h}); // Resto de variables gameCompleted = false; gameCompletedCounter = 0; section->name = SECTION_PROG_GAME; section->subsection = SUBSECTION_GAME_PLAY_1P; menaceCurrent = 0; menaceThreshold = 0; hiScoreAchieved = false; stageBitmapCounter = STAGE_COUNTER; deathCounter = DEATH_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; pauseCounter = 0; leavingPauseMenu = false; if (demo.enabled) { const int num = rand() % 2; if (num == 0) { balloonsPopped = 1000; currentStage = 3; } else { balloonsPopped = 1800; currentStage = 6; } } initPaths(); initEnemyFormations(); initEnemyPools(); initGameStages(); // Mas variables balloonsPopped = 0; for (int i = 0; i < currentStage; ++i) { balloonsPopped += stage[i].powerToComplete; } totalPowerToCompleteGame = 0; for (int i = 0; i < 10; ++i) { totalPowerToCompleteGame += stage[i].powerToComplete; } // Modo demo demo.recording = false; demo.counter = 0; // Inicializa el objeto para el fundido fade->setColor(0x27, 0x27, 0x36); // Con los globos creados, calcula el nivel de amenaza evaluateAndSetMenace(); // Inicializa el bitmap de 1000 puntos n1000Sprite->setPosX(0); n1000Sprite->setPosY(0); n1000Sprite->setWidth(26); n1000Sprite->setHeight(9); n1000Sprite->setVelX(0.0f); n1000Sprite->setVelY(-0.5f); n1000Sprite->setAccelX(0.0f); n1000Sprite->setAccelY(-0.1f); n1000Sprite->setSpriteClip(0, 0, 26, 9); 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(28); n2500Sprite->setHeight(9); n2500Sprite->setVelX(0.0f); n2500Sprite->setVelY(-0.5f); n2500Sprite->setAccelX(0.0f); n2500Sprite->setAccelY(-0.1f); n2500Sprite->setSpriteClip(26, 0, 28, 9); 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(28); n5000Sprite->setHeight(9); n5000Sprite->setVelX(0.0f); n5000Sprite->setVelY(-0.5f); n5000Sprite->setAccelX(0.0f); n5000Sprite->setAccelY(-0.1f); n5000Sprite->setSpriteClip(54, 0, 28, 9); 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; } // Texturas bulletTexture = new Texture(renderer, asset->get("bullet.png")); gameTextTexture = new Texture(renderer, asset->get("game_text.png")); gameOverTexture = new Texture(renderer, asset->get("menu_game_over.png")); gameOverEndTexture = new Texture(renderer, asset->get("menu_game_over_end.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); // 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 *player1Head = new Texture(renderer, asset->get("player_bal1_head.png")); player1Textures.push_back(player1Head); Texture *player1Body = new Texture(renderer, asset->get("player_bal1_body.png")); player1Textures.push_back(player1Body); Texture *player1Legs = new Texture(renderer, asset->get("player_bal1_legs.png")); player1Textures.push_back(player1Legs); Texture *player1Death = new Texture(renderer, asset->get("player_bal1_death.png")); player1Textures.push_back(player1Death); Texture *player1Fire = new Texture(renderer, asset->get("player_bal1_fire.png")); player1Textures.push_back(player1Fire); playerTextures.push_back(player1Textures); // Texturas - Player2 Texture *player2Head = new Texture(renderer, asset->get("player_arounder_head.png")); player2Textures.push_back(player2Head); Texture *player2Body = new Texture(renderer, asset->get("player_arounder_body.png")); player2Textures.push_back(player2Body); Texture *player2Legs = new Texture(renderer, asset->get("player_arounder_legs.png")); player2Textures.push_back(player2Legs); Texture *player2Death = new Texture(renderer, asset->get("player_arounder_death.png")); player2Textures.push_back(player2Death); Texture *player2Fire = new Texture(renderer, asset->get("player_arounder_fire.png")); player2Textures.push_back(player2Fire); playerTextures.push_back(player2Textures); // Animaciones -- Jugador std::vector *playerHeadAnimation = new std::vector; loadAnimations(asset->get("player_head.ani"), playerHeadAnimation); playerAnimations.push_back(playerHeadAnimation); std::vector *playerBodyAnimation = new std::vector; loadAnimations(asset->get("player_body.ani"), playerBodyAnimation); playerAnimations.push_back(playerBodyAnimation); std::vector *playerLegsAnimation = new std::vector; loadAnimations(asset->get("player_legs.ani"), playerLegsAnimation); playerAnimations.push_back(playerLegsAnimation); std::vector *playerDeathAnimation = new std::vector; loadAnimations(asset->get("player_death.ani"), playerDeathAnimation); playerAnimations.push_back(playerDeathAnimation); std::vector *playerFireAnimation = new std::vector; loadAnimations(asset->get("player_fire.ani"), playerFireAnimation); playerAnimations.push_back(playerFireAnimation); // Animaciones -- Globos std::vector *balloon1Animation = new std::vector; loadAnimations(asset->get("balloon1.ani"), balloon1Animation); balloonAnimations.push_back(balloon1Animation); std::vector *balloon2Animation = new std::vector; loadAnimations(asset->get("balloon2.ani"), balloon2Animation); balloonAnimations.push_back(balloon2Animation); std::vector *balloon3Animation = new std::vector; loadAnimations(asset->get("balloon3.ani"), balloon3Animation); balloonAnimations.push_back(balloon3Animation); std::vector *balloon4Animation = new std::vector; loadAnimations(asset->get("balloon4.ani"), balloon4Animation); balloonAnimations.push_back(balloon4Animation); // Animaciones -- Items std::vector *item1Animation = new std::vector; loadAnimations(asset->get("item_points1_disk.ani"), item1Animation); itemAnimations.push_back(item1Animation); std::vector *item2Animation = new std::vector; loadAnimations(asset->get("item_points2_gavina.ani"), item2Animation); itemAnimations.push_back(item2Animation); std::vector *item3Animation = new std::vector; loadAnimations(asset->get("item_points3_pacmar.ani"), item3Animation); itemAnimations.push_back(item3Animation); std::vector *item4Animation = new std::vector; loadAnimations(asset->get("item_clock.ani"), item4Animation); itemAnimations.push_back(item4Animation); std::vector *item5Animation = new std::vector; loadAnimations(asset->get("item_coffee.ani"), item5Animation); itemAnimations.push_back(item5Animation); std::vector *item6Animation = new std::vector; loadAnimations(asset->get("item_coffee_machine.ani"), item6Animation); itemAnimations.push_back(item6Animation); // Texto text = new Text(asset->get("smb2.png"), 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); // Menus gameOverMenu = new Menu(renderer, asset, input, asset->get("gameover.men")); gameOverMenu->setItemCaption(0, lang->getText(48)); gameOverMenu->setItemCaption(1, lang->getText(49)); const int w = text->getCharacterSize() * lang->getText(45).length(); gameOverMenu->setRectSize(w, 0); gameOverMenu->centerMenuOnX(199); pauseMenu = new Menu(renderer, asset, input, asset->get("pause.men")); pauseMenu->setItemCaption(0, lang->getText(41)); pauseMenu->setItemCaption(1, lang->getText(46)); pauseMenu->setItemCaption(2, lang->getText(47)); // 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()); // Musicas gameMusic = JA_LoadMusic(asset->get("playing.ogg").c_str()); if (options->console) { std::cout << "** RESOURCES FOR GAME SECTION LOADED" << std::endl << std::endl; } } // Carga el fichero de puntos bool Game::loadScoreFile() { // Indicador de éxito en la carga bool success = true; const std::string p = asset->get("score.bin"); const std::string filename = p.substr(p.find_last_of("\\/") + 1); SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); // El fichero no existe if (file == nullptr) { if (options->console) { std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl; } // Creamos el fichero para escritura file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != nullptr) { if (options->console) { std::cout << "New file (" << filename.c_str() << ") created!" << std::endl; } // Inicializamos los datos for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { scoreDataFile[i] = 0; SDL_RWwrite(file, &scoreDataFile[i], sizeof(Uint32), 1); } // Cerramos el fichero SDL_RWclose(file); } else { if (options->console) { std::cout << "Error: Unable to create file " << filename.c_str() << std::endl; } success = false; } } // El fichero existe else { // Cargamos los datos if (options->console) { std::cout << "Reading file " << filename.c_str() << std::endl; } for (int i = 0; i < TOTAL_SCORE_DATA; ++i) SDL_RWread(file, &scoreDataFile[i], sizeof(Uint32), 1); // Cierra el fichero SDL_RWclose(file); } // Establece el valor de la máxima puntuación a partir del vector con los datos if (scoreDataFile[0] == 0) { hiScore = 10000; } // Comprueba el checksum para ver si se ha modificado el fichero else if (scoreDataFile[0] % 43 == scoreDataFile[1]) { hiScore = scoreDataFile[0]; } else { hiScore = 10000; } return success; } // Carga el fichero de datos para la demo bool Game::loadDemoFile() { // Indicador de éxito en la carga bool success = true; const std::string p = asset->get("demo.bin"); const std::string filename = p.substr(p.find_last_of("\\/") + 1); SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); // El fichero no existe if (file == nullptr) { if (options->console) { std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl; } // Creamos el fichero para escritura file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != nullptr) { if (options->console) { std::cout << "New file (" << filename.c_str() << ") created!" << std::endl; } // Inicializamos los datos for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { demo.keys.left = 0; demo.keys.right = 0; demo.keys.noInput = 0; demo.keys.fire = 0; demo.keys.fireLeft = 0; demo.keys.fireRight = 0; demo.dataFile[i] = demo.keys; SDL_RWwrite(file, &demo.dataFile[i], sizeof(demoKeys_t), 1); } // Cerramos el fichero SDL_RWclose(file); } else { if (options->console) { std::cout << "Error: Unable to create file " << filename.c_str() << std::endl; } success = false; } } // El fichero existe else { // Cargamos los datos if (options->console) { std::cout << "Reading file " << filename.c_str() << std::endl; } for (int i = 0; i < TOTAL_DEMO_DATA; ++i) SDL_RWread(file, &demo.dataFile[i], sizeof(demoKeys_t), 1); // Cierra el fichero SDL_RWclose(file); } return success; } // Guarda el fichero de puntos bool Game::saveScoreFile() { bool success = true; const std::string p = asset->get("score.bin"); const std::string filename = p.substr(p.find_last_of("\\/") + 1); SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != nullptr) { // Guardamos los datos for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { SDL_RWwrite(file, &scoreDataFile[i], sizeof(Uint32), 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; } // Guarda el fichero de datos para la demo bool Game::saveDemoFile() { bool success = true; const std::string p = asset->get("demo.bin"); const std::string filename = p.substr(p.find_last_of("\\/") + 1); if (demo.recording) { SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != nullptr) { // Guardamos los datos for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { SDL_RWwrite(file, &demo.dataFile[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; } // Inicializa las formaciones enemigas void Game::initEnemyFormations() { const int y4 = (PLAY_AREA_TOP - BLOCK); const int x4_0 = PLAY_AREA_LEFT; const int x4_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_4; const int y3 = (PLAY_AREA_TOP - BLOCK); const int x3_0 = PLAY_AREA_LEFT; const int x3_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_3; const int y2 = (PLAY_AREA_TOP - BLOCK); const int x2_0 = PLAY_AREA_LEFT; const int x2_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_2; const int y1 = (PLAY_AREA_TOP - BLOCK); const int x1_0 = PLAY_AREA_LEFT; const int x1_50 = PLAY_AREA_CENTER_X - (BALLOON_WIDTH_1 / 2); const int x1_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_1; // Inicializa a cero las variables for (int i = 0; i < NUMBER_OF_ENEMY_FORMATIONS; i++) { enemyFormation[i].numberOfEnemies = 0; for (int j = 0; j < MAX_NUMBER_OF_ENEMIES_IN_A_FORMATION; j++) { enemyFormation[i].init[j].x = 0; enemyFormation[i].init[j].y = 0; enemyFormation[i].init[j].velX = 0; enemyFormation[i].init[j].kind = 0; enemyFormation[i].init[j].creationCounter = 0; } } const Uint16 creationTime = 300; int incX = 0; Uint8 incTime = 0; Uint8 j = 0; // #00 - Dos enemigos BALLOON4 uno a cada extremo j = 0; enemyFormation[j].numberOfEnemies = 2; incX = x4_100; incTime = 0; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x4_0 + (i * incX); enemyFormation[j].init[i].y = y4; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1); enemyFormation[j].init[i].kind = BALLOON_4; enemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); } // #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro j = 1; enemyFormation[j].numberOfEnemies = 2; incX = PLAY_AREA_CENTER_X; incTime = 0; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = PLAY_AREA_CENTER_FIRST_QUARTER_X - (BALLOON_WIDTH_4 / 2) + (i * incX); enemyFormation[j].init[i].y = y4; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1); enemyFormation[j].init[i].kind = BALLOON_4; enemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); } // #02 - Cuatro enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro j = 2; enemyFormation[j].numberOfEnemies = 4; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x2_0 + (i * incX); enemyFormation[j].init[i].y = y2; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_2; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #03 - Cuatro enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro j = 3; enemyFormation[j].numberOfEnemies = 4; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x2_100 - (i * incX); enemyFormation[j].init[i].y = y2; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_2; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha j = 4; enemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_0 + (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda j = 5; enemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_100 - (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha j = 6; enemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_0 + (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda j = 7; enemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_100 - (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha j = 8; enemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x1_0 + (i * incX); enemyFormation[j].init[i].y = 13; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_1; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda j = 9; enemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x1_100 - (i * incX); enemyFormation[j].init[i].y = 13; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_1; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #10 - Tres enemigos BALLOON4 seguidos desde la izquierda j = 10; enemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_4 + 1; incTime = 15; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x4_0 + (i * incX); enemyFormation[j].init[i].y = y4; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_4; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #11 - Tres enemigos BALLOON4 seguidos desde la derecha j = 11; enemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_4 + 1; incTime = 15; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x4_100 - (i * incX); enemyFormation[j].init[i].y = y4; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_4; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #12 - Seis enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro j = 12; enemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x2_0 + (i * incX); enemyFormation[j].init[i].y = y2; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_2; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #13 - Seis enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro j = 13; enemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x2_100 - (i * incX); enemyFormation[j].init[i].y = y2; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_2; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados j = 14; enemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_0 + (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados j = 15; enemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_100 - (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos j = 16; enemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_0 + (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos j = 17; enemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x3_100 - (i * incX); enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_3; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos j = 18; enemyFormation[j].numberOfEnemies = 12; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x1_0 + (i * incX); enemyFormation[j].init[i].y = y1; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].kind = BALLOON_1; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos j = 19; enemyFormation[j].numberOfEnemies = 12; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { enemyFormation[j].init[i].x = x1_100 - (i * incX); enemyFormation[j].init[i].y = y1; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].kind = BALLOON_1; enemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simetricos j = 20; enemyFormation[j].numberOfEnemies = 4; incX = BALLOON_WIDTH_4 + 1; incTime = 0; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { Uint8 half = enemyFormation[j].numberOfEnemies / 2; if (i < half) { enemyFormation[j].init[i].x = x4_0 + (i * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; } else { enemyFormation[j].init[i].x = x4_100 - ((i - half) * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; } enemyFormation[j].init[i].y = y4; enemyFormation[j].init[i].kind = BALLOON_4; enemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); } // #21 - Diez enemigos BALLOON2 uno detras del otro. Izquierda/derecha. Simetricos j = 21; enemyFormation[j].numberOfEnemies = 10; incX = BALLOON_WIDTH_2 + 1; incTime = 3; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { Uint8 half = enemyFormation[j].numberOfEnemies / 2; if (i < half) { enemyFormation[j].init[i].x = x2_0 + (i * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { enemyFormation[j].init[i].x = x2_100 - ((i - half) * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } enemyFormation[j].init[i].y = y2; enemyFormation[j].init[i].kind = BALLOON_2; } // #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simetricos j = 22; enemyFormation[j].numberOfEnemies = 10; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { Uint8 half = enemyFormation[j].numberOfEnemies / 2; if (i < half) { enemyFormation[j].init[i].x = x3_0 + (i * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { enemyFormation[j].init[i].x = x3_100 - ((i - half) * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].kind = BALLOON_3; } // #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simetricos j = 23; enemyFormation[j].numberOfEnemies = 10; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { Uint8 half = enemyFormation[j].numberOfEnemies / 2; if (i < half) { enemyFormation[j].init[i].x = x3_0 + (i * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { enemyFormation[j].init[i].x = x3_100 - ((i - half) * incX); enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } enemyFormation[j].init[i].y = y3; enemyFormation[j].init[i].kind = BALLOON_3; } // #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos j = 24; enemyFormation[j].numberOfEnemies = 30; incX = 0; incTime = 5; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { Uint8 half = enemyFormation[j].numberOfEnemies / 2; if (i < half) { enemyFormation[j].init[i].x = x1_50; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * i); } else { enemyFormation[j].init[i].x = x1_50; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * (i - half)); } enemyFormation[j].init[i].y = y1; enemyFormation[j].init[i].kind = BALLOON_1; } // #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos j = 25; enemyFormation[j].numberOfEnemies = 30; incX = BALLOON_WIDTH_1 + 1; incTime = 5; for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) { Uint8 half = enemyFormation[j].numberOfEnemies / 2; if (i < half) { enemyFormation[j].init[i].x = x1_50 + 20; enemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { enemyFormation[j].init[i].x = x1_50 - 20; enemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; enemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } enemyFormation[j].init[i].y = y1; enemyFormation[j].init[i].kind = BALLOON_1; } // Crea las mismas formaciones pero con hexagonos a partir de la posición 50 del vector for (int k = 0; k < j + 1; k++) { enemyFormation[k + 50].numberOfEnemies = enemyFormation[k].numberOfEnemies; for (int i = 0; i < enemyFormation[k + 50].numberOfEnemies; i++) { enemyFormation[k + 50].init[i].x = enemyFormation[k].init[i].x; enemyFormation[k + 50].init[i].y = enemyFormation[k].init[i].y; enemyFormation[k + 50].init[i].velX = enemyFormation[k].init[i].velX; enemyFormation[k + 50].init[i].creationCounter = enemyFormation[k].init[i].creationCounter; enemyFormation[k + 50].init[i].kind = enemyFormation[k].init[i].kind + 4; } } // TEST enemyFormation[99].numberOfEnemies = 4; enemyFormation[99].init[0].x = 10; enemyFormation[99].init[0].y = y1; enemyFormation[99].init[0].velX = 0; enemyFormation[99].init[0].kind = BALLOON_1; enemyFormation[99].init[0].creationCounter = 200; enemyFormation[99].init[1].x = 50; enemyFormation[99].init[1].y = y1; enemyFormation[99].init[1].velX = 0; enemyFormation[99].init[1].kind = BALLOON_2; enemyFormation[99].init[1].creationCounter = 200; enemyFormation[99].init[2].x = 90; enemyFormation[99].init[2].y = y1; enemyFormation[99].init[2].velX = 0; enemyFormation[99].init[2].kind = BALLOON_3; enemyFormation[99].init[2].creationCounter = 200; enemyFormation[99].init[3].x = 140; enemyFormation[99].init[3].y = y1; enemyFormation[99].init[3].velX = 0; enemyFormation[99].init[3].kind = BALLOON_4; enemyFormation[99].init[3].creationCounter = 200; } // Inicializa los conjuntos de formaciones void Game::initEnemyPools() { // EnemyPool #0 enemyPool[0].set[0] = &enemyFormation[0]; enemyPool[0].set[1] = &enemyFormation[1]; enemyPool[0].set[2] = &enemyFormation[2]; enemyPool[0].set[3] = &enemyFormation[3]; enemyPool[0].set[4] = &enemyFormation[4]; enemyPool[0].set[5] = &enemyFormation[5]; enemyPool[0].set[6] = &enemyFormation[6]; enemyPool[0].set[7] = &enemyFormation[7]; enemyPool[0].set[8] = &enemyFormation[8]; enemyPool[0].set[9] = &enemyFormation[9]; // EnemyPool #1 enemyPool[1].set[0] = &enemyFormation[10]; enemyPool[1].set[1] = &enemyFormation[11]; enemyPool[1].set[2] = &enemyFormation[12]; enemyPool[1].set[3] = &enemyFormation[13]; enemyPool[1].set[4] = &enemyFormation[14]; enemyPool[1].set[5] = &enemyFormation[15]; enemyPool[1].set[6] = &enemyFormation[16]; enemyPool[1].set[7] = &enemyFormation[17]; enemyPool[1].set[8] = &enemyFormation[18]; enemyPool[1].set[9] = &enemyFormation[19]; // EnemyPool #2 enemyPool[2].set[0] = &enemyFormation[0]; enemyPool[2].set[1] = &enemyFormation[1]; enemyPool[2].set[2] = &enemyFormation[2]; enemyPool[2].set[3] = &enemyFormation[3]; enemyPool[2].set[4] = &enemyFormation[4]; enemyPool[2].set[5] = &enemyFormation[55]; enemyPool[2].set[6] = &enemyFormation[56]; enemyPool[2].set[7] = &enemyFormation[57]; enemyPool[2].set[8] = &enemyFormation[58]; enemyPool[2].set[9] = &enemyFormation[59]; // EnemyPool #3 enemyPool[3].set[0] = &enemyFormation[50]; enemyPool[3].set[1] = &enemyFormation[51]; enemyPool[3].set[2] = &enemyFormation[52]; enemyPool[3].set[3] = &enemyFormation[53]; enemyPool[3].set[4] = &enemyFormation[54]; enemyPool[3].set[5] = &enemyFormation[5]; enemyPool[3].set[6] = &enemyFormation[6]; enemyPool[3].set[7] = &enemyFormation[7]; enemyPool[3].set[8] = &enemyFormation[8]; enemyPool[3].set[9] = &enemyFormation[9]; // EnemyPool #4 enemyPool[4].set[0] = &enemyFormation[60]; enemyPool[4].set[1] = &enemyFormation[61]; enemyPool[4].set[2] = &enemyFormation[62]; enemyPool[4].set[3] = &enemyFormation[63]; enemyPool[4].set[4] = &enemyFormation[64]; enemyPool[4].set[5] = &enemyFormation[65]; enemyPool[4].set[6] = &enemyFormation[66]; enemyPool[4].set[7] = &enemyFormation[67]; enemyPool[4].set[8] = &enemyFormation[68]; enemyPool[4].set[9] = &enemyFormation[69]; // EnemyPool #5 enemyPool[5].set[0] = &enemyFormation[10]; enemyPool[5].set[1] = &enemyFormation[61]; enemyPool[5].set[2] = &enemyFormation[12]; enemyPool[5].set[3] = &enemyFormation[63]; enemyPool[5].set[4] = &enemyFormation[14]; enemyPool[5].set[5] = &enemyFormation[65]; enemyPool[5].set[6] = &enemyFormation[16]; enemyPool[5].set[7] = &enemyFormation[67]; enemyPool[5].set[8] = &enemyFormation[18]; enemyPool[5].set[9] = &enemyFormation[69]; // EnemyPool #6 enemyPool[6].set[0] = &enemyFormation[60]; enemyPool[6].set[1] = &enemyFormation[11]; enemyPool[6].set[2] = &enemyFormation[62]; enemyPool[6].set[3] = &enemyFormation[13]; enemyPool[6].set[4] = &enemyFormation[64]; enemyPool[6].set[5] = &enemyFormation[15]; enemyPool[6].set[6] = &enemyFormation[66]; enemyPool[6].set[7] = &enemyFormation[17]; enemyPool[6].set[8] = &enemyFormation[68]; enemyPool[6].set[9] = &enemyFormation[19]; // EnemyPool #7 enemyPool[7].set[0] = &enemyFormation[20]; enemyPool[7].set[1] = &enemyFormation[21]; enemyPool[7].set[2] = &enemyFormation[22]; enemyPool[7].set[3] = &enemyFormation[23]; enemyPool[7].set[4] = &enemyFormation[24]; enemyPool[7].set[5] = &enemyFormation[65]; enemyPool[7].set[6] = &enemyFormation[66]; enemyPool[7].set[7] = &enemyFormation[67]; enemyPool[7].set[8] = &enemyFormation[68]; enemyPool[7].set[9] = &enemyFormation[69]; // EnemyPool #8 enemyPool[8].set[0] = &enemyFormation[70]; enemyPool[8].set[1] = &enemyFormation[71]; enemyPool[8].set[2] = &enemyFormation[72]; enemyPool[8].set[3] = &enemyFormation[73]; enemyPool[8].set[4] = &enemyFormation[74]; enemyPool[8].set[5] = &enemyFormation[15]; enemyPool[8].set[6] = &enemyFormation[16]; enemyPool[8].set[7] = &enemyFormation[17]; enemyPool[8].set[8] = &enemyFormation[18]; enemyPool[8].set[9] = &enemyFormation[19]; // EnemyPool #9 enemyPool[9].set[0] = &enemyFormation[20]; enemyPool[9].set[1] = &enemyFormation[21]; enemyPool[9].set[2] = &enemyFormation[22]; enemyPool[9].set[3] = &enemyFormation[23]; enemyPool[9].set[4] = &enemyFormation[24]; enemyPool[9].set[5] = &enemyFormation[70]; enemyPool[9].set[6] = &enemyFormation[71]; enemyPool[9].set[7] = &enemyFormation[72]; enemyPool[9].set[8] = &enemyFormation[73]; enemyPool[9].set[9] = &enemyFormation[74]; } // Inicializa las fases del juego void Game::initGameStages() { // STAGE 1 stage[0].number = 1; stage[0].currentPower = 0; stage[0].powerToComplete = 200; stage[0].minMenace = 7 + (4 * 1); stage[0].maxMenace = 7 + (4 * 3); stage[0].enemyPool = &enemyPool[0]; // STAGE 2 stage[1].number = 2; stage[1].currentPower = 0; stage[1].powerToComplete = 300; stage[1].minMenace = 7 + (4 * 2); stage[1].maxMenace = 7 + (4 * 4); stage[1].enemyPool = &enemyPool[1]; // STAGE 3 stage[2].number = 3; stage[2].currentPower = 0; stage[2].powerToComplete = 600; stage[2].minMenace = 7 + (4 * 3); stage[2].maxMenace = 7 + (4 * 5); stage[2].enemyPool = &enemyPool[2]; // STAGE 4 stage[3].number = 4; stage[3].currentPower = 0; stage[3].powerToComplete = 600; stage[3].minMenace = 7 + (4 * 3); stage[3].maxMenace = 7 + (4 * 5); stage[3].enemyPool = &enemyPool[3]; // STAGE 5 stage[4].number = 5; stage[4].currentPower = 0; stage[4].powerToComplete = 600; stage[4].minMenace = 7 + (4 * 4); stage[4].maxMenace = 7 + (4 * 6); stage[4].enemyPool = &enemyPool[4]; // STAGE 6 stage[5].number = 6; stage[5].currentPower = 0; stage[5].powerToComplete = 600; stage[5].minMenace = 7 + (4 * 4); stage[5].maxMenace = 7 + (4 * 6); stage[5].enemyPool = &enemyPool[5]; // STAGE 7 stage[6].number = 7; stage[6].currentPower = 0; stage[6].powerToComplete = 650; stage[6].minMenace = 7 + (4 * 5); stage[6].maxMenace = 7 + (4 * 7); stage[6].enemyPool = &enemyPool[6]; // STAGE 8 stage[7].number = 8; stage[7].currentPower = 0; stage[7].powerToComplete = 750; stage[7].minMenace = 7 + (4 * 5); stage[7].maxMenace = 7 + (4 * 7); stage[7].enemyPool = &enemyPool[7]; // STAGE 9 stage[8].number = 9; stage[8].currentPower = 0; stage[8].powerToComplete = 850; stage[8].minMenace = 7 + (4 * 6); stage[8].maxMenace = 7 + (4 * 8); stage[8].enemyPool = &enemyPool[8]; // STAGE 10 stage[9].number = 10; stage[9].currentPower = 0; stage[9].powerToComplete = 950; stage[9].minMenace = 7 + (4 * 7); stage[9].maxMenace = 7 + (4 * 10); stage[9].enemyPool = &enemyPool[9]; } // 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 Uint8 set = (rand() % 10); // Evita repetir la ultima formación enemiga desplegada if (set == lastEnemyDeploy) ++set %= 10; lastEnemyDeploy = set; const Uint8 numEnemies = stage[currentStage].enemyPool->set[set]->numberOfEnemies; for (int i = 0; i < numEnemies; ++i) { createBalloon(stage[currentStage].enemyPool->set[set]->init[i].x, stage[currentStage].enemyPool->set[set]->init[i].y, stage[currentStage].enemyPool->set[set]->init[i].kind, stage[currentStage].enemyPool->set[set]->init[i].velX, enemySpeed, stage[currentStage].enemyPool->set[set]->init[i].creationCounter); } enemyDeployCounter = 300; } } } // Aumenta el poder de la fase void Game::increaseStageCurrentPower(Uint8 power) { stage[currentStage].currentPower += power; } // Establece el valor de la variable void Game::setHiScore(Uint32 score) { hiScore = score; } // 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) { // Actualiza la máxima puntuación hiScore = player->getScore(); // Almacena la máxima puntuación en el fichero junto con un checksum scoreDataFile[0] = hiScore; scoreDataFile[1] = hiScore % 43; // 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(); // Comprueba la colisión entre el jugador y los globos if (checkPlayerBalloonCollision(player)) { if (player->isAlive()) { if (demo.enabled) { section->name = SECTION_PROG_HI_SCORE_TABLE; } else { killPlayer(player); } } } // Comprueba las colisiones entre el jugador y los items checkPlayerItemCollision(player); } } // Dibuja a los jugadores void Game::renderPlayers() { for (auto player : players) { player->render(); } } // Actualiza las variables de la fase void Game::updateStage() { if (stage[currentStage].currentPower >= stage[currentStage].powerToComplete) { // Cambio de fase currentStage++; 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 stage[currentStage].currentPower = 0; // Deja el poder a cero para que no vuelva a entrar en esta condición destroyAllBalloons(); // Destruye a todos los enemigos stage[currentStage].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->isAlive()) { 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 muerte void Game::updateDeath() { // Comprueba si todos los jugadores estan muertos bool allPlayersAreDead = true; for (auto player : players) { allPlayersAreDead &= (!player->isAlive()); } if (allPlayersAreDead) { if (deathCounter > 0) { deathCounter--; if ((deathCounter == 250) || (deathCounter == 200) || (deathCounter == 180) || (deathCounter == 120) || (deathCounter == 60)) { // Hace sonar aleatoriamente uno de los 4 sonidos de burbujas const Uint8 index = rand() % 4; JA_Sound_t *sound[4] = {bubble1Sound, bubble2Sound, bubble3Sound, bubble4Sound}; JA_PlaySound(sound[index], 0); } } else { section->subsection = SUBSECTION_GAME_GAMEOVER; } } } // Renderiza el fade final cuando se acaba la partida void Game::renderDeathFade(int counter) { // Counter debe ir de 0 a 150 SDL_SetRenderDrawColor(renderer, 0x27, 0x27, 0x36, 255); const int desp = 16; const int max = param->gameHeight / desp; if (counter < 150) { // 192 / 6 = 32, 6 cuadrados de 32 pixeles SDL_Rect rect[max]; Uint8 h = counter / 3; for (int i = 0; i < max; ++i) { rect[i].x = 0; rect[i].y = i * desp; rect[i].w = param->gameWidth; if (i == 0) { rect[i].h = h; } else { rect[i].h = std::max(rect[i - 1].h - 3, 0); } SDL_RenderFillRect(renderer, &rect[i]); } } else { SDL_Rect rect = {0, 0, param->gameWidth, param->gameHeight}; SDL_RenderFillRect(renderer, &rect); } } // 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 Uint8 Game::createBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 creationtimer) { const int index = (kind - 1) % 4; Balloon *b = new Balloon(x, y, kind, velx, speed, creationtimer, balloonTextures[index], balloonAnimations[index], renderer); balloons.push_back(b); return (Uint8)(balloons.size() - 1); } // Crea una PowerBall void Game::createPowerBall() { const int posY = PLAY_AREA_TOP; const int left = PLAY_AREA_LEFT; const int center = PLAY_AREA_CENTER_X - (BALLOON_WIDTH_4 / 2); const int right = PLAY_AREA_RIGHT - BALLOON_WIDTH_4; const int luck = rand() % 3; const int x[3] = {left, center, right}; const float vx[3] = {BALLOON_VELX_POSITIVE, BALLOON_VELX_POSITIVE, BALLOON_VELX_NEGATIVE}; Balloon *b = new Balloon(x[luck], posY, POWER_BALL, vx[luck], enemySpeed, 100, balloonTextures[3], balloonAnimations[3], renderer); 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)stage[currentStage].currentPower / (float)stage[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 Uint8 kind = balloon->getKind(); switch (kind) { // Si es del tipo más pequeño, simplemente elimina el globo case BALLOON_1: balloon->pop(); break; case HEXAGON_1: balloon->pop(); break; // Si es del tipo PowerBall, destruye todos los globos case POWER_BALL: destroyAllBalloons(); powerBallEnabled = false; enemyDeployCounter = 20; break; // En cualquier otro caso, crea dos globos de un tipo inferior default: 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 balloon->pop(); break; } // Recalcula el nivel de amenaza evaluateAndSetMenace(); } // Explosiona un globo. Lo destruye void Game::destroyBalloon(Balloon *balloon) { int score = 0; Uint8 power = 0; // Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos switch (balloon->getSize()) { case BALLOON_SIZE_4: score = BALLOON_SCORE_4 + (2 * BALLOON_SCORE_3) + (4 * BALLOON_SCORE_2) + (8 * BALLOON_SCORE_1); power = 15; break; case BALLOON_SIZE_3: score = BALLOON_SCORE_3 + (2 * BALLOON_SCORE_2) + (4 * BALLOON_SCORE_1); power = 7; break; case BALLOON_SIZE_2: score = BALLOON_SCORE_2 + (2 * BALLOON_SCORE_1); power = 3; break; case BALLOON_SIZE_1: score = BALLOON_SCORE_1; power = 1; break; default: score = 0; power = 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 increaseStageCurrentPower(power); balloonsPopped += power; // Destruye el globo balloon->pop(); // Recalcula el nivel de amenaza evaluateAndSetMenace(); } // Explosiona todos los globos void Game::popAllBalloons() { for (auto balloon : balloons) { if ((balloon->isEnabled()) && (!balloon->isPopping()) && (!balloon->isBeingCreated())) { popBalloon(balloon); } } JA_PlaySound(balloonSound); } // Destruye todos los globos void Game::destroyAllBalloons() { for (auto balloon : balloons) { if ((balloon->isEnabled()) && (!balloon->isPopping())) { destroyBalloon(balloon); } } enemyDeployCounter = 255; JA_PlaySound(powerBallSound); screen->flash(flashColor, 5); screen->shake(); } // Detiene todos los globos void Game::stopAllBalloons(Uint16 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 numero de globos activos Uint8 Game::countBalloons() { Uint8 num = 0; for (auto balloon : balloons) { if (balloon->isEnabled()) { if (!balloon->isPopping()) { num++; } } } return num; } // Vacia el vector de globos 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->isAlive()) { 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(true); 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 int index = bullet->getOwner(); players[index]->incScoreMultiplier(); players[index]->addScore(Uint32(balloon->getScore() * players[index]->getScoreMultiplier() * difficultyScoreMultiplier)); updateHiScore(); // Explota el globo popBalloon(balloon); // Si no es el modo demo, genera un sonido if (!demo.enabled) { JA_PlaySound(balloonSound); } // Deshabilita la bala bullet->disable(); // Suelta el item en caso de que salga uno const Uint8 droppeditem = dropItem(); if ((droppeditem != NO_KIND) && !(demo.enabled) && !(demo.recording)) { if (droppeditem != ITEM_COFFEE_MACHINE) { createItem(droppeditem, balloon->getPosX(), balloon->getPosY()); JA_PlaySound(itemDropSound); } else { createItem(droppeditem, players[index]->getPosX(), 0); coffeeMachineEnabled = true; } } break; } } } } } // Mueve las balas activas void Game::moveBullets() { for (auto bullet : bullets) { if (bullet->isEnabled()) { if (bullet->move() == BULLET_MOVE_OUT) { players[bullet->getOwner()]->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, Uint8 kind, bool poweredUp, int owner) { Bullet *b = new Bullet(x, y, kind, poweredUp, owner, bulletTexture, renderer); 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 en función del azar Uint8 Game::dropItem() { const Uint8 luckyNumber = rand() % 100; const Uint8 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 NO_KIND; } // Crea un objeto item void Game::createItem(Uint8 kind, float x, float y) { Item *item = new Item(kind, x, y, itemTextures[kind], itemAnimations[kind], renderer); 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, renderer); smartSprites.push_back(ss); // Crea una copia del objeto *ss = *sprite; ss->setPosX(x); ss->setPosY(y); ss->setDestX(x); ss->setDestY(y - 15); 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], renderer); smartSprites.push_back(ss); ss->setPosX(x - 8); ss->setPosY(y - 8); ss->setWidth(16); ss->setHeight(16); 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->gameHeight + 1); ss->setEnabled(true); ss->setEnabledCounter(1); ss->setSpriteClip(0, 0, 16, 16); 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->isInvulnerable()) { if (player->hasExtraHit()) { player->removeExtraHit(); throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2)); JA_PlaySound(coffeeOutSound); screen->shake(); } else { JA_PauseMusic(); stopAllBalloons(10); JA_PlaySound(playerCollisionSound); screen->shake(); JA_PlaySound(coffeeOutSound); player->setAlive(false); if (allPlayersAreDead()) { JA_StopMusic(); } else { JA_ResumeMusic(); } } } } // 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 Uint8 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(Uint16 value) { timeStoppedCounter = value; } // Incrementa el valor de la variable void Game::incTimeStoppedCounter(Uint16 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++; // Actualiza el objeto screen screen->update(); // Comprueba el teclado/mando checkGameInput(); // Actualiza las variables del jugador updatePlayers(); // Actualiza el marcador updateScoreboard(); // Actualiza el fondo updateBackground(); // Mueve los globos updateBalloons(); // Mueve las balas moveBullets(); // Actualiza los items updateItems(); // Actualiza el valor de currentStage updateStage(); // Actualiza el estado de muerte updateDeath(); // 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(); } } // 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 speed = (-0.2f) + (-3.00f * ((float)balloonsPopped / (float)totalPowerToCompleteGame)); // Aplica la velocidad calculada para las nubes background->setCloudsSpeed(speed); 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 la linea que separa la zona ade juego del marcador void Game::renderSeparator() { // Dibuja la linea que separa el marcador de la zona de juego SDL_SetRenderDrawColor(renderer, separator.r, separator.g, separator.b, 255); SDL_RenderDrawLine(renderer, SCOREBOARD_X, SCOREBOARD_Y, SCOREBOARD_X + SCOREBOARD_WIDTH, SCOREBOARD_Y); } // 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(); renderBalloons(); renderBullets(); renderMessages(); renderPlayers(); // Deja el renderizador apuntando donde estaba SDL_SetRenderTarget(renderer, temp); } // Dibuja el juego void Game::render() { // Dibuja los graficos de la zona de juego en la textura fillCanvas(); // 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, &playArea); // Dibuja el marcador scoreboard->render(); // Dibuja el separador del marcador de la zona de juego renderSeparator(); // Dibuja el sprite del jugador al morir if ((deathCounter <= 150) && !players[0]->isAlive()) { renderDeathFade(150 - deathCounter); } // Dibuja el fade de fin de juego if ((gameCompleted) && (gameCompletedCounter >= GAME_COMPLETED_START_FADE)) { renderDeathFade(gameCompletedCounter - GAME_COMPLETED_START_FADE); } // Vuelca el contenido del renderizador en pantalla screen->blit(); } // Gestiona el nivel de amenaza void Game::updateMenace() { if (gameCompleted) { return; } const float percent = stage[currentStage].currentPower / stage[currentStage].powerToComplete; const Uint8 difference = stage[currentStage].maxMenace - stage[currentStage].minMenace; // Aumenta el nivel de amenaza en función de la puntuación menaceThreshold = stage[currentStage].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::checkGameInput() { demo.keys.left = 0; demo.keys.right = 0; demo.keys.noInput = 0; demo.keys.fire = 0; demo.keys.fireLeft = 0; demo.keys.fireRight = 0; // Comprueba las teclas de cambiar el tamaño de la centana y el modo de video if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) { screen->switchVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) { screen->decWindowSize(); } else if (input->checkInput(input_window_inc_size, REPEAT_FALSE)) { screen->incWindowSize(); } else if (input->checkInput(input_video_shaders, REPEAT_FALSE)) { screen->switchShaders(); } // Modo Demo activo if (demo.enabled) { const int index = 0; if (demo.dataFile[demo.counter].left == 1) { players[index]->setInput(input_left); } if (demo.dataFile[demo.counter].right == 1) { players[index]->setInput(input_right); } if (demo.dataFile[demo.counter].noInput == 1) { players[index]->setInput(input_null); } if (demo.dataFile[demo.counter].fire == 1) { if (players[index]->canFire()) { players[index]->setInput(input_fire_center); createBullet(players[index]->getPosX() + (players[index]->getWidth() / 2) - 4, players[index]->getPosY() + (players[index]->getHeight() / 2), BULLET_UP, players[index]->isPowerUp(), index); players[index]->setFireCooldown(10); } } if (demo.dataFile[demo.counter].fireLeft == 1) { if (players[index]->canFire()) { players[index]->setInput(input_fire_left); createBullet(players[index]->getPosX() + (players[index]->getWidth() / 2) - 4, players[index]->getPosY() + (players[index]->getHeight() / 2), BULLET_UP, players[index]->isPowerUp(), index); players[index]->setFireCooldown(10); } } if (demo.dataFile[demo.counter].fireRight == 1) { if (players[index]->canFire()) { players[index]->setInput(input_fire_right); createBullet(players[index]->getPosX() + (players[index]->getWidth() / 2) - 4, players[index]->getPosY() + (players[index]->getHeight() / 2), BULLET_UP, players[index]->isPowerUp(), index); players[index]->setFireCooldown(10); } } // Si pulsamos la tecla de salir, se acaba el programa if (input->checkInput(input_exit)) { section->name = SECTION_PROG_QUIT; } // Si se pulsa cualquier tecla, se sale del modo demo else if (input->checkAnyInput()) { section->name = SECTION_PROG_TITLE; } // Incrementa el contador de la demo if (demo.counter < TOTAL_DEMO_DATA) { demo.counter++; } else { section->name = SECTION_PROG_HI_SCORE_TABLE; } } // Modo Demo no activo else { int i = 0; for (auto player : players) { if (player->isAlive()) { // Input a la izquierda if (input->checkInput(input_left, REPEAT_TRUE, options->game.input[i].deviceType, options->game.input[i].id)) { player->setInput(input_left); demo.keys.left = 1; } else { // Input a la derecha if (input->checkInput(input_right, REPEAT_TRUE, options->game.input[i].deviceType, options->game.input[i].id)) { player->setInput(input_right); demo.keys.right = 1; } else { // Ninguno de los dos inputs anteriores player->setInput(input_null); demo.keys.noInput = 1; } } // Comprueba el input de disparar al centro if (input->checkInput(input_fire_center, REPEAT_TRUE, options->game.input[i].deviceType, options->game.input[i].id)) { if (player->canFire()) { player->setInput(input_fire_center); createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_UP, player->isPowerUp(), i); player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(bulletSound); demo.keys.fire = 1; } } // Comprueba el input de disparar a la izquierda if (input->checkInput(input_fire_left, REPEAT_TRUE, options->game.input[i].deviceType, options->game.input[i].id)) { if (player->canFire()) { player->setInput(input_fire_left); createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_LEFT, player->isPowerUp(), i); player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(bulletSound); demo.keys.fireLeft = 1; } } // Comprueba el input de disparar a la derecha if (input->checkInput(input_fire_right, REPEAT_TRUE, options->game.input[i].deviceType, options->game.input[i].id)) { if (player->canFire()) { player->setInput(input_fire_right); createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_RIGHT, player->isPowerUp(), i); player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(bulletSound); demo.keys.fireRight = 1; } } // Comprueba el input de pausa if (input->checkInput(input_cancel, REPEAT_FALSE, options->game.input[i].deviceType, options->game.input[i].id)) { section->subsection = SUBSECTION_GAME_PAUSE; } if (demo.counter < TOTAL_DEMO_DATA) { if (demo.recording) { demo.dataFile[demo.counter] = demo.keys; } demo.counter++; } else if (demo.recording) { section->name = SECTION_PROG_QUIT; } i++; } else { i++; } } } } // Pinta diferentes mensajes en la pantalla void Game::renderMessages() { // GetReady if ((counter < STAGE_COUNTER) && (!demo.enabled)) { textNokiaBig2->write((int)getReadyBitmapPath[counter], PLAY_AREA_CENTER_Y - 8, lang->getText(75), -2); } // Time Stopped if (timeStopped) { if ((timeStoppedCounter > 100) || (timeStoppedCounter % 10 > 4)) { textNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, lang->getText(36) + std::to_string(timeStoppedCounter / 10), -1, noColor, 1, shdwTxtColor); } if (timeStoppedCounter > 100) { if (timeStoppedCounter % 30 == 0) { JA_PlaySound(clockSound, false); } } else { if (timeStoppedCounter % 15 == 0) { JA_PlaySound(clockSound, false); } } } // D E M O if (demo.enabled) { if (demo.counter % 30 > 14) { textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, lang->getText(37), 0, noColor, 2, shdwTxtColor); } } // STAGE NUMBER if (stageBitmapCounter < STAGE_COUNTER) { const int stageNum = stage[currentStage].number; std::string text; if (stageNum == 10) { // Ultima fase text = lang->getText(79); } else { // X fases restantes text = std::to_string(11 - stage[currentStage].number) + lang->getText(38); } if (!gameCompleted) { // Escribe el numero de fases restantes textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter], text, -2, noColor, 2, shdwTxtColor); } else { // Escribe el texto de juego completado text = lang->getText(50); textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter], text, -2, noColor, 1, shdwTxtColor); textNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, 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) { 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) { JA_ResumeMusic(); } } // Bucle para el juego void Game::run() { while (section->name == SECTION_PROG_GAME) { // Sección juego en pausa if (section->subsection == SUBSECTION_GAME_PAUSE) { runPausedGame(); } // Sección Game Over if (section->subsection == SUBSECTION_GAME_GAMEOVER) { runGameOverScreen(); } // Sección juego jugando if ((section->subsection == SUBSECTION_GAME_PLAY_1P) || (section->subsection == SUBSECTION_GAME_PLAY_2P)) { // Si la música no está sonando if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { // Reproduce la música if (!gameCompleted) { if (players[0]->isAlive()) { JA_PlayMusic(gameMusic); } } } // Actualiza la lógica del juego update(); // Comprueba los eventos que hay en cola checkEvents(); // Dibuja los objetos render(); } } } // Actualiza las variables del menu de pausa del juego void Game::updatePausedGame() { // Calcula la lógica de los objetos if (SDL_GetTicks() - ticks > ticksSpeed) { // Actualiza el contador de ticks ticks = SDL_GetTicks(); if (leavingPauseMenu) { if (pauseCounter > 0) { // El contador está descendiendo const bool a = pauseCounter == 90; const bool b = pauseCounter == 60; const bool c = pauseCounter == 30; if (a || b || c) { JA_PlaySound(clockSound); } pauseCounter--; } else { // Ha finalizado el contador section->name = SECTION_PROG_GAME; section->subsection = numPlayers == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P; if (JA_GetMusicState() == JA_MUSIC_PAUSED) { JA_ResumeMusic(); } } } else { // Actualiza la lógica del menu de pausa pauseMenu->update(); // Comprueba las entradas para el menu pauseMenu->checkInput(); // Comprueba si se ha seleccionado algún item del menú switch (pauseMenu->getItemSelected()) { case 1: leavingPauseMenu = true; break; case 2: fade->setType(FADE_CENTER); fade->activate(); break; default: break; } // Actualiza el fade fade->update(); if (fade->hasEnded()) { section->name = SECTION_PROG_TITLE; section->subsection = SUBSECTION_TITLE_1; JA_StopMusic(); } } } } // Dibuja el menu de pausa del juego void Game::renderPausedGame() { // Prepara para empezar a dibujar en la textura de juego screen->start(); // Limpia la pantalla screen->clean(bgColor); // Pinta el escenario fillCanvas(); SDL_RenderCopy(renderer, canvas, nullptr, &playArea); if ((deathCounter <= 150) && !players[0]->isAlive()) { renderDeathFade(150 - deathCounter); } if ((gameCompleted) && (gameCompletedCounter >= GAME_COMPLETED_START_FADE)) { renderDeathFade(gameCompletedCounter - GAME_COMPLETED_START_FADE); } if (leavingPauseMenu) { textNokiaBig2->writeCentered(GAMECANVAS_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, std::to_string((pauseCounter / 30) + 1)); } else { pauseMenu->render(); } fade->render(); // Vuelca el contenido del renderizador en pantalla screen->blit(); } // Bucle para el menu de pausa del juego void Game::runPausedGame() { // Pone en pausa la música if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); } // Reinicia el menu pauseMenu->reset(); leavingPauseMenu = false; // Inicializa variables pauseCounter = 90; while ((section->subsection == SUBSECTION_GAME_PAUSE) && (section->name == SECTION_PROG_GAME)) { updatePausedGame(); checkEvents(); renderPausedGame(); } } // Actualiza los elementos de la pantalla de game over void Game::updateGameOverScreen() { // Variables static int postFade = 0; // Calcula la lógica de los objetos if (SDL_GetTicks() - ticks > ticksSpeed) { // Actualiza el contador de ticks ticks = SDL_GetTicks(); // Actualiza la lógica del menu gameOverMenu->update(); // Actualiza el fade fade->update(); // Si ha terminado el fade, actua segun se haya operado if (fade->hasEnded()) { switch (postFade) { case 0: // YES section->name = SECTION_PROG_GAME; deleteAllVectorObjects(); init(); section->subsection = numPlayers == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P; break; case 1: // NO section->name = SECTION_PROG_TITLE; section->subsection = SUBSECTION_TITLE_1; break; default: break; } } // Comprueba las entradas para el menu solo si no esta el juego completo if (!gameCompleted) { gameOverMenu->checkInput(); // Comprueba si se ha seleccionado algún item del menú switch (gameOverMenu->getItemSelected()) { case 0: // YES postFade = 0; fade->activate(); break; case 1: // NO postFade = 1; fade->activate(); break; default: break; } } } // 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) { section->name = SECTION_PROG_QUIT; break; } else if (eventHandler->type == SDL_KEYDOWN && eventHandler->key.repeat == 0) { if (gameCompleted) { postFade = 1; fade->activate(); JA_PlaySound(itemPickUpSound); } } } } // Dibuja los elementos de la pantalla de game over void Game::renderGameOverScreen() { // Prepara para empezar a dibujar en la textura de juego screen->start(); // Limpia la pantalla screen->clean(bgColor); // Dibujo if (!gameCompleted) { // Dibujo de haber perdido la partida gameOverSprite->render(); } else { // Dinujo de haber completado la partida gameOverEndSprite->render(); } // Dibuja los objetos if (numPlayers == 1) { // Congratulations!! if (gameCompleted) { text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 8), lang->getText(50)); } // Game Over textBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 6), lang->getText(43)); // Your Score text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 3), lang->getText(44) + std::to_string(players[0]->getScore())); } else { // Congratulations!! if (gameCompleted) { text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 9), lang->getText(50)); } // Game Over textBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 7), lang->getText(43)); // Player1 Score text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), lang->getText(77) + std::to_string(players[0]->getScore())); // Player2 Score text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 2), lang->getText(78) + std::to_string(players[1]->getScore())); } // Continue? if (!gameCompleted) { // Solo dibuja el menu de continuar en el caso de no haber completado la partida text->writeCentered(199, PLAY_AREA_CENTER_Y + BLOCK * 3, lang->getText(45)); gameOverMenu->render(); } // Pinta el fade fade->render(); // Vuelca el contenido del renderizador en pantalla screen->blit(); } // Bucle para la pantalla de game over void Game::runGameOverScreen() { // Guarda los puntos saveScoreFile(); // Reinicia el menu gameOverMenu->reset(); while ((section->subsection == SUBSECTION_GAME_GAMEOVER) && (section->name == SECTION_PROG_GAME)) { updateGameOverScreen(); renderGameOverScreen(); } } // 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 = PLAY_AREA_CENTER_Y - (BLOCK * 2); const int distance = (PLAY_AREA_BOTTOM) - (PLAY_AREA_CENTER_Y - 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 = PLAY_AREA_LEFT - size; const float finish1 = PLAY_AREA_CENTER_X - (size / 2); const float start2 = finish1; const float finish2 = PLAY_AREA_RIGHT; 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->subsection = SUBSECTION_GAME_GAMEOVER; } } // 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 muerto bool Game::allPlayersAreDead() { bool success = true; for (auto player : players) { success &= (!player->isAlive()); } 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_PROG_QUIT; break; } else if (eventHandler->type == SDL_WINDOWEVENT) { if (eventHandler->window.event == SDL_WINDOWEVENT_FOCUS_LOST) { section->subsection = SUBSECTION_GAME_PAUSE; } } } } // Carga las animaciones void Game::loadAnimations(std::string filePath, std::vector *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(); } // Establece la máxima puntuación desde fichero o desde las puntuaciones online void Game::setHiScore() { // Carga el fichero de puntos loadScoreFile(); hiScoreName = ""; } // Actualiza el marcador void Game::updateScoreboard() { scoreboard->setScore1(players[0]->getScore()); scoreboard->setMult1(players[0]->getScoreMultiplier()); scoreboard->setStage(stage[currentStage].number); scoreboard->setPower((float)stage[currentStage].currentPower / (float)stage[currentStage].powerToComplete); scoreboard->setHiScore(hiScore); scoreboard->setHiScoreName(hiScoreName); }