Files
coffee_crisis/source/gamedirector.cpp

3937 lines
108 KiB
C++

#include "const.h"
#include "struct.h"
#include "gamedirector.h"
#include <iostream>
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;
}
}