#include "const.h" #include "struct.h" #include "gamedirector.h" #include const Uint8 *keystates; // Calcula el cuadrado de la distancia entre dos puntos double distanceSquared(int x1, int y1, int x2, int y2) { int deltaX = x2 - x1; int deltaY = y2 - y1; return deltaX * deltaX + deltaY * deltaY; } // Detector de colisiones entre dos circulos bool checkCollision(Circle &a, Circle &b) { // Calcula el radio total al cuadrado int totalRadiusSquared = a.r + b.r; totalRadiusSquared = totalRadiusSquared * totalRadiusSquared; // Si la distancia entre el centro de los circulos es inferior a la suma de sus radios if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared)) { // Los circulos han colisionado return true; } // En caso contrario return false; } // Constructor GameDirector::GameDirector() { // Texturas mTexture[TEXTURE_BALLOON].texture = new LTexture(); mTexture[TEXTURE_BULLET].texture = new LTexture(); mTexture[TEXTURE_FONT_BLACK].texture = new LTexture(); mTexture[TEXTURE_FONT_NOKIA].texture = new LTexture(); mTexture[TEXTURE_FONT_WHITE].texture = new LTexture(); mTexture[TEXTURE_GAME_BG].texture = new LTexture(); mTexture[TEXTURE_GAME_TEXT].texture = new LTexture(); mTexture[TEXTURE_INTRO].texture = new LTexture(); mTexture[TEXTURE_ITEMS].texture = new LTexture(); mTexture[TEXTURE_LOGO].texture = new LTexture(); mTexture[TEXTURE_MENU].texture = new LTexture(); mTexture[TEXTURE_PLAYER_BODY].texture = new LTexture(); mTexture[TEXTURE_PLAYER_LEGS].texture = new LTexture(); mTexture[TEXTURE_PLAYER_DEATH].texture = new LTexture(); mTexture[TEXTURE_TITLE].texture = new LTexture(); // Manejador de eventos mEventHandler = new SDL_Event(); // Objetos para el juego mPlayer = new Player(); for (Uint8 i = 0; i < MAX_BALLOONS; i++) { mBalloon[i] = new Balloon(); } for (Uint8 i = 0; i < MAX_BULLETS; i++) { mBullet[i] = new Bullet(); } for (Uint8 i = 0; i < MAX_ITEMS; i++) { mItem[i] = new Item(); } for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) { mSmartSprite[i] = new SmartSprite(); } mGameBackgroundFront = new Sprite(); mGameBackgroundSky = new Sprite(); mGBClouds1 = new MovingSprite(); mGBClouds1b = new MovingSprite(); mGBClouds2 = new MovingSprite(); mGBClouds2b = new MovingSprite(); mGrass = new Sprite(); mTitleTile = new Sprite(); mText.white = new Text(); mText.black = new Text(); mText.nokia = new Text(); mMenu.title = new Menu(); mMenu.pause = new Menu(); mMenu.gameOver = new Menu(); mMenu.options = new Menu(); mMenu.active = new Menu(); mGetReadyBitmap = new SmartSprite(); mCoffeeBitmap = new SmartSprite(); mCrisisBitmap = new SmartSprite(); mDustSpriteLeft = new AnimatedSprite(); mDustSpriteRight = new AnimatedSprite(); m1000Bitmap = new SmartSprite(); m2500Bitmap = new SmartSprite(); m5000Bitmap = new SmartSprite(); mInstructions = new Sprite(); for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { mIntroBitmap[i] = new SmartSprite(); } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntroText[i] = new Text2(); } mLogo.sprite = new Sprite(); } GameDirector::~GameDirector() { // Libera los recursos globales unLoadMedia(GAME_STATE_INIT); // Libera el mando if (mGameControllerFound) { SDL_JoystickClose(mGameController); mGameController = NULL; } // Destuye el manejador de eventos delete mEventHandler; // Destruye los objetos delete mTexture[TEXTURE_BALLOON].texture; delete mTexture[TEXTURE_BULLET].texture; delete mTexture[TEXTURE_FONT_BLACK].texture; delete mTexture[TEXTURE_FONT_NOKIA].texture; delete mTexture[TEXTURE_FONT_WHITE].texture; delete mTexture[TEXTURE_GAME_BG].texture; delete mTexture[TEXTURE_GAME_TEXT].texture; delete mTexture[TEXTURE_INTRO].texture; delete mTexture[TEXTURE_ITEMS].texture; delete mTexture[TEXTURE_LOGO].texture; delete mTexture[TEXTURE_MENU].texture; delete mTexture[TEXTURE_PLAYER_BODY].texture; delete mTexture[TEXTURE_PLAYER_LEGS].texture; delete mTexture[TEXTURE_PLAYER_DEATH].texture; delete mTexture[TEXTURE_TITLE].texture; delete mPlayer; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { delete mBalloon[i]; } for (Uint8 i = 0; i < MAX_BULLETS; i++) { delete mBullet[i]; } for (Uint8 i = 0; i < MAX_ITEMS; i++) { delete mItem[i]; } for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) { delete mSmartSprite[i]; } delete mGameBackgroundFront; delete mGameBackgroundSky; delete mGBClouds1; delete mGBClouds1b; delete mGBClouds2; delete mGBClouds2b; delete mGrass; delete mTitleTile; delete mText.white; delete mText.black; delete mText.nokia; delete mMenu.title; delete mMenu.pause; delete mMenu.gameOver; delete mMenu.options; mMenu.active = nullptr; delete mGetReadyBitmap; delete mCoffeeBitmap; delete mCrisisBitmap; delete mDustSpriteLeft; delete mDustSpriteRight; delete m1000Bitmap; delete m2500Bitmap; delete m5000Bitmap; delete mInstructions; for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { delete mIntroBitmap[i]; } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { delete mIntroText[i]; } delete mLogo.sprite; // Destruye la ventana SDL_DestroyTexture(mBackbuffer); SDL_DestroyTexture(mTitleSurface); SDL_DestroyTexture(mInstructionsSurface); SDL_DestroyRenderer(mRenderer); SDL_DestroyWindow(mWindow); mBackbuffer = nullptr; mTitleSurface = nullptr; mInstructionsSurface = nullptr; mWindow = nullptr; mRenderer = nullptr; // Sal del subsistema SDL SDL_Quit(); } // Iniciador void GameDirector::init(bool reset) { if (true) { // Variables mTicks = 0; mTicksSpeed = 15; mMenaceLevel = 0; mMenaceLevelThreshold = 0; mGame.score = 0; mGame.hiScoreAchieved = false; mGame.stage = 1; mGame.stageCounter = 0; mGame.paused = false; mGame.deathCounter = DEATH_COUNTER; for (Uint8 i = 0; i < STAGE_COUNTER; i++) { mGame.stagePath[i] = (sin(double(i * 180.0 / double(STAGE_COUNTER)) * 3.14 / 180.0) * (BLOCK * 4)) - BLOCK; } mPlayFieldDrawOnly = true; mDebug = false; mTitleStatus = 0; mTitleTimer = TITLE_TIMER; mExplosionTime = false; mRemainingExplosions = REMAINING_EXPLOSIONS; mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER; mTimeStopped = false; mTimeStoppedTimer = TIME_STOPPED_TIMER; mInstructionsCounter = INSTRUCTIONS_COUNTER; mDemo = false; mDemoRecording = false; mDemoCounter = 0; mGameCounter = 0; mMenuKeyPressed = false; mMenu.active = mMenu.title; for (int i = 0; i < 360; i++) { mSen[i] = sin(i * 3.14 / 180); } mTitleBackgroundTimer = 0; mTitleBackgroundMode = rand() % 2; mTitleMenuVisible = false; mLogo.counter = 0; // Fondo animado mGBClouds1->init(0, 0, 256, 52, -0.1f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGBClouds1->setSpriteClip(256, 0, 256, 52); mGBClouds1b->init(256, 0, 256, 52, -0.1f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGBClouds1b->setSpriteClip(256, 0, 256, 52); mGBClouds2->init(0, 52, 256, 32, -0.05f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGBClouds2->setSpriteClip(256, 52, 256, 32); mGBClouds2b->init(256, 52, 256, 32, -0.05f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGBClouds2b->setSpriteClip(256, 52, 256, 32); mGrass->init(0, 85, SCREEN_WIDTH, 6, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGrass->setPosY(154); // Objeto jugador mPlayer->init(PLAY_AREA_CENTER_X - 12, 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 mBalloon[0]->init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mBalloon[0]->allignTo(PLAY_AREA_CENTER_X); // Con los globos creados, calcula el nivel de amenaza calculateMenaceLevel(); // 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 sprite del logo mLogo.sprite->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_LOGO].texture, mRenderer); // Inicializa el bitmap de GetReady! mGetReadyBitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); mGetReadyBitmap->setPosX(-113); mGetReadyBitmap->setPosY(PLAY_AREA_CENTER_Y - 10); mGetReadyBitmap->setWidth(109); mGetReadyBitmap->setHeight(20); mGetReadyBitmap->setVelX(2.0f); mGetReadyBitmap->setVelY(0.0f); mGetReadyBitmap->setAccelX(0.00f); mGetReadyBitmap->setAccelY(0.0f); mGetReadyBitmap->setSpriteClip(0, 0, 109, 20); mGetReadyBitmap->setEnabled(true); mGetReadyBitmap->setEnabledTimer(0); mGetReadyBitmap->setDestX(PLAY_AREA_RIGHT); mGetReadyBitmap->setDestY(PLAY_AREA_CENTER_Y - 10); // Inicializa el bitmap de Coffee mCoffeeBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mCoffeeBitmap->setId(0); mCoffeeBitmap->setIntroEvents(&mTitleEvents[0]); mCoffeeBitmap->setPosX(45); mCoffeeBitmap->setPosY(11 - 200); mCoffeeBitmap->setWidth(167); mCoffeeBitmap->setHeight(46); mCoffeeBitmap->setVelX(0.0f); mCoffeeBitmap->setVelY(2.5f); mCoffeeBitmap->setAccelX(0.0f); mCoffeeBitmap->setAccelY(0.1f); //mCoffeeBitmap->setSpriteClip(45, 11, 167, 46); mCoffeeBitmap->setSpriteClip(0, 0, 167, 46); mCoffeeBitmap->setEnabled(true); mCoffeeBitmap->setEnabledTimer(0); mCoffeeBitmap->setDestX(45); mCoffeeBitmap->setDestY(11); // Inicializa el bitmap de Crisis mCrisisBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mCrisisBitmap->setId(1); mCrisisBitmap->setIntroEvents(&mTitleEvents[0]); mCrisisBitmap->setPosX(60); mCrisisBitmap->setPosY(57 + 200); mCrisisBitmap->setWidth(137); mCrisisBitmap->setHeight(46); mCrisisBitmap->setVelX(0.0f); mCrisisBitmap->setVelY(-2.5f); mCrisisBitmap->setAccelX(0.0f); mCrisisBitmap->setAccelY(-0.1f); //mCrisisBitmap->setSpriteClip(60, 57, 137, 46); mCrisisBitmap->setSpriteClip(0, 46, 137, 46); mCrisisBitmap->setEnabled(true); mCrisisBitmap->setEnabledTimer(0); mCrisisBitmap->setDestX(60); mCrisisBitmap->setDestY(57); // Inicializa el bitmap de DustRight mDustSpriteRight->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mDustSpriteRight->setPosX(218); mDustSpriteRight->setPosY(47); mDustSpriteRight->setWidth(16); mDustSpriteRight->setHeight(14); mDustSpriteRight->setCurrentFrame(0); mDustSpriteRight->setAnimationCounter(0); mDustSpriteRight->setAnimationNumFrames(0, 7); mDustSpriteRight->setAnimationSpeed(0, 8); mDustSpriteRight->setAnimationLoop(0, false); mDustSpriteRight->setAnimationFrames(0, 0, 160 + (mDustSpriteRight->getWidth() * 0), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); mDustSpriteRight->setAnimationFrames(0, 1, 160 + (mDustSpriteRight->getWidth() * 1), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); mDustSpriteRight->setAnimationFrames(0, 2, 160 + (mDustSpriteRight->getWidth() * 2), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); mDustSpriteRight->setAnimationFrames(0, 3, 160 + (mDustSpriteRight->getWidth() * 3), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); mDustSpriteRight->setAnimationFrames(0, 4, 160 + (mDustSpriteRight->getWidth() * 4), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); mDustSpriteRight->setAnimationFrames(0, 5, 160 + (mDustSpriteRight->getWidth() * 5), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); mDustSpriteRight->setAnimationFrames(0, 6, 160 + (mDustSpriteRight->getWidth() * 6), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); // Inicializa el bitmap de DustLeft mDustSpriteLeft->init(mTexture[TEXTURE_TITLE].texture, mRenderer); mDustSpriteLeft->setPosX(33); mDustSpriteLeft->setPosY(47); mDustSpriteLeft->setWidth(16); mDustSpriteLeft->setHeight(14); mDustSpriteLeft->setCurrentFrame(0); mDustSpriteLeft->setAnimationCounter(0); mDustSpriteLeft->setAnimationNumFrames(0, 7); mDustSpriteLeft->setAnimationSpeed(0, 8); mDustSpriteLeft->setAnimationLoop(0, false); mDustSpriteLeft->setAnimationFrames(0, 0, 160 + (mDustSpriteLeft->getWidth() * 0), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); mDustSpriteLeft->setAnimationFrames(0, 1, 160 + (mDustSpriteLeft->getWidth() * 1), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); mDustSpriteLeft->setAnimationFrames(0, 2, 160 + (mDustSpriteLeft->getWidth() * 2), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); mDustSpriteLeft->setAnimationFrames(0, 3, 160 + (mDustSpriteLeft->getWidth() * 3), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); mDustSpriteLeft->setAnimationFrames(0, 4, 160 + (mDustSpriteLeft->getWidth() * 4), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); mDustSpriteLeft->setAnimationFrames(0, 5, 160 + (mDustSpriteLeft->getWidth() * 5), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); mDustSpriteLeft->setAnimationFrames(0, 6, 160 + (mDustSpriteLeft->getWidth() * 6), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); // Inicializa el bitmap de 1000 puntos m1000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); m1000Bitmap->setPosX(0); m1000Bitmap->setPosY(0); m1000Bitmap->setWidth(26); m1000Bitmap->setHeight(9); m1000Bitmap->setVelX(0.0f); m1000Bitmap->setVelY(-0.5f); m1000Bitmap->setAccelX(0.0f); m1000Bitmap->setAccelY(-0.1f); m1000Bitmap->setSpriteClip(0, 20, 26, 9); m1000Bitmap->setEnabled(false); m1000Bitmap->setEnabledTimer(0); m1000Bitmap->setDestX(0); m1000Bitmap->setDestY(0); // Inicializa el bitmap de 2500 puntos m2500Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); m2500Bitmap->setPosX(0); m2500Bitmap->setPosY(0); m2500Bitmap->setWidth(28); m2500Bitmap->setHeight(9); m2500Bitmap->setVelX(0.0f); m2500Bitmap->setVelY(-0.5f); m2500Bitmap->setAccelX(0.0f); m2500Bitmap->setAccelY(-0.1f); m2500Bitmap->setSpriteClip(26, 20, 28, 9); m2500Bitmap->setEnabled(false); m2500Bitmap->setEnabledTimer(0); m2500Bitmap->setDestX(0); m2500Bitmap->setDestY(0); // Inicializa el bitmap de 5000 puntos m5000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); m5000Bitmap->setPosX(0); m5000Bitmap->setPosY(0); m5000Bitmap->setWidth(28); m5000Bitmap->setHeight(9); m5000Bitmap->setVelX(0.0f); m5000Bitmap->setVelY(-0.5f); m5000Bitmap->setAccelX(0.0f); m5000Bitmap->setAccelY(-0.1f); m5000Bitmap->setSpriteClip(54, 20, 28, 9); m5000Bitmap->setEnabled(false); m5000Bitmap->setEnabledTimer(0); m5000Bitmap->setDestX(0); m5000Bitmap->setDestY(0); // Inicializa el vector de eventos de la intro for (Uint8 i = 0; i < INTRO_TOTAL_EVENTS; i++) { mIntroEvents[i] = EVENT_WAITING; } // Inicializa el vector de eventos de la pantalla de titulo for (Uint8 i = 0; i < TITLE_TOTAL_EVENTS; i++) { mTitleEvents[i] = EVENT_WAITING; } // Inicializa los bitmaps de la intro for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { mIntroBitmap[i]->init(mTexture[TEXTURE_INTRO].texture, mRenderer); mIntroBitmap[i]->setId(i); mIntroBitmap[i]->setIntroEvents(&mIntroEvents[0]); mIntroBitmap[i]->setWidth(128); mIntroBitmap[i]->setHeight(96); mIntroBitmap[i]->setEnabled(false); mIntroBitmap[i]->setEnabledTimer(20); mIntroBitmap[i]->setDestX(SCREEN_CENTER_X - 64); mIntroBitmap[i]->setDestY(SCREEN_FIRST_QUARTER_Y - 24); } mIntroBitmap[0]->setPosX(-128); mIntroBitmap[0]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); mIntroBitmap[0]->setVelX(0.0f); mIntroBitmap[0]->setVelY(0.0f); mIntroBitmap[0]->setAccelX(0.6f); mIntroBitmap[0]->setAccelY(0.0f); mIntroBitmap[0]->setSpriteClip(0, 0, 128, 96); mIntroBitmap[1]->setPosX(SCREEN_WIDTH); mIntroBitmap[1]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); mIntroBitmap[1]->setVelX(-1.0f); mIntroBitmap[1]->setVelY(0.0f); mIntroBitmap[1]->setAccelX(-0.3f); mIntroBitmap[1]->setAccelY(0.0f); mIntroBitmap[1]->setSpriteClip(128, 0, 128, 96); mIntroBitmap[2]->setPosX(SCREEN_CENTER_X - 64); mIntroBitmap[2]->setPosY(-96); mIntroBitmap[2]->setVelX(0.0f); mIntroBitmap[2]->setVelY(3.0f); mIntroBitmap[2]->setAccelX(0.1f); mIntroBitmap[2]->setAccelY(0.3f); mIntroBitmap[2]->setSpriteClip(0, 96, 128, 96); mIntroBitmap[2]->setEnabledTimer(250); mIntroBitmap[3]->setPosX(SCREEN_CENTER_X - 64); mIntroBitmap[3]->setPosY(SCREEN_HEIGHT); mIntroBitmap[3]->setVelX(0.0f); mIntroBitmap[3]->setVelY(-0.7f); mIntroBitmap[3]->setAccelX(0.0f); mIntroBitmap[3]->setAccelY(0.0f); mIntroBitmap[3]->setSpriteClip(128, 96, 128, 96); mIntroBitmap[4]->setPosX(SCREEN_CENTER_X - 64); mIntroBitmap[4]->setPosY(-96); mIntroBitmap[4]->setVelX(0.0f); mIntroBitmap[4]->setVelY(3.0f); mIntroBitmap[4]->setAccelX(0.1f); mIntroBitmap[4]->setAccelY(0.3f); mIntroBitmap[4]->setSpriteClip(0, 192, 128, 96); mIntroBitmap[5]->setPosX(SCREEN_WIDTH); mIntroBitmap[5]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); mIntroBitmap[5]->setVelX(-0.7f); mIntroBitmap[5]->setVelY(0.0f); mIntroBitmap[5]->setAccelX(0.0f); mIntroBitmap[5]->setAccelY(0.0f); mIntroBitmap[5]->setSpriteClip(128, 192, 128, 96); // Inicializa los textos de la intro for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntroText[i]->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); mIntroText[i]->setId(6 + i); mIntroText[i]->setIntroEvents(&mIntroEvents[0]); mIntroText[i]->setPosX(BLOCK * 0); mIntroText[i]->setPosY(SCREEN_HEIGHT - (BLOCK * 6)); mIntroText[i]->setKerning(-1); mIntroText[i]->setEnabled(false); mIntroText[i]->setEnabledTimer(180); } mIntroText[0]->setCaption("Un dia qualsevol de l'any 2000"); mIntroText[0]->setWrittingSpeed(10); mIntroText[1]->setCaption("Tot esta tranquil a la UPV"); mIntroText[1]->setWrittingSpeed(10); mIntroText[2]->setCaption("Fins que un desaprensiu..."); mIntroText[2]->setWrittingSpeed(15); mIntroText[3]->setCaption("HEY! ME ANE A FERME UN CORTAET..."); mIntroText[3]->setWrittingSpeed(10); mIntroText[4]->setCaption("UAAAAAAAAAAAAA!!!"); mIntroText[4]->setWrittingSpeed(1); mIntroText[5]->setCaption("Espera un moment..."); mIntroText[5]->setWrittingSpeed(20); mIntroText[6]->setCaption("Si resulta que no tinc solt!"); mIntroText[6]->setWrittingSpeed(2); mIntroText[7]->setCaption("MERDA DE MAQUINA!"); mIntroText[7]->setWrittingSpeed(3); mIntroText[8]->setCaption("Blop... blop... blop..."); mIntroText[8]->setWrittingSpeed(20); for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntroText[i]->center(SCREEN_CENTER_X); } } // Esta parte solo se ejecuta al arrancar la aplicación if (reset == false) { // Variables mGame.status = GAME_STATE_LOGO; mTitleStatus = 0; mTiteNextGS = GAME_STATE_PLAYING; // Teclado mKeyboard.up = SDL_SCANCODE_UP; mKeyboard.down = SDL_SCANCODE_DOWN; mKeyboard.left = SDL_SCANCODE_LEFT; mKeyboard.right = SDL_SCANCODE_RIGHT; mKeyboard.accept = SDL_SCANCODE_RETURN; mKeyboard.cancel = SDL_SCANCODE_ESCAPE; mKeyboard.fire = SDL_SCANCODE_W; mKeyboard.fireLeft = SDL_SCANCODE_Q; mKeyboard.fireRight = SDL_SCANCODE_E; mKeyboard.pause = SDL_SCANCODE_ESCAPE; mKeyboard.escape = SDL_SCANCODE_ESCAPE; // Buffer de teclado mKeyboardBuffer.up = 0; mKeyboardBuffer.down = 0; mKeyboardBuffer.left = 0; mKeyboardBuffer.right = 0; mKeyboardBuffer.accept = 0; mKeyboardBuffer.cancel = 0; mKeyboardBuffer.fire = 0; mKeyboardBuffer.fireLeft = 0; mKeyboardBuffer.fireRight = 0; mKeyboardBuffer.pause = 0; mKeyboardBuffer.escape = 0; // Carga la máxima puntuación a partir del fichero de datos if (mScoreDataFile[0] == 0) { mGame.hiScore = 10000; } // Comprueba el checksum para ver si se ha modificado el fichero else if (mScoreDataFile[0] % 43 == mScoreDataFile[1]) { mGame.hiScore = mScoreDataFile[0]; } else { mGame.hiScore = 10000; } // Los fondos mGameBackgroundFront->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGameBackgroundSky->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); // El tile de fondo mTitleTile->init(0, 0, 64, 64, mTexture[TEXTURE_TITLE].texture, mRenderer); mTitleTile->setSpriteClip(192, 0, 64, 64); // Las instrucciones //mInstructions->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture.instructions, mRenderer); // Objetos texto, uno de cada color mText.white->init(mTexture[TEXTURE_FONT_WHITE].texture, mRenderer, TEXT_FIXED, BLOCK); mText.black->init(mTexture[TEXTURE_FONT_BLACK].texture, mRenderer, TEXT_FIXED, BLOCK); 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->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(); } } // 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"); } // Inicializa jail_audio JA_Init(48000, AUDIO_S16, 2); // 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"; //Get controller haptic device 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"); //Get initialize rumble 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; } // Crea una textura para dibujar el 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; } // 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; } } } printf("\n"); return success; } // Crea el indice de ficheros void GameDirector::setFileList() { // Ficheros binarios mBinFile[BINFILE_SCORE].file = mExecutablePath + "/" + "../data/score.bin"; mBinFile[BINFILE_DEMO].file = mExecutablePath + "/" + "../data/demo.bin"; mBinFile[BINFILE_CONFIG].file = mExecutablePath + "/" + "../data/config.bin"; // Musicas mMusic[MUSIC_INTRO].file = mExecutablePath + "/" + "../media/music/intro.ogg"; mMusic[MUSIC_PLAYING].file = mExecutablePath + "/" + "../media/music/playing.ogg"; mMusic[MUSIC_TITLE].file = mExecutablePath + "/" + "../media/music/title.ogg"; // Sonidos mSound[SOUND_BALLON].file = mExecutablePath + "/" + "../media/sound/balloon.wav"; mSound[SOUND_BULLET].file = mExecutablePath + "/" + "../media/sound/bullet.wav"; mSound[SOUND_MENU_SELECT].file = mExecutablePath + "/" + "../media/sound/menu_select.wav"; mSound[SOUND_MENU_CANCEL].file = mExecutablePath + "/" + "../media/sound/menu_cancel.wav"; mSound[SOUND_MENU_MOVE].file = mExecutablePath + "/" + "../media/sound/menu_move.wav"; mSound[SOUND_TITLE].file = mExecutablePath + "/" + "../media/sound/title.wav"; mSound[SOUND_PLAYER_COLLISION].file = mExecutablePath + "/" + "../media/sound/player_collision.wav"; mSound[SOUND_HISCORE].file = mExecutablePath + "/" + "../media/sound/hiscore.wav"; mSound[SOUND_ITEM_DROP].file = mExecutablePath + "/" + "../media/sound/itemdrop.wav"; mSound[SOUND_ITEM_PICKUP].file = mExecutablePath + "/" + "../media/sound/itempickup.wav"; mSound[SOUND_COFFEE_OUT].file = mExecutablePath + "/" + "../media/sound/coffeeout.wav"; mSound[SOUND_STAGE_CHANGE].file = mExecutablePath + "/" + "../media/sound/stage_change.wav"; mSound[SOUND_BUBBLE1].file = mExecutablePath + "/" + "../media/sound/bubble1.wav"; mSound[SOUND_BUBBLE2].file = mExecutablePath + "/" + "../media/sound/bubble2.wav"; mSound[SOUND_BUBBLE3].file = mExecutablePath + "/" + "../media/sound/bubble3.wav"; mSound[SOUND_BUBBLE4].file = mExecutablePath + "/" + "../media/sound/bubble4.wav"; // Texturas mTexture[TEXTURE_BALLOON].file = mExecutablePath + "/" + "../media/gfx/balloon.png"; mTexture[TEXTURE_BULLET].file = mExecutablePath + "/" + "../media/gfx/bullet.png"; mTexture[TEXTURE_FONT_BLACK].file = mExecutablePath + "/" + "../media/gfx/font_black.png"; mTexture[TEXTURE_FONT_NOKIA].file = mExecutablePath + "/" + "../media/gfx/font_nokia.png"; mTexture[TEXTURE_FONT_WHITE].file = mExecutablePath + "/" + "../media/gfx/font_white.png"; mTexture[TEXTURE_GAME_BG].file = mExecutablePath + "/" + "../media/gfx/game_bg.png"; mTexture[TEXTURE_GAME_TEXT].file = mExecutablePath + "/" + "../media/gfx/game_text.png"; mTexture[TEXTURE_INTRO].file = mExecutablePath + "/" + "../media/gfx/intro.png"; mTexture[TEXTURE_ITEMS].file = mExecutablePath + "/" + "../media/gfx/items.png"; mTexture[TEXTURE_LOGO].file = mExecutablePath + "/" + "../media/gfx/logo.png"; mTexture[TEXTURE_MENU].file = mExecutablePath + "/" + "../media/gfx/menu.png"; mTexture[TEXTURE_PLAYER_LEGS].file = mExecutablePath + "/" + "../media/gfx/player_legs.png"; mTexture[TEXTURE_PLAYER_BODY].file = mExecutablePath + "/" + "../media/gfx/player_body.png"; mTexture[TEXTURE_PLAYER_DEATH].file = mExecutablePath + "/" + "../media/gfx/player_death.png"; mTexture[TEXTURE_TITLE].file = mExecutablePath + "/" + "../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"); 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 { success = false; printf("Checking file %-20s [ERROR]\n", filename.c_str()); } SDL_RWclose(file); } // Comprueba los ficheros de sonidos printf("\n>> SOUND FILES\n"); 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 { success = false; printf("Checking file %-20s [ERROR]\n", filename.c_str()); } SDL_RWclose(file); } // Comprueba los ficheros con texturas printf("\n>> TEXTURE FILES\n"); 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 { success = false; printf("Checking file %-20s [ERROR]\n", filename.c_str()); } SDL_RWclose(file); } // Resultado if (success) { printf("\n** All files OK.\n\n"); } else { printf("\n** File is missing. Exiting.\n\n"); } 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; } // Carga los recursos necesarios bool GameDirector::loadMedia(Uint8 section) { // Indicador de éxito en la carga bool success = true; std::string path = mExecutablePath + "/"; std::string p; switch (section) { case GAME_STATE_INIT: { p = mBinFile[BINFILE_SCORE].file.c_str(); std::string filename = p.substr(p.find_last_of("\\/") + 1); // Abrimos el fichero con la puntuacion para leer en binario SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); // El fichero no existe if (file == NULL) { printf("Warning: Unable to open %s file\n", filename.c_str()); // Creamos el fichero para escritura file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { printf("New file (%s) created!\n", filename.c_str()); // Inicializamos los datos for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { mScoreDataFile[i] = 0; SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1); } // Cerramos el fichero SDL_RWclose(file); } else { printf("Error: Unable to create file %s\n", filename.c_str()); success = false; } } // El fichero existe else { // Cargamos los datos printf("Reading file %s\n", filename.c_str()); for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { SDL_RWread(file, &mScoreDataFile[i], sizeof(Uint32), 1); } // Cierra el fichero SDL_RWclose(file); } p = mBinFile[BINFILE_DEMO].file.c_str(); filename = p.substr(p.find_last_of("\\/") + 1); // Abrimos el fichero con los datos para el modo demo para leer en binario 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) { mDemoKeys.left = 0; mDemoKeys.right = 0; mDemoKeys.noInput = 0; mDemoKeys.fire = 0; mDemoKeys.fireLeft = 0; mDemoKeys.fireRight = 0; mDemoDataFile[i] = mDemoKeys; SDL_RWwrite(file, &mDemoDataFile[i], sizeof(DemoKeys), 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, &mDemoDataFile[i], sizeof(DemoKeys), 1); } // Cierra el fichero SDL_RWclose(file); } p = mBinFile[BINFILE_CONFIG].file.c_str(); filename = p.substr(p.find_last_of("\\/") + 1); // Abrimos el fichero con la configuracion de las opciones leer en binario 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 mFullScreenMode = 0; SDL_RWwrite(file, &mFullScreenMode, sizeof(mFullScreenMode), 1); mWindowSize = 3; SDL_RWwrite(file, &mWindowSize, sizeof(mWindowSize), 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, &mFullScreenMode, sizeof(mFullScreenMode), 1); SDL_SetWindowFullscreen(mWindow, mFullScreenMode); SDL_RWread(file, &mWindowSize, sizeof(mWindowSize), 1); SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mWindowSize, SCREEN_HEIGHT * mWindowSize); // Cierra el fichero SDL_RWclose(file); } printf("\n"); // Texturas loadTextureFromFile(mTexture[TEXTURE_MENU].texture, mTexture[TEXTURE_MENU].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_FONT_WHITE].texture, mTexture[TEXTURE_FONT_WHITE].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_FONT_BLACK].texture, mTexture[TEXTURE_FONT_BLACK].file, mRenderer); 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()); } break; case GAME_STATE_TITLE: { // Texturas loadTextureFromFile(mTexture[TEXTURE_TITLE].texture, mTexture[TEXTURE_TITLE].file, mRenderer); // Crea el mosaico de fondo del titulo SDL_SetRenderTarget(mRenderer, mTitleSurface); SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF); SDL_RenderClear(mRenderer); for (Uint8 i = 0; i < 8; i++) for (Uint8 j = 0; j < 6; j++) { mTitleTile->setPosX(i * 64); mTitleTile->setPosY(j * 64); mTitleTile->render(); } mBackgroundWindow.x = 0; mBackgroundWindow.y = 0; mBackgroundWindow.w = SCREEN_WIDTH; mBackgroundWindow.h = SCREEN_HEIGHT; SDL_SetRenderTarget(mRenderer, nullptr); // Sonidos mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); } break; case GAME_STATE_PLAYING: { // Texturas loadTextureFromFile(mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_LEGS].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_PLAYER_BODY].texture, mTexture[TEXTURE_PLAYER_BODY].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_PLAYER_DEATH].texture, mTexture[TEXTURE_PLAYER_DEATH].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_BALLOON].texture, mTexture[TEXTURE_BALLOON].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_BULLET].texture, mTexture[TEXTURE_BULLET].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_GAME_BG].texture, mTexture[TEXTURE_GAME_BG].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_GAME_TEXT].texture, mTexture[TEXTURE_GAME_TEXT].file, mRenderer); loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); // Sonidos mSound[SOUND_BALLON].sound = JA_LoadSound(mSound[SOUND_BALLON].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()); } break; case GAME_STATE_GAME_OVER_SCREEN: { } break; case GAME_STATE_INTRO: { // Texturas loadTextureFromFile(mTexture[TEXTURE_INTRO].texture, mTexture[TEXTURE_INTRO].file, mRenderer); // Musicas mMusic[MUSIC_INTRO].music = JA_LoadMusic(mMusic[MUSIC_INTRO].file.c_str()); } break; case GAME_STATE_DEMO: { } break; case GAME_STATE_INSTRUCTIONS: { // Texturas loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); } break; case GAME_STATE_LOGO: { // Texturas loadTextureFromFile(mTexture[TEXTURE_LOGO].texture, mTexture[TEXTURE_LOGO].file, mRenderer); } break; default: { } break; } return success; } // Descrga los recursos necesarios bool GameDirector::unLoadMedia(Uint8 section) { // Indicador de éxito en la carga bool success = true; std::string path = mExecutablePath + "/"; std::string p; switch (section) { case GAME_STATE_INIT: { p = mBinFile[BINFILE_SCORE].file; std::string filename = p.substr(p.find_last_of("\\/") + 1); // Abre el fichero de puntuación para escribir SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { // Guardamos los datos for (int i = 0; i < TOTAL_SCORE_DATA; ++i) { SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1); } printf("Writing file %s\n", filename.c_str()); // Cerramos el fichero SDL_RWclose(file); } else { printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); } p = mBinFile[BINFILE_CONFIG].file; filename = p.substr(p.find_last_of("\\/") + 1); // Abre el fichero de puntuación para escribir SDL_RWops *file2 = SDL_RWFromFile(p.c_str(), "w+b"); if (file != NULL) { // Guardamos los datos SDL_RWwrite(file2, &mFullScreenMode, sizeof(Uint32), 1); SDL_RWwrite(file2, &mWindowSize, sizeof(Uint8), 1); printf("Writing file %s\n", filename.c_str()); // Cerramos el fichero SDL_RWclose(file2); } else { printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); } p = mBinFile[BINFILE_DEMO].file; filename = p.substr(p.find_last_of("\\/") + 1); // Abre el fichero de demo para escribir if (mDemoRecording) { SDL_RWops *file2 = SDL_RWFromFile(p.c_str(), "w+b"); if (file2 != NULL) { // Guardamos los datos for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { SDL_RWwrite(file2, &mDemoDataFile[i], sizeof(DemoKeys), 1); } printf("Writing file %s\n", filename.c_str()); // Cerramos el fichero SDL_RWclose(file2); } else { printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); } } printf("\n"); // Texturas mTexture[TEXTURE_MENU].texture->free(); mTexture[TEXTURE_FONT_WHITE].texture->free(); mTexture[TEXTURE_FONT_BLACK].texture->free(); mTexture[TEXTURE_FONT_NOKIA].texture->free(); // 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); } break; case GAME_STATE_TITLE: { // Texturas mTexture[TEXTURE_TITLE].texture->free(); // Sonidos JA_DeleteSound(mSound[SOUND_TITLE].sound); } break; case GAME_STATE_PLAYING: { // Texturas mTexture[TEXTURE_PLAYER_LEGS].texture->free(); mTexture[TEXTURE_PLAYER_BODY].texture->free(); mTexture[TEXTURE_PLAYER_DEATH].texture->free(); mTexture[TEXTURE_BALLOON].texture->free(); mTexture[TEXTURE_BULLET].texture->free(); mTexture[TEXTURE_GAME_BG].texture->free(); mTexture[TEXTURE_GAME_TEXT].texture->free(); mTexture[TEXTURE_ITEMS].texture->free(); // Sonidos JA_DeleteSound(mSound[SOUND_BALLON].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); } break; case GAME_STATE_GAME_OVER_SCREEN: { } break; case GAME_STATE_INTRO: { // Texturas mTexture[TEXTURE_INTRO].texture->free(); // Musicas JA_DeleteMusic(mMusic[MUSIC_INTRO].music); } break; case GAME_STATE_DEMO: { } break; case GAME_STATE_INSTRUCTIONS: { // Texturas mTexture[TEXTURE_ITEMS].texture->free(); } break; case GAME_STATE_LOGO: { // Texturas mTexture[TEXTURE_LOGO].texture->free(); } break; default: { } break; } return success; } // Establece el valor de la variable void GameDirector::setExecutablePath(std::string path) { mExecutablePath = path.substr(0, path.find_last_of("\\/")); } // 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 mScoreDataFile[0] = mGame.hiScore; mScoreDataFile[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) { if ((num >= 0) && (num <= 9)) { return ("00000" + std::to_string(num)); } if ((num >= 10) && (num <= 99)) { return ("0000" + std::to_string(num)); } if ((num >= 100) && (num <= 999)) { return ("000" + std::to_string(num)); } if ((num >= 1000) && (num <= 9999)) { return ("00" + std::to_string(num)); } if ((num >= 010000) && (num <= 99999)) { return ("0" + std::to_string(num)); } if ((num >= 100000) && (num <= 999999)) { return (std::to_string(num)); } return (std::to_string(num)); } // Pinta el marcador en pantalla usando un objeto texto void GameDirector::renderScoreBoard() { mText.white->write(SCORE_WORD_X, SCORE_WORD_Y, "SCORE", 0); mText.white->write(SCORE_NUMBER_X, SCORE_NUMBER_Y, updateScoreText(mGame.score), 0); mText.white->write(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y, "MULT", 0); mText.white->write(MULTIPLIER_NUMBER_X, MULTIPLIER_NUMBER_Y, std::to_string(mPlayer->getScoreMultiplier()), 0, 3); mText.white->write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE", 0); mText.white->write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, updateScoreText(mGame.hiScore), 0); } // Actualiza el valor de la variable mGame.stage void GameDirector::updateStage() { Uint8 realStage; realStage = (mGame.score / 10000) + 1; if (realStage > mGame.stage) { mGame.stage = realStage; JA_PlaySound(mSound[SOUND_STAGE_CHANGE].sound); mGame.stageCounter = STAGE_COUNTER; } if (mGame.stageCounter > 0) { mGame.stageCounter--; } } // Actualiza el estado de muerte void GameDirector::updateDeath() { if (!mPlayer->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) { mSmartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); } else { mSmartSprite[mGame.deathIndex]->setSpriteClip(24, 0, 24, 24); } // Rebote en los laterales if (mSmartSprite[mGame.deathIndex]->getVelX() > 0) { if (mSmartSprite[mGame.deathIndex]->getPosX() > (SCREEN_WIDTH - mSmartSprite[mGame.deathIndex]->getWidth())) { mSmartSprite[mGame.deathIndex]->setPosX(SCREEN_WIDTH - mSmartSprite[mGame.deathIndex]->getWidth()); mSmartSprite[mGame.deathIndex]->setVelX(mSmartSprite[mGame.deathIndex]->getVelX() * (-1)); mSmartSprite[mGame.deathIndex]->setDestX(mSmartSprite[mGame.deathIndex]->getDestX() * (-1)); } } else { if (mSmartSprite[mGame.deathIndex]->getPosX() < 0) { mSmartSprite[mGame.deathIndex]->setPosX(0); mSmartSprite[mGame.deathIndex]->setVelX(mSmartSprite[mGame.deathIndex]->getVelX() * (-1)); mSmartSprite[mGame.deathIndex]->setDestX(mSmartSprite[mGame.deathIndex]->getDestX() * (-1)); } } } else { setGameStatus(GAME_STATE_GAME_OVER_SCREEN); } } } // Renderiza el fade final cuando se acaba la partida void GameDirector::renderDeathFade() { if (!mPlayer->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]); } } } // Mueve todos los globos activos void GameDirector::moveBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (mBalloon[i]->isActive()) { mBalloon[i]->update(); } } } // Pinta en pantalla todos los globos activos void GameDirector::renderBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (mBalloon[i]->isActive()) { mBalloon[i]->render(); if ((mDebug) && (mBalloon[i]->isPopping() == false)) mText.white->write(mBalloon[i]->getPosX() + (mBalloon[i]->getWidth() / 2) - 4, mBalloon[i]->getPosY() - 8, std::to_string(i)); } } } // Devuelve el primer indice no activo del vector de globos Uint8 GameDirector::getBallonFreeIndex() { int index = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (mBalloon[i]->isActive() == false) { 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, Uint16 creationtimer, LTexture *texture) { Uint8 index = getBallonFreeIndex(); mBalloon[index]->init(x, y, kind, velx, creationtimer, texture, mRenderer); return index; } // Establece a cero todos los valores del vector de objetos globo void GameDirector::resetBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { mBalloon[i]->erase(); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso void GameDirector::popBalloon(Uint8 index) { if (mBalloon[index]->isActive()) { // Otorga los puntos correspondientes al globo mPlayer->addScore(Uint32(mBalloon[index]->getScore() * mPlayer->getScoreMultiplier())); setScore(mPlayer->getScore()); updateHiScore(); Uint8 kind = mBalloon[index]->getKind(); Uint8 freeIndex = 0; switch (kind) { // Si es del tipo más pequeño, simplemente elimina el globo case BALLOON_1: mBalloon[index]->pop(); break; // En cualquier otro caso, crea dos globos de un tipo inferior default: freeIndex = getBallonFreeIndex(); mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_NEGATIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2)); mBalloon[freeIndex]->setVelY(-2.5); // if (isTimeStopped()) mBalloon[freeIndex]->setStop(true); freeIndex = getBallonFreeIndex(); mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2)); mBalloon[freeIndex]->setVelY(-2.5); // if (isTimeStopped()) mBalloon[freeIndex]->setStop(true); // Elimina el globo mBalloon[index]->pop(); break; } // Reproduce el sonido de explotar el globo // JA_PlaySound(mFX.popBalloon); // Recalcula el nivel de amenaza calculateMenaceLevel(); } } // Explosiona todos los globos void GameDirector::popAllBallons() { int candidate[MAX_BALLOONS]; Uint8 j = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if ((mBalloon[i]->isActive()) && (mBalloon[i]->isPopping() == false) && (mBalloon[i]->isBeingCreated() == false)) { candidate[j] = i; } else { candidate[j] = -1; } j += 1; } for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (candidate[i] >= 0) { popBalloon(i); } } JA_PlaySound(mSound[SOUND_BALLON].sound); } // Detiene todos los globos void GameDirector::stopAllBalloons(Uint16 time) { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (mBalloon[i]->isActive()) { mBalloon[i]->setStop(true); mBalloon[i]->setStoppedTimer(time); } } } // Pone en marcha todos los globos void GameDirector::startAllBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if ((mBalloon[i]->isActive()) && (mBalloon[i]->isBeingCreated() == false)) { mBalloon[i]->setStop(false); mBalloon[i]->setStoppedTimer(0); } } } // Obtiene el numero de globos activos Uint8 GameDirector::countBalloons() { Uint8 num = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { if (mBalloon[i]->isActive()) { ++num; } } return num; } // Comprueba la colisión entre el jugador y los globos activos bool GameDirector::checkPlayerBallonCollision() { bool result = false; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { //if ((mBalloon[i]->isActive()) && (mBalloon[i]->isStopped() == false)) if ((mBalloon[i]->isActive()) && !(mBalloon[i]->isStopped()) && !(mBalloon[i]->isInvulnerable())) { if (checkCollision(mPlayer->getCollider(), mBalloon[i]->getCollider())) { result = true; break; } } } return result; } // Comprueba la colisión entre el jugador y los items void GameDirector::checkPlayerItemCollision() { if (mPlayer->isAlive()) for (Uint8 i = 0; i < MAX_ITEMS; i++) { if (mItem[i]->isEnabled()) { if (checkCollision(mPlayer->getCollider(), mItem[i]->getCollider())) { switch (mItem[i]->getClass()) { case ITEM_POINTS_1_DISK: mPlayer->addScore(1000); setScore(mPlayer->getScore()); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m1000Bitmap->getWidth() / 2), mItem[i]->getPosY(), m1000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_2_GAVINA: mPlayer->addScore(2500); setScore(mPlayer->getScore()); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m2500Bitmap->getWidth() / 2), mItem[i]->getPosY(), m2500Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_3_PACMAR: mPlayer->addScore(5000); setScore(mPlayer->getScore()); updateHiScore(); createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), mItem[i]->getPosY(), m5000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_CLOCK: enableTimeStopItem(); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_TNT: popAllBallons(); setExplosionTime(true); JA_PlaySound(mSound[SOUND_TITLE].sound); break; case ITEM_COFFEE: mPlayer->giveExtraHit(); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; default: break; } mItem[i]->erase(); } } } } // Comprueba y procesa la colisión entre las balas y los globos void GameDirector::checkBulletBallonCollision() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { for (Uint8 j = 0; j < MAX_BULLETS; j++) { if (mBalloon[i]->isActive() && !(mBalloon[i]->isInvulnerable()) && mBullet[j]->isActive()) { if (checkCollision(mBalloon[i]->getCollider(), mBullet[j]->getCollider())) { mPlayer->incScoreMultiplier(); popBalloon(i); if (mDemo == false) JA_PlaySound(mSound[SOUND_BALLON].sound); mBullet[j]->erase(); calculateMenaceLevel(); Uint8 droppeditem = dropItem(); if ((droppeditem != NO_KIND) && (mDemo == false) && (mDemoRecording == false)) { createItem(mBalloon[i]->getPosX(), mBalloon[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 (mBullet[i]->isActive()) { if (mBullet[i]->move() == MSG_BULLET_OUT) { mPlayer->decScoreMultiplier(); } } } } // Pinta las balas activas void GameDirector::renderBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { if (mBullet[i]->isActive()) { mBullet[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 (mBullet[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++) { mBullet[i]->erase(); } } // Crea un objeto bala void GameDirector::createBullet(int x, int y, Uint8 kind) { mBullet[getBulletFreeIndex()]->init(x, y, kind, mTexture[TEXTURE_BULLET].texture, mRenderer); } // Actualiza los items void GameDirector::updateItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { mItem[i]->update(); } } // Pinta los items activos void GameDirector::renderItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { mItem[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 (mItem[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++) { mItem[i]->erase(); } } // Devuelve un item en función del azar Uint8 GameDirector::dropItem() { // return ITEM_COFFEE; Uint8 luckyNumber = rand() % 99; Uint8 item = rand() % 6; 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 ITEM_TNT; } break; case 5: if (luckyNumber < 5) { return ITEM_COFFEE; } break; default: break; } return NO_KIND; } // Crea un objeto item void GameDirector::createItem(int x, int y, Uint8 kind) { mItem[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(); *mSmartSprite[index] = *sprite; mSmartSprite[index]->setPosX(x); mSmartSprite[index]->setPosY(y); mSmartSprite[index]->setDestX(x); mSmartSprite[index]->setDestY(y - 15); mSmartSprite[index]->setEnabled(true); mSmartSprite[index]->setEnabledTimer(100); } // Crea un SmartSprite para arrojar el item café al recibir un impacto void GameDirector::throwCoffee(int x, int y) { Uint8 index = getSmartSpriteFreeIndex(); mSmartSprite[index]->init(mTexture[TEXTURE_ITEMS].texture, mRenderer); mSmartSprite[index]->setPosX(x - 8); mSmartSprite[index]->setPosY(y - 8); mSmartSprite[index]->setWidth(16); mSmartSprite[index]->setHeight(16); mSmartSprite[index]->setVelX(-1.0f + ((rand() % 5) * 0.5f)); mSmartSprite[index]->setVelY(-4.0f); mSmartSprite[index]->setAccelX(0.0f); mSmartSprite[index]->setAccelY(0.2f); mSmartSprite[index]->setDestX(x + (mSmartSprite[index]->getVelX() * 50)); mSmartSprite[index]->setDestY(SCREEN_HEIGHT + 1); mSmartSprite[index]->setEnabled(true); mSmartSprite[index]->setEnabledTimer(1); mSmartSprite[index]->setSpriteClip(80, 16, 16, 16); } // 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(); mSmartSprite[mGame.deathIndex]->init(mTexture[TEXTURE_PLAYER_DEATH].texture, mRenderer); mSmartSprite[mGame.deathIndex]->setPosX(x); mSmartSprite[mGame.deathIndex]->setPosY(y); mSmartSprite[mGame.deathIndex]->setWidth(24); mSmartSprite[mGame.deathIndex]->setHeight(24); mSmartSprite[mGame.deathIndex]->setVelX(2.0f * sentit); mSmartSprite[mGame.deathIndex]->setVelY(-5.0f); mSmartSprite[mGame.deathIndex]->setAccelX(0.0f); mSmartSprite[mGame.deathIndex]->setAccelY(0.2f); mSmartSprite[mGame.deathIndex]->setDestX(SCREEN_WIDTH * sentit); mSmartSprite[mGame.deathIndex]->setDestY(SCREEN_HEIGHT + 1); mSmartSprite[mGame.deathIndex]->setEnabled(true); mSmartSprite[mGame.deathIndex]->setEnabledTimer(1); mSmartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); } // Actualiza los SmartSprites void GameDirector::updateSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) { mSmartSprite[i]->update(); } } // Pinta los SmartSprites activos void GameDirector::renderSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) { mSmartSprite[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 (mSmartSprite[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++) { mSmartSprite[i]->erase(); } } // Acciones a realizar cuando el jugador muere void GameDirector::killPlayer() { if (mPlayer->hasExtraHit()) { mPlayer->removeExtraHit(); throwCoffee(mPlayer->getPosX() + (mPlayer->getWidth() / 2), mPlayer->getPosY() + (mPlayer->getHeight() / 2)); JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); } else if (!mPlayer->isInvulnerable()) { 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(mPlayer->getPosX(), mPlayer->getPosY()); mPlayer->setAlive(false); } } // Calcula y establece el valor de amenaza en funcion de los globos activos void GameDirector::calculateMenaceLevel() { mMenaceLevel = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { mMenaceLevel += mBalloon[i]->getMenace(); } } // Obtiene el valor de la variable Uint8 GameDirector::getMenaceLevel() { return mMenaceLevel; } // Obtiene el valor de la variable bool GameDirector::isPlayFieldDrawOnly() { return mPlayFieldDrawOnly; } // Establece el valor de la variable void GameDirector::setPlayFieldDrawOnly(bool state) { mPlayFieldDrawOnly = state; } // Establece el valor de la variable void GameDirector::setTimeStopped(bool value) { mTimeStopped = value; } // Obtiene el valor de la variable bool GameDirector::isTimeStopped() { return mTimeStopped; } // Establece el valor de la variable void GameDirector::setTimeStoppedTimer(Uint16 value) { mTimeStoppedTimer = value; } // Actualiza y comprueba el valor de la variable void GameDirector::updateTimeStoppedTimer() { if (isTimeStopped()) { if (mTimeStoppedTimer > 0) { mTimeStoppedTimer--; stopAllBalloons(TIME_STOPPED_TIMER); } else { disableTimeStopItem(); } } } // Establece el valor de la variable void GameDirector::setExplosionTime(bool value) { mExplosionTime = value; } // Obtiene el valor de la variable bool GameDirector::isExplosionTime() { return mExplosionTime; } // Establece el valor de la variable void GameDirector::setRemainingExplosions(Uint8 value) { mRemainingExplosions = value; } // Actualiza y comprueba el valor de la variable void GameDirector::updateRemainingExplosionsTimer() { if (isExplosionTime()) { if (mRemainingExplosionsTimer > 0) { --mRemainingExplosionsTimer; } else if (mRemainingExplosions > 0) { popAllBallons(); --mRemainingExplosions; mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER; } else { mExplosionTime = false; mRemainingExplosions = REMAINING_EXPLOSIONS; mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER; } } } // Actualiza el campo de juego void GameDirector::updatePlayField() { if (isPlayFieldDrawOnly() == false) { // Comprueba el teclado/mando checkGameInput(); // Actualiza el jugador mPlayer->update(); // Mueve los globos moveBalloons(); // Mueve las balas moveBullets(); // Actualiza los items updateItems(); // Actualiza el valor de mGame.stage updateStage(); // Actualiza el estado de muerte updateDeath(); // Actualiza los SmartSprites updateSmartSprites(); // Actualiza los contadores de estado updateTimeStoppedTimer(); updateRemainingExplosionsTimer(); // Comprueba las colisiones entre globos y balas checkBulletBallonCollision(); // Comprueba las colisiones entre elk jugador y los items checkPlayerItemCollision(); // Comprueba el nivel de amenaza checkMenaceLevel(); // Comprueba la colisión entre el jugador y los globos if (checkPlayerBallonCollision()) { if (mPlayer->isAlive()) { if (mDemo) { mGame.status = GAME_STATE_INSTRUCTIONS; } else { killPlayer(); } } } } else { // Actualiza el mensaje de GetReady mGetReadyBitmap->update(); } } // Actualiza el fondo void GameDirector::updateBackground() { mGBClouds1->move(); mGBClouds1b->move(); mGBClouds2->move(); mGBClouds2b->move(); if (mGBClouds1->getPosX() < -mGBClouds1->getWidth()) { mGBClouds1->setPosX(mGBClouds1->getWidth()); } if (mGBClouds1b->getPosX() < -mGBClouds1b->getWidth()) { mGBClouds1b->setPosX(mGBClouds1b->getWidth()); } if (mGBClouds2->getPosX() < -mGBClouds2->getWidth()) { mGBClouds2->setPosX(mGBClouds2->getWidth()); } if (mGBClouds2b->getPosX() < -mGBClouds2b->getWidth()) { mGBClouds2b->setPosX(mGBClouds2b->getWidth()); } mGrass->setSpriteClip(256, 85 + (6 * (mGameCounter / 20 % 2)), SCREEN_WIDTH, 6); } // Dibuja el fondo void GameDirector::renderBackground() { int alpha = (255 - (mGame.score / 392)); if (alpha < 0) { alpha = 0; } updateBackground(); mGameBackgroundSky->setSpriteClip(SCREEN_WIDTH, 192, SCREEN_WIDTH, SCREEN_HEIGHT); mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); mGameBackgroundSky->render(); mGameBackgroundSky->setSpriteClip(0, 192, SCREEN_WIDTH, SCREEN_HEIGHT); mTexture[TEXTURE_GAME_BG].texture->setAlpha(alpha); mGameBackgroundSky->render(); mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); mGBClouds1->render(); mGBClouds1b->render(); mGBClouds2->render(); mGBClouds2b->render(); mGameBackgroundFront->render(); mGrass->render(); } // Dibuja el campo de juego void GameDirector::renderPlayField() { renderBackground(); renderBalloons(); renderBullets(); renderItems(); renderSmartSprites(); mPlayer->render(); renderDeathFade(); if (!mDemo) { renderScoreBoard(); } 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); } renderMessages(); renderDebugInfo(); } // Gestiona el nivel de amenaza void GameDirector::checkMenaceLevel() { // Aumenta el nivel de amenaza en función de la puntuación mMenaceLevelThreshold = 7 + (4 * (mGame.score / 10000)); // Si el nivel de amenza es inferior al umbral if (mMenaceLevel < mMenaceLevelThreshold) { Uint8 index = 0; // Obtiene el centro del jugador en el eje X int x = mPlayer->getPosX() + (mPlayer->getWidth() / 2); // Crea un globo sobre el jugador en dirección hacia el centro if (x < (PLAY_AREA_WIDTH / 2)) { index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_POSITIVE, 400, mTexture[TEXTURE_BALLOON].texture); } else { index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400, mTexture[TEXTURE_BALLOON].texture); } mBalloon[index]->allignTo(x); // Recalcula el nivel de amenaza con el nuevo globo calculateMenaceLevel(); } } // 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); mDemoKeys.left = 0; mDemoKeys.right = 0; mDemoKeys.noInput = 0; mDemoKeys.fire = 0; mDemoKeys.fireLeft = 0; mDemoKeys.fireRight = 0; // Modo Demo activo if (mDemo) { if (mDemoDataFile[mDemoCounter].left == 1) mPlayer->setInput(INPUT_LEFT); if (mDemoDataFile[mDemoCounter].right == 1) mPlayer->setInput(INPUT_RIGHT); if (mDemoDataFile[mDemoCounter].noInput == 1) mPlayer->setInput(NO_INPUT); if (mDemoDataFile[mDemoCounter].fire == 1) if (mPlayer->canFire()) { mPlayer->setInput(INPUT_FIRE_UP); createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); mPlayer->setFireCooldown(10); } if (mDemoDataFile[mDemoCounter].fireLeft == 1) if (mPlayer->canFire()) { mPlayer->setInput(INPUT_FIRE_LEFT); createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); mPlayer->setFireCooldown(10); } if (mDemoDataFile[mDemoCounter].fireRight == 1) if (mPlayer->canFire()) { mPlayer->setInput(INPUT_FIRE_RIGHT); createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); mPlayer->setFireCooldown(10); } // Comprobamos la tecla o el botón de pausa/menu if ((keystates[mKeyboard.accept] != 0) || (keystates[mKeyboard.cancel] != 0) || (checkGameController(INPUT_PAUSE)) || (checkGameController(INPUT_ACCEPT)) || (checkGameController(INPUT_CANCEL))) { setGameStatus(GAME_STATE_TITLE); } if (mDemoCounter < TOTAL_DEMO_DATA) { mDemoCounter++; } else { mGame.status = GAME_STATE_INSTRUCTIONS; //mDemoCounter = 0; } } // Modo Demo no activo else if (mPlayer->isAlive()) { // Tecla izquierda o el mando hacia la izquierda if ((keystates[mKeyboard.left] != 0) || (checkGameController(INPUT_LEFT))) { mPlayer->setInput(INPUT_LEFT); mDemoKeys.left = 1; } else { // Tecla derecha o el mando hacia la derecha if ((keystates[mKeyboard.right] != 0) || (checkGameController(INPUT_RIGHT))) { mPlayer->setInput(INPUT_RIGHT); mDemoKeys.right = 1; } else { // Ninguna de las dos direcciones pulsadas mPlayer->setInput(NO_INPUT); mDemoKeys.noInput = 1; } } // Comprueba la tecla o el botón de disparo central if ((keystates[mKeyboard.fire] != 0) || (checkGameController(INPUT_FIRE_UP))) { if (mPlayer->canFire()) { mPlayer->setInput(INPUT_FIRE_UP); createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); mPlayer->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); mDemoKeys.fire = 1; } } // Comprueba la tecla o el botón de disparo izquierdo if ((keystates[mKeyboard.fireLeft] != 0) || (checkGameController(INPUT_FIRE_LEFT))) { if (mPlayer->canFire()) { mPlayer->setInput(INPUT_FIRE_LEFT); createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_LEFT); mPlayer->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); mDemoKeys.fireLeft = 1; } } // Comprueba la tecla o el botón de disparo derecho if ((keystates[mKeyboard.fireRight] != 0) || (checkGameController(INPUT_FIRE_RIGHT))) { if (mPlayer->canFire()) { mPlayer->setInput(INPUT_FIRE_RIGHT); createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_RIGHT); mPlayer->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); mDemoKeys.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[mKeyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mKeyboardBuffer.pause == 0)) //|| ((mGameControllerFound) && (SDL_JoystickGetButton(mGameController, BUTTON_START)))) { // Se acaba de pulsar la tecla. Lo anotamos en su buffer mKeyboardBuffer.pause = 1; } else { // Aqui sigue la tecla pulsada, mantenemos el buffer activo if (((keystates[mKeyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mKeyboardBuffer.pause != 0)) { mKeyboardBuffer.pause = 1; } else { // Se ha soltado la tecla, procedemos a ejecutar la accion if (((keystates[mKeyboard.pause] == 0) || (!checkGameController(INPUT_PAUSE))) && (mKeyboardBuffer.pause != 0)) { //setGameStatus(GAME_STATE_PAUSED); mGame.paused = true; if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); } mKeyboardBuffer.pause = 0; } } } if (mDemoCounter < TOTAL_DEMO_DATA) { if (mDemoRecording) { mDemoDataFile[mDemoCounter] = mDemoKeys; } mDemoCounter++; } else if (mDemoRecording) { mGame.status = GAME_STATE_QUIT; } } } // 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 (!mMenuKeyPressed) { // Tecla arriba o el mando hacia arriba if ((keystates[mKeyboard.up] != 0) || (checkGameController(INPUT_UP))) { if (menu->checkInput(INPUT_UP)) { mMenuKeyPressed = true; JA_PlaySound(mSound[SOUND_MENU_MOVE].sound); } } // Tecla abajo o el mando hacia abajo if ((keystates[mKeyboard.down] != 0) || (checkGameController(INPUT_DOWN))) { if (menu->checkInput(INPUT_DOWN)) { mMenuKeyPressed = true; JA_PlaySound(mSound[SOUND_MENU_MOVE].sound); } } // Tecla o el botón de aceptar if ((keystates[mKeyboard.accept] != 0) || (checkGameController(INPUT_ACCEPT))) { menu->checkInput(INPUT_ACCEPT); mMenuKeyPressed = true; } // Tecla o el botón de cancelar if ((keystates[mKeyboard.cancel] != 0) || (checkGameController(INPUT_CANCEL))) { menu->checkInput(INPUT_CANCEL); mMenuKeyPressed = true; } } // Ningún botón, libera la variable if ((keystates[mKeyboard.up] == 0) && (keystates[mKeyboard.down] == 0) && (keystates[mKeyboard.accept] == 0) && (keystates[mKeyboard.cancel] == 0) && (checkGameController(NO_INPUT))) { mMenuKeyPressed = false; } } // Obtiene el valor de la variable Uint8 GameDirector::getGameStatus() { return mGame.status; } // Establece el valor de la variable void GameDirector::setGameStatus(Uint8 status) { mGame.status = status; } // 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() { //mGetReadyBitmap->update(); mGetReadyBitmap->render(); if (!mGetReadyBitmap->isEnabled()) setPlayFieldDrawOnly(false); if (mTimeStopped) { if ((mTimeStoppedTimer > 100) || (mTimeStoppedTimer % 10 > 4)) { mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "Time Stopped: " + std::to_string(mTimeStoppedTimer / 10), 0); mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "Time Stopped: " + std::to_string(mTimeStoppedTimer / 10), 0); } } if (mDemo) { if (mDemoCounter % 30 > 14) { mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "D E M O", 0); mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "D E M O", 0); } } if (mGame.stageCounter > 0) { mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, int(mGame.stagePath[mGame.stageCounter - 1]) + 1, "STAGE " + std::to_string(mGame.stage), 0); mText.white->writeCentered(PLAY_AREA_CENTER_X, int(mGame.stagePath[mGame.stageCounter - 1]), "STAGE " + std::to_string(mGame.stage), 0); } } // Habilita el efecto del item de detener el tiempo void GameDirector::enableTimeStopItem() { stopAllBalloons(TIME_STOPPED_TIMER); setTimeStopped(true); setTimeStoppedTimer(TIME_STOPPED_TIMER); if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); } } // Deshabilita el efecto del item de detener el tiempo void GameDirector::disableTimeStopItem() { mTimeStopped = false; mTimeStoppedTimer = TIME_STOPPED_TIMER; startAllBalloons(); if (JA_GetMusicState() == JA_MUSIC_PAUSED) { JA_ResumeMusic(); } } // Cambia el valor de la variable de modo de pantalla completa void GameDirector::changeFullScreenMode() { switch (mFullScreenMode) { case 0: mFullScreenMode = SDL_WINDOW_FULLSCREEN; break; case SDL_WINDOW_FULLSCREEN: mFullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP; break; case SDL_WINDOW_FULLSCREEN_DESKTOP: mFullScreenMode = 0; break; default: mFullScreenMode = 0; break; } } // Actualiza los elementos del menu de opciones void GameDirector::updateOptionsMenu() { switch (mFullScreenMode) { 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(mWindowSize)); 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 mGameBackgroundFront->setPosX(0); mGameBackgroundFront->setWidth(1); mGameBackgroundFront->setSpriteClip(0, 0, 1, 192); renderBackground(); mGameBackgroundFront->setPosX(255); mGameBackgroundFront->setSpriteClip(255, 0, 1, 192); mGameBackgroundFront->render(); mGameBackgroundFront->setPosX(v[n]); mGameBackgroundFront->setWidth(256); mGameBackgroundFront->setSpriteClip(0, 0, 256, 192); mGameBackgroundFront->render(); mGrass->render(); renderBalloons(); renderBullets(); renderItems(); mPlayer->render(); renderScoreBoard(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); SDL_Delay(50); } } // Bucle para el logo del juego void GameDirector::runLogo() { while (mGame.status == GAME_STATE_LOGO) { // 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) { setGameStatus(GAME_STATE_QUIT); break; } // Cualquier tecla pulsada if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { setGameStatus(GAME_STATE_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() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); if (mLogo.counter == 0) { if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_StopMusic(); } } if (mLogo.counter == 500) // minimo 200 + 255 { mLogo.counter = 0; setGameStatus(GAME_STATE_INTRO); } else { mLogo.counter++; } } } } // Bucle para la intro del juego void GameDirector::runIntro() { // 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 (mGame.status == GAME_STATE_INTRO) { if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = 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) { setGameStatus(GAME_STATE_QUIT); break; } if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { JA_StopMusic(); setGameStatus(GAME_STATE_TITLE); } } // Actualiza los objetos for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { mIntroBitmap[i]->update(); } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntroText[i]->update(); } // Guión de eventos // Primera imagen - UPV if (mIntroEvents[BITMAP0] == EVENT_WAITING) { mIntroBitmap[0]->setEnabled(true); mIntroEvents[BITMAP0] = EVENT_RUNNING; } // Primer texto de la primera imagen if ((mIntroEvents[BITMAP0] == EVENT_COMPLETED) && (mIntroEvents[TEXT0] == EVENT_WAITING)) { mIntroText[0]->setEnabled(true); mIntroEvents[TEXT0] = EVENT_RUNNING; } // Segundo texto de la primera imagen if ((mIntroEvents[TEXT0] == EVENT_COMPLETED) && (mIntroEvents[TEXT1] == EVENT_WAITING)) { mIntroText[0]->setEnabled(false); mIntroText[1]->setEnabled(true); mIntroEvents[TEXT1] = EVENT_RUNNING; } // Tercer texto de la primera imagen if ((mIntroEvents[TEXT1] == EVENT_COMPLETED) && (mIntroEvents[TEXT2] == EVENT_WAITING)) { mIntroText[1]->setEnabled(false); mIntroText[2]->setEnabled(true); mIntroEvents[TEXT2] = EVENT_RUNNING; } // Segunda imagen - Máquina if ((mIntroEvents[TEXT2] == EVENT_COMPLETED) && (mIntroEvents[BITMAP1] == EVENT_WAITING)) { mIntroBitmap[0]->setEnabled(false); mIntroText[2]->setEnabled(false); mIntroBitmap[1]->setEnabled(true); mIntroEvents[BITMAP1] = EVENT_RUNNING; } // Primer texto de la segunda imagen if ((mIntroEvents[BITMAP1] == EVENT_COMPLETED) && (mIntroEvents[TEXT3] == EVENT_WAITING)) { mIntroText[3]->setEnabled(true); mIntroEvents[TEXT3] = EVENT_RUNNING; } // Tercera imagen junto con primer texto - GRITO if ((mIntroEvents[TEXT3] == EVENT_COMPLETED) && (mIntroEvents[BITMAP2] == EVENT_WAITING) && (mIntroEvents[TEXT4] == EVENT_WAITING)) { mIntroBitmap[1]->setEnabled(false); mIntroText[3]->setEnabled(false); mIntroBitmap[2]->setEnabled(true); mIntroText[4]->setEnabled(true); mIntroEvents[BITMAP2] = EVENT_RUNNING; mIntroEvents[TEXT4] = EVENT_RUNNING; } // Cuarta imagen junto con primer texto - Reflexión if ((mIntroEvents[TEXT4] == EVENT_COMPLETED) && (mIntroEvents[BITMAP3] == EVENT_WAITING) && (mIntroEvents[TEXT5] == EVENT_WAITING)) { mIntroBitmap[2]->setEnabled(false); mIntroText[4]->setEnabled(false); mIntroBitmap[3]->setEnabled(true); mIntroText[5]->setEnabled(true); mIntroEvents[BITMAP3] = EVENT_RUNNING; mIntroEvents[TEXT5] = EVENT_RUNNING; } // Segundo texto de la cuarta imagen if ((mIntroEvents[TEXT5] == EVENT_COMPLETED) && (mIntroEvents[TEXT6] == EVENT_WAITING)) { mIntroText[5]->setEnabled(false); mIntroText[6]->setEnabled(true); mIntroEvents[TEXT6] = EVENT_RUNNING; } // Quinta imagen - Patada if ((mIntroEvents[TEXT6] == EVENT_COMPLETED) && (mIntroEvents[BITMAP4] == EVENT_WAITING)) { mIntroBitmap[3]->setEnabled(false); mIntroText[6]->setEnabled(false); mIntroBitmap[4]->setEnabled(true); mIntroEvents[BITMAP4] = EVENT_RUNNING; } // Primer texto de la quinta imagen if ((mIntroEvents[BITMAP4] == EVENT_COMPLETED) && (mIntroEvents[TEXT7] == EVENT_WAITING)) { mIntroText[7]->setEnabled(true); mIntroEvents[TEXT7] = EVENT_RUNNING; } // Sexta imagen junto con texto - Globos de café if ((mIntroEvents[TEXT7] == EVENT_COMPLETED) && (mIntroEvents[BITMAP5] == EVENT_WAITING) && (mIntroEvents[TEXT8] == EVENT_WAITING)) { mIntroBitmap[4]->setEnabled(false); mIntroText[7]->setEnabled(false); mIntroBitmap[5]->setEnabled(true); mIntroText[8]->setEnabled(true); mIntroEvents[BITMAP5] = EVENT_RUNNING; mIntroEvents[TEXT8] = EVENT_RUNNING; } // Acaba el último texto if (mIntroEvents[TEXT8] == EVENT_COMPLETED) { mIntroText[8]->setEnabled(false); JA_StopMusic(); mGame.status = GAME_STATE_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++) { mIntroBitmap[i]->render(); } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { mIntroText[i]->render(); } // Actualiza la pantalla SDL_RenderPresent(mRenderer); } } // Bucle para el titulo del juego void GameDirector::runTitle() { Uint8 R, G, B; R = 0x27; G = 0x27; B = 0x36; while (mGame.status == GAME_STATE_TITLE) { // Estado 0 - Titulo desplazandose if (mTitleStatus == 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) { setGameStatus(GAME_STATE_QUIT); break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza los objetos mCoffeeBitmap->update(); mCrisisBitmap->update(); } // Limpia la pantalla SDL_SetRenderDrawColor(mRenderer, R, G, B, 255); SDL_RenderClear(mRenderer); // Dibuja los objetos mCoffeeBitmap->render(); mCrisisBitmap->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); // Si los objetos han llegado a su destino, cambiamos de estado if ((mTitleEvents[0] == EVENT_COMPLETED) && (mTitleEvents[0] == EVENT_COMPLETED)) { mTitleStatus = 1; // Pantallazo blanco SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(mRenderer); SDL_RenderPresent(mRenderer); } } // Estado 1 - Titulo vibrando if (mTitleStatus == 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) { setGameStatus(GAME_STATE_QUIT); 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 = mCoffeeBitmap->getPosX(); int b = mCrisisBitmap->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 mCoffeeBitmap->setPosX(a + v[n / 3]); mCrisisBitmap->setPosX(b + v[n / 3]); mCoffeeBitmap->render(); mCrisisBitmap->render(); mDustSpriteRight->animate(0); mDustSpriteLeft->animate(0); mDustSpriteRight->render(); mDustSpriteLeft->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } mTitleStatus = 2; } // Estado 2 - La pantalla de titulo con el menú y la música if (mTitleStatus == 2) { if (mTitleTimer > 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) { setGameStatus(GAME_STATE_QUIT); break; } if ((mEventHandler->type == SDL_KEYUP) || (mEventHandler->type == SDL_JOYBUTTONUP)) { mTitleMenuVisible = true; mTitleTimer = TITLE_TIMER; } } // 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() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = 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 (mTitleBackgroundMode) { case 0: mBackgroundWindow.x++; mBackgroundWindow.x %= 64; mBackgroundWindow.y++; mBackgroundWindow.y %= 64; break; case 1: mTitleBackgroundTimer++; mTitleBackgroundTimer %= 360; mBackgroundWindow.x = 128 + (int(mSen[(mTitleBackgroundTimer + 270) % 360] * 128)); mBackgroundWindow.y = 96 + (int(mSen[(360 - mTitleBackgroundTimer) % 360] * 96)); break; default: break; } SDL_RenderCopy(mRenderer, mTitleSurface, &mBackgroundWindow, NULL); // Dibuja los objetos mCoffeeBitmap->render(); mCrisisBitmap->render(); if (mTitleMenuVisible == true) { mMenu.active->render(); } mDustSpriteRight->animate(0); mDustSpriteLeft->animate(0); mDustSpriteRight->render(); mDustSpriteLeft->render(); if ((mTitleTimer % 50 > 14) && (mTitleMenuVisible == 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); } 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 (mTitleMenuVisible == 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: setGameStatus(GAME_STATE_PLAYING); mMenu.active->reset(); mMenuKeyPressed = false; JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); renderFade(1); JA_StopMusic(); init(true); disableDemoMode(); break; case 1: JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); mMenu.active->reset(); mMenu.active = mMenu.options; mFullScreenModePrevious = mFullScreenMode; mWindowSizePrevious = mWindowSize; break; case 2: setGameStatus(GAME_STATE_QUIT); mMenu.active->reset(); mMenuKeyPressed = 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); mWindowSize++; if (mWindowSize == 5) mWindowSize = 1; updateOptionsMenu(); mMenu.active->deselectItem(); break; case 2: // OK JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); SDL_SetWindowFullscreen(mWindow, mFullScreenMode); SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mWindowSize, SCREEN_HEIGHT * mWindowSize); mMenu.active->reset(); mMenu.active = mMenu.title; break; case 3: // CANCEL JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); mFullScreenMode = mFullScreenModePrevious; mWindowSize = mWindowSizePrevious; updateOptionsMenu(); mMenu.active->reset(); mMenu.active = mMenu.title; break; default: break; } } if (mMenu.active->getName() == "TITLE") { mTitleTimer--; } } else if (mTitleTimer == 0) { mTitleTimer = TITLE_TIMER; mGame.status = mTiteNextGS; mMenu.active->reset(); toogleTitleNextGS(); renderFade(1); if (mGame.status == GAME_STATE_INTRO) { JA_StopMusic(); } init(true); enableDemoMode(); } } } } // Bucle para el juego void GameDirector::runGame() { while (mGame.status == GAME_STATE_PLAYING) { // Si la música no está sonando if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { // Reproduce la música if (mPlayer->isAlive()) JA_PlayMusic(mMusic[MUSIC_PLAYING].music, true); } // Lógica del juego if (mGame.paused) runPausedGame(); // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza el contador de juego mGameCounter++; // 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) { setGameStatus(GAME_STATE_QUIT); 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); // Actualiza la pantalla if (mGame.status == GAME_STATE_PLAYING) SDL_RenderPresent(mRenderer); } } // Bucle para el menu de pausa del juego void GameDirector::runPausedGame() { while (mGame.paused) { // 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) { setGameStatus(GAME_STATE_QUIT); break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.pause->update(); } // Cambia el destino donde se pinta todo SDL_SetRenderTarget(mRenderer, mBackbuffer); // Dibuja los objetos renderBackground(); renderBalloons(); renderBullets(); mPlayer->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); //setGameStatus(GAME_STATE_PLAYING); mGame.paused = false; mMenu.pause->reset(); mMenuKeyPressed = false; if (JA_GetMusicState() == JA_MUSIC_PAUSED) { JA_ResumeMusic(); } break; case 1: JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); setGameStatus(GAME_STATE_TITLE); mMenu.pause->reset(); mMenuKeyPressed = false; JA_StopMusic(); renderFade(1); init(true); 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}; SDL_Rect rect2 = {60, 104, 16, 16}; SDL_Rect rect3 = {60, 120, 16, 16}; SDL_Rect rect4 = {60, 136, 16, 16}; SDL_Rect rect5 = {60, 152, 16, 16}; SDL_Rect rect6 = {60, 168, 16, 16}; SDL_Rect rect = {0, 0, 16, 16}; sprite->init(rect1, mTexture[TEXTURE_ITEMS].texture, mRenderer); while (mGame.status == GAME_STATE_INSTRUCTIONS) { 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) { setGameStatus(GAME_STATE_QUIT); break; } if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { JA_StopMusic(); setGameStatus(GAME_STATE_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 * (((mInstructionsCounter + 3) / 36) % 2); sprite->render(); sprite->init(rect2, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mInstructionsCounter + 6) / 36) % 2); sprite->render(); sprite->init(rect3, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mInstructionsCounter + 9) / 36) % 2); sprite->render(); sprite->init(rect4, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mInstructionsCounter + 12) / 36) % 2); sprite->render(); sprite->init(rect5, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; rect.y = 16 * (((mInstructionsCounter + 15) / 36) % 2); sprite->render(); sprite->init(rect6, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x = 0; rect.y = 16 * (((mInstructionsCounter + 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 - mInstructionsCounter) + 100; if (y < 0) { y = 0; } window.y = y; SDL_RenderCopy(mRenderer, mInstructionsSurface, NULL, &window); // Muestra la pantalla SDL_RenderPresent(mRenderer); mInstructionsCounter--; if (mInstructionsCounter == 0) { mInstructionsCounter = INSTRUCTIONS_COUNTER; setGameStatus(GAME_STATE_TITLE); } } delete sprite; } // Bucle para la pantalla de game over void GameDirector::runGameOverScreen() { while (mGame.status == GAME_STATE_GAME_OVER_SCREEN) { // 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) { setGameStatus(GAME_STATE_QUIT); break; } } // Calcula la lógica de los objetos if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Actualiza la lógica del menu 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 renderScoreBoard(); mText.white->write(PLAY_AREA_CENTER_X - (mText.white->lenght("GAME OVER", 0) / 2), PLAY_AREA_CENTER_Y - BLOCK, "GAME OVER", 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: setGameStatus(GAME_STATE_PLAYING); mMenu.gameOver->reset(); mMenuKeyPressed = false; JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); JA_StopMusic(); init(true); renderFade(1); disableDemoMode(); break; case 1: setGameStatus(GAME_STATE_TITLE); mMenu.gameOver->reset(); mMenuKeyPressed = false; JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); JA_StopMusic(); init(true); renderFade(1); mTiteNextGS = GAME_STATE_PLAYING; enableDemoMode(); break; default: break; } } } // Dibuja la informacion de debug en pantalla void GameDirector::renderDebugInfo() { if (mDebug) { mText.white->write(0, 0 * BLOCK, "menace: " + std::to_string(mMenaceLevelThreshold) + "/" + std::to_string(mMenaceLevel)); mText.white->write(0, 1 * BLOCK, "item[0].TTL: " + std::to_string(mItem[0]->mTimeToLive)); mText.white->write(0, 2 * BLOCK, "mGame.stage: " + std::to_string(mGame.stage)); mText.white->write(0, 3 * BLOCK, "mGameCounter : " + std::to_string(mGameCounter)); mText.white->write(0, 4 * BLOCK, ": "); mText.white->write(0, 5 * BLOCK, ": "); } } // Activa el modo Demo void GameDirector::enableDemoMode() { mDemo = true; mGetReadyBitmap->setEnabled(false); } // Desactiva el modo Demo void GameDirector::disableDemoMode() { mDemo = false; mGetReadyBitmap->setEnabled(true); } // Actualiza el proximo estado del juego despues del titulo void GameDirector::toogleTitleNextGS() { if (mTiteNextGS == GAME_STATE_LOGO) { mTiteNextGS = GAME_STATE_PLAYING; } else { mTiteNextGS = GAME_STATE_LOGO; } }