#include "const.h" #include "struct.h" #include "gamedirector.h" #include const Uint8 *keystates; float mSin[360]; // Vector con los valores del seno para 360 grados // Constructor GameDirector::GameDirector(std::string path) { // Crea los objetos mEventHandler = new SDL_Event(); mTexture[TEXTURE_MENU].texture = new LTexture(); mTexture[TEXTURE_FONT_WHITE].texture = new LTexture(); mTexture[TEXTURE_FONT_WHITE_X2].texture = new LTexture(); mTexture[TEXTURE_FONT_BLACK].texture = new LTexture(); mTexture[TEXTURE_FONT_BLACK_X2].texture = new LTexture(); mTexture[TEXTURE_FONT_NOKIA].texture = new LTexture(); mMenu.gameOver = new Menu(); mMenu.options = new Menu(); mMenu.pause = new Menu(); mMenu.title = new Menu(); mText.black = new Text(); mText.blackX2 = new Text(); mText.nokia = new Text(); mText.white = new Text(); mText.whiteX2 = new Text(); // Inicializa variables setExecutablePath(path); setFileList(); checkFileList(); // Inicializa SDL initSDL(); // Inicializa JailAudio initJailAudio(); // Carga recursos loadMediaProg(); loadScoreFile(); loadConfigFile(); loadDemoFile(); // Inicializa el resto de variables initProg(); } GameDirector::~GameDirector() { quitProg(); // Destuye el manejador de eventos delete mEventHandler; // Libera el mando if (mGameControllerFound) { SDL_JoystickClose(mGameController); mGameController = NULL; } // Destruye los objetos delete mTexture[TEXTURE_MENU].texture; delete mTexture[TEXTURE_FONT_WHITE].texture; delete mTexture[TEXTURE_FONT_WHITE_X2].texture; delete mTexture[TEXTURE_FONT_BLACK].texture; delete mTexture[TEXTURE_FONT_BLACK_X2].texture; delete mTexture[TEXTURE_FONT_NOKIA].texture; delete mMenu.gameOver; delete mMenu.options; delete mMenu.pause; delete mMenu.title; mMenu.active = nullptr; delete mText.black; delete mText.blackX2; delete mText.nokia; delete mText.white; delete mText.whiteX2; SDL_DestroyTexture(mBackbuffer); SDL_DestroyRenderer(mRenderer); SDL_DestroyWindow(mWindow); mBackbuffer = nullptr; mRenderer = nullptr; mWindow = nullptr; SDL_Quit(); } // Inicia las variables necesarias para arrancar el programa void GameDirector::initProg() { // Debug mode mDebug.enabled = false; mDebug.enemySet = 0; mDebug.gradR = mDebug.gradG = mDebug.gradB = 0; // Variables mProg.quit = false; mProg.ticks = 0; mProg.ticksSpeed = 15; mProg.section = PROG_SECTION_LOGO; initSin(); initPaths(); initEnemyFormations(); initEnemyPools(); mOptions.displayCoffeeDrops = false; mDemo.enabled = false; mDemo.recording = false; mDemo.counter = 0; mMenu.keyPressed = false; mMenu.active = mMenu.title; // Teclado mProg.keyboard.up = SDL_SCANCODE_UP; mProg.keyboard.down = SDL_SCANCODE_DOWN; mProg.keyboard.left = SDL_SCANCODE_LEFT; mProg.keyboard.right = SDL_SCANCODE_RIGHT; mProg.keyboard.accept = SDL_SCANCODE_RETURN; mProg.keyboard.cancel = SDL_SCANCODE_ESCAPE; mProg.keyboard.fire = SDL_SCANCODE_W; mProg.keyboard.fireLeft = SDL_SCANCODE_Q; mProg.keyboard.fireRight = SDL_SCANCODE_E; mProg.keyboard.pause = SDL_SCANCODE_ESCAPE; mProg.keyboard.escape = SDL_SCANCODE_ESCAPE; // Buffer de teclado mProg.keyboardBuffer.up = 0; mProg.keyboardBuffer.down = 0; mProg.keyboardBuffer.left = 0; mProg.keyboardBuffer.right = 0; mProg.keyboardBuffer.accept = 0; mProg.keyboardBuffer.cancel = 0; mProg.keyboardBuffer.fire = 0; mProg.keyboardBuffer.fireLeft = 0; mProg.keyboardBuffer.fireRight = 0; mProg.keyboardBuffer.pause = 0; mProg.keyboardBuffer.escape = 0; // Inicializa los objetos de texto mText.white->init(mTexture[TEXTURE_FONT_WHITE].texture, mRenderer, TEXT_FIXED, BLOCK); mText.whiteX2->init(mTexture[TEXTURE_FONT_WHITE_X2].texture, mRenderer, TEXT_FIXED, BLOCK * 2); mText.black->init(mTexture[TEXTURE_FONT_BLACK].texture, mRenderer, TEXT_FIXED, BLOCK); mText.blackX2->init(mTexture[TEXTURE_FONT_BLACK_X2].texture, mRenderer, TEXT_FIXED, BLOCK * 2); mText.nokia->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); // Inicializa el objeto con el menu del titulo mMenu.title->init("TITLE", 0, 15 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); mMenu.title->addItem("PLAY"); mMenu.title->addItem("OPTIONS", 0, 5); mMenu.title->addItem("QUIT"); mMenu.title->setDefaultActionWhenCancel(2); mMenu.title->setBackgroundColor(0x30, 0x30, 0x40, 192); mMenu.title->setSelectorColor(0xe5, 0x1c, 0x23, 255); mMenu.title->setSelectorTextColor(0xFF, 0xF1, 0x76); mMenu.title->centerMenu(SCREEN_CENTER_X); mMenu.title->centerMenuElements(); // Inicializa el objeto con el menu de pausa mMenu.pause->init("PAUSE", 0, 12 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); mMenu.pause->addItem("CONTINUE"); mMenu.pause->addItem("EXIT TO TITLE"); mMenu.pause->setDefaultActionWhenCancel(0); mMenu.pause->setBackgroundColor(0x29, 0x39, 0x41, 240); mMenu.pause->setSelectorColor(0xFF, 0x7A, 0x00, 255); mMenu.pause->setSelectorTextColor(0xFF, 0xFF, 0xFF); mMenu.pause->centerMenu(SCREEN_CENTER_X); mMenu.pause->centerMenuElements(); // Inicializa el objeto con el menu de la pantalla de game over mMenu.gameOver->init("GAME OVER", 0, PLAY_AREA_CENTER_Y + BLOCK * 4, MENU_BACKGROUND_TRANSPARENT, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); mMenu.gameOver->addItem("YES"); mMenu.gameOver->addItem("NO"); mMenu.gameOver->setDefaultActionWhenCancel(1); mMenu.gameOver->setBackgroundColor(0, 0, 0, 255); mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 255); mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 0); mMenu.gameOver->setSelectorTextColor(0xFF, 0xFF, 0xFF); mMenu.gameOver->setSelectorTextColor(0xFF, 0xF1, 0x76); mMenu.gameOver->setSelectorTextColor(0xFF, 0x7A, 0x00); mMenu.gameOver->centerMenu(SCREEN_CENTER_X); mMenu.gameOver->centerMenuElements(); // Inicializa el objeto con el menu de las opciones mMenu.options->init("OPTIONS", 0, 15 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); mMenu.options->addItem("FULLSCREEN"); mMenu.options->addItem("WINDOWS SIZE", 0, 5); mMenu.options->addItem("[OK]"); mMenu.options->addItem("[CANCEL]"); mMenu.options->setDefaultActionWhenCancel(3); mMenu.options->setBackgroundColor(0x30, 0x30, 0x40, 192); mMenu.options->setSelectorColor(0xe5, 0x1c, 0x23, 255); mMenu.options->setSelectorTextColor(0xFF, 0xF1, 0x76); mMenu.options->centerMenu(SCREEN_CENTER_X); mMenu.options->centerMenuElements(); // Actualiza los elementos del menu de opciones con los valores correspondientes updateOptionsMenu(); } // Carga los recursos necesarios para el programa bool GameDirector::loadMediaProg() { bool success = true; // Texturas success &= loadTextureFromFile(mTexture[TEXTURE_MENU].texture, mTexture[TEXTURE_MENU].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_FONT_WHITE].texture, mTexture[TEXTURE_FONT_WHITE].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_FONT_WHITE_X2].texture, mTexture[TEXTURE_FONT_WHITE_X2].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_FONT_BLACK].texture, mTexture[TEXTURE_FONT_BLACK].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_FONT_BLACK_X2].texture, mTexture[TEXTURE_FONT_BLACK_X2].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_FONT_NOKIA].texture, mTexture[TEXTURE_FONT_NOKIA].file, mRenderer); // Sonidos mSound[SOUND_MENU_SELECT].sound = JA_LoadSound(mSound[SOUND_MENU_SELECT].file.c_str()); mSound[SOUND_MENU_CANCEL].sound = JA_LoadSound(mSound[SOUND_MENU_CANCEL].file.c_str()); mSound[SOUND_MENU_MOVE].sound = JA_LoadSound(mSound[SOUND_MENU_MOVE].file.c_str()); // Musicas mMusic[MUSIC_TITLE].music = JA_LoadMusic(mMusic[MUSIC_TITLE].file.c_str()); return success; } // Libera las variables del programa void GameDirector::quitProg() { // Guarda los ficheros saveScoreFile(); saveConfigFile(); saveDemoFile(); // Texturas mTexture[TEXTURE_MENU].texture->unload(); mTexture[TEXTURE_FONT_WHITE].texture->unload(); mTexture[TEXTURE_FONT_WHITE_X2].texture->unload(); mTexture[TEXTURE_FONT_BLACK].texture->unload(); mTexture[TEXTURE_FONT_BLACK_X2].texture->unload(); mTexture[TEXTURE_FONT_NOKIA].texture->unload(); // Sonidos JA_DeleteSound(mSound[SOUND_MENU_SELECT].sound); JA_DeleteSound(mSound[SOUND_MENU_CANCEL].sound); JA_DeleteSound(mSound[SOUND_MENU_MOVE].sound); // Musicas JA_DeleteMusic(mMusic[MUSIC_TITLE].music); } // Inicializa JailAudio void GameDirector::initJailAudio() { JA_Init(48000, AUDIO_S16, 2); } // Arranca SDL y crea la ventana bool GameDirector::initSDL() { // Indicador de inicialización bool success = true; // Inicializa SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_HAPTIC) < 0) { printf("SDL could not initialize!\nSDL Error: %s\n", SDL_GetError()); success = false; } else { // Establece el filtro de la textura a nearest if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0")) { printf("Warning: Nearest texture filtering not enabled!\n"); } // Comprueba si hay algun mando conectado if (SDL_NumJoysticks() < 1) { printf("Warning: No joysticks connected!\n"); mGameControllerFound = false; } else { // Carga el mando mGameController = SDL_JoystickOpen(0); mGameControllerFound = true; if (mGameController == NULL) { printf("Warning: Unable to open game controller!\nSDL Error: %s\n", SDL_GetError()); mGameControllerFound = false; } else { printf("%i joysticks were found.\n", SDL_NumJoysticks()); std::cout << SDL_JoystickNumButtons(mGameController) << " buttons\n"; // Obtiene el dispositivo de control háptico mControllerHaptic = SDL_HapticOpenFromJoystick(mGameController); if (mControllerHaptic == NULL) { printf("Warning: Controller does not support haptics!\nSDL Error: %s\n", SDL_GetError()); } else { printf("Haptics detected\n"); // Inicializa la vibración if (SDL_HapticRumbleInit(mControllerHaptic) < 0) { printf("Warning: Unable to initialize rumble!\nSDL Error: %s\n", SDL_GetError()); } } } } // Crea la ventana mWindow = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, VIEW_WIDTH, VIEW_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); if (mWindow == NULL) { printf("Window could not be created!\nSDL Error: %s\n", SDL_GetError()); success = false; } else { // Crea un renderizador para la ventana con vsync mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (mRenderer == NULL) { printf("Renderer could not be created!\nSDL Error: %s\n", SDL_GetError()); success = false; } else { // Inicializa el color de renderizado SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); // Establece el tamaño del buffer de renderizado SDL_RenderSetLogicalSize(mRenderer, SCREEN_WIDTH, SCREEN_HEIGHT); // Establece el modo de mezcla SDL_SetRenderDrawBlendMode(mRenderer, SDL_BLENDMODE_BLEND); } // Crea un backbuffer para el renderizador mBackbuffer = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT); if (mBackbuffer == NULL) { printf("Backbuffer could not be created!\nSDL Error: %s\n", SDL_GetError()); success = false; } } } printf("\n"); return success; } // Inicializa el vector con los valores del seno void GameDirector::initSin() { // Vector con los valores del seno para 360 grados for (int i = 0; i < 360; i++) { mSin[i] = sin((float)i * 3.14f / 180.0f); } } // Inicializa las variables que contienen puntos de ruta para mover objetos void GameDirector::initPaths() { // Letrero de STAGE # int firstPart = STAGE_COUNTER / 4; // 50 int secondPart = firstPart * 3; // 150 int centerPoint = PLAY_AREA_CENTER_Y - (BLOCK * 2); int distance = (PLAY_AREA_BOTTOM) - (PLAY_AREA_CENTER_Y - 16); for (int i = 0; i < STAGE_COUNTER; i++) { if (i < firstPart) mGame.stageBitmapPath[i] = (mSin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint); else if (i < secondPart) mGame.stageBitmapPath[i] = (int)centerPoint; else mGame.stageBitmapPath[i] = (mSin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17); } float start1 = PLAY_AREA_LEFT - 130; float finish1 = PLAY_AREA_CENTER_X - 55; float start2 = finish1; float finish2 = PLAY_AREA_RIGHT + 20; float distance1 = finish1 - start1; float distance2 = finish2 - start2; // Letrero de GetReady for (int i = 0; i < STAGE_COUNTER; i++) { if (i < firstPart) { mGame.getReadyBitmapPath[i] = mSin[(int)(i * 1.8f)]; mGame.getReadyBitmapPath[i] *= distance1; mGame.getReadyBitmapPath[i] -= 130; } else if (i < secondPart) mGame.getReadyBitmapPath[i] = (int)finish1; else { mGame.getReadyBitmapPath[i] = mSin[(int)((i - 150) * 1.8f)]; mGame.getReadyBitmapPath[i] *= distance2; mGame.getReadyBitmapPath[i] += finish1; } } } // Inicializa las variables necesarias para la sección 'Logo' void GameDirector::initLogo() { // Reserva memoria para los punteros mTexture[TEXTURE_LOGO].texture = new LTexture(); mLogo.sprite = new Sprite(); // Carga los recursos loadMediaLogo(); // Inicializa variables mLogo.counter = 0; mLogo.sprite->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_LOGO].texture, mRenderer); } // Carga los recursos necesarios para la sección 'Logo' bool GameDirector::loadMediaLogo() { bool success = true; success &= loadTextureFromFile(mTexture[TEXTURE_LOGO].texture, mTexture[TEXTURE_LOGO].file, mRenderer); return success; } // Libera las variables necesarias para la sección 'Logo' void GameDirector::quitLogo() { // Texturas mTexture[TEXTURE_LOGO].texture->unload(); // Libera la memoria de los punteros delete mTexture[TEXTURE_LOGO].texture; delete mLogo.sprite; mTexture[TEXTURE_LOGO].texture = nullptr; mLogo.sprite = nullptr; } // Inicializa las variables necesarias para la sección 'Intro' void GameDirector::initIntro() { // Reserva memoria para los punteros mTexture[TEXTURE_INTRO].texture = new LTexture(); for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) mIntro.bitmap[i] = new SmartSprite(); for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) mIntro.text[i] = new Text2(); // Carga los recursos loadMediaIntro(); // Inicializa el vector de eventos de la intro for (Uint8 i = 0; i < INTRO_TOTAL_EVENTS; i++) mIntro.events[i] = EVENT_WAITING; // Inicializa los bitmaps de la intro for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { mIntro.bitmap[i]->init(mTexture[TEXTURE_INTRO].texture, mRenderer); mIntro.bitmap[i]->setId(i); mIntro.bitmap[i]->setIntroEvents(&mIntro.events[0]); mIntro.bitmap[i]->setWidth(128); mIntro.bitmap[i]->setHeight(96); mIntro.bitmap[i]->setEnabled(false); mIntro.bitmap[i]->setEnabledTimer(20); mIntro.bitmap[i]->setDestX(SCREEN_CENTER_X - 64); mIntro.bitmap[i]->setDestY(SCREEN_FIRST_QUARTER_Y - 24); } mIntro.bitmap[0]->setPosX(-128); mIntro.bitmap[0]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); mIntro.bitmap[0]->setVelX(0.0f); mIntro.bitmap[0]->setVelY(0.0f); mIntro.bitmap[0]->setAccelX(0.6f); mIntro.bitmap[0]->setAccelY(0.0f); mIntro.bitmap[0]->setSpriteClip(0, 0, 128, 96); mIntro.bitmap[1]->setPosX(SCREEN_WIDTH); mIntro.bitmap[1]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); mIntro.bitmap[1]->setVelX(-1.0f); mIntro.bitmap[1]->setVelY(0.0f); mIntro.bitmap[1]->setAccelX(-0.3f); mIntro.bitmap[1]->setAccelY(0.0f); mIntro.bitmap[1]->setSpriteClip(128, 0, 128, 96); mIntro.bitmap[2]->setPosX(SCREEN_CENTER_X - 64); mIntro.bitmap[2]->setPosY(-96); mIntro.bitmap[2]->setVelX(0.0f); mIntro.bitmap[2]->setVelY(3.0f); mIntro.bitmap[2]->setAccelX(0.1f); mIntro.bitmap[2]->setAccelY(0.3f); mIntro.bitmap[2]->setSpriteClip(0, 96, 128, 96); mIntro.bitmap[2]->setEnabledTimer(250); mIntro.bitmap[3]->setPosX(SCREEN_CENTER_X - 64); mIntro.bitmap[3]->setPosY(SCREEN_HEIGHT); mIntro.bitmap[3]->setVelX(0.0f); mIntro.bitmap[3]->setVelY(-0.7f); mIntro.bitmap[3]->setAccelX(0.0f); mIntro.bitmap[3]->setAccelY(0.0f); mIntro.bitmap[3]->setSpriteClip(128, 96, 128, 96); mIntro.bitmap[4]->setPosX(SCREEN_CENTER_X - 64); mIntro.bitmap[4]->setPosY(-96); mIntro.bitmap[4]->setVelX(0.0f); mIntro.bitmap[4]->setVelY(3.0f); mIntro.bitmap[4]->setAccelX(0.1f); mIntro.bitmap[4]->setAccelY(0.3f); mIntro.bitmap[4]->setSpriteClip(0, 192, 128, 96); mIntro.bitmap[5]->setPosX(SCREEN_WIDTH); mIntro.bitmap[5]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); mIntro.bitmap[5]->setVelX(-0.7f); mIntro.bitmap[5]->setVelY(0.0f); mIntro.bitmap[5]->setAccelX(0.0f); mIntro.bitmap[5]->setAccelY(0.0f); mIntro.bitmap[5]->setSpriteClip(128, 192, 128, 96); // Inicializa los textos de la intro for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntro.text[i]->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); mIntro.text[i]->setId(6 + i); mIntro.text[i]->setIntroEvents(&mIntro.events[0]); mIntro.text[i]->setPosX(BLOCK * 0); mIntro.text[i]->setPosY(SCREEN_HEIGHT - (BLOCK * 6)); mIntro.text[i]->setKerning(-1); mIntro.text[i]->setEnabled(false); mIntro.text[i]->setEnabledTimer(180); } mIntro.text[0]->setCaption("Un dia qualsevol de l'any 2000"); mIntro.text[0]->setWrittingSpeed(10); mIntro.text[1]->setCaption("Tot esta tranquil a la UPV"); mIntro.text[1]->setWrittingSpeed(10); mIntro.text[2]->setCaption("Fins que un desaprensiu..."); mIntro.text[2]->setWrittingSpeed(15); mIntro.text[3]->setCaption("HEY! ME ANE A FERME UN CORTAET..."); mIntro.text[3]->setWrittingSpeed(10); mIntro.text[4]->setCaption("UAAAAAAAAAAAAA!!!"); mIntro.text[4]->setWrittingSpeed(1); mIntro.text[5]->setCaption("Espera un moment..."); mIntro.text[5]->setWrittingSpeed(20); mIntro.text[6]->setCaption("Si resulta que no tinc solt!"); mIntro.text[6]->setWrittingSpeed(2); mIntro.text[7]->setCaption("MERDA DE MAQUINA!"); mIntro.text[7]->setWrittingSpeed(3); mIntro.text[8]->setCaption("Blop... blop... blop..."); mIntro.text[8]->setWrittingSpeed(20); for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntro.text[i]->center(SCREEN_CENTER_X); } } // Carga los recursos necesarios para la sección 'Intro' bool GameDirector::loadMediaIntro() { bool success = true; // Texturas success &= loadTextureFromFile(mTexture[TEXTURE_INTRO].texture, mTexture[TEXTURE_INTRO].file, mRenderer); // Musicas mMusic[MUSIC_INTRO].music = JA_LoadMusic(mMusic[MUSIC_INTRO].file.c_str()); return success; } // Libera las variables necesarias para la sección 'Intro' void GameDirector::quitIntro() { // Texturas mTexture[TEXTURE_INTRO].texture->unload(); // Musicas JA_DeleteMusic(mMusic[MUSIC_INTRO].music); // Libera la memoria de los punteros delete mTexture[TEXTURE_INTRO].texture; mTexture[TEXTURE_INTRO].texture = nullptr; for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { delete mIntro.bitmap[i]; mIntro.bitmap[i] = nullptr; } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { delete mIntro.text[i]; mIntro.text[i] = nullptr; } } // Inicializa las variables necesarias para la sección 'Title' void GameDirector::initTitle(Uint8 subsection) { // Reserva memoria para los punteros mTexture[TEXTURE_TITLE].texture = new LTexture(); mTexture[TEXTURE_ITEMS].texture = new LTexture(); mTitle.coffeeBitmap = new SmartSprite(); mTitle.crisisBitmap = new SmartSprite(); mTitle.dustBitmapL = new AnimatedSprite(); mTitle.dustBitmapR = new AnimatedSprite(); mTitle.tile = new Sprite(); // Carga los recursos loadMediaTitle(); // Inicializa variables mTitle.section = subsection; mTitle.counter = TITLE_COUNTER; mTitle.backgroundCounter = 0; mTitle.backgroundMode = rand() % 2; mTitle.menuVisible = false; mTitle.nextProgSection = PROG_SECTION_GAME; mTitle.instructionsCounter = INSTRUCTIONS_COUNTER; // Inicializa el bitmap de Coffee mTitle.coffeeBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mTitle.coffeeBitmap->setId(0); mTitle.coffeeBitmap->setIntroEvents(&mTitle.events[0]); mTitle.coffeeBitmap->setPosX(45); mTitle.coffeeBitmap->setPosY(11 - 200); mTitle.coffeeBitmap->setWidth(167); mTitle.coffeeBitmap->setHeight(46); mTitle.coffeeBitmap->setVelX(0.0f); mTitle.coffeeBitmap->setVelY(2.5f); mTitle.coffeeBitmap->setAccelX(0.0f); mTitle.coffeeBitmap->setAccelY(0.1f); mTitle.coffeeBitmap->setSpriteClip(0, 0, 167, 46); mTitle.coffeeBitmap->setEnabled(true); mTitle.coffeeBitmap->setEnabledTimer(0); mTitle.coffeeBitmap->setDestX(45); mTitle.coffeeBitmap->setDestY(11); // Inicializa el bitmap de Crisis mTitle.crisisBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mTitle.crisisBitmap->setId(1); mTitle.crisisBitmap->setIntroEvents(&mTitle.events[0]); mTitle.crisisBitmap->setPosX(60); mTitle.crisisBitmap->setPosY(57 + 200); mTitle.crisisBitmap->setWidth(137); mTitle.crisisBitmap->setHeight(46); mTitle.crisisBitmap->setVelX(0.0f); mTitle.crisisBitmap->setVelY(-2.5f); mTitle.crisisBitmap->setAccelX(0.0f); mTitle.crisisBitmap->setAccelY(-0.1f); mTitle.crisisBitmap->setSpriteClip(0, 46, 137, 46); mTitle.crisisBitmap->setEnabled(true); mTitle.crisisBitmap->setEnabledTimer(0); mTitle.crisisBitmap->setDestX(60); mTitle.crisisBitmap->setDestY(57); // Inicializa el bitmap de DustRight mTitle.dustBitmapR->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mTitle.dustBitmapR->setPosX(218); mTitle.dustBitmapR->setPosY(47); mTitle.dustBitmapR->setWidth(16); mTitle.dustBitmapR->setHeight(14); mTitle.dustBitmapR->setCurrentFrame(0); mTitle.dustBitmapR->setAnimationCounter(0); mTitle.dustBitmapR->setAnimationNumFrames(0, 7); mTitle.dustBitmapR->setAnimationSpeed(0, 8); mTitle.dustBitmapR->setAnimationLoop(0, false); mTitle.dustBitmapR->setAnimationFrames(0, 0, 160 + (mTitle.dustBitmapR->getWidth() * 0), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); mTitle.dustBitmapR->setAnimationFrames(0, 1, 160 + (mTitle.dustBitmapR->getWidth() * 1), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); mTitle.dustBitmapR->setAnimationFrames(0, 2, 160 + (mTitle.dustBitmapR->getWidth() * 2), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); mTitle.dustBitmapR->setAnimationFrames(0, 3, 160 + (mTitle.dustBitmapR->getWidth() * 3), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); mTitle.dustBitmapR->setAnimationFrames(0, 4, 160 + (mTitle.dustBitmapR->getWidth() * 4), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); mTitle.dustBitmapR->setAnimationFrames(0, 5, 160 + (mTitle.dustBitmapR->getWidth() * 5), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); mTitle.dustBitmapR->setAnimationFrames(0, 6, 160 + (mTitle.dustBitmapR->getWidth() * 6), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); // Inicializa el bitmap de DustLeft mTitle.dustBitmapL->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mTitle.dustBitmapL->setPosX(33); mTitle.dustBitmapL->setPosY(47); mTitle.dustBitmapL->setWidth(16); mTitle.dustBitmapL->setHeight(14); mTitle.dustBitmapL->setCurrentFrame(0); mTitle.dustBitmapL->setAnimationCounter(0); mTitle.dustBitmapL->setAnimationNumFrames(0, 7); mTitle.dustBitmapL->setAnimationSpeed(0, 8); mTitle.dustBitmapL->setAnimationLoop(0, false); mTitle.dustBitmapL->setAnimationFrames(0, 0, 160 + (mTitle.dustBitmapL->getWidth() * 0), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); mTitle.dustBitmapL->setAnimationFrames(0, 1, 160 + (mTitle.dustBitmapL->getWidth() * 1), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); mTitle.dustBitmapL->setAnimationFrames(0, 2, 160 + (mTitle.dustBitmapL->getWidth() * 2), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); mTitle.dustBitmapL->setAnimationFrames(0, 3, 160 + (mTitle.dustBitmapL->getWidth() * 3), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); mTitle.dustBitmapL->setAnimationFrames(0, 4, 160 + (mTitle.dustBitmapL->getWidth() * 4), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); mTitle.dustBitmapL->setAnimationFrames(0, 5, 160 + (mTitle.dustBitmapL->getWidth() * 5), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); mTitle.dustBitmapL->setAnimationFrames(0, 6, 160 + (mTitle.dustBitmapL->getWidth() * 6), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); // Inicializa el vector de eventos de la pantalla de titulo for (Uint8 i = 0; i < TITLE_TOTAL_EVENTS; i++) mTitle.events[i] = EVENT_WAITING; } // Carga los recursos necesarios para la sección 'Title' bool GameDirector::loadMediaTitle() { // Indicador de éxito en la carga bool success = true; // Texturas success &= loadTextureFromFile(mTexture[TEXTURE_TITLE].texture, mTexture[TEXTURE_TITLE].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); // Crea el mosaico de fondo del titulo mTitleSurface = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2); if (mTitleSurface == NULL) { printf("TitleSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); success = false; } else { SDL_SetRenderTarget(mRenderer, mTitleSurface); SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF); SDL_RenderClear(mRenderer); mTitle.tile->init(0, 0, 64, 64, mTexture[TEXTURE_TITLE].texture, mRenderer); mTitle.tile->setSpriteClip(192, 0, 64, 64); for (Uint8 i = 0; i < 8; i++) for (Uint8 j = 0; j < 6; j++) { mTitle.tile->setPosX(i * 64); mTitle.tile->setPosY(j * 64); mTitle.tile->render(); } mTitle.backgroundWindow.x = 0; mTitle.backgroundWindow.y = 0; mTitle.backgroundWindow.w = SCREEN_WIDTH; mTitle.backgroundWindow.h = SCREEN_HEIGHT; SDL_SetRenderTarget(mRenderer, nullptr); } // Crea una textura para dibujar las instrucciones mInstructionsSurface = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT); if (mInstructionsSurface == NULL) { printf("InstructionsSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); success = false; } // Sonidos mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); return success; } // Libera las variables necesarias para la sección 'Title' void GameDirector::quitTitle() { // Texturas mTexture[TEXTURE_TITLE].texture->unload(); mTexture[TEXTURE_ITEMS].texture->unload(); // Sonidos JA_DeleteSound(mSound[SOUND_TITLE].sound); // Libera la memoria de los punteros delete mTexture[TEXTURE_TITLE].texture; delete mTexture[TEXTURE_ITEMS].texture; delete mTitle.coffeeBitmap; delete mTitle.crisisBitmap; delete mTitle.dustBitmapL; delete mTitle.dustBitmapR; delete mTitle.tile; mTexture[TEXTURE_TITLE].texture = nullptr; mTexture[TEXTURE_ITEMS].texture = nullptr; mTitle.coffeeBitmap = nullptr; mTitle.crisisBitmap = nullptr; mTitle.dustBitmapL = nullptr; mTitle.dustBitmapR = nullptr; mTitle.tile = nullptr; SDL_DestroyTexture(mTitleSurface); SDL_DestroyTexture(mInstructionsSurface); mTitleSurface = nullptr; mInstructionsSurface = nullptr; } // Inicializa las variables necesarias para la sección 'Game' void GameDirector::initGame() { // Reserva memoria para los punteros for (Uint8 i = 0; i < MAX_BALLOONS; i++) mGame.balloon[i] = new Balloon(); for (Uint8 i = 0; i < MAX_BULLETS; i++) mGame.bullet[i] = new Bullet(); for (Uint8 i = 0; i < MAX_ITEMS; i++) mGame.item[i] = new Item(); for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) mGame.smartSprite[i] = new SmartSprite(); mTexture[TEXTURE_PLAYER_LEGS].texture = new LTexture(); mTexture[TEXTURE_PLAYER_BODY].texture = new LTexture(); mTexture[TEXTURE_PLAYER_DEATH].texture = new LTexture(); mTexture[TEXTURE_BALLOON].texture = new LTexture(); mTexture[TEXTURE_BULLET].texture = new LTexture(); mTexture[TEXTURE_GAME_BG].texture = new LTexture(); mTexture[TEXTURE_GAME_TEXT].texture = new LTexture(); mTexture[TEXTURE_ITEMS].texture = new LTexture(); mGame.player = new Player(); mGame._1000Bitmap = new SmartSprite(); mGame._2500Bitmap = new SmartSprite(); mGame._5000Bitmap = new SmartSprite(); mGame.background = new Sprite(); mGame.clouds1a = new MovingSprite(); mGame.clouds1b = new MovingSprite(); mGame.clouds2a = new MovingSprite(); mGame.clouds2b = new MovingSprite(); mGame.getReadyBitmap = new Sprite(); mGame.gradient = new Sprite(); mGame.grass = new Sprite(); mGame.scoreBoard = new Sprite(); mGame.powerMeter = new Sprite(); // Carga los recursos loadMediaGame(); // Inicializa variables resetGame(); } // Inicializa las variables especificas de la sección 'Game' para empezar una nueva partida void GameDirector::resetGame() { // Inicializa las variables mGame.section = GAME_SECTION_PLAY; mGame.menaceCurrent = 0; mGame.menaceThreshold = 0; mGame.score = 0; mGame.hiScoreAchieved = false; mGame.currentStage = 0; mGame.stageBitmapCounter = STAGE_COUNTER; mGame.deathCounter = DEATH_COUNTER; mGame.explosionTime = false; mGame.remainingExplosions = REMAINING_EXPLOSIONS; mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; mGame.timeStopped = false; mGame.timeStoppedCounter = TIME_STOPPED_COUNTER; mGame.counter = 0; mGame.balloonsPopped = 0; mGame.lastEnemyDeploy = 0; mGame.enemyDeployCounter = 0; mGame.enemySpeed = BALLOON_SPEED_1; mGame.effect.flash = false; mGame.effect.shake = false; mGame.effect.shakeCounter = SHAKE_COUNTER; initGameStages(); // Sprites mGame.clouds1a->init(0, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.clouds1a->setSpriteClip(256, 0, 256, 52); mGame.clouds1b->init(256, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.clouds1b->setSpriteClip(256, 0, 256, 52); mGame.clouds2a->init(0, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.clouds2a->setSpriteClip(256, 52, 256, 32); mGame.clouds2b->init(256, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.clouds2b->setSpriteClip(256, 52, 256, 32); mGame.grass->init(0, 85, SCREEN_WIDTH, 6, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.grass->setPosY(154); mGame.scoreBoard->init(0, 160, SCREEN_WIDTH, 32, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.scoreBoard->setSpriteClip(0, 160, 256, 32); mGame.powerMeter->init(PLAY_AREA_CENTER_THIRD_QUARTER_X - 20, HISCORE_NUMBER_Y + 4, 40, 8, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.powerMeter->setSpriteClip(256, 192 - 8, 40, 8); // Objeto jugador mGame.player->init(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_BODY].texture, mRenderer); // Establece a cero todos los valores del vector de objetos globo resetBalloons(); // Crea objetos globo y los centra en el area de juego //mGame.balloon[0]->init(0, -BLOCK, POWER_BALL, BALLOON_VELX_POSITIVE, mGame.enemySpeed, 300, mTexture[TEXTURE_BALLOON].texture, mRenderer); //mGame.balloon[0]->allignTo(PLAY_AREA_CENTER_X); // Con los globos creados, calcula el nivel de amenaza setMenace(); // 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 GetLeady! mGame.getReadyBitmap->init(0, PLAY_AREA_CENTER_Y - 10, 109, 20, mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); mGame.getReadyBitmap->setSpriteClip(0, 0, 109, 20); // Inicializa el bitmap de 1000 puntos mGame._1000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); mGame._1000Bitmap->setPosX(0); mGame._1000Bitmap->setPosY(0); mGame._1000Bitmap->setWidth(26); mGame._1000Bitmap->setHeight(9); mGame._1000Bitmap->setVelX(0.0f); mGame._1000Bitmap->setVelY(-0.5f); mGame._1000Bitmap->setAccelX(0.0f); mGame._1000Bitmap->setAccelY(-0.1f); mGame._1000Bitmap->setSpriteClip(0, 20, 26, 9); mGame._1000Bitmap->setEnabled(false); mGame._1000Bitmap->setEnabledTimer(0); mGame._1000Bitmap->setDestX(0); mGame._1000Bitmap->setDestY(0); // Inicializa el bitmap de 2500 puntos mGame._2500Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); mGame._2500Bitmap->setPosX(0); mGame._2500Bitmap->setPosY(0); mGame._2500Bitmap->setWidth(28); mGame._2500Bitmap->setHeight(9); mGame._2500Bitmap->setVelX(0.0f); mGame._2500Bitmap->setVelY(-0.5f); mGame._2500Bitmap->setAccelX(0.0f); mGame._2500Bitmap->setAccelY(-0.1f); mGame._2500Bitmap->setSpriteClip(26, 20, 28, 9); mGame._2500Bitmap->setEnabled(false); mGame._2500Bitmap->setEnabledTimer(0); mGame._2500Bitmap->setDestX(0); mGame._2500Bitmap->setDestY(0); // Inicializa el bitmap de 5000 puntos mGame._5000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); mGame._5000Bitmap->setPosX(0); mGame._5000Bitmap->setPosY(0); mGame._5000Bitmap->setWidth(28); mGame._5000Bitmap->setHeight(9); mGame._5000Bitmap->setVelX(0.0f); mGame._5000Bitmap->setVelY(-0.5f); mGame._5000Bitmap->setAccelX(0.0f); mGame._5000Bitmap->setAccelY(-0.1f); mGame._5000Bitmap->setSpriteClip(54, 20, 28, 9); mGame._5000Bitmap->setEnabled(false); mGame._5000Bitmap->setEnabledTimer(0); mGame._5000Bitmap->setDestX(0); mGame._5000Bitmap->setDestY(0); // Los fondos mGame.background->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.gradient->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.gradientRect[0] = {0, 192, SCREEN_WIDTH, SCREEN_HEIGHT}; mGame.gradientRect[1] = {256, 192, SCREEN_WIDTH, SCREEN_HEIGHT}; mGame.gradientRect[2] = {0, 384, SCREEN_WIDTH, SCREEN_HEIGHT}; mGame.gradientRect[3] = {256, 384, SCREEN_WIDTH, SCREEN_HEIGHT}; } // Carga los recursos necesarios para la sección 'Game' bool GameDirector::loadMediaGame() { bool success = true; // Texturas success &= loadTextureFromFile(mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_LEGS].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_PLAYER_BODY].texture, mTexture[TEXTURE_PLAYER_BODY].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_PLAYER_DEATH].texture, mTexture[TEXTURE_PLAYER_DEATH].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_BALLOON].texture, mTexture[TEXTURE_BALLOON].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_BULLET].texture, mTexture[TEXTURE_BULLET].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_GAME_BG].texture, mTexture[TEXTURE_GAME_BG].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_GAME_TEXT].texture, mTexture[TEXTURE_GAME_TEXT].file, mRenderer); success &= loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); // Sonidos mSound[SOUND_BALLOON].sound = JA_LoadSound(mSound[SOUND_BALLOON].file.c_str()); mSound[SOUND_BULLET].sound = JA_LoadSound(mSound[SOUND_BULLET].file.c_str()); mSound[SOUND_PLAYER_COLLISION].sound = JA_LoadSound(mSound[SOUND_PLAYER_COLLISION].file.c_str()); mSound[SOUND_HISCORE].sound = JA_LoadSound(mSound[SOUND_HISCORE].file.c_str()); mSound[SOUND_ITEM_DROP].sound = JA_LoadSound(mSound[SOUND_ITEM_DROP].file.c_str()); mSound[SOUND_ITEM_PICKUP].sound = JA_LoadSound(mSound[SOUND_ITEM_PICKUP].file.c_str()); mSound[SOUND_COFFEE_OUT].sound = JA_LoadSound(mSound[SOUND_COFFEE_OUT].file.c_str()); mSound[SOUND_STAGE_CHANGE].sound = JA_LoadSound(mSound[SOUND_STAGE_CHANGE].file.c_str()); mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); mSound[SOUND_BUBBLE1].sound = JA_LoadSound(mSound[SOUND_BUBBLE1].file.c_str()); mSound[SOUND_BUBBLE2].sound = JA_LoadSound(mSound[SOUND_BUBBLE2].file.c_str()); mSound[SOUND_BUBBLE3].sound = JA_LoadSound(mSound[SOUND_BUBBLE3].file.c_str()); mSound[SOUND_BUBBLE4].sound = JA_LoadSound(mSound[SOUND_BUBBLE4].file.c_str()); // Musicas mMusic[MUSIC_PLAYING].music = JA_LoadMusic(mMusic[MUSIC_PLAYING].file.c_str()); return success; } // Libera las variables necesarias para la sección 'Game' void GameDirector::quitGame() { // Texturas mTexture[TEXTURE_PLAYER_LEGS].texture->unload(); mTexture[TEXTURE_PLAYER_BODY].texture->unload(); mTexture[TEXTURE_PLAYER_DEATH].texture->unload(); mTexture[TEXTURE_BALLOON].texture->unload(); mTexture[TEXTURE_BULLET].texture->unload(); mTexture[TEXTURE_GAME_BG].texture->unload(); mTexture[TEXTURE_GAME_TEXT].texture->unload(); mTexture[TEXTURE_ITEMS].texture->unload(); // Sonidos JA_DeleteSound(mSound[SOUND_BALLOON].sound); JA_DeleteSound(mSound[SOUND_BULLET].sound); JA_DeleteSound(mSound[SOUND_PLAYER_COLLISION].sound); JA_DeleteSound(mSound[SOUND_HISCORE].sound); JA_DeleteSound(mSound[SOUND_ITEM_DROP].sound); JA_DeleteSound(mSound[SOUND_ITEM_PICKUP].sound); JA_DeleteSound(mSound[SOUND_COFFEE_OUT].sound); JA_DeleteSound(mSound[SOUND_STAGE_CHANGE].sound); JA_DeleteSound(mSound[SOUND_TITLE].sound); JA_DeleteSound(mSound[SOUND_BUBBLE1].sound); JA_DeleteSound(mSound[SOUND_BUBBLE2].sound); JA_DeleteSound(mSound[SOUND_BUBBLE3].sound); JA_DeleteSound(mSound[SOUND_BUBBLE4].sound); // Musicas JA_DeleteMusic(mMusic[MUSIC_PLAYING].music); // Libera la memoria de los punteros for (Uint8 i = 0; i < MAX_BALLOONS; i++) { delete mGame.balloon[i]; mGame.balloon[i] = nullptr; } for (Uint8 i = 0; i < MAX_BULLETS; i++) { delete mGame.bullet[i]; mGame.bullet[i] = nullptr; } for (Uint8 i = 0; i < MAX_ITEMS; i++) { delete mGame.item[i]; mGame.item[i] = nullptr; } for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) { delete mGame.smartSprite[i]; mGame.smartSprite[i] = nullptr; } delete mTexture[TEXTURE_PLAYER_LEGS].texture; delete mTexture[TEXTURE_PLAYER_BODY].texture; delete mTexture[TEXTURE_PLAYER_DEATH].texture; delete mTexture[TEXTURE_BALLOON].texture; delete mTexture[TEXTURE_BULLET].texture; delete mTexture[TEXTURE_GAME_BG].texture; delete mTexture[TEXTURE_GAME_TEXT].texture; delete mTexture[TEXTURE_ITEMS].texture; delete mGame.player; delete mGame._1000Bitmap; delete mGame._2500Bitmap; delete mGame._5000Bitmap; delete mGame.background; delete mGame.clouds1a; delete mGame.clouds1b; delete mGame.clouds2a; delete mGame.clouds2b; delete mGame.getReadyBitmap; delete mGame.gradient; delete mGame.grass; delete mGame.scoreBoard; delete mGame.powerMeter; mTexture[TEXTURE_PLAYER_LEGS].texture = nullptr; mTexture[TEXTURE_PLAYER_BODY].texture = nullptr; mTexture[TEXTURE_PLAYER_DEATH].texture = nullptr; mTexture[TEXTURE_BALLOON].texture = nullptr; mTexture[TEXTURE_BULLET].texture = nullptr; mTexture[TEXTURE_GAME_BG].texture = nullptr; mTexture[TEXTURE_GAME_TEXT].texture = nullptr; mTexture[TEXTURE_ITEMS].texture = nullptr; mGame.player = nullptr; mGame._1000Bitmap = nullptr; mGame._2500Bitmap = nullptr; mGame._5000Bitmap = nullptr; mGame.background = nullptr; mGame.clouds1a = nullptr; mGame.clouds1b = nullptr; mGame.clouds2a = nullptr; mGame.clouds2b = nullptr; mGame.getReadyBitmap = nullptr; mGame.gradient = nullptr; mGame.grass = nullptr; mGame.scoreBoard = nullptr; mGame.powerMeter = nullptr; } // Crea el indice de ficheros void GameDirector::setFileList() { // Ficheros binarios mBinFile[BINFILE_SCORE].file = mProg.executablePath + "/" + "../data/score.bin"; mBinFile[BINFILE_DEMO].file = mProg.executablePath + "/" + "../data/demo.bin"; mBinFile[BINFILE_CONFIG].file = mProg.executablePath + "/" + "../data/config.bin"; // Musicas mMusic[MUSIC_INTRO].file = mProg.executablePath + "/" + "../media/music/intro.ogg"; mMusic[MUSIC_PLAYING].file = mProg.executablePath + "/" + "../media/music/playing.ogg"; mMusic[MUSIC_TITLE].file = mProg.executablePath + "/" + "../media/music/title.ogg"; // Sonidos mSound[SOUND_BALLOON].file = mProg.executablePath + "/" + "../media/sound/balloon.wav"; mSound[SOUND_BUBBLE1].file = mProg.executablePath + "/" + "../media/sound/bubble1.wav"; mSound[SOUND_BUBBLE2].file = mProg.executablePath + "/" + "../media/sound/bubble2.wav"; mSound[SOUND_BUBBLE3].file = mProg.executablePath + "/" + "../media/sound/bubble3.wav"; mSound[SOUND_BUBBLE4].file = mProg.executablePath + "/" + "../media/sound/bubble4.wav"; mSound[SOUND_BULLET].file = mProg.executablePath + "/" + "../media/sound/bullet.wav"; mSound[SOUND_COFFEE_OUT].file = mProg.executablePath + "/" + "../media/sound/coffeeout.wav"; mSound[SOUND_HISCORE].file = mProg.executablePath + "/" + "../media/sound/hiscore.wav"; mSound[SOUND_ITEM_DROP].file = mProg.executablePath + "/" + "../media/sound/itemdrop.wav"; mSound[SOUND_ITEM_PICKUP].file = mProg.executablePath + "/" + "../media/sound/itempickup.wav"; mSound[SOUND_MENU_CANCEL].file = mProg.executablePath + "/" + "../media/sound/menu_cancel.wav"; mSound[SOUND_MENU_MOVE].file = mProg.executablePath + "/" + "../media/sound/menu_move.wav"; mSound[SOUND_MENU_SELECT].file = mProg.executablePath + "/" + "../media/sound/menu_select.wav"; mSound[SOUND_PLAYER_COLLISION].file = mProg.executablePath + "/" + "../media/sound/player_collision.wav"; mSound[SOUND_STAGE_CHANGE].file = mProg.executablePath + "/" + "../media/sound/stage_change.wav"; mSound[SOUND_TITLE].file = mProg.executablePath + "/" + "../media/sound/title.wav"; // Texturas mTexture[TEXTURE_BALLOON].file = mProg.executablePath + "/" + "../media/gfx/balloon.png"; mTexture[TEXTURE_BULLET].file = mProg.executablePath + "/" + "../media/gfx/bullet.png"; mTexture[TEXTURE_FONT_BLACK_X2].file = mProg.executablePath + "/" + "../media/gfx/font_black_x2.png"; mTexture[TEXTURE_FONT_BLACK].file = mProg.executablePath + "/" + "../media/gfx/font_black.png"; mTexture[TEXTURE_FONT_NOKIA].file = mProg.executablePath + "/" + "../media/gfx/font_nokia.png"; mTexture[TEXTURE_FONT_WHITE_X2].file = mProg.executablePath + "/" + "../media/gfx/font_white_x2.png"; mTexture[TEXTURE_FONT_WHITE].file = mProg.executablePath + "/" + "../media/gfx/font_white.png"; mTexture[TEXTURE_GAME_BG].file = mProg.executablePath + "/" + "../media/gfx/game_bg.png"; mTexture[TEXTURE_GAME_TEXT].file = mProg.executablePath + "/" + "../media/gfx/game_text.png"; mTexture[TEXTURE_INTRO].file = mProg.executablePath + "/" + "../media/gfx/intro.png"; mTexture[TEXTURE_ITEMS].file = mProg.executablePath + "/" + "../media/gfx/items.png"; mTexture[TEXTURE_LOGO].file = mProg.executablePath + "/" + "../media/gfx/logo.png"; mTexture[TEXTURE_MENU].file = mProg.executablePath + "/" + "../media/gfx/menu.png"; mTexture[TEXTURE_PLAYER_BODY].file = mProg.executablePath + "/" + "../media/gfx/player_body.png"; mTexture[TEXTURE_PLAYER_DEATH].file = mProg.executablePath + "/" + "../media/gfx/player_death.png"; mTexture[TEXTURE_PLAYER_LEGS].file = mProg.executablePath + "/" + "../media/gfx/player_legs.png"; mTexture[TEXTURE_TITLE].file = mProg.executablePath + "/" + "../media/gfx/title.png"; } // Comprueba que todos los ficheros existen bool GameDirector::checkFileList() { bool success = true; std::string p; std::string filename; SDL_RWops *file; // Comprueba los ficheros de musica printf("\n>> MUSIC FILES\n"); if (success) for (Uint8 i = 0; i < TOTAL_MUSIC; i++) { p = mMusic[i].file.c_str(); filename = p.substr(p.find_last_of("\\/") + 1); file = SDL_RWFromFile(p.c_str(), "r+b"); if (file != NULL) { printf("Checking file %-20s [OK]\n", filename.c_str()); } else { printf("Checking file %-20s [ERROR]\n", filename.c_str()); success = false; break; } SDL_RWclose(file); } // Comprueba los ficheros de sonidos printf("\n>> SOUND FILES\n"); if (success) for (Uint8 i = 0; i < TOTAL_SOUND; i++) { p = mSound[i].file.c_str(); filename = p.substr(p.find_last_of("\\/") + 1); file = SDL_RWFromFile(p.c_str(), "r+b"); if (file != NULL) { printf("Checking file %-20s [OK]\n", filename.c_str()); } else { printf("Checking file %-20s [ERROR]\n", filename.c_str()); success = false; break; } SDL_RWclose(file); } // Comprueba los ficheros con texturas printf("\n>> TEXTURE FILES\n"); if (success) for (Uint8 i = 0; i < TOTAL_TEXTURE; i++) { p = mTexture[i].file.c_str(); filename = p.substr(p.find_last_of("\\/") + 1); file = SDL_RWFromFile(p.c_str(), "r+b"); if (file != NULL) { printf("Checking file %-20s [OK]\n", filename.c_str()); } else { printf("Checking file %-20s [ERROR]\n", filename.c_str()); success = false; break; } SDL_RWclose(file); } // Resultado if (success) printf("\n** All files OK.\n\n"); else printf("\n** A file is missing. Exiting.\n\n"); return success; } // Carga el fichero de puntos bool GameDirector::loadScoreFile() { // Indicador de éxito en la carga bool success = true; std::string path = mProg.executablePath + "/"; std::string p = mBinFile[BINFILE_SCORE].file.c_str(); 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) { mGame.scoreDataFile[i] = 0; SDL_RWwrite(file, &mGame.scoreDataFile[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, &mGame.scoreDataFile[i], sizeof(Uint32), 1); // Cierra el fichero SDL_RWclose(file); } // Establece el valor de la máxima puntuación a partir del vector con los datos if (mGame.scoreDataFile[0] == 0) mGame.hiScore = 10000; // Comprueba el checksum para ver si se ha modificado el fichero else if (mGame.scoreDataFile[0] % 43 == mGame.scoreDataFile[1]) mGame.hiScore = mGame.scoreDataFile[0]; else mGame.hiScore = 10000; return success; } // Carga el fichero de configuración bool GameDirector::loadConfigFile() { // Indicador de éxito en la carga bool success = true; std::string path = mProg.executablePath + "/"; std::string p = mBinFile[BINFILE_CONFIG].file.c_str(); 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 mOptions.fullScreenMode = 0; SDL_RWwrite(file, &mOptions.fullScreenMode, sizeof(mOptions.fullScreenMode), 1); mOptions.windowSize = 3; SDL_RWwrite(file, &mOptions.windowSize, sizeof(mOptions.windowSize), 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()); SDL_RWread(file, &mOptions.fullScreenMode, sizeof(mOptions.fullScreenMode), 1); SDL_SetWindowFullscreen(mWindow, mOptions.fullScreenMode); SDL_RWread(file, &mOptions.windowSize, sizeof(mOptions.windowSize), 1); SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mOptions.windowSize, SCREEN_HEIGHT * mOptions.windowSize); // Cierra el fichero SDL_RWclose(file); } return success; } // Carga el fichero de datos para la demo bool GameDirector::loadDemoFile() { // Indicador de éxito en la carga bool success = true; std::string path = mProg.executablePath + "/"; std::string p = mBinFile[BINFILE_DEMO].file.c_str(); 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 GameDirector::saveScoreFile() { bool success = true; std::string path = mProg.executablePath + "/"; std::string p = mBinFile[BINFILE_SCORE].file; 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, &mGame.scoreDataFile[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 configuración bool GameDirector::saveConfigFile() { bool success = true; std::string path = mProg.executablePath + "/"; std::string p = mBinFile[BINFILE_CONFIG].file; std::string filename = p.substr(p.find_last_of("\\/") + 1); SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { // Guarda los datos SDL_RWwrite(file, &mOptions.fullScreenMode, sizeof(Uint32), 1); SDL_RWwrite(file, &mOptions.windowSize, sizeof(Uint8), 1); printf("Writing file %s\n", filename.c_str()); // Cierra 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 GameDirector::saveDemoFile() { bool success = true; std::string path = mProg.executablePath + "/"; std::string p = mBinFile[BINFILE_DEMO].file; 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; } // Carga un archivo de imagen en una textura bool GameDirector::loadTextureFromFile(LTexture *texture, std::string path, SDL_Renderer *renderer) { bool success = true; if (!texture->loadFromFile(path, renderer)) { printf("Failed to load %s texture!\n", path.c_str()); success = false; } return success; } // Comprueba el valor de la variable 'quit' bool GameDirector::exit() { return mProg.quit; } // Establece el valor de la variable void GameDirector::setExecutablePath(std::string path) { mProg.executablePath = path.substr(0, path.find_last_of("\\/")); } // Inicializa las formaciones enemigas void GameDirector::initEnemyFormations() { const int y4 = (PLAY_AREA_TOP - BLOCK); const int x4_0 = PLAY_AREA_LEFT; //const int x4_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_4 / 2); const int x4_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_4; const int y3 = (PLAY_AREA_TOP - BLOCK); const int x3_0 = PLAY_AREA_LEFT; //const int x3_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_3 / 2); const int x3_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_3; const int y2 = (PLAY_AREA_TOP - BLOCK); const int x2_0 = PLAY_AREA_LEFT; //const int x2_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_2 / 2); const int x2_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_2; const int y1 = (PLAY_AREA_TOP - BLOCK); const int x1_0 = PLAY_AREA_LEFT; const int x1_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_1 / 2); const int x1_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_1; 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 (Uint8 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 (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) { mEnemyFormation[j].init[i].x = PLAY_AREA_CENTER_FIRST_QUARTER_X - (BALLOON_SIZE_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_SIZE_2 + 1; incTime = 10; for (Uint8 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_SIZE_2 + 1; incTime = 10; for (Uint8 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_SIZE_3 * 2; incTime = 10; for (Uint8 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_SIZE_3 * 2; incTime = 10; for (Uint8 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_SIZE_3 + 1; incTime = 10; for (Uint8 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_SIZE_3 + 1; incTime = 10; for (Uint8 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_SIZE_1 + 1; incTime = 10; for (Uint8 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_SIZE_1 + 1; incTime = 10; for (Uint8 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_SIZE_4 + 1; incTime = 15; for (Uint8 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_SIZE_4 + 1; incTime = 15; for (Uint8 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_SIZE_2 + 1; incTime = 10; for (Uint8 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_SIZE_2 + 1; incTime = 10; for (Uint8 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_SIZE_3 * 2; incTime = 10; for (Uint8 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_SIZE_3 * 2; incTime = 10; for (Uint8 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_SIZE_3 + 1; incTime = 10; for (Uint8 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_SIZE_3 + 1; incTime = 10; for (Uint8 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_SIZE_1 + 1; incTime = 10; for (Uint8 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_SIZE_1 + 1; incTime = 10; for (Uint8 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_SIZE_4 + 1; incTime = 0; for (Uint8 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_SIZE_2 + 1; incTime = 3; for (Uint8 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_SIZE_3 * 2; incTime = 10; for (Uint8 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_SIZE_3 + 1; incTime = 10; for (Uint8 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 (Uint8 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_SIZE_1 + 1; incTime = 5; for (Uint8 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 (Uint8 k = 0; k < 50; k++) { mEnemyFormation[k + 50].numberOfEnemies = mEnemyFormation[k].numberOfEnemies; for (Uint8 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 GameDirector::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 GameDirector::initGameStages() { // STAGE 1 mGame.stage[0].number = 1; mGame.stage[0].currentPower = 0; mGame.stage[0].powerToComplete = 200; mGame.stage[0].minMenace = 7 + (4 * 1); mGame.stage[0].maxMenace = 7 + (4 * 3); mGame.stage[0].enemyPool = &mEnemyPool[0]; // STAGE 2 mGame.stage[1].number = 2; mGame.stage[1].currentPower = 0; mGame.stage[1].powerToComplete = 300; mGame.stage[1].minMenace = 7 + (4 * 2); mGame.stage[1].maxMenace = 7 + (4 * 4); mGame.stage[1].enemyPool = &mEnemyPool[1]; // STAGE 3 mGame.stage[2].number = 3; mGame.stage[2].currentPower = 0; mGame.stage[2].powerToComplete = 600; mGame.stage[2].minMenace = 7 + (4 * 3); mGame.stage[2].maxMenace = 7 + (4 * 6); mGame.stage[2].enemyPool = &mEnemyPool[2]; // STAGE 4 mGame.stage[3].number = 4; mGame.stage[3].currentPower = 0; mGame.stage[3].powerToComplete = 600; mGame.stage[3].minMenace = 7 + (4 * 3); mGame.stage[3].maxMenace = 7 + (4 * 7); mGame.stage[3].enemyPool = &mEnemyPool[3]; // STAGE 5 mGame.stage[4].number = 5; mGame.stage[4].currentPower = 0; mGame.stage[4].powerToComplete = 600; mGame.stage[4].minMenace = 7 + (4 * 4); mGame.stage[4].maxMenace = 7 + (4 * 8); mGame.stage[4].enemyPool = &mEnemyPool[4]; // STAGE 6 mGame.stage[5].number = 6; mGame.stage[5].currentPower = 0; mGame.stage[5].powerToComplete = 600; mGame.stage[5].minMenace = 7 + (4 * 5); mGame.stage[5].maxMenace = 7 + (4 * 10); mGame.stage[5].enemyPool = &mEnemyPool[5]; // STAGE 7 mGame.stage[6].number = 7; mGame.stage[6].currentPower = 0; mGame.stage[6].powerToComplete = 650; mGame.stage[6].minMenace = 7 + (4 * 6); mGame.stage[6].maxMenace = 7 + (4 * 11); mGame.stage[6].enemyPool = &mEnemyPool[6]; // STAGE 8 mGame.stage[7].number = 8; mGame.stage[7].currentPower = 0; mGame.stage[7].powerToComplete = 750; mGame.stage[7].minMenace = 7 + (4 * 7); mGame.stage[7].maxMenace = 7 + (4 * 12); mGame.stage[7].enemyPool = &mEnemyPool[7]; // STAGE 9 mGame.stage[8].number = 9; mGame.stage[8].currentPower = 0; mGame.stage[8].powerToComplete = 850; mGame.stage[8].minMenace = 7 + (4 * 8); mGame.stage[8].maxMenace = 7 + (4 * 13); mGame.stage[8].enemyPool = &mEnemyPool[8]; // STAGE 10 mGame.stage[9].number = 10; mGame.stage[9].currentPower = 0; mGame.stage[9].powerToComplete = 950; mGame.stage[9].minMenace = 7 + (4 * 9); mGame.stage[9].maxMenace = 7 + (4 * 15); mGame.stage[9].enemyPool = &mEnemyPool[9]; } // Crea una formación de enemigos void GameDirector::deployEnemyFormation() { // Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última if (mGame.enemyDeployCounter == 0) { mGame.enemyDeployCounter = 255; Uint8 set = (rand() % 10); if (set == mGame.lastEnemyDeploy) { set++; set %= 10; } mGame.lastEnemyDeploy = set; if (mDebug.enabled) set = mDebug.enemySet; Uint8 numEnemies = mGame.stage[mGame.currentStage].enemyPool->set[set]->numberOfEnemies; for (Uint8 i = 0; i < numEnemies; i++) createNewBalloon(mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].x, mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].y, mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].kind, mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].velX, mGame.enemySpeed, mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].creationCounter, mTexture[TEXTURE_BALLOON].texture); } } // Aumenta el poder de la fase void GameDirector::increaseStageCurrentPower() { mGame.stage[mGame.currentStage].currentPower++; } // Establece el valor de la variable void GameDirector::setScore(Uint32 score) { mGame.score = score; } // Establece el valor de la variable void GameDirector::setHiScore(Uint32 score) { mGame.hiScore = score; } // Actualiza el valor de HiScore en caso necesario void GameDirector::updateHiScore() { // La puntuación es mayor que la máxima puntuación if (mGame.score > mGame.hiScore) { // Actualiza la máxima puntuación mGame.hiScore = mGame.score; // Almacena la máxima puntuación en el fichero junto con un checksum mGame.scoreDataFile[0] = mGame.hiScore; mGame.scoreDataFile[1] = mGame.hiScore % 43; // Si superamos la máxima puntuación if (mGame.hiScoreAchieved == false) { mGame.hiScoreAchieved = true; JA_PlaySound(mSound[SOUND_HISCORE].sound); } } } // Transforma un valor numérico en una cadena de 6 cifras std::string GameDirector::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 GameDirector::renderScoreBoard() { // Si el jugador esta muerto, no pintes el fondo del marcador, así que pase por encima cuando sale despedido if (mGame.player->isAlive()) mGame.scoreBoard->render(); if (!mDemo.enabled) { // SCORE mText.white->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_WORD_Y - 6, "SCORE", 0); mText.white->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_NUMBER_Y - 6, updateScoreText(mGame.score), 0); // HI-SCORE mText.white->writeCentered(PLAY_AREA_CENTER_THIRD_QUARTER_X, HISCORE_WORD_Y - 6, "HI-SCORE", 0); mText.white->writeCentered(PLAY_AREA_CENTER_THIRD_QUARTER_X, HISCORE_NUMBER_Y - 6, updateScoreText(mGame.hiScore), 0); // MULT mText.white->writeColored(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y - 6, "MULT", 0, 255, 0); mText.whiteX2->writeShadowed(PLAY_AREA_CENTER_X - 16, SCORE_WORD_Y + 5, std::to_string(mGame.player->getScoreMultiplier()).substr(0, 1), 0, 192, 0); mText.white->writeShadowed(PLAY_AREA_CENTER_X - 2, SCORE_WORD_Y + 12, ".", 0, 192, 0); mText.white->writeShadowed(PLAY_AREA_CENTER_X + 4, SCORE_WORD_Y + 12, std::to_string(mGame.player->getScoreMultiplier()).substr(2, 1), 0, 192, 0); // STAGE mText.white->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_NUMBER_Y + 4, "STAGE " + std::to_string(mGame.stage[mGame.currentStage].number), 0); // POWER mGame.powerMeter->setSpriteClip(256, 184, 40, 8); mGame.powerMeter->render(); float percent = (mGame.stage[mGame.currentStage].currentPower * 40.0f) / mGame.stage[mGame.currentStage].powerToComplete; mGame.powerMeter->setSpriteClip(296, 184, (int)percent, 8); mGame.powerMeter->render(); } else { mText.nokia->writeCentered(SCREEN_FIRST_QUARTER_X, SCORE_WORD_Y - 3, "A game by", -1); mText.nokia->writeCentered(SCREEN_FIRST_QUARTER_X, SCORE_NUMBER_Y, "JailDesigner", -1); mText.nokia->writeCentered(SCREEN_THIRD_QUARTER_X, HISCORE_WORD_Y - 3, "Intro music by", -1); mText.nokia->writeCentered(SCREEN_THIRD_QUARTER_X, HISCORE_NUMBER_Y, "JailDoctor", -1); } } // Actualiza las variables del jugador void GameDirector::updatePlayer() { mGame.player->update(); // Comprueba la colisión entre el jugador y los globos if (checkPlayerBalloonCollision()) { if (mGame.player->isAlive()) { if (mDemo.enabled) setProgSection(PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS); else killPlayer(); } } } // Actualiza las variables de la fase void GameDirector::updateStage() { if (mGame.stage[mGame.currentStage].currentPower >= mGame.stage[mGame.currentStage].powerToComplete) { // Cambio de fase mGame.currentStage++; JA_PlaySound(mSound[SOUND_STAGE_CHANGE].sound); mGame.stageBitmapCounter = 0; mGame.enemySpeed = BALLOON_SPEED_1; setBalloonSpeed(mGame.enemySpeed); mGame.effect.flash = true; mGame.effect.shake = true; } if (mGame.stageBitmapCounter < STAGE_COUNTER) { mGame.stageBitmapCounter++; } } // Actualiza el estado de muerte void GameDirector::updateDeath() { if (!mGame.player->isAlive()) { if (mGame.deathCounter > 0) { mGame.deathCounter--; if ((mGame.deathCounter == 250) || (mGame.deathCounter == 200) || (mGame.deathCounter == 180) || (mGame.deathCounter == 120) || (mGame.deathCounter == 60)) { Uint8 sound = rand() % 4; switch (sound) { case 0: JA_PlaySound(mSound[SOUND_BUBBLE1].sound, 0); break; case 1: JA_PlaySound(mSound[SOUND_BUBBLE2].sound, 0); break; case 2: JA_PlaySound(mSound[SOUND_BUBBLE3].sound, 0); break; case 3: JA_PlaySound(mSound[SOUND_BUBBLE4].sound, 0); break; default: break; } } // Animación if ((mGame.deathCounter / 5) % 2 == 0) { mGame.smartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); } else { mGame.smartSprite[mGame.deathIndex]->setSpriteClip(24, 0, 24, 24); } // Rebote en los laterales if (mGame.smartSprite[mGame.deathIndex]->getVelX() > 0) { if (mGame.smartSprite[mGame.deathIndex]->getPosX() > (SCREEN_WIDTH - mGame.smartSprite[mGame.deathIndex]->getWidth())) { mGame.smartSprite[mGame.deathIndex]->setPosX(SCREEN_WIDTH - mGame.smartSprite[mGame.deathIndex]->getWidth()); mGame.smartSprite[mGame.deathIndex]->setVelX(mGame.smartSprite[mGame.deathIndex]->getVelX() * (-1)); mGame.smartSprite[mGame.deathIndex]->setDestX(mGame.smartSprite[mGame.deathIndex]->getDestX() * (-1)); } } else { if (mGame.smartSprite[mGame.deathIndex]->getPosX() < 0) { mGame.smartSprite[mGame.deathIndex]->setPosX(0); mGame.smartSprite[mGame.deathIndex]->setVelX(mGame.smartSprite[mGame.deathIndex]->getVelX() * (-1)); mGame.smartSprite[mGame.deathIndex]->setDestX(mGame.smartSprite[mGame.deathIndex]->getDestX() * (-1)); } } } else { mGame.section = GAME_SECTION_GAMEOVER; } } } // Renderiza el fade final cuando se acaba la partida void GameDirector::renderDeathFade() { if (!mGame.player->isAlive() && (mGame.deathCounter < 150)) { // 192 / 6 = 32, 6 cuadrados de 32 pixeles SDL_Rect rect[12]; Uint8 h = (150 - mGame.deathCounter) / 3; SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255); for (Uint8 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]); } } } // Actualiza los globos void GameDirector::updateBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) mGame.balloon[i]->update(); } // Pinta en pantalla todos los globos activos void GameDirector::renderBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { mGame.balloon[i]->render(); if ((mDebug.enabled) && (mGame.balloon[i]->isPopping() == false)) mText.white->writeCentered(mGame.balloon[i]->getPosX() + (mGame.balloon[i]->getWidth() / 2), mGame.balloon[i]->getPosY() - 8, std::to_string(i), 0); } } // Devuelve el primer indice no activo del vector de globos Uint8 GameDirector::getBalloonFreeIndex() { Uint8 index = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) if (!mGame.balloon[i]->isEnabled()) { index = i; break; } return index; } // Crea un globo nuevo en el vector de globos Uint8 GameDirector::createNewBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 creationtimer, LTexture *texture) { Uint8 index = getBalloonFreeIndex(); mGame.balloon[index]->init(x, y, kind, velx, speed, creationtimer, texture, mRenderer); return index; } // Crea una PowerBall void GameDirector::createPowerBall() { const int y4 = (PLAY_AREA_TOP - BLOCK); const int x4_0 = PLAY_AREA_LEFT; const int x4_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_4 / 2); const int x4_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_4; const int x[3] = {x4_0, x4_50, x4_100}; int x4 = x[rand() % 3]; mGame.balloon[getBalloonFreeIndex()]->init(x4, y4, POWER_BALL, BALLOON_VELX_POSITIVE * (((rand() % 2) * 2) - 1), mGame.enemySpeed, 350, mTexture[TEXTURE_BALLOON].texture, mRenderer); } // Establece a cero todos los valores del vector de objetos globo void GameDirector::resetBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { mGame.balloon[i]->disable(); } } // Establece la velocidad de los globos void GameDirector::setBalloonSpeed(float speed) { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (mGame.balloon[i]->isEnabled()) mGame.balloon[i]->setSpeed(speed); } } // Incrementa la velocidad de los globos void GameDirector::incBalloonSpeed() { if (mGame.enemySpeed == BALLOON_SPEED_1) mGame.enemySpeed = BALLOON_SPEED_2; else if (mGame.enemySpeed == BALLOON_SPEED_2) mGame.enemySpeed = BALLOON_SPEED_3; else if (mGame.enemySpeed == BALLOON_SPEED_3) mGame.enemySpeed = BALLOON_SPEED_4; else if (mGame.enemySpeed == BALLOON_SPEED_4) mGame.enemySpeed = BALLOON_SPEED_5; setBalloonSpeed(mGame.enemySpeed); } // Decrementa la velocidad de los globos void GameDirector::decBalloonSpeed() { if (mGame.enemySpeed == BALLOON_SPEED_5) mGame.enemySpeed = BALLOON_SPEED_4; else if (mGame.enemySpeed == BALLOON_SPEED_4) mGame.enemySpeed = BALLOON_SPEED_3; else if (mGame.enemySpeed == BALLOON_SPEED_3) mGame.enemySpeed = BALLOON_SPEED_2; else if (mGame.enemySpeed == BALLOON_SPEED_2) mGame.enemySpeed = BALLOON_SPEED_1; setBalloonSpeed(mGame.enemySpeed); } // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase void GameDirector::updateBalloonSpeed() { float percent = (float)mGame.stage[mGame.currentStage].currentPower / (float)mGame.stage[mGame.currentStage].powerToComplete; if (mGame.enemySpeed == BALLOON_SPEED_1) { if (percent > 0.2f) incBalloonSpeed(); } else if (mGame.enemySpeed == BALLOON_SPEED_2) { if (percent > 0.4f) incBalloonSpeed(); } else if (mGame.enemySpeed == BALLOON_SPEED_3) { if (percent > 0.6f) incBalloonSpeed(); } else if (mGame.enemySpeed == BALLOON_SPEED_4) { if (percent > 0.8f) incBalloonSpeed(); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso void GameDirector::popBalloon(Uint8 index) { //if (mGame.balloon[index]->isActive()) { // Otorga los puntos correspondientes al globo mGame.player->addScore(Uint32(mGame.balloon[index]->getScore() * mGame.player->getScoreMultiplier())); setScore(mGame.player->getScore()); updateHiScore(); // Aumenta el poder de la fase increaseStageCurrentPower(); mGame.balloonsPopped++; Uint8 kind = mGame.balloon[index]->getKind(); Uint8 freeIndex = 0; switch (kind) { // Si es del tipo más pequeño, simplemente elimina el globo case BALLOON_1: mGame.balloon[index]->pop(); break; case HEXAGON_1: mGame.balloon[index]->pop(); break; // Si es del tipo PowerBall, destruye todos los globos case POWER_BALL: destroyAllBalloons(); break; // En cualquier otro caso, crea dos globos de un tipo inferior default: freeIndex = getBalloonFreeIndex(); mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_NEGATIVE, mGame.enemySpeed, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mGame.balloon[freeIndex]->allignTo(mGame.balloon[index]->getPosX() + (mGame.balloon[index]->getWidth() / 2)); if (mGame.balloon[freeIndex]->getClass() == BALLOON_CLASS) mGame.balloon[freeIndex]->setVelY(-2.50f); else if (mGame.balloon[freeIndex]->getClass() == HEXAGON_CLASS) mGame.balloon[freeIndex]->setVelY(BALLOON_VELX_NEGATIVE); freeIndex = getBalloonFreeIndex(); mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_POSITIVE, mGame.enemySpeed, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mGame.balloon[freeIndex]->allignTo(mGame.balloon[index]->getPosX() + (mGame.balloon[index]->getWidth() / 2)); if (mGame.balloon[freeIndex]->getClass() == BALLOON_CLASS) mGame.balloon[freeIndex]->setVelY(-2.50f); else if (mGame.balloon[freeIndex]->getClass() == HEXAGON_CLASS) mGame.balloon[freeIndex]->setVelY(BALLOON_VELX_NEGATIVE); // Elimina el globo mGame.balloon[index]->pop(); break; } // Recalcula el nivel de amenaza setMenace(); } } // Explosiona un globo. Lo destruye void GameDirector::destroyBalloon(Uint8 index) { //if (mGame.balloon[index]->isActive()) { // Otorga los puntos correspondientes al globo mGame.player->addScore(Uint32(mGame.balloon[index]->getScore() * mGame.player->getScoreMultiplier())); setScore(mGame.player->getScore()); updateHiScore(); // Aumenta el poder de la fase increaseStageCurrentPower(); mGame.balloonsPopped++; // Destruye el globo mGame.balloon[index]->pop(); // Recalcula el nivel de amenaza setMenace(); } } // Explosiona todos los globos void GameDirector::popAllBalloons() { int candidate[MAX_BALLOONS]; Uint8 j = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if ((mGame.balloon[i]->isEnabled()) && (!mGame.balloon[i]->isPopping()) && (!mGame.balloon[i]->isBeingCreated())) candidate[j] = i; else candidate[j] = -1; j++; } for (Uint8 i = 0; i < MAX_BALLOONS; i++) if (candidate[i] >= 0) popBalloon(i); JA_PlaySound(mSound[SOUND_BALLOON].sound); } // Destruye todos los globos void GameDirector::destroyAllBalloons() { int candidate[MAX_BALLOONS]; Uint8 j = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if ((mGame.balloon[i]->isEnabled()) && (!mGame.balloon[i]->isPopping()) && (!mGame.balloon[i]->isBeingCreated())) candidate[j] = i; else candidate[j] = -1; j++; } for (Uint8 i = 0; i < MAX_BALLOONS; i++) if (candidate[i] >= 0) destroyBalloon(i); JA_PlaySound(mSound[SOUND_BALLOON].sound); mGame.effect.flash = true; mGame.effect.shake = true; } // Detiene todos los globos void GameDirector::stopAllBalloons(Uint16 time) { for (Uint8 i = 0; i < MAX_BALLOONS; i++) if (mGame.balloon[i]->isEnabled()) { mGame.balloon[i]->setStop(true); mGame.balloon[i]->setStoppedTimer(time); } } // Pone en marcha todos los globos void GameDirector::startAllBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) if ((mGame.balloon[i]->isEnabled()) && (!mGame.balloon[i]->isBeingCreated())) { mGame.balloon[i]->setStop(false); mGame.balloon[i]->setStoppedTimer(0); } } // Obtiene el numero de globos activos Uint8 GameDirector::countBalloons() { Uint8 num = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) if (mGame.balloon[i]->isEnabled()) if (!mGame.balloon[i]->isPopping()) num++; return num; } // Comprueba la colisión entre el jugador y los globos activos bool GameDirector::checkPlayerBalloonCollision() { bool result = false; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if ((mGame.balloon[i]->isEnabled()) && !(mGame.balloon[i]->isStopped()) && !(mGame.balloon[i]->isInvulnerable())) if (checkCollision(mGame.player->getCollider(), mGame.balloon[i]->getCollider())) { result = true; break; } } return result; } // Comprueba la colisión entre el jugador y los items void GameDirector::checkPlayerItemCollision() { if (mGame.player->isAlive()) for (Uint8 i = 0; i < MAX_ITEMS; i++) { if (mGame.item[i]->isEnabled()) { if (checkCollision(mGame.player->getCollider(), mGame.item[i]->getCollider())) { switch (mGame.item[i]->getClass()) { case ITEM_POINTS_1_DISK: mGame.player->addScore(1000); setScore(mGame.player->getScore()); updateHiScore(); createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._1000Bitmap->getWidth() / 2), mGame.player->getPosY(), mGame._1000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_2_GAVINA: mGame.player->addScore(2500); setScore(mGame.player->getScore()); updateHiScore(); createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._2500Bitmap->getWidth() / 2), mGame.player->getPosY(), mGame._2500Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_3_PACMAR: mGame.player->addScore(5000); setScore(mGame.player->getScore()); updateHiScore(); createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._5000Bitmap->getWidth() / 2), mGame.player->getPosY(), mGame._5000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_CLOCK: enableTimeStopItem(); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_TNT: popAllBalloons(); setExplosionTime(true); JA_PlaySound(mSound[SOUND_TITLE].sound); break; case ITEM_COFFEE: mGame.player->giveExtraHit(); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; default: break; } mGame.item[i]->erase(); } } } } // Comprueba y procesa la colisión entre las balas y los globos void GameDirector::checkBulletBalloonCollision() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) for (Uint8 j = 0; j < MAX_BULLETS; j++) if (mGame.balloon[i]->isEnabled() && (!mGame.balloon[i]->isInvulnerable()) && mGame.bullet[j]->isActive()) if (checkCollision(mGame.balloon[i]->getCollider(), mGame.bullet[j]->getCollider())) { mGame.player->incScoreMultiplier(); popBalloon(i); if (!mDemo.enabled) JA_PlaySound(mSound[SOUND_BALLOON].sound); mGame.bullet[j]->erase(); setMenace(); Uint8 droppeditem = dropItem(); if ((droppeditem != NO_KIND) && !(mDemo.enabled) && !(mDemo.recording)) { if (droppeditem == ITEM_POWER_BALL) { createPowerBall(); } else { createItem(mGame.balloon[i]->getPosX(), mGame.balloon[i]->getPosY(), droppeditem); JA_PlaySound(mSound[SOUND_ITEM_DROP].sound); } } break; } } // Mueve las balas activas void GameDirector::moveBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { if (mGame.bullet[i]->isActive()) { if (mGame.bullet[i]->move() == MSG_BULLET_OUT) { mGame.player->decScoreMultiplier(); } } } } // Pinta las balas activas void GameDirector::renderBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { if (mGame.bullet[i]->isActive()) { mGame.bullet[i]->render(); } } } // Devuelve el primer indice no activo del vector de balas Uint8 GameDirector::getBulletFreeIndex() { Uint8 index = 0; for (int i = 0; i < MAX_BULLETS; i++) { if (mGame.bullet[i]->isActive() == false) { index = i; break; } } return index; } // Establece a cero todos los valores del vector de objetos bala void GameDirector::resetBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { mGame.bullet[i]->erase(); } } // Crea un objeto bala void GameDirector::createBullet(int x, int y, Uint8 kind) { mGame.bullet[getBulletFreeIndex()]->init(x, y, kind, mTexture[TEXTURE_BULLET].texture, mRenderer); } // Actualiza los items void GameDirector::updateItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { mGame.item[i]->update(); } } // Pinta los items activos void GameDirector::renderItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { mGame.item[i]->render(); } } // Devuelve el primer indice no activo del vector de items Uint8 GameDirector::getItemFreeIndex() { Uint8 index = 0; for (int i = 0; i < MAX_ITEMS; i++) { if (mGame.item[i]->getClass() == NO_KIND) { index = i; break; } } return index; } // Establece a cero todos los valores del vector de objetos item void GameDirector::resetItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { mGame.item[i]->erase(); } } // Devuelve un item en función del azar Uint8 GameDirector::dropItem() { //return ITEM_COFFEE; Uint8 luckyNumber = rand() % 99; Uint8 item = rand() % 7; switch (item) { case 0: if (luckyNumber < 10) return ITEM_POINTS_1_DISK; break; case 1: if (luckyNumber < 6) return ITEM_POINTS_2_GAVINA; break; case 2: if (luckyNumber < 3) return ITEM_POINTS_3_PACMAR; break; case 3: if (luckyNumber < 5) return ITEM_CLOCK; break; case 4: if (luckyNumber < 5) return NO_KIND; break; case 5: if (luckyNumber < 5) return ITEM_COFFEE; break; case 6: if (luckyNumber < 5) if (countBalloons() > 10) return ITEM_POWER_BALL; break; default: break; } return NO_KIND; } // Crea un objeto item void GameDirector::createItem(int x, int y, Uint8 kind) { mGame.item[getItemFreeIndex()]->init(kind, x, y, mTexture[TEXTURE_ITEMS].texture, mRenderer); } // Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto void GameDirector::createItemScoreSprite(int x, int y, SmartSprite *sprite) { Uint8 index = getSmartSpriteFreeIndex(); *mGame.smartSprite[index] = *sprite; mGame.smartSprite[index]->setPosX(x); mGame.smartSprite[index]->setPosY(y); mGame.smartSprite[index]->setDestX(x); mGame.smartSprite[index]->setDestY(y - 15); mGame.smartSprite[index]->setEnabled(true); mGame.smartSprite[index]->setEnabledTimer(100); } // Crea un objeto de bonus en función del azar void GameDirector::dropBonus() { } // Dibuja el efecto de flash void GameDirector::renderFlashEffect() { if (mGame.effect.flash) { // Pantallazo blanco SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(mRenderer); mGame.effect.flash = false; } } // Actualiza el efecto de agitar la pantalla void GameDirector::updateShakeEffect() { if (mGame.effect.shake) { if (mGame.effect.shakeCounter > 0) mGame.effect.shakeCounter--; else mGame.effect.shake = false; } } // Crea un SmartSprite para arrojar el item café al recibir un impacto void GameDirector::throwCoffee(int x, int y) { Uint8 index = getSmartSpriteFreeIndex(); mGame.smartSprite[index]->init(mTexture[TEXTURE_ITEMS].texture, mRenderer); mGame.smartSprite[index]->setPosX(x - 8); mGame.smartSprite[index]->setPosY(y - 8); mGame.smartSprite[index]->setWidth(16); mGame.smartSprite[index]->setHeight(16); mGame.smartSprite[index]->setVelX(-1.0f + ((rand() % 5) * 0.5f)); mGame.smartSprite[index]->setVelY(-4.0f); mGame.smartSprite[index]->setAccelX(0.0f); mGame.smartSprite[index]->setAccelY(0.2f); mGame.smartSprite[index]->setDestX(x + (mGame.smartSprite[index]->getVelX() * 50)); mGame.smartSprite[index]->setDestY(SCREEN_HEIGHT + 1); mGame.smartSprite[index]->setEnabled(true); mGame.smartSprite[index]->setEnabledTimer(1); mGame.smartSprite[index]->setSpriteClip(80, 16, 16, 16); } // Crea un SmartSprite para arrojar el item café al recibir un impacto void GameDirector::throwPlayer(int x, int y) { int sentit = ((rand() % 2) ? 1 : -1); mGame.deathIndex = getSmartSpriteFreeIndex(); mGame.smartSprite[mGame.deathIndex]->init(mTexture[TEXTURE_PLAYER_DEATH].texture, mRenderer); mGame.smartSprite[mGame.deathIndex]->setPosX(x); mGame.smartSprite[mGame.deathIndex]->setPosY(y); mGame.smartSprite[mGame.deathIndex]->setWidth(24); mGame.smartSprite[mGame.deathIndex]->setHeight(24); mGame.smartSprite[mGame.deathIndex]->setVelX(2.0f * sentit); mGame.smartSprite[mGame.deathIndex]->setVelY(-5.0f); mGame.smartSprite[mGame.deathIndex]->setAccelX(0.0f); mGame.smartSprite[mGame.deathIndex]->setAccelY(0.2f); mGame.smartSprite[mGame.deathIndex]->setDestX(SCREEN_WIDTH * sentit); mGame.smartSprite[mGame.deathIndex]->setDestY(SCREEN_HEIGHT + 1); mGame.smartSprite[mGame.deathIndex]->setEnabled(true); mGame.smartSprite[mGame.deathIndex]->setEnabledTimer(1); mGame.smartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); } // Actualiza los SmartSprites void GameDirector::updateSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) { mGame.smartSprite[i]->update(); } } // Pinta los SmartSprites activos void GameDirector::renderSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) { mGame.smartSprite[i]->render(); } } // Devuelve el primer indice no activo del vector de SmartSprites Uint8 GameDirector::getSmartSpriteFreeIndex() { Uint8 index = 0; for (int i = 0; i < MAX_SMART_SPRITES; i++) { if (mGame.smartSprite[i]->isEnabled() == false) { index = i; break; } } return index; } // Establece a cero todos los valores del vector de objetos SmafrtSprite void GameDirector::resetSmartSprites() { for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) { mGame.smartSprite[i]->erase(); } } #ifndef UNUSED // Deshabilita todas las gotas de café void GameDirector::resetCoffeeDrops() { for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) { mCoffeeDrop[i]->disable(); } } // Actualiza las gotas de cafe void GameDirector::updateCoffeeDrops() { for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) { mCoffeeDrop[i]->update(); } } // Dibuja las gotas de cafe void GameDirector::renderCoffeeDrops() { for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) { mCoffeeDrop[i]->render(); } } // Devuelve el primer indice libre del vector de CoffeeDrops Uint8 GameDirector::getCoffeDropFreeIndex() { Uint8 index = 0; for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) { if (!(mCoffeeDrop[i]->isEnabled())) { index = i; break; } } return index; } // Crea un numero de gotas de cafe void GameDirector::createCoffeDrops(Uint8 num, int x, int y) { for (Uint8 i = 0; i < num; i++) { mCoffeeDrop[getCoffeDropFreeIndex()]->init(mTexture[TEXTURE_GAME_BG].texture, mRenderer, x, y, ((rand() % 7) * 0.5f) - 1.5f, (rand() % 7) * (-0.5f) + 1.0f, PLAY_AREA_BOTTOM); } } #endif // Acciones a realizar cuando el jugador muere void GameDirector::killPlayer() { if (!mGame.player->isInvulnerable()) { if (mGame.player->hasExtraHit()) { mGame.player->removeExtraHit(); throwCoffee(mGame.player->getPosX() + (mGame.player->getWidth() / 2), mGame.player->getPosY() + (mGame.player->getHeight() / 2)); JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); } else { stopAllBalloons(10); // 5000 JA_StopMusic(); JA_PlaySound(mSound[SOUND_PLAYER_COLLISION].sound); shakeScreen(); SDL_Delay(500); // 1000 JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); throwPlayer(mGame.player->getPosX(), mGame.player->getPosY()); mGame.player->setAlive(false); } } } // Obtiene el valor de la variable Uint8 GameDirector::getSubsection() { return mProg.subsection; } // Calcula y establece el valor de amenaza en funcion de los globos activos void GameDirector::setMenace() { mGame.menaceCurrent = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) if (mGame.balloon[i]->isEnabled()) mGame.menaceCurrent += mGame.balloon[i]->getMenace(); } // Obtiene el valor de la variable Uint8 GameDirector::getMenace() { return mGame.menaceCurrent; } // Establece el valor de la variable void GameDirector::setTimeStopped(bool value) { mGame.timeStopped = value; } // Obtiene el valor de la variable bool GameDirector::isTimeStopped() { return mGame.timeStopped; } // Establece el valor de la variable void GameDirector::setTimeStoppedCounter(Uint16 value) { mGame.timeStoppedCounter = value; } // Actualiza y comprueba el valor de la variable void GameDirector::updateTimeStoppedCounter() { if (isTimeStopped()) { if (mGame.timeStoppedCounter > 0) { mGame.timeStoppedCounter--; stopAllBalloons(TIME_STOPPED_COUNTER); } else { disableTimeStopItem(); } } } // Establece el valor de la variable void GameDirector::setExplosionTime(bool value) { mGame.explosionTime = value; } // Obtiene el valor de la variable bool GameDirector::isExplosionTime() { return mGame.explosionTime; } // Establece el valor de la variable void GameDirector::setRemainingExplosions(Uint8 value) { mGame.remainingExplosions = value; } // Actualiza y comprueba el valor de la variable void GameDirector::updateRemainingExplosionsCounter() { if (isExplosionTime()) { if (mGame.remainingExplosionsCounter > 0) { --mGame.remainingExplosionsCounter; } else if (mGame.remainingExplosions > 0) { popAllBalloons(); --mGame.remainingExplosions; mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; } else { mGame.explosionTime = false; mGame.remainingExplosions = REMAINING_EXPLOSIONS; mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; } } } // Actualiza la variable EnemyDeployCounter void GameDirector::updateEnemyDeployCounter() { if (mGame.enemyDeployCounter > 0) mGame.enemyDeployCounter--; } // Actualiza el campo de juego void GameDirector::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 mGame.currentStage updateStage(); // Actualiza el estado de muerte updateDeath(); // Actualiza los SmartSprites updateSmartSprites(); // Actualiza los contadores de estado y efectos updateTimeStoppedCounter(); updateRemainingExplosionsCounter(); updateEnemyDeployCounter(); updateShakeEffect(); // Comprueba las colisiones entre globos y balas checkBulletBalloonCollision(); // Comprueba las colisiones entre elk jugador y los items checkPlayerItemCollision(); // Comprueba el nivel de amenaza updateMenace(); // Actualiza la velocidad de los enemigos updateBalloonSpeed(); } // Actualiza el fondo void GameDirector::updateBackground() { mGame.clouds1a->move(); mGame.clouds1b->move(); mGame.clouds2a->move(); mGame.clouds2b->move(); if (mGame.clouds1a->getPosX() < -mGame.clouds1a->getWidth()) mGame.clouds1a->setPosX(mGame.clouds1a->getWidth()); if (mGame.clouds1b->getPosX() < -mGame.clouds1b->getWidth()) mGame.clouds1b->setPosX(mGame.clouds1b->getWidth()); if (mGame.clouds2a->getPosX() < -mGame.clouds2a->getWidth()) mGame.clouds2a->setPosX(mGame.clouds2a->getWidth()); if (mGame.clouds2b->getPosX() < -mGame.clouds2b->getWidth()) mGame.clouds2b->setPosX(mGame.clouds2b->getWidth()); mGame.grass->setSpriteClip(256, 85 + (6 * (mGame.counter / 20 % 2)), SCREEN_WIDTH, 6); if (mGame.effect.shake) mGame.background->setPosX(((mGame.effect.shakeCounter % 2) * 2) - 1); else mGame.background->setPosX(0); } // Dibuja el fondo void GameDirector::renderBackground() { float gradientNumber = std::min(((float)mGame.balloonsPopped / 900.0f), 3.0f); float percent = gradientNumber - (int)gradientNumber; int alpha = std::max((255 - (int)(255 * percent)), 0); // Dibuja el gradiente 2 mGame.gradient->setSpriteClip(mGame.gradientRect[((int)gradientNumber + 1) % 4]); mGame.gradient->render(); // Dibuja el gradiente 1 con una opacidad cada vez menor mGame.gradient->setSpriteClip(mGame.gradientRect[(int)gradientNumber]); mTexture[TEXTURE_GAME_BG].texture->setAlpha(alpha); mGame.gradient->render(); mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); mGame.clouds1a->render(); mGame.clouds1b->render(); mGame.clouds2a->render(); mGame.clouds2b->render(); mGame.background->render(); mGame.grass->render(); } // Dibuja el campo de juego void GameDirector::renderPlayField() { renderBackground(); renderBalloons(); renderBullets(); renderItems(); renderSmartSprites(); mGame.player->render(); renderMessages(); renderDeathFade(); renderScoreBoard(); renderFlashEffect(); } // Gestiona el nivel de amenaza void GameDirector::old_updateMenace() { // Aumenta el nivel de amenaza en función de la puntuación mGame.menaceThreshold = 7 + (4 * (mGame.score / 10000)); // Si el nivel de amenza es inferior al umbral if (mGame.menaceCurrent < mGame.menaceThreshold) { Uint8 index = 0; // Obtiene el centro del jugador en el eje X int x = mGame.player->getPosX() + (mGame.player->getWidth() / 2); // Crea un globo sobre el jugador en dirección hacia el centro if (x < (PLAY_AREA_WIDTH / 2)) { index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_POSITIVE, mGame.enemySpeed, 400, mTexture[TEXTURE_BALLOON].texture); } else { index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_NEGATIVE, mGame.enemySpeed, 400, mTexture[TEXTURE_BALLOON].texture); } mGame.balloon[index]->allignTo(x); // Recalcula el nivel de amenaza con el nuevo globo setMenace(); } } // Gestiona el nivel de amenaza void GameDirector::updateMenace() { float percent = mGame.stage[mGame.currentStage].currentPower / mGame.stage[mGame.currentStage].powerToComplete; Uint8 difference = mGame.stage[mGame.currentStage].maxMenace - mGame.stage[mGame.currentStage].minMenace; // Aumenta el nivel de amenaza en función de la puntuación mGame.menaceThreshold = mGame.stage[mGame.currentStage].minMenace + (difference * percent); // Si el nivel de amenza es inferior al umbral if (mGame.menaceCurrent < mGame.menaceThreshold) { // Crea una formación de enemigos deployEnemyFormation(); // Recalcula el nivel de amenaza con el nuevo globo setMenace(); } } // Gestiona las entradas desde el mando de juego bool GameDirector::checkGameController(Uint8 state) { // No hay mando. Siempre devuelve falso salvo NO_INPUT que siempre es cierto if (!mGameControllerFound) { if (state == NO_INPUT) return true; else return false; } bool success = false; switch (state) { case NO_INPUT: success = !checkGameController(INPUT_UP) && !checkGameController(INPUT_DOWN) && !checkGameController(INPUT_LEFT) && !checkGameController(INPUT_RIGHT) && !checkGameController(INPUT_ACCEPT) && !checkGameController(INPUT_CANCEL) && !checkGameController(INPUT_PAUSE) && !checkGameController(INPUT_FIRE_UP) && !checkGameController(INPUT_FIRE_LEFT) && !checkGameController(INPUT_FIRE_RIGHT); break; case INPUT_UP: success = (SDL_JoystickGetAxis(mGameController, 1) < -JOYSTICK_DEAD_ZONE) || (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_DPAD_UP)); break; case INPUT_DOWN: success = (SDL_JoystickGetAxis(mGameController, 1) > JOYSTICK_DEAD_ZONE) || (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_DPAD_DOWN)); break; case INPUT_LEFT: success = (SDL_JoystickGetAxis(mGameController, 0) < -JOYSTICK_DEAD_ZONE) || (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_DPAD_LEFT)); break; case INPUT_RIGHT: success = (SDL_JoystickGetAxis(mGameController, 0) > JOYSTICK_DEAD_ZONE) || (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_DPAD_RIGHT)); break; case INPUT_ACCEPT: success = (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_B)); break; case INPUT_CANCEL: success = (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_A)); break; case INPUT_PAUSE: success = (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_START)); break; case INPUT_FIRE_UP: success = (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_Y)); break; case INPUT_FIRE_LEFT: success = (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_X)); break; case INPUT_FIRE_RIGHT: success = (SDL_JoystickGetButton(mGameController, SDL_CONTROLLER_BUTTON_B)); break; default: break; } return success; } // Gestiona la entrada durante el juego void GameDirector::checkGameInput() { // Obtiene el estado de las teclas pulsadas del teclado keystates = SDL_GetKeyboardState(NULL); 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) { if (mDemo.dataFile[mDemo.counter].left == 1) mGame.player->setInput(INPUT_LEFT); if (mDemo.dataFile[mDemo.counter].right == 1) mGame.player->setInput(INPUT_RIGHT); if (mDemo.dataFile[mDemo.counter].noInput == 1) mGame.player->setInput(NO_INPUT); if (mDemo.dataFile[mDemo.counter].fire == 1) if (mGame.player->canFire()) { mGame.player->setInput(INPUT_FIRE_UP); createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); mGame.player->setFireCooldown(10); } if (mDemo.dataFile[mDemo.counter].fireLeft == 1) if (mGame.player->canFire()) { mGame.player->setInput(INPUT_FIRE_LEFT); createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); mGame.player->setFireCooldown(10); } if (mDemo.dataFile[mDemo.counter].fireRight == 1) if (mGame.player->canFire()) { mGame.player->setInput(INPUT_FIRE_RIGHT); createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); mGame.player->setFireCooldown(10); } // Comprobamos la tecla o el botón de pausa/menu if ((keystates[mProg.keyboard.accept] != 0) || (keystates[mProg.keyboard.cancel] != 0) || (checkGameController(INPUT_PAUSE)) || (checkGameController(INPUT_ACCEPT)) || (checkGameController(INPUT_CANCEL))) { setProgSection(PROG_SECTION_TITLE); } if (mDemo.counter < TOTAL_DEMO_DATA) mDemo.counter++; else setProgSection(PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS); } // Modo Demo no activo else if (mGame.player->isAlive()) { // Tecla izquierda o el mando hacia la izquierda if ((keystates[mProg.keyboard.left] != 0) || (checkGameController(INPUT_LEFT))) { mGame.player->setInput(INPUT_LEFT); mDemo.keys.left = 1; } else { // Tecla derecha o el mando hacia la derecha if ((keystates[mProg.keyboard.right] != 0) || (checkGameController(INPUT_RIGHT))) { mGame.player->setInput(INPUT_RIGHT); mDemo.keys.right = 1; } else { // Ninguna de las dos direcciones pulsadas mGame.player->setInput(NO_INPUT); mDemo.keys.noInput = 1; } } // Comprueba la tecla o el botón de disparo central if ((keystates[mProg.keyboard.fire] != 0) || (checkGameController(INPUT_FIRE_UP))) { if (mGame.player->canFire()) { mGame.player->setInput(INPUT_FIRE_UP); createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); mGame.player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); mDemo.keys.fire = 1; } } // Comprueba la tecla o el botón de disparo izquierdo if ((keystates[mProg.keyboard.fireLeft] != 0) || (checkGameController(INPUT_FIRE_LEFT))) { if (mGame.player->canFire()) { mGame.player->setInput(INPUT_FIRE_LEFT); createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_LEFT); mGame.player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); mDemo.keys.fireLeft = 1; } } // Comprueba la tecla o el botón de disparo derecho if ((keystates[mProg.keyboard.fireRight] != 0) || (checkGameController(INPUT_FIRE_RIGHT))) { if (mGame.player->canFire()) { mGame.player->setInput(INPUT_FIRE_RIGHT); createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_RIGHT); mGame.player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); mDemo.keys.fireRight = 1; } } // Para la pausa actuaremos cuando se suelte la tecla de pausa // Comprueba la tecla o el botón de pausa/menu y su buffer if (((keystates[mProg.keyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mProg.keyboardBuffer.pause == 0)) //|| ((mGameControllerFound) && (SDL_JoystickGetButton(mGameController, BUTTON_START)))) { // Se acaba de pulsar la tecla. Lo anotamos en su buffer mProg.keyboardBuffer.pause = 1; } else { // Aqui sigue la tecla pulsada, mantenemos el buffer activo if (((keystates[mProg.keyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mProg.keyboardBuffer.pause != 0)) { mProg.keyboardBuffer.pause = 1; } else { // Se ha soltado la tecla, procedemos a ejecutar la accion if (((keystates[mProg.keyboard.pause] == 0) || (!checkGameController(INPUT_PAUSE))) && (mProg.keyboardBuffer.pause != 0)) { mGame.section = GAME_SECTION_PAUSE; if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); } mProg.keyboardBuffer.pause = 0; } } } if (mDemo.counter < TOTAL_DEMO_DATA) { if (mDemo.recording) { mDemo.dataFile[mDemo.counter] = mDemo.keys; } mDemo.counter++; } else if (mDemo.recording) { mProg.quit = true; ; } } } // Gestiona la entrada de teclado y mando durante el menu void GameDirector::checkMenuInput(Menu *menu) { // Obtiene el estado de las teclas pulsadas del teclado keystates = SDL_GetKeyboardState(NULL); if (!mMenu.keyPressed) { // Tecla arriba o el mando hacia arriba if ((keystates[mProg.keyboard.up] != 0) || (checkGameController(INPUT_UP))) { if (menu->checkInput(INPUT_UP)) { mMenu.keyPressed = true; JA_PlaySound(mSound[SOUND_MENU_MOVE].sound); } } // Tecla abajo o el mando hacia abajo if ((keystates[mProg.keyboard.down] != 0) || (checkGameController(INPUT_DOWN))) { if (menu->checkInput(INPUT_DOWN)) { mMenu.keyPressed = true; JA_PlaySound(mSound[SOUND_MENU_MOVE].sound); } } // Tecla o el botón de aceptar if ((keystates[mProg.keyboard.accept] != 0) || (checkGameController(INPUT_ACCEPT))) { menu->checkInput(INPUT_ACCEPT); mMenu.keyPressed = true; } // Tecla o el botón de cancelar if ((keystates[mProg.keyboard.cancel] != 0) || (checkGameController(INPUT_CANCEL))) { menu->checkInput(INPUT_CANCEL); mMenu.keyPressed = true; } } // Ningún botón, libera la variable if ((keystates[mProg.keyboard.up] == 0) && (keystates[mProg.keyboard.down] == 0) && (keystates[mProg.keyboard.accept] == 0) && (keystates[mProg.keyboard.cancel] == 0) && (checkGameController(NO_INPUT))) { mMenu.keyPressed = false; } } // Obtiene el valor de la variable Uint8 GameDirector::getProgSection() { return mProg.section; } // Establece el valor de la variable void GameDirector::setProgSection(Uint8 section, Uint8 subsection) { mProg.section = section; mProg.subsection = subsection; } // Pinta una transición en pantalla void GameDirector::renderFade(Uint8 index) { SDL_Rect rect1; SDL_Rect rect2; Uint8 R, G, B; // Copia el backbuffer al renderer SDL_SetRenderTarget(mRenderer, NULL); SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); SDL_RenderPresent(mRenderer); SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); SDL_RenderPresent(mRenderer); switch (index) { case 0: rect1 = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; R = 0x27; G = 0x27; B = 0x36; for (int i = 0; i < 256; i += 4) { // Dibujamos sobre el renderizador SDL_SetRenderTarget(mRenderer, NULL); // Copia el backbuffer con la imagen que había al renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); SDL_SetRenderDrawColor(mRenderer, R, G, B, i); SDL_RenderFillRect(mRenderer, &rect1); // Vuelca el renderizador en pantalla SDL_RenderPresent(mRenderer); } // Deja todos los bufferes del mismo color SDL_SetRenderTarget(mRenderer, mBackbuffer); SDL_SetRenderDrawColor(mRenderer, R, G, B, 255); SDL_RenderClear(mRenderer); SDL_SetRenderTarget(mRenderer, NULL); SDL_SetRenderDrawColor(mRenderer, R, G, B, 255); SDL_RenderClear(mRenderer); break; case 1: rect1 = {0, 0, SCREEN_WIDTH, 0}; rect2 = {0, 0, SCREEN_WIDTH, 0}; R = 0x27; G = 0x27; B = 0x36; SDL_SetRenderDrawColor(mRenderer, R, G, B, 64); for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i += 4) { // Dibujamos sobre el backbuffer SDL_SetRenderTarget(mRenderer, mBackbuffer); rect1.h = rect2.h = i; rect2.y = SCREEN_HEIGHT - (i); SDL_RenderFillRect(mRenderer, &rect1); SDL_RenderFillRect(mRenderer, &rect2); // Volvemos a usar el renderizador de forma normal SDL_SetRenderTarget(mRenderer, NULL); // Copiamos el backbuffer al renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Volcamos el renderizador en pantalla SDL_RenderPresent(mRenderer); } rect1 = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; for (Uint8 i = 0; i < 32; i++) { // Dibujamos sobre el backbuffer SDL_SetRenderTarget(mRenderer, mBackbuffer); SDL_RenderFillRect(mRenderer, &rect1); // Volvemos a usar el renderizador de forma normal SDL_SetRenderTarget(mRenderer, NULL); // Copiamos el backbuffer al renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Volcamos el renderizador en pantalla SDL_RenderPresent(mRenderer); } break; case 2: rect1 = {0, 0, 32, 32}; for (Uint16 i = 0; i < 50; i++) { // Crea un color al azar R = 255 * (rand() % 2); G = 255 * (rand() % 2); B = 255 * (rand() % 2); SDL_SetRenderDrawColor(mRenderer, R, G, B, 64); // Dibujamos sobre el backbuffer SDL_SetRenderTarget(mRenderer, mBackbuffer); rect1.x = rand() % (SCREEN_WIDTH - rect1.w); rect1.y = rand() % (SCREEN_HEIGHT - rect1.h); SDL_RenderFillRect(mRenderer, &rect1); // Volvemos a usar el renderizador de forma normal SDL_SetRenderTarget(mRenderer, NULL); // Copiamos el backbuffer al renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Volcamos el renderizador en pantalla SDL_RenderPresent(mRenderer); SDL_Delay(100); } break; default: break; } } // Pinta diferentes mensajes en la pantalla void GameDirector::renderMessages() { // GetReady if (mGame.counter < STAGE_COUNTER) { mGame.getReadyBitmap->setPosX((int)mGame.getReadyBitmapPath[mGame.counter]); mGame.getReadyBitmap->render(); } // Time Stopped if (mGame.timeStopped) { if ((mGame.timeStoppedCounter > 100) || (mGame.timeStoppedCounter % 10 > 4)) { mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "Time Stopped: " + std::to_string(mGame.timeStoppedCounter / 10)); mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "Time Stopped: " + std::to_string(mGame.timeStoppedCounter / 10)); } } // D E M O if (mDemo.enabled) { if (mDemo.counter % 30 > 14) { mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "D E M O"); mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "D E M O"); } } // STAGE NUMBER if (mGame.stageBitmapCounter < STAGE_COUNTER) { mText.blackX2->writeCentered(PLAY_AREA_CENTER_X + 2, mGame.stageBitmapPath[mGame.stageBitmapCounter] + 2, "STAGE " + std::to_string(mGame.stage[mGame.currentStage].number)); mText.whiteX2->writeCentered(PLAY_AREA_CENTER_X, mGame.stageBitmapPath[mGame.stageBitmapCounter], "STAGE " + std::to_string(mGame.stage[mGame.currentStage].number)); } } // Habilita el efecto del item de detener el tiempo void GameDirector::enableTimeStopItem() { stopAllBalloons(TIME_STOPPED_COUNTER); setTimeStopped(true); setTimeStoppedCounter(TIME_STOPPED_COUNTER); if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); } } // Deshabilita el efecto del item de detener el tiempo void GameDirector::disableTimeStopItem() { mGame.timeStopped = false; mGame.timeStoppedCounter = TIME_STOPPED_COUNTER; startAllBalloons(); if (JA_GetMusicState() == JA_MUSIC_PAUSED) { JA_ResumeMusic(); } } // Cambia el valor de la variable de modo de pantalla completa void GameDirector::changeFullScreenMode() { switch (mOptions.fullScreenMode) { case 0: mOptions.fullScreenMode = SDL_WINDOW_FULLSCREEN; break; case SDL_WINDOW_FULLSCREEN: mOptions.fullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP; break; case SDL_WINDOW_FULLSCREEN_DESKTOP: mOptions.fullScreenMode = 0; break; default: mOptions.fullScreenMode = 0; break; } } // Actualiza los elementos del menu de opciones void GameDirector::updateOptionsMenu() { switch (mOptions.fullScreenMode) { case 0: mMenu.options->setItemCaption(0, "WINDOWED"); break; case SDL_WINDOW_FULLSCREEN: mMenu.options->setItemCaption(0, "FULLSCREEN"); break; case SDL_WINDOW_FULLSCREEN_DESKTOP: mMenu.options->setItemCaption(0, "FAKE FULLSCREEN"); break; default: mMenu.options->setItemCaption(0, "WINDOWED"); break; } mMenu.options->setItemCaption(1, "WINDOWS SIZE " + std::to_string(mOptions.windowSize)); mMenu.options->centerMenu(SCREEN_CENTER_X); mMenu.options->centerMenuElements(); } // Agita la pantalla void GameDirector::shakeScreen() { int v[] = {-1, 1, -1, 1, -1, 1, -1, 0}; for (Uint8 n = 0; n < 8; n++) { // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos mGame.background->setPosX(0); mGame.background->setWidth(1); mGame.background->setSpriteClip(0, 0, 1, 192); renderBackground(); mGame.background->setPosX(255); mGame.background->setSpriteClip(255, 0, 1, 192); mGame.background->render(); mGame.background->setPosX(v[n]); mGame.background->setWidth(256); mGame.background->setSpriteClip(0, 0, 256, 192); mGame.background->render(); mGame.grass->render(); renderBalloons(); renderBullets(); renderItems(); mGame.player->render(); renderScoreBoard(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); SDL_Delay(50); } } // Agita la pantalla void GameDirector::shakeScreen2() { int v[] = {-1, 1, -1, 1, -1, 1, -1, 0}; for (Uint8 n = 0; n < 8; n++) { // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos mGame.background->setPosX(0); mGame.background->setWidth(1); mGame.background->setSpriteClip(0, 0, 1, 192); renderBackground(); mGame.background->setPosX(255); mGame.background->setSpriteClip(255, 0, 1, 192); mGame.background->render(); mGame.background->setPosX(v[n]); mGame.background->setWidth(256); mGame.background->setSpriteClip(0, 0, 256, 192); mGame.background->render(); mGame.grass->render(); renderBalloons(); renderBullets(); renderItems(); mGame.player->render(); renderScoreBoard(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); SDL_Delay(50); } } // Bucle para el logo del juego void GameDirector::runLogo() { initLogo(); while ((mProg.section == PROG_SECTION_LOGO) && (!exit())) { // 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) { mProg.quit = true; break; } // Cualquier tecla pulsada if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { setProgSection(PROG_SECTION_TITLE); } } // Cambia el destino donde se pinta todo SDL_SetRenderTarget(mRenderer, mBackbuffer); // Limpia el destino SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255); SDL_RenderClear(mRenderer); // Dibuja los objetos mLogo.sprite->render(); // Dibuja el fade if (mLogo.counter >= 200) { SDL_Rect rect = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; Uint16 alpha = mLogo.counter - 200; if (alpha < 256) { SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, alpha); } else { SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255); // alpha - 0 trans, 255 opaco } SDL_RenderFillRect(mRenderer, &rect); } // Vuelve a usar el renderizador como destino SDL_SetRenderTarget(mRenderer, NULL); // Copia el backbufer al renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Actualiza la pantalla SDL_RenderPresent(mRenderer); // Comprueba si ha terminado el logo if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); if (mLogo.counter == 0) { if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_StopMusic(); } } if (mLogo.counter == 500) // minimo 200 + 255 { mLogo.counter = 0; setProgSection(PROG_SECTION_INTRO); } else { mLogo.counter++; } } } quitLogo(); } // Bucle para la intro del juego void GameDirector::runIntro() { initIntro(); // Si la música no está sonando if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { // Reproduce la música JA_PlayMusic(mMusic[MUSIC_INTRO].music, false); } while ((mProg.section == PROG_SECTION_INTRO) && (!exit())) { if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) { // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { mProg.quit = true; break; } if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { JA_StopMusic(); setProgSection(PROG_SECTION_TITLE); } } // Actualiza los objetos for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { mIntro.bitmap[i]->update(); } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntro.text[i]->update(); } // Guión de eventos // Primera imagen - UPV if (mIntro.events[BITMAP0] == EVENT_WAITING) { mIntro.bitmap[0]->setEnabled(true); mIntro.events[BITMAP0] = EVENT_RUNNING; } // Primer texto de la primera imagen if ((mIntro.events[BITMAP0] == EVENT_COMPLETED) && (mIntro.events[TEXT0] == EVENT_WAITING)) { mIntro.text[0]->setEnabled(true); mIntro.events[TEXT0] = EVENT_RUNNING; } // Segundo texto de la primera imagen if ((mIntro.events[TEXT0] == EVENT_COMPLETED) && (mIntro.events[TEXT1] == EVENT_WAITING)) { mIntro.text[0]->setEnabled(false); mIntro.text[1]->setEnabled(true); mIntro.events[TEXT1] = EVENT_RUNNING; } // Tercer texto de la primera imagen if ((mIntro.events[TEXT1] == EVENT_COMPLETED) && (mIntro.events[TEXT2] == EVENT_WAITING)) { mIntro.text[1]->setEnabled(false); mIntro.text[2]->setEnabled(true); mIntro.events[TEXT2] = EVENT_RUNNING; } // Segunda imagen - Máquina if ((mIntro.events[TEXT2] == EVENT_COMPLETED) && (mIntro.events[BITMAP1] == EVENT_WAITING)) { mIntro.bitmap[0]->setEnabled(false); mIntro.text[2]->setEnabled(false); mIntro.bitmap[1]->setEnabled(true); mIntro.events[BITMAP1] = EVENT_RUNNING; } // Primer texto de la segunda imagen if ((mIntro.events[BITMAP1] == EVENT_COMPLETED) && (mIntro.events[TEXT3] == EVENT_WAITING)) { mIntro.text[3]->setEnabled(true); mIntro.events[TEXT3] = EVENT_RUNNING; } // Tercera imagen junto con primer texto - GRITO if ((mIntro.events[TEXT3] == EVENT_COMPLETED) && (mIntro.events[BITMAP2] == EVENT_WAITING) && (mIntro.events[TEXT4] == EVENT_WAITING)) { mIntro.bitmap[1]->setEnabled(false); mIntro.text[3]->setEnabled(false); mIntro.bitmap[2]->setEnabled(true); mIntro.text[4]->setEnabled(true); mIntro.events[BITMAP2] = EVENT_RUNNING; mIntro.events[TEXT4] = EVENT_RUNNING; } // Cuarta imagen junto con primer texto - Reflexión if ((mIntro.events[TEXT4] == EVENT_COMPLETED) && (mIntro.events[BITMAP3] == EVENT_WAITING) && (mIntro.events[TEXT5] == EVENT_WAITING)) { mIntro.bitmap[2]->setEnabled(false); mIntro.text[4]->setEnabled(false); mIntro.bitmap[3]->setEnabled(true); mIntro.text[5]->setEnabled(true); mIntro.events[BITMAP3] = EVENT_RUNNING; mIntro.events[TEXT5] = EVENT_RUNNING; } // Segundo texto de la cuarta imagen if ((mIntro.events[TEXT5] == EVENT_COMPLETED) && (mIntro.events[TEXT6] == EVENT_WAITING)) { mIntro.text[5]->setEnabled(false); mIntro.text[6]->setEnabled(true); mIntro.events[TEXT6] = EVENT_RUNNING; } // Quinta imagen - Patada if ((mIntro.events[TEXT6] == EVENT_COMPLETED) && (mIntro.events[BITMAP4] == EVENT_WAITING)) { mIntro.bitmap[3]->setEnabled(false); mIntro.text[6]->setEnabled(false); mIntro.bitmap[4]->setEnabled(true); mIntro.events[BITMAP4] = EVENT_RUNNING; } // Primer texto de la quinta imagen if ((mIntro.events[BITMAP4] == EVENT_COMPLETED) && (mIntro.events[TEXT7] == EVENT_WAITING)) { mIntro.text[7]->setEnabled(true); mIntro.events[TEXT7] = EVENT_RUNNING; } // Sexta imagen junto con texto - Globos de café if ((mIntro.events[TEXT7] == EVENT_COMPLETED) && (mIntro.events[BITMAP5] == EVENT_WAITING) && (mIntro.events[TEXT8] == EVENT_WAITING)) { mIntro.bitmap[4]->setEnabled(false); mIntro.text[7]->setEnabled(false); mIntro.bitmap[5]->setEnabled(true); mIntro.text[8]->setEnabled(true); mIntro.events[BITMAP5] = EVENT_RUNNING; mIntro.events[TEXT8] = EVENT_RUNNING; } // Acaba el último texto if (mIntro.events[TEXT8] == EVENT_COMPLETED) { mIntro.text[8]->setEnabled(false); JA_StopMusic(); setProgSection(PROG_SECTION_TITLE); } } // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) mIntro.bitmap[i]->render(); for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) mIntro.text[i]->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } quitIntro(); } // Bucle para el titulo del juego void GameDirector::runTitle(Uint8 subsection) { Uint8 R = 0x27; Uint8 G = 0x27; Uint8 B = 0x36; initTitle(subsection); while ((mProg.section == PROG_SECTION_TITLE) && (!exit())) { // Sección 1 - Titulo desplazandose if (mTitle.section == TITLE_SECTION_1) { // 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) { mProg.quit = true; break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); // Actualiza los objetos mTitle.coffeeBitmap->update(); mTitle.crisisBitmap->update(); } // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, R, G, B, 255); SDL_RenderClear(mRenderer); // Dibuja los objetos mTitle.coffeeBitmap->render(); mTitle.crisisBitmap->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); // Si los objetos han llegado a su destino, cambiamos de Sección if ((mTitle.events[0] == EVENT_COMPLETED) && (mTitle.events[0] == EVENT_COMPLETED)) { mTitle.section = TITLE_SECTION_2; // Pantallazo blanco SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(mRenderer); SDL_RenderPresent(mRenderer); } } // Sección 2 - Titulo vibrando if (mTitle.section == TITLE_SECTION_2) { // 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) { mProg.quit = true; break; } } // Reproduce el efecto sonoro JA_PlaySound(mSound[SOUND_TITLE].sound); // Agita la pantalla int v[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0}; int a = mTitle.coffeeBitmap->getPosX(); int b = mTitle.crisisBitmap->getPosX(); for (Uint8 n = 0; n < 11 * 3; n++) { // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, R, G, B, 255); SDL_RenderClear(mRenderer); // Dibuja los objetos mTitle.coffeeBitmap->setPosX(a + v[n / 3]); mTitle.crisisBitmap->setPosX(b + v[n / 3]); mTitle.coffeeBitmap->render(); mTitle.crisisBitmap->render(); mTitle.dustBitmapR->animate(0); mTitle.dustBitmapL->animate(0); mTitle.dustBitmapR->render(); mTitle.dustBitmapL->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } mTitle.section = 2; } // Sección 3 - La pantalla de titulo con el menú y la música if (mTitle.section == TITLE_SECTION_3) { if (mTitle.counter > 0) { // 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) { mProg.quit = true; break; } if ((mEventHandler->type == SDL_KEYUP) || (mEventHandler->type == SDL_JOYBUTTONUP)) { mTitle.menuVisible = true; mTitle.counter = TITLE_COUNTER; } } // Si la música no está sonando if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { // Reproduce la música JA_PlayMusic(mMusic[MUSIC_TITLE].music, true); } // Calcula la lógica de los objetos if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.active->update(); } // Cambia el destino donde se pinta todo SDL_SetRenderTarget(mRenderer, mBackbuffer); // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, R, G, B, 255); SDL_RenderClear(mRenderer); // Pinta el tileado de fondo switch (mTitle.backgroundMode) { case 0: // El tileado de fondo se desplaza en diagonal mTitle.backgroundWindow.x++; mTitle.backgroundWindow.x %= 64; mTitle.backgroundWindow.y++; mTitle.backgroundWindow.y %= 64; break; case 1: // El tileado de fondo se desplaza en circulo ++mTitle.backgroundCounter %= 360; mTitle.backgroundWindow.x = 128 + (int(mSin[(mTitle.backgroundCounter + 270) % 360] * 128)); mTitle.backgroundWindow.y = 96 + (int(mSin[(360 - mTitle.backgroundCounter) % 360] * 96)); break; default: break; } SDL_RenderCopy(mRenderer, mTitleSurface, &mTitle.backgroundWindow, NULL); // Dibuja los objetos mTitle.coffeeBitmap->render(); mTitle.crisisBitmap->render(); if (mTitle.menuVisible == true) { mMenu.active->render(); } mTitle.dustBitmapR->animate(0); mTitle.dustBitmapL->animate(0); mTitle.dustBitmapR->render(); mTitle.dustBitmapL->render(); // PRESS ANY KEY! if ((mTitle.counter % 50 > 14) && (mTitle.menuVisible == false)) { mText.black->writeCentered(SCREEN_CENTER_X + 1, PLAY_AREA_THIRD_QUARTER_Y + BLOCK + 1, "PRESS ANY KEY!", 0); mText.white->writeCentered(SCREEN_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, "PRESS ANY KEY!", 0); } // Texto con el copyright y versión mText.black->writeCentered(SCREEN_CENTER_X + 1, SCREEN_HEIGHT - (BLOCK * 2) + 1, TEXT_COPYRIGHT, 0); mText.white->writeCentered(SCREEN_CENTER_X, SCREEN_HEIGHT - (BLOCK * 2), TEXT_COPYRIGHT, 0); // Vuelve a usar el renderizador como destino SDL_SetRenderTarget(mRenderer, NULL); // Volca el contenido de la textura en el renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Actualiza la pantalla SDL_RenderPresent(mRenderer); // Comprueba las entradas para el menu if (mTitle.menuVisible == true) { checkMenuInput(mMenu.active); } // Comprueba si se ha seleccionado algún item del menú if (mMenu.active->getName() == "TITLE") { switch (mMenu.active->getItemSelected()) { case 0: setProgSection(PROG_SECTION_GAME); mMenu.active->reset(); mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); renderFade(1); JA_StopMusic(); disableDemoMode(); break; case 1: JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); mMenu.active->reset(); mMenu.active = mMenu.options; mOptions.fullScreenModePrevious = mOptions.fullScreenMode; mOptions.windowSizePrevious = mOptions.windowSize; break; case 2: mProg.quit = true; mMenu.active->reset(); mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); renderFade(1); JA_StopMusic(); break; default: break; } } // Comprueba si se ha seleccionado algún item de opciones if (mMenu.active->getName() == "OPTIONS") { switch (mMenu.active->getItemSelected()) { case 0: // Fullscreen mode JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); changeFullScreenMode(); updateOptionsMenu(); mMenu.active->deselectItem(); break; case 1: // Windows size JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); mOptions.windowSize++; if (mOptions.windowSize == 5) mOptions.windowSize = 1; updateOptionsMenu(); mMenu.active->deselectItem(); break; case 2: // OK JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); SDL_SetWindowFullscreen(mWindow, mOptions.fullScreenMode); SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mOptions.windowSize, SCREEN_HEIGHT * mOptions.windowSize); mMenu.active->reset(); mMenu.active = mMenu.title; break; case 3: // CANCEL JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); mOptions.fullScreenMode = mOptions.fullScreenModePrevious; mOptions.windowSize = mOptions.windowSizePrevious; updateOptionsMenu(); mMenu.active->reset(); mMenu.active = mMenu.title; break; default: break; } } if (mMenu.active->getName() == "TITLE") { mTitle.counter--; } } else if (mTitle.counter == 0) { mTitle.counter = TITLE_COUNTER; setProgSection(mTitle.nextProgSection); mMenu.active->reset(); toogleTitleNextGS(); renderFade(1); enableDemoMode(); } } // Sección Instrucciones if (mTitle.section == TITLE_SECTION_INSTRUCTIONS) runInstructions(); } quitTitle(); } // Bucle para el juego void GameDirector::runGame() { initGame(); while ((mProg.section == PROG_SECTION_GAME) && (!exit())) { // Sección juego en pausa if (mGame.section == GAME_SECTION_PAUSE) runPausedGame(); // Sección Game Over if (mGame.section == GAME_SECTION_GAMEOVER) runGameOverScreen(); // Sección juego jugando if (mGame.section == GAME_SECTION_PLAY) { // Si la música no está sonando if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { // Reproduce la música if (mGame.player->isAlive()) JA_PlayMusic(mMusic[MUSIC_PLAYING].music, true); } // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); // Actualiza el contador de juego mGame.counter++; // 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) { mProg.quit = true; 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: mProg.ticksSpeed *= 2; break; case SDL_SCANCODE_G: createNewBalloon(100, 0, BALLOON_1, BALLOON_VELX_POSITIVE, 1.0F, 0, mTexture[TEXTURE_BALLOON].texture); break; case SDL_SCANCODE_P: popAllBalloons(); //mGame.balloonsPopped += 10; 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: mGame.player->setInvulnerable(true); mGame.player->setInvulnerableCounter(65000); break; case SDL_SCANCODE_M: break; case SDL_SCANCODE_N: break; default: break; } } } } // Actualiza la lógica del juego updatePlayField(); } // Cambia el destino donde se pinta todo SDL_SetRenderTarget(mRenderer, mBackbuffer); // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos renderPlayField(); // Vuelve a usar el renderizador como destino SDL_SetRenderTarget(mRenderer, NULL); // Volca el contenido de la textura en el renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); renderDebugInfo(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } } quitGame(); } // Bucle para el menu de pausa del juego void GameDirector::runPausedGame() { while ((mGame.section == GAME_SECTION_PAUSE) && (mProg.section == PROG_SECTION_GAME) && (!exit())) { // 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) { mProg.quit = true; break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.pause->update(); } // Cambia el destino donde se pinta todo SDL_SetRenderTarget(mRenderer, mBackbuffer); // Dibuja los objetos renderBackground(); renderBalloons(); renderBullets(); mGame.player->render(); renderScoreBoard(); mMenu.pause->render(); // Vuelve a usar el renderizador como destino SDL_SetRenderTarget(mRenderer, NULL); // Volca el contenido de la textura en el renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Actualiza la pantalla SDL_RenderPresent(mRenderer); // Comprueba las entradas para el menu checkMenuInput(mMenu.pause); // Comprueba si se ha seleccionado algún item del menú switch (mMenu.pause->getItemSelected()) { case 0: JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); mGame.section = GAME_SECTION_PLAY; mMenu.pause->reset(); mMenu.keyPressed = false; if (JA_GetMusicState() == JA_MUSIC_PAUSED) { JA_ResumeMusic(); } break; case 1: JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); setProgSection(PROG_SECTION_TITLE); mMenu.pause->reset(); mMenu.keyPressed = false; JA_StopMusic(); renderFade(1); break; default: break; } } } // Bucle para la pantalla de instrucciones void GameDirector::runInstructions() { SDL_Rect window = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; Sprite *sprite = new Sprite(); SDL_Rect rect1 = {60, 88, 16, 16}; // Disquito SDL_Rect rect2 = {60, 104, 16, 16}; // Gavineixon SDL_Rect rect3 = {60, 120, 16, 16}; // Pacmar SDL_Rect rect4 = {60, 136, 16, 16}; // Time Stopper SDL_Rect rect5 = {60, 152, 16, 16}; // Explosive SDL_Rect rect6 = {60, 168, 16, 16}; // Coffee SDL_Rect rect = {0, 0, 16, 16}; sprite->init(rect1, mTexture[TEXTURE_ITEMS].texture, mRenderer); while ((mTitle.section == TITLE_SECTION_INSTRUCTIONS) && (!exit())) { int y = 0; // 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) { mProg.quit = true; break; } if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { JA_StopMusic(); setProgSection(PROG_SECTION_TITLE); } } // Pinta en la surface el texto y los sprites SDL_SetRenderTarget(mRenderer, mInstructionsSurface); SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255); SDL_RenderClear(mRenderer); mText.white->writeCentered(SCREEN_CENTER_X, 8, "OBJECTIVE", 0); mText.white->writeCentered(SCREEN_CENTER_X, 24, "YOU HAVE TO POP AS MANY", 0); mText.white->writeCentered(SCREEN_CENTER_X, 34, "BALLOONS AS YOU CAN", 0); mText.white->writeCentered(SCREEN_CENTER_X, 48, "DIFFICULTY WILL BE INCREASED", 0); mText.white->writeCentered(SCREEN_CENTER_X, 58, "AS YOU SCORE POINTS", 0); mText.white->writeCentered(SCREEN_CENTER_X, 75, "ITEMS", 0); mText.white->write(84, 92, "1.000 POINTS", 0); mText.white->write(84, 108, "2.500 POINTS", 0); mText.white->write(84, 124, "5.000 POINTS", 0); mText.white->write(84, 140, "TIME STOPPER", 0); mText.white->write(84, 156, "FOUR EXPLOSIONS", 0); mText.white->write(84, 172, "EXTRA HIT", 0); sprite->init(rect1, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mTitle.instructionsCounter + 3) / 36) % 2); sprite->render(); sprite->init(rect2, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mTitle.instructionsCounter + 6) / 36) % 2); sprite->render(); sprite->init(rect3, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mTitle.instructionsCounter + 9) / 36) % 2); sprite->render(); sprite->init(rect4, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mTitle.instructionsCounter + 12) / 36) % 2); sprite->render(); sprite->init(rect5, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mTitle.instructionsCounter + 15) / 36) % 2); sprite->render(); sprite->init(rect6, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x = 0; rect.y = 16 * (((mTitle.instructionsCounter + 0) / 36) % 2); sprite->render(); SDL_SetRenderTarget(mRenderer, nullptr); // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255); SDL_RenderClear(mRenderer); // Dibuja los objetos y = SCREEN_HEIGHT - (INSTRUCTIONS_COUNTER - mTitle.instructionsCounter) + 100; if (y < 0) { y = 0; } window.y = y; SDL_RenderCopy(mRenderer, mInstructionsSurface, NULL, &window); // Muestra la pantalla SDL_RenderPresent(mRenderer); mTitle.instructionsCounter--; if (mTitle.instructionsCounter == 0) { mTitle.instructionsCounter = INSTRUCTIONS_COUNTER; mTitle.section = TITLE_SECTION_1; mTitle.nextProgSection = PROG_SECTION_LOGO; } } delete sprite; } // Bucle para la pantalla de game over void GameDirector::runGameOverScreen() { while ((mGame.section == GAME_SECTION_GAMEOVER) && (mProg.section == PROG_SECTION_GAME) && (!exit())) { // 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) { mProg.quit = true; break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks mProg.ticks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.gameOver->update(); } // Cambia el destino donde se pinta todo SDL_SetRenderTarget(mRenderer, mBackbuffer); // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF); SDL_RenderClear(mRenderer); // Dibuja los objetos mText.whiteX2->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), "GAME OVER", 0); mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 1), "YOUR SCORE: " + std::to_string(mGame.player->getScore()), 0); mText.white->write(PLAY_AREA_CENTER_X - (mText.white->lenght("RETRY?", 0) / 2), PLAY_AREA_CENTER_Y + BLOCK * 2, "RETRY?", 0); mMenu.gameOver->render(); // Vuelve a usar el renderizador como destino SDL_SetRenderTarget(mRenderer, NULL); // Volca el contenido de la textura en el renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); // Muestra la pantalla SDL_RenderPresent(mRenderer); // Comprueba las entradas para el menu checkMenuInput(mMenu.gameOver); // Comprueba si se ha seleccionado algún item del menú switch (mMenu.gameOver->getItemSelected()) { case 0: setProgSection(PROG_SECTION_GAME); mGame.section = GAME_SECTION_PLAY; mMenu.gameOver->reset(); mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); JA_StopMusic(); renderFade(1); disableDemoMode(); resetGame(); break; case 1: setProgSection(PROG_SECTION_TITLE); mMenu.gameOver->reset(); mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); JA_StopMusic(); renderFade(1); mTitle.nextProgSection = PROG_SECTION_GAME; enableDemoMode(); break; default: break; } } } // Activa el modo Demo void GameDirector::enableDemoMode() { mDemo.enabled = true; } // Desactiva el modo Demo void GameDirector::disableDemoMode() { mDemo.enabled = false; } // Actualiza el proximo estado del juego despues del titulo void GameDirector::toogleTitleNextGS() { if (mTitle.nextProgSection == PROG_SECTION_LOGO) mTitle.nextProgSection = PROG_SECTION_GAME; else mTitle.nextProgSection = PROG_SECTION_LOGO; } // Dibuja la informacion de debug en pantalla void GameDirector::renderDebugInfo() { if (mDebug.enabled) { Uint8 R = 0xFF; Uint8 G = 0x20; Uint8 B = 0x20; SDL_RenderSetLogicalSize(mRenderer, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2); mText.white->writeShadowed(2, 2 + 0 * BLOCK, "menace(umb): " + std::to_string(mGame.menaceCurrent) + "(" + std::to_string(mGame.menaceThreshold) + ")", R, G, B); mText.white->writeShadowed(2, 2 + 1 * BLOCK, "currentPower: " + std::to_string(mGame.stage[mGame.currentStage].currentPower), R, G, B); mText.white->writeShadowed(2, 2 + 2 * BLOCK, "currentStage: " + std::to_string(mGame.currentStage), R, G, B); mText.white->writeShadowed(2, 2 + 3 * BLOCK, "counter: " + std::to_string(mGame.counter), R, G, B); mText.white->writeShadowed(2, 2 + 4 * BLOCK, "(R)enemyset: " + std::to_string(mDebug.enemySet), R, G, B); mText.white->writeShadowed(2, 2 + 5 * BLOCK, "RGB: " + std::to_string(mDebug.gradR) + "," + std::to_string(mDebug.gradG) + "," + std::to_string(mDebug.gradB), R, G, B); mText.white->writeShadowed(2, 2 + 6 * BLOCK, "(I)invuln : " + std::to_string(mGame.player->getInvulnerableCounter()), R, G, B); mText.white->writeShadowed(2, 2 + 7 * BLOCK, "balloons: " + std::to_string(countBalloons()), R, G, B); mText.white->writeShadowed(2, 2 + 8 * BLOCK, "balloonsPop: " + std::to_string(mGame.balloonsPopped), R, G, B); mText.white->writeShadowed(2, 2 + 9 * BLOCK, "(Z-X)ballSped:" + std::to_string(mGame.enemySpeed), R, G, B); } }