#include "game.h" #ifdef __MIPSEL__ #include #include #endif // Constructor Game::Game(int numPlayers, SDL_Renderer *renderer, std::string *filelist, Lang *lang, Input *input, bool demo, options_t *options) { // Copia los punteros mRenderer = renderer; mFileList = filelist; mLang = lang; mInput = input; mOptions = options; mOnePlayerControl = mOptions->input[0].deviceType; // Pasa variables mDemo.enabled = demo; mNumPlayers = numPlayers; if (mNumPlayers == 1) mOptions->input[0].deviceType = INPUT_USE_ANY; mDifficulty = mOptions->difficulty; // Crea los objetos for (int i = 0; i < mNumPlayers; i++) mPlayer[i] = new Player(); for (int i = 0; i < MAX_BALLOONS; i++) mBalloon[i] = new Balloon(); for (int i = 0; i < MAX_BULLETS; i++) mBullet[i] = new Bullet(); for (int i = 0; i < MAX_ITEMS; i++) mItem[i] = new Item(); for (int i = 0; i < MAX_SMART_SPRITES; i++) mSmartSprite[i] = new SmartSprite(); mTextureBalloon = new LTexture(); mTextureBullet = new LTexture(); mTextureGameBG = new LTexture(); mTextureGameText = new LTexture(); mTextureItems = new LTexture(); mTexturePlayer1Head = new LTexture(); mTexturePlayer1Body = new LTexture(); mTexturePlayer1Death = new LTexture(); mTexturePlayer1Legs = new LTexture(); mTexturePlayer2Head = new LTexture(); mTexturePlayer2Body = new LTexture(); mTexturePlayer2Death = new LTexture(); mTexturePlayer2Legs = new LTexture(); mTextureText = new LTexture(); mTextureTextScoreBoard = new LTexture(); mTextureTextBig = new LTexture(); mTextureTextNokia2 = new LTexture(); mTextureTextNokiaBig2 = new LTexture(); mText = new Text(mFileList[48], mTextureText, mRenderer); mTextScoreBoard = new Text(mFileList[46], mTextureTextScoreBoard, mRenderer); mTextBig = new Text(mFileList[47], mTextureTextBig, mRenderer); mTextNokia2 = new Text(mFileList[57], mTextureTextNokia2, mRenderer); mTextNokiaBig2 = new Text(mFileList[55], mTextureTextNokiaBig2, mRenderer); mMenuGameOver = new Menu(mRenderer, mText, mInput, mFileList); mMenuPause = new Menu(mRenderer, mText, mInput, mFileList); mFade = new Fade(mRenderer); mEventHandler = new SDL_Event(); mClouds1a = new MovingSprite(); mClouds1b = new MovingSprite(); mClouds2a = new MovingSprite(); mClouds2b = new MovingSprite(); m1000Bitmap = new SmartSprite(); m2500Bitmap = new SmartSprite(); m5000Bitmap = new SmartSprite(); mSpriteBackground = new Sprite(); mSpriteGetReady = new Sprite(); mSpriteGradient = new Sprite(); mSpriteGrass = new Sprite(); mSpritePowerMeter = new Sprite(); mSpriteScoreBoard = new Sprite(); #ifdef __MIPSEL__ DIR *dir = opendir("/media/data/local/home/.coffee_crisis"); if (dir) { closedir(dir); } else if (ENOENT == errno) { int status = mkdir("/media/data/local/home/.coffee_crisis", 755); } #endif } Game::~Game() { saveScoreFile(); saveDemoFile(); mOptions->input[0].deviceType = mOnePlayerControl; mRenderer = nullptr; mFileList = nullptr; mLang = nullptr; mInput = nullptr; for (int i = 0; i < mNumPlayers; i++) { delete mPlayer[i]; mPlayer[i] = nullptr; } for (int i = 0; i < MAX_BALLOONS; i++) { delete mBalloon[i]; mBalloon[i] = nullptr; } for (int i = 0; i < MAX_BULLETS; i++) { delete mBullet[i]; mBullet[i] = nullptr; } for (int i = 0; i < MAX_ITEMS; i++) { delete mItem[i]; mItem[i] = nullptr; } for (int i = 0; i < MAX_SMART_SPRITES; i++) { delete mSmartSprite[i]; mSmartSprite[i] = nullptr; } mTextureBalloon->unload(); delete mTextureBalloon; mTextureBalloon = nullptr; mTextureBullet->unload(); delete mTextureBullet; mTextureBullet = nullptr; mTextureGameBG->unload(); delete mTextureGameBG; mTextureGameBG = nullptr; mTextureGameText->unload(); delete mTextureGameText; mTextureGameText = nullptr; mTextureItems->unload(); delete mTextureItems; mTextureItems = nullptr; mTexturePlayer1Head->unload(); delete mTexturePlayer1Head; mTexturePlayer1Head = nullptr; mTexturePlayer1Body->unload(); delete mTexturePlayer1Body; mTexturePlayer1Body = nullptr; mTexturePlayer1Death->unload(); delete mTexturePlayer1Death; mTexturePlayer1Death = nullptr; mTexturePlayer1Legs->unload(); delete mTexturePlayer1Legs; mTexturePlayer1Legs = nullptr; mTexturePlayer2Head->unload(); delete mTexturePlayer2Head; mTexturePlayer2Head = nullptr; mTexturePlayer2Body->unload(); delete mTexturePlayer2Body; mTexturePlayer2Body = nullptr; mTexturePlayer2Death->unload(); delete mTexturePlayer2Death; mTexturePlayer2Death = nullptr; mTexturePlayer2Legs->unload(); delete mTexturePlayer2Legs; mTexturePlayer2Legs = nullptr; mTextureText->unload(); delete mTextureText; mTextureText = nullptr; mTextureTextScoreBoard->unload(); delete mTextureTextScoreBoard; mTextureTextScoreBoard = nullptr; mTextureTextBig->unload(); delete mTextureTextBig; mTextureTextBig = nullptr; mTextureTextNokia2->unload(); delete mTextureTextNokia2; mTextureTextNokia2 = nullptr; mTextureTextNokiaBig2->unload(); delete mTextureTextNokiaBig2; mTextureTextNokiaBig2 = nullptr; delete mText; mText = nullptr; delete mTextBig; mTextBig = nullptr; delete mTextScoreBoard; mTextScoreBoard = nullptr; delete mTextNokia2; mTextNokia2 = nullptr; delete mTextNokiaBig2; mTextNokiaBig2 = nullptr; delete mMenuGameOver; mMenuGameOver = nullptr; delete mMenuPause; mMenuPause = nullptr; delete mFade; mFade = nullptr; delete mEventHandler; mEventHandler = nullptr; delete mClouds1a; mClouds1a = nullptr; delete mClouds1b; mClouds1b = nullptr; delete mClouds2a; mClouds2a = nullptr; delete mClouds2b; mClouds2b = nullptr; delete m1000Bitmap; m1000Bitmap = nullptr; delete m2500Bitmap; m2500Bitmap = nullptr; delete m5000Bitmap; m5000Bitmap = nullptr; delete mSpriteBackground; mSpriteBackground = nullptr; delete mSpriteGetReady; mSpriteGetReady = nullptr; delete mSpriteGradient; mSpriteGradient = nullptr; delete mSpriteGrass; mSpriteGrass = nullptr; delete mSpritePowerMeter; mSpritePowerMeter = nullptr; delete mSpriteScoreBoard; mSpriteScoreBoard = nullptr; JA_DeleteSound(mSoundBalloon); JA_DeleteSound(mSoundBullet); JA_DeleteSound(mSoundPlayerCollision); JA_DeleteSound(mSoundHiScore); JA_DeleteSound(mSoundItemDrop); JA_DeleteSound(mSoundItemPickup); JA_DeleteSound(mSoundCoffeeOut); JA_DeleteSound(mSoundStageChange); JA_DeleteSound(mSoundBubble1); JA_DeleteSound(mSoundBubble2); JA_DeleteSound(mSoundBubble3); JA_DeleteSound(mSoundBubble4); JA_DeleteSound(mSoundClock); JA_DeleteSound(mSoundPowerBall); JA_DeleteSound(mSoundCollision); JA_DeleteMusic(mMusicPlaying); } // Inicializa el vector con los valores del seno void Game::initSin() { // Vector con los valores del seno para 360 grados for (int i = 0; i < 360; i++) mSin[i] = SDL_sinf((float)i * 3.14f / 180.0f); } // Inicializa las variables necesarias para la sección 'Game' void Game::init() { // Carga los recursos loadMedia(); // Carga ficheros loadScoreFile(); loadDemoFile(); mTicks = 0; mTicksSpeed = 15; // Inicializa las variables switch (mDifficulty) { case DIFFICULTY_EASY: mDefaultEnemySpeed = BALLOON_SPEED_1; mDifficultyScoreMultiplier = 0.5f; break; case DIFFICULTY_NORMAL: mDefaultEnemySpeed = BALLOON_SPEED_1; mDifficultyScoreMultiplier = 1.0f; break; case DIFFICULTY_HARD: mDefaultEnemySpeed = BALLOON_SPEED_5; mDifficultyScoreMultiplier = 1.5f; break; default: break; } mGameCompleted = false; mGameCompletedCounter = 0; mSection.name = PROG_SECTION_GAME; mSection.subsection = GAME_SECTION_PLAY_1P; mMenaceCurrent = 0; mMenaceThreshold = 0; mHiScoreAchieved = false; mCurrentStage = 0; mStageBitmapCounter = STAGE_COUNTER; mDeathCounter = DEATH_COUNTER; mExplosionTime = false; mRemainingExplosions = REMAINING_EXPLOSIONS; mRemainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; mTimeStopped = false; mTimeStoppedCounter = 0; mCounter = 0; mBalloonsPopped = 0; mLastEnemyDeploy = 0; mEnemyDeployCounter = 0; mEnemySpeed = mDefaultEnemySpeed; mEffect.flash = false; mEffect.shake = false; mEffect.shakeCounter = SHAKE_COUNTER; mHelper.needCoffee = false; mHelper.needCoffeeMachine = false; mHelper.needPowerBall = false; mHelper.counter = HELP_COUNTER; mHelper.itemPoints1Odds = ITEM_POINTS_1_DISK_ODDS; mHelper.itemPoints2Odds = ITEM_POINTS_2_GAVINA_ODDS; mHelper.itemPoints3Odds = ITEM_POINTS_3_PACMAR_ODDS; mHelper.itemClockOdds = ITEM_CLOCK_ODDS; mHelper.itemCoffeeOdds = ITEM_COFFEE_ODDS; mHelper.itemCoffeeMachineOdds = ITEM_COFFEE_MACHINE_ODDS; mPowerBallEnabled = false; mPowerBallCounter = 0; mCoffeeMachineEnabled = false; mPostFade = 0; if (mDemo.enabled) { int num = rand() % 2; if (num == 0) { mBalloonsPopped = 1000; mCurrentStage = 3; } else { mBalloonsPopped = 1800; mCurrentStage = 6; } } initSin(); initPaths(); initEnemyFormations(); initEnemyPools(); initGameStages(); // BORRAR //mStage[mCurrentStage].currentPower = mStage[mCurrentStage].powerToComplete - 10; // Modo debug mDebug.enabled = false; mDebug.enemySet = 0; mDebug.gradR = mDebug.gradG = mDebug.gradB = 0; // Modo demo mDemo.recording = false; mDemo.counter = 0; // Iniciualiza el objeto para el fundido mFade->init(0x27, 0x27, 0x36); // Inicializa el objeto con el menu de pausa mMenuPause->init("PAUSE", 0, 12 * BLOCK, MENU_BACKGROUND_SOLID); mMenuPause->addItem(mLang->getText(46), 2); mMenuPause->addItem(mLang->getText(47), 0); mMenuPause->setDefaultActionWhenCancel(0); mMenuPause->setBackgroundColor(0x29, 0x39, 0x41, 240); mMenuPause->setSelectorColor(0xFF, 0x7A, 0x00, 255); mMenuPause->setSelectorTextColor(0xFF, 0xFF, 0xFF); mMenuPause->centerMenuOnX(SCREEN_CENTER_X); mMenuPause->centerMenuElementsOnX(); // Inicializa el objeto con el menu de la pantalla de game over mMenuGameOver->init("GAME OVER", 0, PLAY_AREA_CENTER_Y + BLOCK * 4, MENU_BACKGROUND_TRANSPARENT); mMenuGameOver->addItem(mLang->getText(48), 2); mMenuGameOver->addItem(mLang->getText(49)); mMenuGameOver->setDefaultActionWhenCancel(1); mMenuGameOver->setBackgroundColor(0, 0, 0, 255); mMenuGameOver->setSelectorColor(0x54, 0x6e, 0x7a, 255); mMenuGameOver->setSelectorColor(0x54, 0x6e, 0x7a, 0); mMenuGameOver->setSelectorTextColor(0xFF, 0xFF, 0xFF); mMenuGameOver->setSelectorTextColor(0xFF, 0xF1, 0x76); mMenuGameOver->setSelectorTextColor(0xFF, 0x7A, 0x00); mMenuGameOver->centerMenuOnX(SCREEN_CENTER_X); mMenuGameOver->centerMenuElementsOnX(); // Sprites mClouds1a->init(0, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer); mClouds1a->setSpriteClip(256, 0, 256, 52); mClouds1b->init(256, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer); mClouds1b->setSpriteClip(256, 0, 256, 52); mClouds2a->init(0, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer); mClouds2a->setSpriteClip(256, 52, 256, 32); mClouds2b->init(256, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer); mClouds2b->setSpriteClip(256, 52, 256, 32); mSpriteGrass->init(0, 85, SCREEN_WIDTH, 6, mTextureGameBG, mRenderer); mSpriteGrass->setPosY(154); mSpriteScoreBoard->init(0, 160, SCREEN_WIDTH, 32, mTextureGameBG, mRenderer); mSpriteScoreBoard->setSpriteClip(0, 160, 256, 32); mSpritePowerMeter->init(PLAY_AREA_CENTER_X - 20, 170, 40, 8, mTextureGameBG, mRenderer); mSpritePowerMeter->setSpriteClip(256, 192 - 8, 40, 8); // Vector de jugadores if (mNumPlayers == 1) mPlayer[0]->init(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, mTexturePlayer1Legs, mTexturePlayer1Body, mTexturePlayer1Head, mTexturePlayer1Death, mRenderer); if (mNumPlayers == 2) { mPlayer[0]->init((PLAY_AREA_CENTER_FIRST_QUARTER_X * ((0 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24, mTexturePlayer1Legs, mTexturePlayer1Body, mTexturePlayer1Head, mTexturePlayer1Death, mRenderer); mPlayer[1]->init((PLAY_AREA_CENTER_FIRST_QUARTER_X * ((1 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24, mTexturePlayer2Legs, mTexturePlayer2Body, mTexturePlayer2Head, mTexturePlayer2Death, mRenderer); } // Establece a cero todos los valores del vector de objetos globo resetBalloons(); // Con los globos creados, calcula el nivel de amenaza evaluateAndSetMenace(); // Establece a cero todos los valores del vector de objetos bala resetBullets(); // Establece a cero todos los valores del vector de objetos item resetItems(); // Establece a cero todos los valores del vector de objetos SmafrtSprite resetSmartSprites(); // Inicializa el bitmap de GetReady! mSpriteGetReady->init(0, PLAY_AREA_CENTER_Y - 10, 109, 20, mTextureGameText, mRenderer); mSpriteGetReady->setSpriteClip(0, 0, 109, 20); // Inicializa el bitmap de 1000 puntos m1000Bitmap->init(mTextureGameText, mRenderer); m1000Bitmap->setPosX(0); m1000Bitmap->setPosY(0); m1000Bitmap->setWidth(26); m1000Bitmap->setHeight(9); m1000Bitmap->setVelX(0.0f); m1000Bitmap->setVelY(-0.5f); m1000Bitmap->setAccelX(0.0f); m1000Bitmap->setAccelY(-0.1f); m1000Bitmap->setSpriteClip(0, 20, 26, 9); m1000Bitmap->setEnabled(false); m1000Bitmap->setEnabledTimer(0); m1000Bitmap->setDestX(0); m1000Bitmap->setDestY(0); // Inicializa el bitmap de 2500 puntos m2500Bitmap->init(mTextureGameText, mRenderer); m2500Bitmap->setPosX(0); m2500Bitmap->setPosY(0); m2500Bitmap->setWidth(28); m2500Bitmap->setHeight(9); m2500Bitmap->setVelX(0.0f); m2500Bitmap->setVelY(-0.5f); m2500Bitmap->setAccelX(0.0f); m2500Bitmap->setAccelY(-0.1f); m2500Bitmap->setSpriteClip(26, 20, 28, 9); m2500Bitmap->setEnabled(false); m2500Bitmap->setEnabledTimer(0); m2500Bitmap->setDestX(0); m2500Bitmap->setDestY(0); // Inicializa el bitmap de 5000 puntos m5000Bitmap->init(mTextureGameText, mRenderer); m5000Bitmap->setPosX(0); m5000Bitmap->setPosY(0); m5000Bitmap->setWidth(28); m5000Bitmap->setHeight(9); m5000Bitmap->setVelX(0.0f); m5000Bitmap->setVelY(-0.5f); m5000Bitmap->setAccelX(0.0f); m5000Bitmap->setAccelY(-0.1f); m5000Bitmap->setSpriteClip(54, 20, 28, 9); m5000Bitmap->setEnabled(false); m5000Bitmap->setEnabledTimer(0); m5000Bitmap->setDestX(0); m5000Bitmap->setDestY(0); // Los fondos mSpriteBackground->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTextureGameBG, mRenderer); mSpriteGradient->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTextureGameBG, mRenderer); mGradientRect[0] = {0, 192, SCREEN_WIDTH, SCREEN_HEIGHT}; mGradientRect[1] = {256, 192, SCREEN_WIDTH, SCREEN_HEIGHT}; mGradientRect[2] = {0, 384, SCREEN_WIDTH, SCREEN_HEIGHT}; mGradientRect[3] = {256, 384, SCREEN_WIDTH, SCREEN_HEIGHT}; } // Carga los recursos necesarios para la sección 'Game' bool Game::loadMedia() { bool success = true; // Texturas success &= loadTextureFromFile(mTextureText, mFileList[30], mRenderer); success &= loadTextureFromFile(mTextureTextScoreBoard, mFileList[27], mRenderer); success &= loadTextureFromFile(mTextureTextBig, mFileList[29], mRenderer); success &= loadTextureFromFile(mTextureTextNokia2, mFileList[56], mRenderer); success &= loadTextureFromFile(mTextureTextNokiaBig2, mFileList[54], mRenderer); success &= loadTextureFromFile(mTexturePlayer1Legs, mFileList[39], mRenderer); success &= loadTextureFromFile(mTexturePlayer1Head, mFileList[41], mRenderer); success &= loadTextureFromFile(mTexturePlayer1Body, mFileList[37], mRenderer); success &= loadTextureFromFile(mTexturePlayer1Death, mFileList[38], mRenderer); success &= loadTextureFromFile(mTexturePlayer2Legs, mFileList[44], mRenderer); success &= loadTextureFromFile(mTexturePlayer2Head, mFileList[45], mRenderer); success &= loadTextureFromFile(mTexturePlayer2Body, mFileList[42], mRenderer); success &= loadTextureFromFile(mTexturePlayer2Death, mFileList[43], mRenderer); success &= loadTextureFromFile(mTextureBalloon, mFileList[24], mRenderer); success &= loadTextureFromFile(mTextureBullet, mFileList[25], mRenderer); success &= loadTextureFromFile(mTextureGameBG, mFileList[31], mRenderer); success &= loadTextureFromFile(mTextureItems, mFileList[34], mRenderer); success &= loadTextureFromFile(mTextureGameText, mFileList[32], mRenderer); // Sonidos mSoundBalloon = JA_LoadSound(mFileList[6].c_str()); mSoundBubble1 = JA_LoadSound(mFileList[7].c_str()); mSoundBubble2 = JA_LoadSound(mFileList[8].c_str()); mSoundBubble3 = JA_LoadSound(mFileList[9].c_str()); mSoundBubble4 = JA_LoadSound(mFileList[10].c_str()); mSoundBullet = JA_LoadSound(mFileList[11].c_str()); mSoundClock = JA_LoadSound(mFileList[22].c_str()); mSoundCoffeeOut = JA_LoadSound(mFileList[12].c_str()); mSoundHiScore = JA_LoadSound(mFileList[13].c_str()); mSoundItemDrop = JA_LoadSound(mFileList[14].c_str()); mSoundItemPickup = JA_LoadSound(mFileList[15].c_str()); mSoundPlayerCollision = JA_LoadSound(mFileList[19].c_str()); mSoundPowerBall = JA_LoadSound(mFileList[23].c_str()); mSoundStageChange = JA_LoadSound(mFileList[20].c_str()); mSoundCollision = JA_LoadSound(mFileList[21].c_str()); // Musicas mMusicPlaying = JA_LoadMusic(mFileList[4].c_str()); return success; } // Carga el fichero de puntos bool Game::loadScoreFile() { // Indicador de éxito en la carga bool success = true; const std::string p = mFileList[0]; 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 == NULL) { printf("Warning: Unable to open %s file\n", filename.c_str()); // Creamos el fichero para escritura file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { printf("New file (%s) created!\n", filename.c_str()); // Inicializamos los datos for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { mScoreDataFile[i] = 0; SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1); } // Cerramos el fichero SDL_RWclose(file); } else { printf("Error: Unable to create file %s\n", filename.c_str()); success = false; } } // El fichero existe else { // Cargamos los datos printf("Reading file %s\n", filename.c_str()); for (int i = 0; i < TOTAL_SCORE_DATA; ++i) SDL_RWread(file, &mScoreDataFile[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 (mScoreDataFile[0] == 0) mHiScore = 10000; // Comprueba el checksum para ver si se ha modificado el fichero else if (mScoreDataFile[0] % 43 == mScoreDataFile[1]) mHiScore = mScoreDataFile[0]; else mHiScore = 10000; 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 = mFileList[1]; 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 == NULL) { printf("Warning: Unable to open %s file\n", filename.c_str()); // Creamos el fichero para escritura file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { printf("New file (%s) created!\n", filename.c_str()); // Inicializamos los datos for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { mDemo.keys.left = 0; mDemo.keys.right = 0; mDemo.keys.noInput = 0; mDemo.keys.fire = 0; mDemo.keys.fireLeft = 0; mDemo.keys.fireRight = 0; mDemo.dataFile[i] = mDemo.keys; SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1); } // Cerramos el fichero SDL_RWclose(file); } else { printf("Error: Unable to create file %s\n", filename.c_str()); success = false; } } // El fichero existe else { // Cargamos los datos printf("Reading file %s\n", filename.c_str()); for (int i = 0; i < TOTAL_DEMO_DATA; ++i) SDL_RWread(file, &mDemo.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 = mFileList[0]; const std::string filename = p.substr(p.find_last_of("\\/") + 1); SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { // Guardamos los datos for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1); } printf("Writing file %s\n", filename.c_str()); // Cerramos el fichero SDL_RWclose(file); } else { printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); } return success; } // Guarda el fichero de datos para la demo bool Game::saveDemoFile() { bool success = true; const std::string p = mFileList[1]; const std::string filename = p.substr(p.find_last_of("\\/") + 1); if (mDemo.recording) { SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { // Guardamos los datos for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1); } printf("Writing file %s\n", filename.c_str()); // Cerramos el fichero SDL_RWclose(file); } else { printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); } } 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++) { mEnemyFormation[i].numberOfEnemies = 0; for (int j = 0; j < MAX_NUMBER_OF_ENEMIES_IN_A_FORMATION; j++) { mEnemyFormation[i].init[j].x = 0; mEnemyFormation[i].init[j].y = 0; mEnemyFormation[i].init[j].velX = 0; mEnemyFormation[i].init[j].kind = 0; mEnemyFormation[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; mEnemyFormation[j].numberOfEnemies = 2; incX = x4_100; incTime = 0; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x4_0 + (i * incX); mEnemyFormation[j].init[i].y = y4; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1); mEnemyFormation[j].init[i].kind = BALLOON_4; mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); } // #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro j = 1; mEnemyFormation[j].numberOfEnemies = 2; incX = PLAY_AREA_CENTER_X; incTime = 0; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = PLAY_AREA_CENTER_FIRST_QUARTER_X - (BALLOON_WIDTH_4 / 2) + (i * incX); mEnemyFormation[j].init[i].y = y4; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1); mEnemyFormation[j].init[i].kind = BALLOON_4; mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); } // #02 - Cuatro enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro j = 2; mEnemyFormation[j].numberOfEnemies = 4; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x2_0 + (i * incX); mEnemyFormation[j].init[i].y = y2; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_2; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #03 - Cuatro enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro j = 3; mEnemyFormation[j].numberOfEnemies = 4; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x2_100 - (i * incX); mEnemyFormation[j].init[i].y = y2; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_2; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha j = 4; mEnemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_0 + (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda j = 5; mEnemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_100 - (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha j = 6; mEnemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_0 + (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda j = 7; mEnemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_100 - (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha j = 8; mEnemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x1_0 + (i * incX); mEnemyFormation[j].init[i].y = 13; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_1; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda j = 9; mEnemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x1_100 - (i * incX); mEnemyFormation[j].init[i].y = 13; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_1; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #10 - Tres enemigos BALLOON4 seguidos desde la izquierda j = 10; mEnemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_4 + 1; incTime = 15; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x4_0 + (i * incX); mEnemyFormation[j].init[i].y = y4; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_4; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #11 - Tres enemigos BALLOON4 seguidos desde la derecha j = 11; mEnemyFormation[j].numberOfEnemies = 3; incX = BALLOON_WIDTH_4 + 1; incTime = 15; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x4_100 - (i * incX); mEnemyFormation[j].init[i].y = y4; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_4; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #12 - Seis enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro j = 12; mEnemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x2_0 + (i * incX); mEnemyFormation[j].init[i].y = y2; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_2; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #13 - Seis enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro j = 13; mEnemyFormation[j].numberOfEnemies = 6; incX = BALLOON_WIDTH_2 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x2_100 - (i * incX); mEnemyFormation[j].init[i].y = y2; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_2; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados j = 14; mEnemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_0 + (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados j = 15; mEnemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_100 - (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos j = 16; mEnemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_0 + (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos j = 17; mEnemyFormation[j].numberOfEnemies = 5; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x3_100 - (i * incX); mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_3; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos j = 18; mEnemyFormation[j].numberOfEnemies = 12; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x1_0 + (i * incX); mEnemyFormation[j].init[i].y = y1; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].kind = BALLOON_1; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos j = 19; mEnemyFormation[j].numberOfEnemies = 12; incX = BALLOON_WIDTH_1 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = x1_100 - (i * incX); mEnemyFormation[j].init[i].y = y1; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].kind = BALLOON_1; mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); } // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simetricos j = 20; mEnemyFormation[j].numberOfEnemies = 4; incX = BALLOON_WIDTH_4 + 1; incTime = 0; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; if (i < half) { mEnemyFormation[j].init[i].x = x4_0 + (i * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; } else { mEnemyFormation[j].init[i].x = x4_100 - ((i - half) * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; } mEnemyFormation[j].init[i].y = y4; mEnemyFormation[j].init[i].kind = BALLOON_4; mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); } // #21 - Diez enemigos BALLOON2 uno detras del otro. Izquierda/derecha. Simetricos j = 21; mEnemyFormation[j].numberOfEnemies = 10; incX = BALLOON_WIDTH_2 + 1; incTime = 3; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; if (i < half) { mEnemyFormation[j].init[i].x = x2_0 + (i * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { mEnemyFormation[j].init[i].x = x2_100 - ((i - half) * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } mEnemyFormation[j].init[i].y = y2; mEnemyFormation[j].init[i].kind = BALLOON_2; } // #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simetricos j = 22; mEnemyFormation[j].numberOfEnemies = 10; incX = BALLOON_WIDTH_3 * 2; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; if (i < half) { mEnemyFormation[j].init[i].x = x3_0 + (i * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { mEnemyFormation[j].init[i].x = x3_100 - ((i - half) * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].kind = BALLOON_3; } // #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simetricos j = 23; mEnemyFormation[j].numberOfEnemies = 10; incX = BALLOON_WIDTH_3 + 1; incTime = 10; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; if (i < half) { mEnemyFormation[j].init[i].x = x3_0 + (i * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { mEnemyFormation[j].init[i].x = x3_100 - ((i - half) * incX); mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } mEnemyFormation[j].init[i].y = y3; mEnemyFormation[j].init[i].kind = BALLOON_3; } // #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos j = 24; mEnemyFormation[j].numberOfEnemies = 30; incX = 0; incTime = 5; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; if (i < half) { mEnemyFormation[j].init[i].x = x1_50; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * i); } else { mEnemyFormation[j].init[i].x = x1_50; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * (i - half)); } mEnemyFormation[j].init[i].y = y1; mEnemyFormation[j].init[i].kind = BALLOON_1; } // #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos j = 25; mEnemyFormation[j].numberOfEnemies = 30; incX = BALLOON_WIDTH_1 + 1; incTime = 5; for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; if (i < half) { mEnemyFormation[j].init[i].x = x1_50 + 20; mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); } else { mEnemyFormation[j].init[i].x = x1_50 - 20; mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); } mEnemyFormation[j].init[i].y = y1; mEnemyFormation[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++) { mEnemyFormation[k + 50].numberOfEnemies = mEnemyFormation[k].numberOfEnemies; for (int i = 0; i < mEnemyFormation[k + 50].numberOfEnemies; i++) { mEnemyFormation[k + 50].init[i].x = mEnemyFormation[k].init[i].x; mEnemyFormation[k + 50].init[i].y = mEnemyFormation[k].init[i].y; mEnemyFormation[k + 50].init[i].velX = mEnemyFormation[k].init[i].velX; mEnemyFormation[k + 50].init[i].creationCounter = mEnemyFormation[k].init[i].creationCounter; mEnemyFormation[k + 50].init[i].kind = mEnemyFormation[k].init[i].kind + 4; } } // TEST mEnemyFormation[99].numberOfEnemies = 4; mEnemyFormation[99].init[0].x = 10; mEnemyFormation[99].init[0].y = y1; mEnemyFormation[99].init[0].velX = 0; mEnemyFormation[99].init[0].kind = BALLOON_1; mEnemyFormation[99].init[0].creationCounter = 200; mEnemyFormation[99].init[1].x = 50; mEnemyFormation[99].init[1].y = y1; mEnemyFormation[99].init[1].velX = 0; mEnemyFormation[99].init[1].kind = BALLOON_2; mEnemyFormation[99].init[1].creationCounter = 200; mEnemyFormation[99].init[2].x = 90; mEnemyFormation[99].init[2].y = y1; mEnemyFormation[99].init[2].velX = 0; mEnemyFormation[99].init[2].kind = BALLOON_3; mEnemyFormation[99].init[2].creationCounter = 200; mEnemyFormation[99].init[3].x = 140; mEnemyFormation[99].init[3].y = y1; mEnemyFormation[99].init[3].velX = 0; mEnemyFormation[99].init[3].kind = BALLOON_4; mEnemyFormation[99].init[3].creationCounter = 200; } // Inicializa los conjuntos de formaciones void Game::initEnemyPools() { // EnemyPool #0 mEnemyPool[0].set[0] = &mEnemyFormation[0]; mEnemyPool[0].set[1] = &mEnemyFormation[1]; mEnemyPool[0].set[2] = &mEnemyFormation[2]; mEnemyPool[0].set[3] = &mEnemyFormation[3]; mEnemyPool[0].set[4] = &mEnemyFormation[4]; mEnemyPool[0].set[5] = &mEnemyFormation[5]; mEnemyPool[0].set[6] = &mEnemyFormation[6]; mEnemyPool[0].set[7] = &mEnemyFormation[7]; mEnemyPool[0].set[8] = &mEnemyFormation[8]; mEnemyPool[0].set[9] = &mEnemyFormation[9]; // EnemyPool #1 mEnemyPool[1].set[0] = &mEnemyFormation[10]; mEnemyPool[1].set[1] = &mEnemyFormation[11]; mEnemyPool[1].set[2] = &mEnemyFormation[12]; mEnemyPool[1].set[3] = &mEnemyFormation[13]; mEnemyPool[1].set[4] = &mEnemyFormation[14]; mEnemyPool[1].set[5] = &mEnemyFormation[15]; mEnemyPool[1].set[6] = &mEnemyFormation[16]; mEnemyPool[1].set[7] = &mEnemyFormation[17]; mEnemyPool[1].set[8] = &mEnemyFormation[18]; mEnemyPool[1].set[9] = &mEnemyFormation[19]; // EnemyPool #2 mEnemyPool[2].set[0] = &mEnemyFormation[0]; mEnemyPool[2].set[1] = &mEnemyFormation[1]; mEnemyPool[2].set[2] = &mEnemyFormation[2]; mEnemyPool[2].set[3] = &mEnemyFormation[3]; mEnemyPool[2].set[4] = &mEnemyFormation[4]; mEnemyPool[2].set[5] = &mEnemyFormation[55]; mEnemyPool[2].set[6] = &mEnemyFormation[56]; mEnemyPool[2].set[7] = &mEnemyFormation[57]; mEnemyPool[2].set[8] = &mEnemyFormation[58]; mEnemyPool[2].set[9] = &mEnemyFormation[59]; // EnemyPool #3 mEnemyPool[3].set[0] = &mEnemyFormation[50]; mEnemyPool[3].set[1] = &mEnemyFormation[51]; mEnemyPool[3].set[2] = &mEnemyFormation[52]; mEnemyPool[3].set[3] = &mEnemyFormation[53]; mEnemyPool[3].set[4] = &mEnemyFormation[54]; mEnemyPool[3].set[5] = &mEnemyFormation[5]; mEnemyPool[3].set[6] = &mEnemyFormation[6]; mEnemyPool[3].set[7] = &mEnemyFormation[7]; mEnemyPool[3].set[8] = &mEnemyFormation[8]; mEnemyPool[3].set[9] = &mEnemyFormation[9]; // EnemyPool #4 mEnemyPool[4].set[0] = &mEnemyFormation[60]; mEnemyPool[4].set[1] = &mEnemyFormation[61]; mEnemyPool[4].set[2] = &mEnemyFormation[62]; mEnemyPool[4].set[3] = &mEnemyFormation[63]; mEnemyPool[4].set[4] = &mEnemyFormation[64]; mEnemyPool[4].set[5] = &mEnemyFormation[65]; mEnemyPool[4].set[6] = &mEnemyFormation[66]; mEnemyPool[4].set[7] = &mEnemyFormation[67]; mEnemyPool[4].set[8] = &mEnemyFormation[68]; mEnemyPool[4].set[9] = &mEnemyFormation[69]; // EnemyPool #5 mEnemyPool[5].set[0] = &mEnemyFormation[10]; mEnemyPool[5].set[1] = &mEnemyFormation[61]; mEnemyPool[5].set[2] = &mEnemyFormation[12]; mEnemyPool[5].set[3] = &mEnemyFormation[63]; mEnemyPool[5].set[4] = &mEnemyFormation[14]; mEnemyPool[5].set[5] = &mEnemyFormation[65]; mEnemyPool[5].set[6] = &mEnemyFormation[16]; mEnemyPool[5].set[7] = &mEnemyFormation[67]; mEnemyPool[5].set[8] = &mEnemyFormation[18]; mEnemyPool[5].set[9] = &mEnemyFormation[69]; // EnemyPool #6 mEnemyPool[6].set[0] = &mEnemyFormation[60]; mEnemyPool[6].set[1] = &mEnemyFormation[11]; mEnemyPool[6].set[2] = &mEnemyFormation[62]; mEnemyPool[6].set[3] = &mEnemyFormation[13]; mEnemyPool[6].set[4] = &mEnemyFormation[64]; mEnemyPool[6].set[5] = &mEnemyFormation[15]; mEnemyPool[6].set[6] = &mEnemyFormation[66]; mEnemyPool[6].set[7] = &mEnemyFormation[17]; mEnemyPool[6].set[8] = &mEnemyFormation[68]; mEnemyPool[6].set[9] = &mEnemyFormation[19]; // EnemyPool #7 mEnemyPool[7].set[0] = &mEnemyFormation[20]; mEnemyPool[7].set[1] = &mEnemyFormation[21]; mEnemyPool[7].set[2] = &mEnemyFormation[22]; mEnemyPool[7].set[3] = &mEnemyFormation[23]; mEnemyPool[7].set[4] = &mEnemyFormation[24]; mEnemyPool[7].set[5] = &mEnemyFormation[65]; mEnemyPool[7].set[6] = &mEnemyFormation[66]; mEnemyPool[7].set[7] = &mEnemyFormation[67]; mEnemyPool[7].set[8] = &mEnemyFormation[68]; mEnemyPool[7].set[9] = &mEnemyFormation[69]; // EnemyPool #8 mEnemyPool[8].set[0] = &mEnemyFormation[70]; mEnemyPool[8].set[1] = &mEnemyFormation[71]; mEnemyPool[8].set[2] = &mEnemyFormation[72]; mEnemyPool[8].set[3] = &mEnemyFormation[73]; mEnemyPool[8].set[4] = &mEnemyFormation[74]; mEnemyPool[8].set[5] = &mEnemyFormation[15]; mEnemyPool[8].set[6] = &mEnemyFormation[16]; mEnemyPool[8].set[7] = &mEnemyFormation[17]; mEnemyPool[8].set[8] = &mEnemyFormation[18]; mEnemyPool[8].set[9] = &mEnemyFormation[19]; // EnemyPool #9 mEnemyPool[9].set[0] = &mEnemyFormation[20]; mEnemyPool[9].set[1] = &mEnemyFormation[21]; mEnemyPool[9].set[2] = &mEnemyFormation[22]; mEnemyPool[9].set[3] = &mEnemyFormation[23]; mEnemyPool[9].set[4] = &mEnemyFormation[24]; mEnemyPool[9].set[5] = &mEnemyFormation[70]; mEnemyPool[9].set[6] = &mEnemyFormation[71]; mEnemyPool[9].set[7] = &mEnemyFormation[72]; mEnemyPool[9].set[8] = &mEnemyFormation[73]; mEnemyPool[9].set[9] = &mEnemyFormation[74]; } // Inicializa las fases del juego void Game::initGameStages() { // STAGE 1 mStage[0].number = 1; mStage[0].currentPower = 0; mStage[0].powerToComplete = 200; mStage[0].minMenace = 7 + (4 * 1); mStage[0].maxMenace = 7 + (4 * 3); mStage[0].enemyPool = &mEnemyPool[0]; // STAGE 2 mStage[1].number = 2; mStage[1].currentPower = 0; mStage[1].powerToComplete = 300; mStage[1].minMenace = 7 + (4 * 2); mStage[1].maxMenace = 7 + (4 * 4); mStage[1].enemyPool = &mEnemyPool[1]; // STAGE 3 mStage[2].number = 3; mStage[2].currentPower = 0; mStage[2].powerToComplete = 600; mStage[2].minMenace = 7 + (4 * 3); mStage[2].maxMenace = 7 + (4 * 6); mStage[2].enemyPool = &mEnemyPool[2]; // STAGE 4 mStage[3].number = 4; mStage[3].currentPower = 0; mStage[3].powerToComplete = 600; mStage[3].minMenace = 7 + (4 * 3); mStage[3].maxMenace = 7 + (4 * 7); mStage[3].enemyPool = &mEnemyPool[3]; // STAGE 5 mStage[4].number = 5; mStage[4].currentPower = 0; mStage[4].powerToComplete = 600; mStage[4].minMenace = 7 + (4 * 4); mStage[4].maxMenace = 7 + (4 * 8); mStage[4].enemyPool = &mEnemyPool[4]; // STAGE 6 mStage[5].number = 6; mStage[5].currentPower = 0; mStage[5].powerToComplete = 600; mStage[5].minMenace = 7 + (4 * 5); mStage[5].maxMenace = 7 + (4 * 10); mStage[5].enemyPool = &mEnemyPool[5]; // STAGE 7 mStage[6].number = 7; mStage[6].currentPower = 0; mStage[6].powerToComplete = 650; mStage[6].minMenace = 7 + (4 * 6); mStage[6].maxMenace = 7 + (4 * 11); mStage[6].enemyPool = &mEnemyPool[6]; // STAGE 8 mStage[7].number = 8; mStage[7].currentPower = 0; mStage[7].powerToComplete = 750; mStage[7].minMenace = 7 + (4 * 7); mStage[7].maxMenace = 7 + (4 * 12); mStage[7].enemyPool = &mEnemyPool[7]; // STAGE 9 mStage[8].number = 9; mStage[8].currentPower = 0; mStage[8].powerToComplete = 850; mStage[8].minMenace = 7 + (4 * 8); mStage[8].maxMenace = 7 + (4 * 13); mStage[8].enemyPool = &mEnemyPool[8]; // STAGE 10 mStage[9].number = 10; mStage[9].currentPower = 0; mStage[9].powerToComplete = 950; mStage[9].minMenace = 7 + (4 * 9); mStage[9].maxMenace = 7 + (4 * 15); mStage[9].enemyPool = &mEnemyPool[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 (mEnemyDeployCounter == 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 mEnemyDeployCounter = 50; } else { // Decrementa el contador de despliegues enemigos de la PowerBall mPowerBallCounter > 0 ? mPowerBallCounter-- : mPowerBallCounter = 0; // Elige una formación enemiga la azar Uint8 set = (rand() % 10); // Evita repetir la ultima formación enemiga desplegada if (set == mLastEnemyDeploy) ++set %= 10; mLastEnemyDeploy = set; if (mDebug.enabled) set = mDebug.enemySet; Uint8 numEnemies = mStage[mCurrentStage].enemyPool->set[set]->numberOfEnemies; for (int i = 0; i < numEnemies; i++) createNewBalloon(mStage[mCurrentStage].enemyPool->set[set]->init[i].x, mStage[mCurrentStage].enemyPool->set[set]->init[i].y, mStage[mCurrentStage].enemyPool->set[set]->init[i].kind, mStage[mCurrentStage].enemyPool->set[set]->init[i].velX, mEnemySpeed, mStage[mCurrentStage].enemyPool->set[set]->init[i].creationCounter, mTextureBalloon); mEnemyDeployCounter = 255; } } } // Aumenta el poder de la fase void Game::increaseStageCurrentPower(Uint8 power) { mStage[mCurrentStage].currentPower += power; } // Establece el valor de la variable /*void Game::setScore(Uint32 score) { mScore = score; }*/ // Establece el valor de la variable void Game::setHiScore(Uint32 score) { mHiScore = score; } // Actualiza el valor de mHiScore en caso necesario void Game::updateHiScore() { // Si la puntuación actual es mayor que la máxima puntuación for (int i = 0; i < mNumPlayers; i++) if (mPlayer[i]->getScore() > mHiScore) { // Actualiza la máxima puntuación mHiScore = mPlayer[i]->getScore(); // Almacena la máxima puntuación en el fichero junto con un checksum mScoreDataFile[0] = mHiScore; mScoreDataFile[1] = mHiScore % 43; // Si se supera la máxima puntuación emite sonido if (mHiScoreAchieved == false) { mHiScoreAchieved = true; JA_PlaySound(mSoundHiScore); } } } // Transforma un valor numérico en una cadena de 6 cifras std::string Game::updateScoreText(Uint32 num) { //return (std::to_string(num)); if ((num >= 0) && (num <= 9)) { return ("000000" + std::to_string(num)); } if ((num >= 10) && (num <= 99)) { return ("00000" + std::to_string(num)); } if ((num >= 100) && (num <= 999)) { return ("0000" + std::to_string(num)); } if ((num >= 1000) && (num <= 9999)) { return ("000" + std::to_string(num)); } if ((num >= 010000) && (num <= 99999)) { return ("00" + std::to_string(num)); } if ((num >= 100000) && (num <= 999999)) { return ("0" + std::to_string(num)); } if ((num >= 1000000) && (num <= 9999999)) { return (std::to_string(num)); } return (std::to_string(num)); } // Pinta el marcador en pantalla usando un objeto texto void Game::renderScoreBoard() { //color_t color = {0, 0, 0}; // Si el jugador esta muerto, no pintes el fondo del marcador, así que pase por encima cuando sale despedido //if (mPlayer[0]->isAlive()) mSpriteScoreBoard->render(); /* // SCORE mText->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_WORD_Y - 6, mLang->getText(39), 0); mText->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_NUMBER_Y - 6, updateScoreText(mPlayer[0]->getScore()), 0); // HI-SCORE mText->writeCentered(PLAY_AREA_CENTER_THIRD_QUARTER_X, HISCORE_WORD_Y - 6, mLang->getText(40), 0); mText->writeCentered(PLAY_AREA_CENTER_THIRD_QUARTER_X, HISCORE_NUMBER_Y - 6, updateScoreText(mHiScore), 0); // MULT color.g = 255; mText->writeColored(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y - 6, mLang->getText(41), color); color.g = 192; mTextX2->writeShadowed(PLAY_AREA_CENTER_X - 16, SCORE_WORD_Y + 5, std::to_string(mPlayer[0]->getScoreMultiplier()).substr(0, 1), color, 1); mText->writeShadowed(PLAY_AREA_CENTER_X - 2, SCORE_WORD_Y + 12, ".", color); mText->writeShadowed(PLAY_AREA_CENTER_X + 4, SCORE_WORD_Y + 12, std::to_string(mPlayer[0]->getScoreMultiplier()).substr(2, 1), color); // STAGE mText->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_NUMBER_Y + 4, mLang->getText(42) + std::to_string(mStage[mCurrentStage].number), 0); // POWER mSpritePowerMeter->setSpriteClip(256, 184, 40, 8); mSpritePowerMeter->render(); const float percent = (mStage[mCurrentStage].currentPower * 40.0f) / mStage[mCurrentStage].powerToComplete; mSpritePowerMeter->setSpriteClip(296, 184, (int)percent, 8); mSpritePowerMeter->render(); */ const int offset1 = 162; const int offset2 = offset1 + 7; const int offset3 = offset2 + 7; const int offset4 = offset3 + 7; const int offsetLeft = PLAY_AREA_LEFT + 45; const int offsetRight = PLAY_AREA_RIGHT - 45; // PLAYER1 - SCORE mTextScoreBoard->writeCentered(offsetLeft, offset1, mLang->getText(53)); mTextScoreBoard->writeCentered(offsetLeft, offset2, updateScoreText(mPlayer[0]->getScore())); // PLAYER1 - MULT mTextScoreBoard->writeCentered(offsetLeft, offset3, mLang->getText(55)); mTextScoreBoard->writeCentered(offsetLeft, offset4, std::to_string(mPlayer[0]->getScoreMultiplier()).substr(0, 3)); if (mNumPlayers == 2) { // PLAYER2 - SCORE mTextScoreBoard->writeCentered(offsetRight, offset1, mLang->getText(54)); mTextScoreBoard->writeCentered(offsetRight, offset2, updateScoreText(mPlayer[1]->getScore())); // PLAYER2 - MULT mTextScoreBoard->writeCentered(offsetRight, offset3, mLang->getText(55)); mTextScoreBoard->writeCentered(offsetRight, offset4, std::to_string(mPlayer[1]->getScoreMultiplier()).substr(0, 3)); } else { // PLAYER2 - SCORE mTextScoreBoard->writeCentered(offsetRight, offset1, mLang->getText(54)); mTextScoreBoard->writeCentered(offsetRight, offset2, "0000000"); // PLAYER2 - MULT mTextScoreBoard->writeCentered(offsetRight, offset3, mLang->getText(55)); mTextScoreBoard->writeCentered(offsetRight, offset4, "1.0"); } // STAGE mTextScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset1, mLang->getText(57) + std::to_string(mStage[mCurrentStage].number)); // POWER mSpritePowerMeter->setPosY(offset2); mSpritePowerMeter->setSpriteClip(256, 184, 40, 8); mSpritePowerMeter->render(); const float percent = (mStage[mCurrentStage].currentPower * 40.0f) / mStage[mCurrentStage].powerToComplete; mSpritePowerMeter->setSpriteClip(296, 184, (int)percent, 8); mSpritePowerMeter->render(); // HI-SCORE mTextScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset3, mLang->getText(56)); mTextScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset4, updateScoreText(mHiScore)); } // Actualiza las variables del jugador void Game::updatePlayer() { for (int i = 0; i < mNumPlayers; i++) { mPlayer[i]->update(); // Comprueba la colisión entre el jugador y los globos if (checkPlayerBalloonCollision(i)) { if (mPlayer[i]->isAlive()) { if (mDemo.enabled) mSection = {PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS}; else killPlayer(i); } } // Comprueba las colisiones entre el jugador y los items checkPlayerItemCollision(i); } } // Actualiza las variables de la fase void Game::updateStage() { if (mStage[mCurrentStage].currentPower >= mStage[mCurrentStage].powerToComplete) { // Cambio de fase mCurrentStage++; if (mCurrentStage == 10) // Ha llegado al final el juego { mGameCompleted = true; // Marca el juego como completado mCurrentStage = 9; // Deja el valor dentro de los limites mStage[mCurrentStage].currentPower = 0; // Deja el poder a cero para que no vuelva a entrar en esta condición destroyAllBalloons(); // Destruye a todos los enemigos mStage[mCurrentStage].currentPower = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos lo globos mMenaceCurrent = 255; // Sube el nivel de amenaza para que no cree mas globos for (int i = 0; i < mNumPlayers; i++) // Añade un millon de puntos a los jugadores que queden vivos if (mPlayer[i]->isAlive()) mPlayer[i]->addScore(1000000); updateHiScore(); JA_StopMusic(); } JA_PlaySound(mSoundStageChange); mStageBitmapCounter = 0; mEnemySpeed = mDefaultEnemySpeed; setBalloonSpeed(mEnemySpeed); mEffect.flash = true; mEffect.shake = true; } // Incrementa el contador del bitmap que aparece mostrando el cambio de fase if (mStageBitmapCounter < STAGE_COUNTER) mStageBitmapCounter++; // Si el juego se ha completado, el bitmap se detiene en el centro de la pantalla if (mGameCompleted) if (mStageBitmapCounter > 100) mStageBitmapCounter = 100; } // Actualiza el estado de muerte void Game::updateDeath() { // Comprueba si todos los jugadores estan muertos bool allAreDead = true; for (int i = 0; i < mNumPlayers; i++) { allAreDead &= (!mPlayer[i]->isAlive()); if (!mPlayer[i]->isAlive()) { // Animación if ((mPlayer[i]->getDeathCounter() / 5) % 4 == 0) mSmartSprite[mPlayer[i]->mDeathIndex]->setSpriteClip(24 * 0, 24, 24, 24); else if ((mPlayer[i]->getDeathCounter() / 5) % 4 == 1) mSmartSprite[mPlayer[i]->mDeathIndex]->setSpriteClip(24 * 1, 24, 24, 24); else if ((mPlayer[i]->getDeathCounter() / 5) % 4 == 2) mSmartSprite[mPlayer[i]->mDeathIndex]->setSpriteClip(24 * 2, 24, 24, 24); else if ((mPlayer[i]->getDeathCounter() / 5) % 4 == 3) mSmartSprite[mPlayer[i]->mDeathIndex]->setSpriteClip(24 * 3, 24, 24, 24); // Rebote en los laterales if (mSmartSprite[mPlayer[i]->mDeathIndex]->getVelX() > 0) { if (mSmartSprite[mPlayer[i]->mDeathIndex]->getPosX() > (SCREEN_WIDTH - mSmartSprite[mPlayer[i]->mDeathIndex]->getWidth())) { mSmartSprite[mPlayer[i]->mDeathIndex]->setPosX(SCREEN_WIDTH - mSmartSprite[mPlayer[i]->mDeathIndex]->getWidth()); mSmartSprite[mPlayer[i]->mDeathIndex]->setVelX(mSmartSprite[mPlayer[i]->mDeathIndex]->getVelX() * (-1)); mSmartSprite[mPlayer[i]->mDeathIndex]->setDestX(mSmartSprite[mPlayer[i]->mDeathIndex]->getDestX() * (-1)); } } else { if (mSmartSprite[mPlayer[i]->mDeathIndex]->getPosX() < 0) { mSmartSprite[mPlayer[i]->mDeathIndex]->setPosX(0); mSmartSprite[mPlayer[i]->mDeathIndex]->setVelX(mSmartSprite[mPlayer[i]->mDeathIndex]->getVelX() * (-1)); mSmartSprite[mPlayer[i]->mDeathIndex]->setDestX(mSmartSprite[mPlayer[i]->mDeathIndex]->getDestX() * (-1)); } } } } if (allAreDead) { //JA_StopMusic(); if (mDeathCounter > 0) { mDeathCounter--; if ((mDeathCounter == 250) || (mDeathCounter == 200) || (mDeathCounter == 180) || (mDeathCounter == 120) || (mDeathCounter == 60)) { Uint8 sound = rand() % 4; switch (sound) { case 0: JA_PlaySound(mSoundBubble1, 0); break; case 1: JA_PlaySound(mSoundBubble2, 0); break; case 2: JA_PlaySound(mSoundBubble3, 0); break; case 3: JA_PlaySound(mSoundBubble4, 0); break; default: break; } } } else { mSection.subsection = GAME_SECTION_GAMEOVER; } } } // Renderiza el fade final cuando se acaba la partida void Game::renderDeathFade(int counter) // Counter debe ir de 0 a 150 { SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255); if (counter < 150) { // 192 / 6 = 32, 6 cuadrados de 32 pixeles SDL_Rect rect[12]; Uint8 h = counter / 3; for (int i = 0; i < 12; i++) { rect[i].x = 0; rect[i].y = i * 16; rect[i].w = SCREEN_WIDTH; if (i == 0) rect[i].h = h; else rect[i].h = std::max(rect[i - 1].h - 3, 0); SDL_RenderFillRect(mRenderer, &rect[i]); } } else { SDL_Rect rect = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; SDL_RenderFillRect(mRenderer, &rect); } } // Actualiza los globos void Game::updateBalloons() { for (int i = 0; i < MAX_BALLOONS; i++) mBalloon[i]->update(); } // Pinta en pantalla todos los globos activos void Game::renderBalloons() { for (int i = 0; i < MAX_BALLOONS; i++) { mBalloon[i]->render(); if ((mDebug.enabled) && (mBalloon[i]->isPopping() == false)) mText->writeCentered(mBalloon[i]->getPosX() + (mBalloon[i]->getWidth() / 2), mBalloon[i]->getPosY() - 8, std::to_string(i)); } } // Devuelve el primer indice no activo del vector de globos Uint8 Game::getBalloonFreeIndex() { for (int i = 0; i < MAX_BALLOONS; i++) if (!mBalloon[i]->isEnabled()) return i; return 0; } // Crea un globo nuevo en el vector de globos Uint8 Game::createNewBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 creationtimer, LTexture *texture) { const Uint8 index = getBalloonFreeIndex(); mBalloon[index]->init(x, y, kind, velx, speed, creationtimer, texture, mRenderer); return index; } // 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 x[3] = {left, center, right}; const int posX = x[rand() % 3]; mBalloon[getBalloonFreeIndex()]->init(posX, posY, POWER_BALL, BALLOON_VELX_POSITIVE * (((rand() % 2) * 2) - 1), mEnemySpeed, 100, mTextureBalloon, mRenderer); mPowerBallEnabled = true; mPowerBallCounter = POWERBALL_COUNTER; } // Establece a cero todos los valores del vector de objetos globo void Game::resetBalloons() { for (int i = 0; i < MAX_BALLOONS; i++) mBalloon[i]->disable(); } // Establece la velocidad de los globos void Game::setBalloonSpeed(float speed) { for (int i = 0; i < MAX_BALLOONS; i++) if (mBalloon[i]->isEnabled()) mBalloon[i]->setSpeed(speed); } // Incrementa la velocidad de los globos void Game::incBalloonSpeed() { // La velocidad solo se incrementa en el modo normal if (mDifficulty == DIFFICULTY_NORMAL) { if (mEnemySpeed == BALLOON_SPEED_1) mEnemySpeed = BALLOON_SPEED_2; else if (mEnemySpeed == BALLOON_SPEED_2) mEnemySpeed = BALLOON_SPEED_3; else if (mEnemySpeed == BALLOON_SPEED_3) mEnemySpeed = BALLOON_SPEED_4; else if (mEnemySpeed == BALLOON_SPEED_4) mEnemySpeed = BALLOON_SPEED_5; setBalloonSpeed(mEnemySpeed); } } // Decrementa la velocidad de los globos void Game::decBalloonSpeed() { // La velocidad solo se decrementa en el modo normal if (mDifficulty == DIFFICULTY_NORMAL) { if (mEnemySpeed == BALLOON_SPEED_5) mEnemySpeed = BALLOON_SPEED_4; else if (mEnemySpeed == BALLOON_SPEED_4) mEnemySpeed = BALLOON_SPEED_3; else if (mEnemySpeed == BALLOON_SPEED_3) mEnemySpeed = BALLOON_SPEED_2; else if (mEnemySpeed == BALLOON_SPEED_2) mEnemySpeed = BALLOON_SPEED_1; setBalloonSpeed(mEnemySpeed); } } // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase void Game::updateBalloonSpeed() { const float percent = (float)mStage[mCurrentStage].currentPower / (float)mStage[mCurrentStage].powerToComplete; if (mEnemySpeed == BALLOON_SPEED_1) { if (percent > 0.2f) incBalloonSpeed(); } else if (mEnemySpeed == BALLOON_SPEED_2) { if (percent > 0.4f) incBalloonSpeed(); } else if (mEnemySpeed == BALLOON_SPEED_3) { if (percent > 0.6f) incBalloonSpeed(); } else if (mEnemySpeed == BALLOON_SPEED_4) { if (percent > 0.8f) incBalloonSpeed(); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso void Game::popBalloon(Uint8 index) { // Aumenta el poder de la fase increaseStageCurrentPower(1); mBalloonsPopped++; const Uint8 kind = mBalloon[index]->getKind(); Uint8 freeIndex = 0; switch (kind) { // Si es del tipo más pequeño, simplemente elimina el globo case BALLOON_1: mBalloon[index]->pop(); break; case HEXAGON_1: mBalloon[index]->pop(); break; // Si es del tipo PowerBall, destruye todos los globos case POWER_BALL: destroyAllBalloons(); mPowerBallEnabled = false; mEnemyDeployCounter = 20; break; // En cualquier otro caso, crea dos globos de un tipo inferior default: freeIndex = getBalloonFreeIndex(); mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLOON_VELX_NEGATIVE, mEnemySpeed, 0, mTextureBalloon, mRenderer); mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2)); if (mBalloon[freeIndex]->getClass() == BALLOON_CLASS) mBalloon[freeIndex]->setVelY(-2.50f); else if (mBalloon[freeIndex]->getClass() == HEXAGON_CLASS) mBalloon[freeIndex]->setVelY(BALLOON_VELX_NEGATIVE); freeIndex = getBalloonFreeIndex(); mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLOON_VELX_POSITIVE, mEnemySpeed, 0, mTextureBalloon, mRenderer); mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2)); if (mBalloon[freeIndex]->getClass() == BALLOON_CLASS) mBalloon[freeIndex]->setVelY(-2.50f); else if (mBalloon[freeIndex]->getClass() == HEXAGON_CLASS) mBalloon[freeIndex]->setVelY(BALLOON_VELX_NEGATIVE); // Elimina el globo mBalloon[index]->pop(); break; } // Recalcula el nivel de amenaza evaluateAndSetMenace(); } // Explosiona un globo. Lo destruye void Game::destroyBalloon(Uint8 index) { 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 (mBalloon[index]->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 (int i = 0; i < mNumPlayers; i++) mPlayer[i]->addScore(Uint32(score * mPlayer[i]->getScoreMultiplier() * mDifficultyScoreMultiplier)); updateHiScore(); // Aumenta el poder de la fase increaseStageCurrentPower(power); mBalloonsPopped += power; // Destruye el globo mBalloon[index]->pop(); // Recalcula el nivel de amenaza evaluateAndSetMenace(); } // Explosiona todos los globos void Game::popAllBalloons() { for (int i = 0; i < MAX_BALLOONS; i++) if ((mBalloon[i]->isEnabled()) && (!mBalloon[i]->isPopping()) && (!mBalloon[i]->isBeingCreated())) popBalloon(i); JA_PlaySound(mSoundBalloon); } // Destruye todos los globos void Game::destroyAllBalloons() { for (int i = 0; i < MAX_BALLOONS; i++) //if ((mBalloon[i]->isEnabled()) && (!mBalloon[i]->isPopping()) && (!mBalloon[i]->isBeingCreated())) if ((mBalloon[i]->isEnabled()) && (!mBalloon[i]->isPopping())) destroyBalloon(i); mEnemyDeployCounter = 255; JA_PlaySound(mSoundPowerBall); mEffect.flash = true; mEffect.shake = true; } // Detiene todos los globos void Game::stopAllBalloons(Uint16 time) { for (int i = 0; i < MAX_BALLOONS; i++) if (mBalloon[i]->isEnabled()) { mBalloon[i]->setStop(true); mBalloon[i]->setStoppedTimer(time); } } // Pone en marcha todos los globos void Game::startAllBalloons() { for (int i = 0; i < MAX_BALLOONS; i++) if ((mBalloon[i]->isEnabled()) && (!mBalloon[i]->isBeingCreated())) { mBalloon[i]->setStop(false); mBalloon[i]->setStoppedTimer(0); } } // Obtiene el numero de globos activos Uint8 Game::countBalloons() { Uint8 num = 0; for (int i = 0; i < MAX_BALLOONS; i++) if (mBalloon[i]->isEnabled()) if (!mBalloon[i]->isPopping()) num++; return num; } // Comprueba la colisión entre el jugador y los globos activos bool Game::checkPlayerBalloonCollision(int index) { for (int i = 0; i < MAX_BALLOONS; i++) if ((mBalloon[i]->isEnabled()) && !(mBalloon[i]->isStopped()) && !(mBalloon[i]->isInvulnerable())) if (checkCollision(mPlayer[index]->getCollider(), mBalloon[i]->getCollider())) return true; return false; } // Comprueba la colisión entre el jugador y los items void Game::checkPlayerItemCollision(int index) { if (mPlayer[index]->isAlive()) for (int i = 0; i < MAX_ITEMS; i++) { if (mItem[i]->isEnabled()) { if (checkCollision(mPlayer[index]->getCollider(), mItem[i]->getCollider())) { switch (mItem[i]->getClass()) { case ITEM_POINTS_1_DISK: mPlayer[index]->addScore(1000); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m1000Bitmap->getWidth() / 2), mPlayer[index]->getPosY(), m1000Bitmap); JA_PlaySound(mSoundItemPickup); break; case ITEM_POINTS_2_GAVINA: mPlayer[index]->addScore(2500); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m2500Bitmap->getWidth() / 2), mPlayer[index]->getPosY(), m2500Bitmap); JA_PlaySound(mSoundItemPickup); break; case ITEM_POINTS_3_PACMAR: mPlayer[index]->addScore(5000); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), mPlayer[index]->getPosY(), m5000Bitmap); JA_PlaySound(mSoundItemPickup); break; case ITEM_CLOCK: enableTimeStopItem(); JA_PlaySound(mSoundItemPickup); break; case ITEM_COFFEE: if (mPlayer[index]->getCoffees() == 2) { mPlayer[index]->addScore(5000); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), mPlayer[index]->getPosY(), m5000Bitmap); } mPlayer[index]->giveExtraHit(); JA_PlaySound(mSoundItemPickup); break; case ITEM_COFFEE_MACHINE: mPlayer[index]->setPowerUp(true); JA_PlaySound(mSoundItemPickup); mCoffeeMachineEnabled = false; break; default: break; } mItem[i]->erase(); } } } } // Comprueba y procesa la colisión entre las balas y los globos void Game::checkBulletBalloonCollision() { for (int i = 0; i < MAX_BALLOONS; i++) for (int j = 0; j < MAX_BULLETS; j++) if (mBalloon[i]->isEnabled() && (!mBalloon[i]->isInvulnerable()) && mBullet[j]->isActive()) if (checkCollision(mBalloon[i]->getCollider(), mBullet[j]->getCollider())) { // Otorga los puntos correspondientes al globo al jugador que disparó la bala int index = mBullet[j]->getOwner(); mPlayer[index]->incScoreMultiplier(); mPlayer[index]->addScore(Uint32(mBalloon[i]->getScore() * mPlayer[index]->getScoreMultiplier() * mDifficultyScoreMultiplier)); //setScore(mPlayer[index]->getScore()); updateHiScore(); // Explota el globo popBalloon(i); // Si no es el modo demo, genera un sonido if (!mDemo.enabled) JA_PlaySound(mSoundBalloon); // Destruye la bala mBullet[j]->erase(); // Suelta el item en caso de que salga uno const Uint8 droppeditem = dropItem(); if ((droppeditem != NO_KIND) && !(mDemo.enabled) && !(mDemo.recording)) { if (droppeditem != ITEM_COFFEE_MACHINE) { createItem(mBalloon[i]->getPosX(), mBalloon[i]->getPosY(), droppeditem); JA_PlaySound(mSoundItemDrop); } else { createItem(mPlayer[index]->getPosX(), 0, droppeditem); mCoffeeMachineEnabled = true; } } break; } } // Mueve las balas activas void Game::moveBullets() { for (int i = 0; i < MAX_BULLETS; i++) if (mBullet[i]->isActive()) if (mBullet[i]->move() == MSG_BULLET_OUT) mPlayer[mBullet[i]->getOwner()]->decScoreMultiplier(); } // Pinta las balas activas void Game::renderBullets() { for (int i = 0; i < MAX_BULLETS; i++) if (mBullet[i]->isActive()) mBullet[i]->render(); } // Devuelve el primer indice no activo del vector de balas Uint8 Game::getBulletFreeIndex() { for (int i = 0; i < MAX_BULLETS; i++) if (mBullet[i]->isActive() == false) return i; return 0; } // Establece a cero todos los valores del vector de objetos bala void Game::resetBullets() { for (int i = 0; i < MAX_BULLETS; i++) mBullet[i]->erase(); } // Crea un objeto bala void Game::createBullet(int x, int y, Uint8 kind, bool poweredUp, int owner) { mBullet[getBulletFreeIndex()]->init(x, y, kind, poweredUp, owner, mTextureBullet, mRenderer); } // Actualiza los items void Game::updateItems() { for (int i = 0; i < MAX_ITEMS; i++) { mItem[i]->update(); if (mItem[i]->floorCollision()) { JA_PlaySound(mSoundCollision); mEffect.shake = true; } } } // Pinta los items activos void Game::renderItems() { for (int i = 0; i < MAX_ITEMS; i++) mItem[i]->render(); } // Devuelve el primer indice no activo del vector de items Uint8 Game::getItemFreeIndex() { for (int i = 0; i < MAX_ITEMS; i++) if (mItem[i]->getClass() == NO_KIND) return i; return 0; } // Establece a cero todos los valores del vector de objetos item void Game::resetItems() { for (int i = 0; i < MAX_ITEMS; i++) mItem[i]->erase(); } // Devuelve un item en función del azar Uint8 Game::dropItem() { //if (mPlayer[0]->isPowerUp() || (mCoffeeMachineEnabled)) // return NO_KIND; //else // return ITEM_COFFEE_MACHINE; const Uint8 luckyNumber = rand() % 100; const Uint8 item = rand() % 6; switch (item) { case 0: if (luckyNumber < mHelper.itemPoints1Odds) return ITEM_POINTS_1_DISK; break; case 1: if (luckyNumber < mHelper.itemPoints2Odds) return ITEM_POINTS_2_GAVINA; break; case 2: if (luckyNumber < mHelper.itemPoints3Odds) return ITEM_POINTS_3_PACMAR; break; case 3: if (luckyNumber < mHelper.itemClockOdds) return ITEM_CLOCK; break; case 4: if (luckyNumber < mHelper.itemCoffeeOdds) { mHelper.itemCoffeeOdds = ITEM_COFFEE_ODDS; return ITEM_COFFEE; } else { if (mHelper.needCoffee) mHelper.itemCoffeeOdds++; } break; case 5: if (luckyNumber < mHelper.itemCoffeeMachineOdds) { mHelper.itemCoffeeMachineOdds = ITEM_COFFEE_MACHINE_ODDS; if ((!mCoffeeMachineEnabled) && (mHelper.needCoffeeMachine)) return ITEM_COFFEE_MACHINE; } else { if (mHelper.needCoffeeMachine) mHelper.itemCoffeeMachineOdds++; } break; default: break; } return NO_KIND; } // Crea un objeto item void Game::createItem(int x, int y, Uint8 kind) { mItem[getItemFreeIndex()]->init(kind, x, y, mTextureItems, mRenderer); } // Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto void Game::createItemScoreSprite(int x, int y, SmartSprite *sprite) { const Uint8 index = getSmartSpriteFreeIndex(); *mSmartSprite[index] = *sprite; mSmartSprite[index]->setPosX(x); mSmartSprite[index]->setPosY(y); mSmartSprite[index]->setDestX(x); mSmartSprite[index]->setDestY(y - 15); mSmartSprite[index]->setEnabled(true); mSmartSprite[index]->setEnabledTimer(100); } // Dibuja el efecto de flash void Game::renderFlashEffect() { if (mEffect.flash) { // Pantallazo blanco SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(mRenderer); mEffect.flash = false; } } // Actualiza el efecto de agitar la pantalla void Game::updateShakeEffect() { if (mEffect.shake) { if (mEffect.shakeCounter > 0) mEffect.shakeCounter--; else { mEffect.shake = false; mEffect.shakeCounter = SHAKE_COUNTER; } } } // Crea un SmartSprite para arrojar el item café al recibir un impacto void Game::throwCoffee(int x, int y) { const Uint8 index = getSmartSpriteFreeIndex(); mSmartSprite[index]->init(mTextureItems, mRenderer); mSmartSprite[index]->setPosX(x - 8); mSmartSprite[index]->setPosY(y - 8); mSmartSprite[index]->setWidth(16); mSmartSprite[index]->setHeight(16); mSmartSprite[index]->setVelX(-1.0f + ((rand() % 5) * 0.5f)); mSmartSprite[index]->setVelY(-4.0f); mSmartSprite[index]->setAccelX(0.0f); mSmartSprite[index]->setAccelY(0.2f); mSmartSprite[index]->setDestX(x + (mSmartSprite[index]->getVelX() * 50)); mSmartSprite[index]->setDestY(SCREEN_HEIGHT + 1); mSmartSprite[index]->setEnabled(true); mSmartSprite[index]->setEnabledTimer(1); mSmartSprite[index]->setSpriteClip(80, 16, 16, 16); mSmartSprite[index]->setRotate(true); mSmartSprite[index]->setRotateSpeed(10); mSmartSprite[index]->setRotateAmount(90.0); } // Crea un SmartSprite para arrojar al jugador al morir void Game::throwPlayer(int x, int y, int index) { const int sentit = ((rand() % 2) ? 1 : -1); mPlayer[index]->mDeathIndex = getSmartSpriteFreeIndex(); mSmartSprite[mPlayer[index]->mDeathIndex]->init(mPlayer[index]->getDeadTexture(), mRenderer); mSmartSprite[mPlayer[index]->mDeathIndex]->setPosX(x); mSmartSprite[mPlayer[index]->mDeathIndex]->setPosY(y); mSmartSprite[mPlayer[index]->mDeathIndex]->setWidth(24); mSmartSprite[mPlayer[index]->mDeathIndex]->setHeight(24); mSmartSprite[mPlayer[index]->mDeathIndex]->setVelX(2.0f * sentit); mSmartSprite[mPlayer[index]->mDeathIndex]->setVelY(-5.0f); mSmartSprite[mPlayer[index]->mDeathIndex]->setAccelX(0.0f); mSmartSprite[mPlayer[index]->mDeathIndex]->setAccelY(0.2f); mSmartSprite[mPlayer[index]->mDeathIndex]->setDestX(SCREEN_WIDTH * sentit); mSmartSprite[mPlayer[index]->mDeathIndex]->setDestY(SCREEN_HEIGHT + 1); mSmartSprite[mPlayer[index]->mDeathIndex]->setEnabled(true); mSmartSprite[mPlayer[index]->mDeathIndex]->setEnabledTimer(1); mSmartSprite[mPlayer[index]->mDeathIndex]->setSpriteClip(0, 0, 24, 24); } // Actualiza los SmartSprites void Game::updateSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) mSmartSprite[i]->update(); } // Pinta los SmartSprites activos void Game::renderSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) mSmartSprite[i]->render(); } // Devuelve el primer indice no activo del vector de SmartSprites Uint8 Game::getSmartSpriteFreeIndex() { for (int i = 0; i < MAX_SMART_SPRITES; i++) if (!mSmartSprite[i]->isEnabled()) return i; return 0; } // Establece a cero todos los valores del vector de objetos SmafrtSprite void Game::resetSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) mSmartSprite[i]->erase(); } // Acciones a realizar cuando el jugador muere void Game::killPlayer(int index) { if (!mPlayer[index]->isInvulnerable()) { if (mPlayer[index]->hasExtraHit()) { mPlayer[index]->removeExtraHit(); throwCoffee(mPlayer[index]->getPosX() + (mPlayer[index]->getWidth() / 2), mPlayer[index]->getPosY() + (mPlayer[index]->getHeight() / 2)); JA_PlaySound(mSoundCoffeeOut); } else { JA_PauseMusic(); stopAllBalloons(10); JA_PlaySound(mSoundPlayerCollision); shakeScreen(); SDL_Delay(500); JA_PlaySound(mSoundCoffeeOut); throwPlayer(mPlayer[index]->getPosX(), mPlayer[index]->getPosY(), index); mPlayer[index]->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() { mMenaceCurrent = 0; for (int i = 0; i < MAX_BALLOONS; i++) if (mBalloon[i]->isEnabled()) mMenaceCurrent += mBalloon[i]->getMenace(); } // Obtiene el valor de la variable Uint8 Game::getMenace() { return mMenaceCurrent; } // Establece el valor de la variable void Game::setTimeStopped(bool value) { mTimeStopped = value; } // Obtiene el valor de la variable bool Game::isTimeStopped() { return mTimeStopped; } // Establece el valor de la variable void Game::setTimeStoppedCounter(Uint16 value) { mTimeStoppedCounter = value; } // Incrementa el valor de la variable void Game::incTimeStoppedCounter(Uint16 value) { mTimeStoppedCounter += value; } // Actualiza y comprueba el valor de la variable void Game::updateTimeStoppedCounter() { if (isTimeStopped()) { if (mTimeStoppedCounter > 0) { mTimeStoppedCounter--; stopAllBalloons(TIME_STOPPED_COUNTER); } else { disableTimeStopItem(); } } } // Establece el valor de la variable void Game::setExplosionTime(bool value) { mExplosionTime = value; } // Obtiene el valor de la variable bool Game::isExplosionTime() { return mExplosionTime; } // Establece el valor de la variable void Game::setRemainingExplosions(Uint8 value) { mRemainingExplosions = value; } // Actualiza y comprueba el valor de la variable void Game::updateRemainingExplosionsCounter() { if (isExplosionTime()) { if (mRemainingExplosionsCounter > 0) { mRemainingExplosionsCounter--; } else if (mRemainingExplosions > 0) { popAllBalloons(); mRemainingExplosions--; mRemainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; } else { mExplosionTime = false; mRemainingExplosions = REMAINING_EXPLOSIONS; mRemainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; } } } // Actualiza la variable mEnemyDeployCounter void Game::updateEnemyDeployCounter() { if (mEnemyDeployCounter > 0) mEnemyDeployCounter--; } // Actualiza el campo de juego void Game::updatePlayField() { // Comprueba el teclado/mando checkGameInput(); // Actualiza las variables del jugador updatePlayer(); // Actualiza el fondo updateBackground(); // Mueve los globos updateBalloons(); // Mueve las balas moveBullets(); // Actualiza los items updateItems(); // Actualiza el valor de mCurrentStage updateStage(); // Actualiza el estado de muerte updateDeath(); // Actualiza los SmartSprites updateSmartSprites(); // Actualiza los contadores de estado y efectos updateTimeStoppedCounter(); updateRemainingExplosionsCounter(); updateEnemyDeployCounter(); updateShakeEffect(); // 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 if (!mGameCompleted) updateMenace(); // Actualiza la velocidad de los enemigos updateBalloonSpeed(); // Actualiza el tramo final de juego, una vez completado updateGameCompleted(); } // Actualiza el fondo void Game::updateBackground() { mClouds1a->move(); mClouds1b->move(); mClouds2a->move(); mClouds2b->move(); if (mClouds1a->getPosX() < -mClouds1a->getWidth()) mClouds1a->setPosX(mClouds1a->getWidth()); if (mClouds1b->getPosX() < -mClouds1b->getWidth()) mClouds1b->setPosX(mClouds1b->getWidth()); if (mClouds2a->getPosX() < -mClouds2a->getWidth()) mClouds2a->setPosX(mClouds2a->getWidth()); if (mClouds2b->getPosX() < -mClouds2b->getWidth()) mClouds2b->setPosX(mClouds2b->getWidth()); mSpriteGrass->setSpriteClip(256, 85 + (6 * (mCounter / 20 % 2)), SCREEN_WIDTH, 6); if (mEffect.shake) mSpriteBackground->setPosX(((mEffect.shakeCounter % 2) * 2) - 1); else mSpriteBackground->setPosX(0); } // Dibuja el fondo void Game::renderBackground() { const float gradientNumber = std::min(((float)mBalloonsPopped / 1000.0f), 3.0f); const float percent = gradientNumber - (int)gradientNumber; const int alpha = std::max((255 - (int)(255 * percent)), 0); // Dibuja el gradiente 2 mSpriteGradient->setSpriteClip(mGradientRect[((int)gradientNumber + 1) % 4]); mSpriteGradient->render(); // Dibuja el gradiente 1 con una opacidad cada vez menor mSpriteGradient->setSpriteClip(mGradientRect[(int)gradientNumber]); mTextureGameBG->setAlpha(alpha); mSpriteGradient->render(); mTextureGameBG->setAlpha(255); mClouds1a->render(); mClouds1b->render(); mClouds2a->render(); mClouds2b->render(); mSpriteBackground->render(); mSpriteGrass->render(); } // Dibuja el campo de juego void Game::renderPlayField() { renderBackground(); renderBalloons(); renderBullets(); renderMessages(); renderItems(); renderSmartSprites(); renderScoreBoard(); for (int i = 0; i < mNumPlayers; i++) mPlayer[i]->render(); if ((mDeathCounter <= 150) && !mPlayer[0]->isAlive()) renderDeathFade(150 - mDeathCounter); if ((mGameCompleted) && (mGameCompletedCounter >= 300)) renderDeathFade(mGameCompletedCounter - 300); renderFlashEffect(); } // Gestiona el nivel de amenaza void Game::updateMenace() { const float percent = mStage[mCurrentStage].currentPower / mStage[mCurrentStage].powerToComplete; const Uint8 difference = mStage[mCurrentStage].maxMenace - mStage[mCurrentStage].minMenace; // Aumenta el nivel de amenaza en función de la puntuación mMenaceThreshold = mStage[mCurrentStage].minMenace + (difference * percent); // Si el nivel de amenza es inferior al umbral if (mMenaceCurrent < mMenaceThreshold) { // 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() { mDemo.keys.left = 0; mDemo.keys.right = 0; mDemo.keys.noInput = 0; mDemo.keys.fire = 0; mDemo.keys.fireLeft = 0; mDemo.keys.fireRight = 0; // Modo Demo activo if (mDemo.enabled) { const int index = 0; if (mDemo.dataFile[mDemo.counter].left == 1) mPlayer[index]->setInput(INPUT_LEFT); if (mDemo.dataFile[mDemo.counter].right == 1) mPlayer[index]->setInput(INPUT_RIGHT); if (mDemo.dataFile[mDemo.counter].noInput == 1) mPlayer[index]->setInput(NO_INPUT); if (mDemo.dataFile[mDemo.counter].fire == 1) if (mPlayer[index]->canFire()) { mPlayer[index]->setInput(INPUT_FIRE_UP); createBullet(mPlayer[index]->getPosX() + (mPlayer[index]->getWidth() / 2) - 4, mPlayer[index]->getPosY() + (mPlayer[index]->getHeight() / 2), BULLET_UP, mPlayer[index]->isPowerUp(), index); mPlayer[index]->setFireCooldown(10); } if (mDemo.dataFile[mDemo.counter].fireLeft == 1) if (mPlayer[index]->canFire()) { mPlayer[index]->setInput(INPUT_FIRE_LEFT); createBullet(mPlayer[index]->getPosX() + (mPlayer[index]->getWidth() / 2) - 4, mPlayer[index]->getPosY() + (mPlayer[index]->getHeight() / 2), BULLET_UP, mPlayer[index]->isPowerUp(), index); mPlayer[index]->setFireCooldown(10); } if (mDemo.dataFile[mDemo.counter].fireRight == 1) if (mPlayer[index]->canFire()) { mPlayer[index]->setInput(INPUT_FIRE_RIGHT); createBullet(mPlayer[index]->getPosX() + (mPlayer[index]->getWidth() / 2) - 4, mPlayer[index]->getPosY() + (mPlayer[index]->getHeight() / 2), BULLET_UP, mPlayer[index]->isPowerUp(), index); mPlayer[index]->setFireCooldown(10); } // Comprueba el input de pausa if (mInput->checkInput(INPUT_BUTTON_PAUSE, REPEAT_FALSE)) mSection.name = PROG_SECTION_TITLE; // Incrementa el contador de la demo if (mDemo.counter < TOTAL_DEMO_DATA) mDemo.counter++; else mSection = {PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS}; } // Modo Demo no activo else for (int i = 0; i < mNumPlayers; i++) if (mPlayer[i]->isAlive()) { // Input a la izquierda if (mInput->checkInput(INPUT_LEFT, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id)) { mPlayer[i]->setInput(INPUT_LEFT); mDemo.keys.left = 1; } else { // Input a la derecha if (mInput->checkInput(INPUT_RIGHT, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id)) { mPlayer[i]->setInput(INPUT_RIGHT); mDemo.keys.right = 1; } else { // Ninguno de los dos inputs anteriores mPlayer[i]->setInput(NO_INPUT); mDemo.keys.noInput = 1; } } // Comprueba el input de disparar al centro if (mInput->checkInput(INPUT_BUTTON_2, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id)) { if (mPlayer[i]->canFire()) { mPlayer[i]->setInput(INPUT_FIRE_UP); createBullet(mPlayer[i]->getPosX() + (mPlayer[i]->getWidth() / 2) - 4, mPlayer[i]->getPosY() + (mPlayer[i]->getHeight() / 2), BULLET_UP, mPlayer[i]->isPowerUp(), i); mPlayer[i]->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSoundBullet); mDemo.keys.fire = 1; } } // Comprueba el input de disparar a la izquierda if (mInput->checkInput(INPUT_BUTTON_1, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id)) { if (mPlayer[i]->canFire()) { mPlayer[i]->setInput(INPUT_FIRE_LEFT); createBullet(mPlayer[i]->getPosX() + (mPlayer[i]->getWidth() / 2) - 4, mPlayer[i]->getPosY() + (mPlayer[i]->getHeight() / 2), BULLET_LEFT, mPlayer[i]->isPowerUp(), i); mPlayer[i]->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSoundBullet); mDemo.keys.fireLeft = 1; } } // Comprueba el input de disparar a la derecha if (mInput->checkInput(INPUT_BUTTON_3, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id)) { if (mPlayer[i]->canFire()) { mPlayer[i]->setInput(INPUT_FIRE_RIGHT); createBullet(mPlayer[i]->getPosX() + (mPlayer[i]->getWidth() / 2) - 4, mPlayer[i]->getPosY() + (mPlayer[i]->getHeight() / 2), BULLET_RIGHT, mPlayer[i]->isPowerUp(), i); mPlayer[i]->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSoundBullet); mDemo.keys.fireRight = 1; } } // Comprueba el input de pausa if (mInput->checkInput(INPUT_CANCEL, REPEAT_FALSE, mOptions->input[i].deviceType, mOptions->input[i].id)) { mSection.subsection = GAME_SECTION_PAUSE; if (JA_GetMusicState() == JA_MUSIC_PLAYING) JA_PauseMusic(); } if (mDemo.counter < TOTAL_DEMO_DATA) { if (mDemo.recording) mDemo.dataFile[mDemo.counter] = mDemo.keys; mDemo.counter++; } else if (mDemo.recording) { mSection.name = PROG_SECTION_QUIT; } } } // Pinta diferentes mensajes en la pantalla void Game::renderMessages() { // GetReady if ((mCounter < STAGE_COUNTER) && (!mDemo.enabled)) { mSpriteGetReady->setPosX((int)mGetReadyBitmapPath[mCounter]); //mSpriteGetReady->render(); //const color_t color = {0x17, 0x17, 0x26}; //mTextBig->writeShadowed((int)mGetReadyBitmapPath[mCounter], PLAY_AREA_CENTER_Y - 8, mLang->getText(75), color, 2); //mTextBig->writeDX(TXT_STROKE, (int)mGetReadyBitmapPath[mCounter], PLAY_AREA_CENTER_Y - 8, mLang->getText(75), 1, noColor, 1, shdwTxtColor); mTextNokiaBig2->write((int)mGetReadyBitmapPath[mCounter], PLAY_AREA_CENTER_Y - 8, mLang->getText(75), -2); } // Time Stopped if (mTimeStopped) { if ((mTimeStoppedCounter > 100) || (mTimeStoppedCounter % 10 > 4)) mTextNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, mLang->getText(36) + std::to_string(mTimeStoppedCounter / 10), -1, noColor, 1, shdwTxtColor); //{ // mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, mLang->getText(36), -2); // mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y + mTextNokiaBig2->getCharacterWidth() + 2, std::to_string(mTimeStoppedCounter / 10), -2); //} if (mTimeStoppedCounter > 100) { if (mTimeStoppedCounter % 30 == 0) JA_PlaySound(mSoundClock, false); } else { if (mTimeStoppedCounter % 15 == 0) JA_PlaySound(mSoundClock, false); } } // D E M O if (mDemo.enabled) if (mDemo.counter % 30 > 14) mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, mLang->getText(37), 0, noColor, 2, shdwTxtColor); // STAGE NUMBER if (mStageBitmapCounter < STAGE_COUNTER) { std::string text = mLang->getText(38) + std::to_string(mStage[mCurrentStage].number); if (!mGameCompleted) { mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, mStageBitmapPath[mStageBitmapCounter], text, -2, noColor, 2, shdwTxtColor); } else { // Texto de juego completado text = mLang->getText(50); mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, mStageBitmapPath[mStageBitmapCounter], text, -2, noColor, 1, shdwTxtColor); mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, mStageBitmapPath[mStageBitmapCounter] + mTextNokiaBig2->getCharacterWidth() + 2, mLang->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() { mTimeStopped = false; setTimeStoppedCounter(0); startAllBalloons(); if (JA_GetMusicState() == JA_MUSIC_PAUSED) JA_ResumeMusic(); } // Agita la pantalla void Game::shakeScreen() { const int v[] = {-1, 1, -1, 1, -1, 1, -1, 0}; for (int n = 0; n < 8; n++) { // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos mSpriteBackground->setPosX(0); mSpriteBackground->setWidth(1); mSpriteBackground->setSpriteClip(0, 0, 1, 192); renderBackground(); mSpriteBackground->setPosX(255); mSpriteBackground->setSpriteClip(255, 0, 1, 192); mSpriteBackground->render(); mSpriteBackground->setPosX(v[n]); mSpriteBackground->setWidth(256); mSpriteBackground->setSpriteClip(0, 0, 256, 192); mSpriteBackground->render(); mSpriteGrass->render(); renderBalloons(); renderBullets(); renderItems(); for (int i = 0; i < mNumPlayers; i++) mPlayer[i]->render(); renderScoreBoard(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); SDL_Delay(50); } } // Bucle para el juego section_t Game::run() { init(); while (mSection.name == PROG_SECTION_GAME) { // Sección juego en pausa if (mSection.subsection == GAME_SECTION_PAUSE) runPausedGame(); // Sección Game Over if (mSection.subsection == GAME_SECTION_GAMEOVER) runGameOverScreen(); // Sección juego jugando if ((mSection.subsection == GAME_SECTION_PLAY_1P) || (mSection.subsection == GAME_SECTION_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 (!mGameCompleted) if (mPlayer[0]->isAlive()) JA_PlayMusic(mMusicPlaying); } // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza el contador de juego mCounter++; // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) { // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { mSection.name = PROG_SECTION_QUIT; break; } //else if ((mEventHandler->type == SDL_KEYDOWN) && (mEventHandler->key.repeat == 0)) //{ // if (mDebug.enabled) // { // switch (mEventHandler->key.keysym.scancode) // { // case SDL_SCANCODE_F: // mDebug.enemySet--; // if (mDebug.enemySet == 255) // mDebug.enemySet = 0; // break; // // case SDL_SCANCODE_R: // mDebug.enemySet = std::min((int)++mDebug.enemySet, 9); // break; // // case SDL_SCANCODE_T: // mTicksSpeed *= 2; // break; // // case SDL_SCANCODE_G: // createNewBalloon(100, 0, BALLOON_1, BALLOON_VELX_POSITIVE, 1.0F, 0, mTextureBalloon); // break; // // case SDL_SCANCODE_P: // popAllBalloons(); // break; // // case SDL_SCANCODE_Z: // incBalloonSpeed(); // break; // // case SDL_SCANCODE_X: // decBalloonSpeed(); // break; // // case SDL_SCANCODE_C: // mDebug.gradB += 10; // break; // // case SDL_SCANCODE_I: // mPlayer[0]->setInvulnerable(true); // mPlayer[0]->setInvulnerableCounter(65000); // break; // // case SDL_SCANCODE_M: // break; // // case SDL_SCANCODE_N: // break; // // default: // break; // } // } // } } // Actualiza la lógica del juego updatePlayField(); } // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos renderPlayField(); // Pinta la informacion de debug renderDebugInfo(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } } return mSection; } // Bucle para el menu de pausa del juego void Game::runPausedGame() { // Reinicia el menu mMenuPause->reset(); while ((mSection.subsection == GAME_SECTION_PAUSE) && (mSection.name == PROG_SECTION_GAME)) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) { // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { mSection.name = PROG_SECTION_QUIT; break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza la lógica del menu mMenuPause->update(); mFade->update(); if (mFade->hasEnded()) { mSection.name = PROG_SECTION_TITLE; mSection.subsection = TITLE_SECTION_1; JA_StopMusic(); break; } } // Dibuja los objetos //renderBackground(); //renderBalloons(); //renderBullets(); //for (int i = 0; i < mNumPlayers; i++) // mPlayer[i]->render(); //renderScoreBoard(); renderPlayField(); mMenuPause->render(); mFade->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); // Comprueba las entradas para el menu mMenuPause->checkInput(); // Comprueba si se ha seleccionado algún item del menú switch (mMenuPause->getItemSelected()) { case 0: //mMenuPause->reset(); mSection.name = PROG_SECTION_GAME; if (mNumPlayers == 1) mSection.subsection = GAME_SECTION_PLAY_1P; else mSection.subsection = GAME_SECTION_PLAY_2P; if (JA_GetMusicState() == JA_MUSIC_PAUSED) JA_ResumeMusic(); break; case 1: //mMenuPause->reset(); mFade->setFadeType(FADE_CENTER); mFade->activateFade(); break; default: break; } } } // Bucle para la pantalla de game over void Game::runGameOverScreen() { // Guarda los puntos saveScoreFile(); // Reinicia el menu mMenuGameOver->reset(); while ((mSection.subsection == GAME_SECTION_GAMEOVER) && (mSection.name == PROG_SECTION_GAME)) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) { // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { mSection.name = PROG_SECTION_QUIT; break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza la lógica del menu mMenuGameOver->update(); mFade->update(); if (mFade->hasEnded()) { switch (mPostFade) { case 0: // YES mSection.name = PROG_SECTION_GAME; if (mNumPlayers == 1) mSection.subsection = GAME_SECTION_PLAY_1P; else mSection.subsection = GAME_SECTION_PLAY_2P; init(); break; case 1: // NO mSection.name = PROG_SECTION_TITLE; mSection.subsection = TITLE_SECTION_1; break; default: break; } break; } } // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos if (mNumPlayers == 1) { mTextBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), mLang->getText(43)); mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 1), mLang->getText(44) + std::to_string(mPlayer[0]->getScore())); } else { mTextBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - 36, mLang->getText(43)); mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - 12, mLang->getText(77) + std::to_string(mPlayer[0]->getScore())); mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y, mLang->getText(78) + std::to_string(mPlayer[1]->getScore())); } mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y + BLOCK * 2, mLang->getText(45)); mMenuGameOver->render(); mFade->render(); // Muestra la pantalla SDL_RenderPresent(mRenderer); // Comprueba las entradas para el menu mMenuGameOver->checkInput(); // Comprueba si se ha seleccionado algún item del menú switch (mMenuGameOver->getItemSelected()) { case 0: // YES mPostFade = 0; mFade->activateFade(); break; case 1: // NO mPostFade = 1; mFade->activateFade(); break; default: break; } } } // Dibuja la informacion de debug en pantalla void Game::renderDebugInfo() { const color_t color = {0xFF, 0x20, 0x20}; //mText->writeShadowed(2, 2 + 0 * BLOCK, "POW: " + std::to_string(mPlayer[0]->mPowerUpCounter), color); //if (mHelper.needCoffeeMachine) // mText->writeShadowed(2, 2 + 1 * BLOCK, "NEED COFFEMACHINE", color); if (mDebug.enabled) { //SDL_RenderSetLogicalSize(mRenderer, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2); mText->writeShadowed(2, 2 + 0 * BLOCK, "menace(umb): " + std::to_string(mMenaceCurrent) + "(" + std::to_string(mMenaceThreshold) + ")", color); mText->writeShadowed(2, 2 + 1 * BLOCK, "currentPower: " + std::to_string(mStage[mCurrentStage].currentPower), color); mText->writeShadowed(2, 2 + 2 * BLOCK, "mCurrentStage:" + std::to_string(mCurrentStage), color); mText->writeShadowed(2, 2 + 3 * BLOCK, "mCounter: " + std::to_string(mCounter), color); mText->writeShadowed(2, 2 + 4 * BLOCK, "(R)enemyset: " + std::to_string(mDebug.enemySet), color); mText->writeShadowed(2, 2 + 5 * BLOCK, "RGB: " + std::to_string(mDebug.gradR) + "," + std::to_string(mDebug.gradG) + "," + std::to_string(mDebug.gradB), color); mText->writeShadowed(2, 2 + 6 * BLOCK, "(I)invuln: " + std::to_string(mPlayer[0]->getInvulnerableCounter()), color); mText->writeShadowed(2, 2 + 7 * BLOCK, "balloons: " + std::to_string(countBalloons()), color); mText->writeShadowed(2, 2 + 8 * BLOCK, "balloonsPop: " + std::to_string(mBalloonsPopped), color); mText->writeShadowed(2, 2 + 9 * BLOCK, "(Z-X)ballSped:" + std::to_string(mEnemySpeed), color); mText->writeShadowed(2, 2 + 10 * BLOCK, "EGcounter: " + std::to_string(mGameCompletedCounter), color); } } // Indica si se puede crear una powerball bool Game::canPowerBallBeCreated() { if ((!mPowerBallEnabled) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (mPowerBallCounter == 0)) return true; else return false; } // Calcula el poder actual de los globos en pantalla int Game::calculateScreenPower() { int power = 0; for (int i = 0; i < MAX_BALLOONS; i++) if (mBalloon[i]->isEnabled()) power += mBalloon[i]->getPower(); return power; } // Inicializa las variables que contienen puntos de ruta para mover objetos void Game::initPaths() { // 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) mStageBitmapPath[i] = (mSin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint); else if (i < secondPart) mStageBitmapPath[i] = (int)centerPoint; else mStageBitmapPath[i] = (mSin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17); } // Letrero de GetReady const int size = mTextNokiaBig2->lenght(mLang->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) { mGetReadyBitmapPath[i] = mSin[(int)(i * 1.8f)]; mGetReadyBitmapPath[i] *= distance1; mGetReadyBitmapPath[i] -= size; } else if (i < secondPart) mGetReadyBitmapPath[i] = (int)finish1; else { mGetReadyBitmapPath[i] = mSin[(int)((i - 150) * 1.8f)]; mGetReadyBitmapPath[i] *= distance2; mGetReadyBitmapPath[i] += finish1; } } } // Actualiza el tramo final de juego, una vez completado void Game::updateGameCompleted() { if (mGameCompleted) mGameCompletedCounter++; if (mGameCompletedCounter == 500) mSection.subsection = GAME_SECTION_GAMEOVER; } // Actualiza las variables de ayuda void Game::updateHelper() { // El ayudante solo funciona para un jugador // Solo ofrece ayuda cuando la amenaza o la velocidad es elevada if (mMenaceCurrent > 15) { if (mPlayer[0]->getCoffees() == 0) mHelper.needCoffee = true; else mHelper.needCoffee = false; if (!mPlayer[0]->isPowerUp()) mHelper.needCoffeeMachine = true; else mHelper.needCoffeeMachine = false; } else { mHelper.needCoffee = false; mHelper.needCoffeeMachine = false; } } // Comprueba si todos los jugadores han muerto bool Game::allPlayersAreDead() { bool success = true; for (int i = 0; i < mNumPlayers; i++) success &= (!mPlayer[i]->isAlive()); return success; }