Files
coffee_crisis/source/game.cpp

3638 lines
93 KiB
C++

#include "game.h"
// Constructor
Game::Game(int numPlayers, int currentStage, SDL_Renderer *renderer, Screen *screen, Asset *mAsset, Lang *lang, Input *input, bool demo, options_t *options)
{
// Copia los punteros
mRenderer = renderer;
mScreen = screen;
this->mAsset = mAsset;
mLang = lang;
mInput = input;
mOptions = options;
mOnePlayerControl = mOptions->input[0].deviceType;
// Pasa variables
mDemo.enabled = demo;
mNumPlayers = numPlayers;
mCurrentStage = currentStage;
mLastStageReached = currentStage;
if (mNumPlayers == 1)
{
mOptions->input[0].deviceType = INPUT_USE_ANY;
}
mDifficulty = mOptions->difficulty;
// Crea los objetos
// Vector de jugadores
if (mNumPlayers == 1)
{
Player *player = new Player(mRenderer, mAsset, PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24);
players.push_back(player);
}
else if (mNumPlayers == 2)
{
Player *player1 = new Player(mRenderer, mAsset, (PLAY_AREA_CENTER_FIRST_QUARTER_X * ((0 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24);
Player *player2 = new Player(mRenderer, mAsset, (PLAY_AREA_CENTER_FIRST_QUARTER_X * ((1 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24);
players.push_back(player1);
players.push_back(player2);
}
balloon1Texture = new LTexture(mRenderer, mAsset->get("balloon1.png"));
balloon2Texture = new LTexture(mRenderer, mAsset->get("balloon2.png"));
balloon3Texture = new LTexture(mRenderer, mAsset->get("balloon3.png"));
balloon4Texture = new LTexture(mRenderer, mAsset->get("balloon4.png"));
mTextureBullet = new LTexture(mRenderer, mAsset->get("bullet.png"));
mTextureGameBG = new LTexture(mRenderer, mAsset->get("game_bg.png"));
mTextureGameText = new LTexture(mRenderer, mAsset->get("game_text.png"));
mTextureItems = new LTexture(mRenderer, mAsset->get("items.png"));
balloon1Animation = new std::vector<std::string>;
balloon2Animation = new std::vector<std::string>;
balloon3Animation = new std::vector<std::string>;
balloon4Animation = new std::vector<std::string>;
loadAnimations(mAsset->get("balloon1.ani"), balloon1Animation);
loadAnimations(mAsset->get("balloon2.ani"), balloon2Animation);
loadAnimations(mAsset->get("balloon3.ani"), balloon3Animation);
loadAnimations(mAsset->get("balloon4.ani"), balloon4Animation);
mText = new Text(mAsset->get("smb2.png"), mAsset->get("smb2.txt"), mRenderer);
mTextScoreBoard = new Text(mAsset->get("8bithud.png"), mAsset->get("8bithud.txt"), mRenderer);
mTextBig = new Text(mAsset->get("smb2_big.png"), mAsset->get("smb2_big.txt"), mRenderer);
mTextNokia2 = new Text(mAsset->get("nokia2.png"), mAsset->get("nokia2.txt"), mRenderer);
mTextNokiaBig2 = new Text(mAsset->get("nokia_big2.png"), mAsset->get("nokia_big2.txt"), mRenderer);
mMenuGameOver = new Menu(mRenderer, mAsset, mInput, mAsset->get("gameover.men"));
mMenuPause = new Menu(mRenderer, mAsset, mInput, mAsset->get("pause.men"));
mFade = new Fade(mRenderer);
mEventHandler = new SDL_Event();
mClouds1a = new MovingSprite(0, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer);
mClouds1b = new MovingSprite(256, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer);
mClouds2a = new MovingSprite(0, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer);
mClouds2b = new MovingSprite(256, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTextureGameBG, mRenderer);
m1000Bitmap = new SmartSprite(mTextureGameText, mRenderer);
m2500Bitmap = new SmartSprite(mTextureGameText, mRenderer);
m5000Bitmap = new SmartSprite(mTextureGameText, mRenderer);
mSpriteBackground = new Sprite(0, 0, GAME_WIDTH, GAME_HEIGHT, mTextureGameBG, mRenderer);
mSpriteGetReady = new Sprite(0, PLAY_AREA_CENTER_Y - 10, 109, 20, mTextureGameText, mRenderer);
mSpriteGradient = new Sprite(0, 0, GAME_WIDTH, GAME_HEIGHT, mTextureGameBG, mRenderer);
mSpriteGrass = new Sprite(0, 85, GAME_WIDTH, 6, mTextureGameBG, mRenderer);
mSpritePowerMeter = new Sprite(PLAY_AREA_CENTER_X - 20, 170, 40, 8, mTextureGameBG, mRenderer);
mSpriteScoreBoard = new Sprite(0, 160, GAME_WIDTH, 32, mTextureGameBG, mRenderer);
}
Game::~Game()
{
saveScoreFile();
saveDemoFile();
mOptions->input[0].deviceType = mOnePlayerControl;
for (auto player : players)
{
delete player;
};
for (auto ballon : balloons)
{
delete ballon;
};
for (auto bullet : bullets)
{
delete bullet;
};
for (auto item : items)
{
delete item;
};
for (auto smartSprite : smartSprites)
{
delete smartSprite;
};
balloon1Texture->unload();
delete balloon1Texture;
balloon2Texture->unload();
delete balloon2Texture;
balloon3Texture->unload();
delete balloon3Texture;
balloon4Texture->unload();
delete balloon4Texture;
delete balloon1Animation;
delete balloon2Animation;
delete balloon3Animation;
delete balloon4Animation;
mTextureBullet->unload();
delete mTextureBullet;
mTextureGameBG->unload();
delete mTextureGameBG;
mTextureGameText->unload();
delete mTextureGameText;
mTextureItems->unload();
delete mTextureItems;
delete mText;
delete mTextBig;
delete mTextScoreBoard;
delete mTextNokia2;
delete mTextNokiaBig2;
delete mMenuGameOver;
delete mMenuPause;
delete mFade;
delete mEventHandler;
delete mClouds1a;
delete mClouds1b;
delete mClouds2a;
delete mClouds2b;
delete m1000Bitmap;
delete m2500Bitmap;
delete m5000Bitmap;
delete mSpriteBackground;
delete mSpriteGetReady;
delete mSpriteGradient;
delete mSpriteGrass;
delete mSpritePowerMeter;
delete mSpriteScoreBoard;
JA_DeleteSound(mSoundBalloon);
JA_DeleteSound(mSoundBullet);
JA_DeleteSound(mSoundPlayerCollision);
JA_DeleteSound(mSoundHiScore);
JA_DeleteSound(mSoundItemDrop);
JA_DeleteSound(mSoundItemPickup);
JA_DeleteSound(mSoundCoffeeOut);
JA_DeleteSound(mSoundStageChange);
JA_DeleteSound(mSoundBubble1);
JA_DeleteSound(mSoundBubble2);
JA_DeleteSound(mSoundBubble3);
JA_DeleteSound(mSoundBubble4);
JA_DeleteSound(mSoundClock);
JA_DeleteSound(mSoundPowerBall);
JA_DeleteSound(mSoundCollision);
JA_DeleteMusic(mMusicPlaying);
}
// Inicializa el vector con los valores del seno
void Game::initSin()
{
// Vector con los valores del seno para 360 grados
for (int i = 0; i < 360; i++)
{
mSin[i] = SDL_sinf((float)i * 3.14f / 180.0f);
}
}
// Inicializa las variables necesarias para la sección 'Game'
void Game::init()
{
// Carga los recursos
loadMedia();
// Carga ficheros
loadScoreFile();
loadDemoFile();
mTicks = 0;
mTicksSpeed = 15;
// Inicializa las variables
switch (mDifficulty)
{
case DIFFICULTY_EASY:
mDefaultEnemySpeed = BALLOON_SPEED_1;
mDifficultyScoreMultiplier = 0.5f;
break;
case DIFFICULTY_NORMAL:
mDefaultEnemySpeed = BALLOON_SPEED_1;
mDifficultyScoreMultiplier = 1.0f;
break;
case DIFFICULTY_HARD:
mDefaultEnemySpeed = BALLOON_SPEED_5;
mDifficultyScoreMultiplier = 1.5f;
break;
default:
break;
}
mGameCompleted = false;
mGameCompletedCounter = 0;
mSection.name = PROG_SECTION_GAME;
mSection.subsection = GAME_SECTION_PLAY_1P;
mMenaceCurrent = 0;
mMenaceThreshold = 0;
mHiScoreAchieved = false;
// mCurrentStage = 0;
mStageBitmapCounter = STAGE_COUNTER;
mDeathCounter = DEATH_COUNTER;
mTimeStopped = false;
mTimeStoppedCounter = 0;
mCounter = 0;
mLastEnemyDeploy = 0;
mEnemyDeployCounter = 0;
mEnemySpeed = mDefaultEnemySpeed;
mEffect.flash = false;
mEffect.shake = false;
mEffect.shakeCounter = SHAKE_COUNTER;
mHelper.needCoffee = false;
mHelper.needCoffeeMachine = false;
mHelper.needPowerBall = false;
mHelper.counter = HELP_COUNTER;
mHelper.itemPoints1Odds = ITEM_POINTS_1_DISK_ODDS;
mHelper.itemPoints2Odds = ITEM_POINTS_2_GAVINA_ODDS;
mHelper.itemPoints3Odds = ITEM_POINTS_3_PACMAR_ODDS;
mHelper.itemClockOdds = ITEM_CLOCK_ODDS;
mHelper.itemCoffeeOdds = ITEM_COFFEE_ODDS;
mHelper.itemCoffeeMachineOdds = ITEM_COFFEE_MACHINE_ODDS;
mPowerBallEnabled = false;
mPowerBallCounter = 0;
mCoffeeMachineEnabled = false;
mPostFade = 0;
// mLastStageReached = 0;
if (mDemo.enabled)
{
const int num = rand() % 2;
if (num == 0)
{
mBalloonsPopped = 1000;
mCurrentStage = 3;
}
else
{
mBalloonsPopped = 1800;
mCurrentStage = 6;
}
}
initSin();
initPaths();
initEnemyFormations();
initEnemyPools();
initGameStages();
// Mas variables
mBalloonsPopped = 0;
for (int i = 0; i < mCurrentStage; ++i)
{
mBalloonsPopped += mStage[i].powerToComplete;
}
// Modo debug
mDebug.enabled = false;
mDebug.enemySet = 0;
mDebug.gradR = mDebug.gradG = mDebug.gradB = 0;
// Modo demo
mDemo.recording = false;
mDemo.counter = 0;
// Inicializa el objeto para el fundido
mFade->init(0x27, 0x27, 0x36);
// Inicializa el objeto con el menu de pausa
// mMenuPause->setName("PAUSE");
// mMenuPause->setPos(0, 12 * BLOCK);
// mMenuPause->setBackgroundType(MENU_BACKGROUND_SOLID);
// mMenuPause->addItem(mLang->getText(46), 2);
// mMenuPause->addItem(mLang->getText(47), 0);
// mMenuPause->setDefaultActionWhenCancel(0);
// mMenuPause->setBackgroundColor({0x29, 0x39, 0x41}, 240);
// mMenuPause->setSelectorColor({0xFF, 0x7A, 0x00}, 255);
// mMenuPause->setSelectorTextColor({0xFF, 0xFF, 0xFF});
// mMenuPause->centerMenuOnX(SCREEN_CENTER_X);
// mMenuPause->centerMenuElementsOnX();
// Inicializa el objeto con el menu de la pantalla de game over
// mMenuGameOver->setName("GAME OVER");
// mMenuGameOver->setPos(0, PLAY_AREA_CENTER_Y + BLOCK * 4);
// mMenuGameOver->setBackgroundType(MENU_BACKGROUND_TRANSPARENT);
// mMenuGameOver->addItem(mLang->getText(48), 2);
// mMenuGameOver->addItem(mLang->getText(49));
// mMenuGameOver->setDefaultActionWhenCancel(1);
// mMenuGameOver->setBackgroundColor({0, 0, 0}, 255);
// mMenuGameOver->setSelectorColor({0x54, 0x6e, 0x7a}, 255);
// mMenuGameOver->setSelectorColor({0x54, 0x6e, 0x7a}, 0);
// mMenuGameOver->setSelectorTextColor({0xFF, 0xFF, 0xFF});
// mMenuGameOver->setSelectorTextColor({0xFF, 0xF1, 0x76});
// mMenuGameOver->setSelectorTextColor({0xFF, 0x7A, 0x00});
// mMenuGameOver->centerMenuOnX(SCREEN_CENTER_X);
// mMenuGameOver->centerMenuElementsOnX();
// Sprites
mClouds1a->setSpriteClip(256, 0, 256, 52);
mClouds1b->setSpriteClip(256, 0, 256, 52);
mClouds2a->setSpriteClip(256, 52, 256, 32);
mClouds2b->setSpriteClip(256, 52, 256, 32);
mSpriteGrass->setPosY(154);
mSpriteScoreBoard->setSpriteClip(0, 160, 256, 32);
mSpritePowerMeter->setSpriteClip(256, 192 - 8, 40, 8);
// Con los globos creados, calcula el nivel de amenaza
evaluateAndSetMenace();
// Inicializa el bitmap de GetReady!
mSpriteGetReady->setSpriteClip(0, 0, 109, 20);
// Inicializa el bitmap de 1000 puntos
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->setEnabledCounter(0);
m1000Bitmap->setDestX(0);
m1000Bitmap->setDestY(0);
// Inicializa el bitmap de 2500 puntos
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->setEnabledCounter(0);
m2500Bitmap->setDestX(0);
m2500Bitmap->setDestY(0);
// Inicializa el bitmap de 5000 puntos
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->setEnabledCounter(0);
m5000Bitmap->setDestX(0);
m5000Bitmap->setDestY(0);
// Los fondos
mGradientRect[0] = {0, 192, GAME_WIDTH, GAME_HEIGHT};
mGradientRect[1] = {256, 192, GAME_WIDTH, GAME_HEIGHT};
mGradientRect[2] = {0, 384, GAME_WIDTH, GAME_HEIGHT};
mGradientRect[3] = {256, 384, GAME_WIDTH, GAME_HEIGHT};
}
// Carga los recursos necesarios para la sección 'Game'
bool Game::loadMedia()
{
bool success = true;
// Sonidos
mSoundBalloon = JA_LoadSound(mAsset->get("balloon.wav").c_str());
mSoundBubble1 = JA_LoadSound(mAsset->get("bubble1.wav").c_str());
mSoundBubble2 = JA_LoadSound(mAsset->get("bubble2.wav").c_str());
mSoundBubble3 = JA_LoadSound(mAsset->get("bubble3.wav").c_str());
mSoundBubble4 = JA_LoadSound(mAsset->get("bubble4.wav").c_str());
mSoundBullet = JA_LoadSound(mAsset->get("bullet.wav").c_str());
mSoundClock = JA_LoadSound(mAsset->get("clock.wav").c_str());
mSoundCoffeeOut = JA_LoadSound(mAsset->get("coffeeout.wav").c_str());
mSoundHiScore = JA_LoadSound(mAsset->get("hiscore.wav").c_str());
mSoundItemDrop = JA_LoadSound(mAsset->get("itemdrop.wav").c_str());
mSoundItemPickup = JA_LoadSound(mAsset->get("itempickup.wav").c_str());
mSoundPlayerCollision = JA_LoadSound(mAsset->get("player_collision.wav").c_str());
mSoundPowerBall = JA_LoadSound(mAsset->get("powerball.wav").c_str());
mSoundStageChange = JA_LoadSound(mAsset->get("stage_change.wav").c_str());
mSoundCollision = JA_LoadSound(mAsset->get("title.wav").c_str());
// Musicas
mMusicPlaying = JA_LoadMusic(mAsset->get("playing.ogg").c_str());
return success;
}
// Carga el fichero de puntos
bool Game::loadScoreFile()
{
// Indicador de éxito en la carga
bool success = true;
const std::string p = mAsset->get("score.bin");
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b");
// El fichero no existe
if (file == nullptr)
{
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 != nullptr)
{
printf("New file (%s) created!\n", filename.c_str());
// Inicializamos los datos
for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
{
mScoreDataFile[i] = 0;
SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1);
}
// Cerramos el fichero
SDL_RWclose(file);
}
else
{
printf("Error: Unable to create file %s\n", filename.c_str());
success = false;
}
}
// El fichero existe
else
{
// Cargamos los datos
printf("Reading file %s\n", filename.c_str());
for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
SDL_RWread(file, &mScoreDataFile[i], sizeof(Uint32), 1);
// Cierra el fichero
SDL_RWclose(file);
}
// Establece el valor de la máxima puntuación a partir del vector con los datos
if (mScoreDataFile[0] == 0)
{
mHiScore = 10000;
}
// Comprueba el checksum para ver si se ha modificado el fichero
else if (mScoreDataFile[0] % 43 == mScoreDataFile[1])
{
mHiScore = mScoreDataFile[0];
}
else
{
mHiScore = 10000;
}
return success;
}
// Carga el fichero de datos para la demo
bool Game::loadDemoFile()
{
// Indicador de éxito en la carga
bool success = true;
const std::string p = mAsset->get("demo.bin");
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b");
// El fichero no existe
if (file == nullptr)
{
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 != nullptr)
{
printf("New file (%s) created!\n", filename.c_str());
// Inicializamos los datos
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
{
mDemo.keys.left = 0;
mDemo.keys.right = 0;
mDemo.keys.noInput = 0;
mDemo.keys.fire = 0;
mDemo.keys.fireLeft = 0;
mDemo.keys.fireRight = 0;
mDemo.dataFile[i] = mDemo.keys;
SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1);
}
// Cerramos el fichero
SDL_RWclose(file);
}
else
{
printf("Error: Unable to create file %s\n", filename.c_str());
success = false;
}
}
// El fichero existe
else
{
// Cargamos los datos
printf("Reading file %s\n", filename.c_str());
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
SDL_RWread(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1);
// Cierra el fichero
SDL_RWclose(file);
}
return success;
}
// Guarda el fichero de puntos
bool Game::saveScoreFile()
{
bool success = true;
const std::string p = mAsset->get("score.bin");
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b");
if (file != nullptr)
{
// Guardamos los datos
for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
{
SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1);
}
printf("Writing file %s\n", filename.c_str());
// Cerramos el fichero
SDL_RWclose(file);
}
else
{
printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError());
}
return success;
}
// Guarda el fichero de datos para la demo
bool Game::saveDemoFile()
{
bool success = true;
const std::string p = mAsset->get("demo.bin");
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
if (mDemo.recording)
{
SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b");
if (file != nullptr)
{
// Guardamos los datos
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
{
SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1);
}
printf("Writing file %s\n", filename.c_str());
// Cerramos el fichero
SDL_RWclose(file);
}
else
{
printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError());
}
}
return success;
}
// Inicializa las formaciones enemigas
void Game::initEnemyFormations()
{
const int y4 = (PLAY_AREA_TOP - BLOCK);
const int x4_0 = PLAY_AREA_LEFT;
const int x4_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_4;
const int y3 = (PLAY_AREA_TOP - BLOCK);
const int x3_0 = PLAY_AREA_LEFT;
const int x3_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_3;
const int y2 = (PLAY_AREA_TOP - BLOCK);
const int x2_0 = PLAY_AREA_LEFT;
const int x2_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_2;
const int y1 = (PLAY_AREA_TOP - BLOCK);
const int x1_0 = PLAY_AREA_LEFT;
const int x1_50 = PLAY_AREA_CENTER_X - (BALLOON_WIDTH_1 / 2);
const int x1_100 = (PLAY_AREA_RIGHT)-BALLOON_WIDTH_1;
// Inicializa a cero las variables
for (int i = 0; i < NUMBER_OF_ENEMY_FORMATIONS; i++)
{
mEnemyFormation[i].numberOfEnemies = 0;
for (int j = 0; j < MAX_NUMBER_OF_ENEMIES_IN_A_FORMATION; j++)
{
mEnemyFormation[i].init[j].x = 0;
mEnemyFormation[i].init[j].y = 0;
mEnemyFormation[i].init[j].velX = 0;
mEnemyFormation[i].init[j].kind = 0;
mEnemyFormation[i].init[j].creationCounter = 0;
}
}
const Uint16 creationTime = 300;
int incX = 0;
Uint8 incTime = 0;
Uint8 j = 0;
// #00 - Dos enemigos BALLOON4 uno a cada extremo
j = 0;
mEnemyFormation[j].numberOfEnemies = 2;
incX = x4_100;
incTime = 0;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x4_0 + (i * incX);
mEnemyFormation[j].init[i].y = y4;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1);
mEnemyFormation[j].init[i].kind = BALLOON_4;
mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i);
}
// #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro
j = 1;
mEnemyFormation[j].numberOfEnemies = 2;
incX = PLAY_AREA_CENTER_X;
incTime = 0;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = PLAY_AREA_CENTER_FIRST_QUARTER_X - (BALLOON_WIDTH_4 / 2) + (i * incX);
mEnemyFormation[j].init[i].y = y4;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1);
mEnemyFormation[j].init[i].kind = BALLOON_4;
mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i);
}
// #02 - Cuatro enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro
j = 2;
mEnemyFormation[j].numberOfEnemies = 4;
incX = BALLOON_WIDTH_2 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x2_0 + (i * incX);
mEnemyFormation[j].init[i].y = y2;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_2;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #03 - Cuatro enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro
j = 3;
mEnemyFormation[j].numberOfEnemies = 4;
incX = BALLOON_WIDTH_2 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x2_100 - (i * incX);
mEnemyFormation[j].init[i].y = y2;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_2;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha
j = 4;
mEnemyFormation[j].numberOfEnemies = 3;
incX = BALLOON_WIDTH_3 * 2;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_0 + (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda
j = 5;
mEnemyFormation[j].numberOfEnemies = 3;
incX = BALLOON_WIDTH_3 * 2;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_100 - (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha
j = 6;
mEnemyFormation[j].numberOfEnemies = 3;
incX = BALLOON_WIDTH_3 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_0 + (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda
j = 7;
mEnemyFormation[j].numberOfEnemies = 3;
incX = BALLOON_WIDTH_3 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_100 - (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha
j = 8;
mEnemyFormation[j].numberOfEnemies = 6;
incX = BALLOON_WIDTH_1 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x1_0 + (i * incX);
mEnemyFormation[j].init[i].y = 13;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_1;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda
j = 9;
mEnemyFormation[j].numberOfEnemies = 6;
incX = BALLOON_WIDTH_1 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x1_100 - (i * incX);
mEnemyFormation[j].init[i].y = 13;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_1;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #10 - Tres enemigos BALLOON4 seguidos desde la izquierda
j = 10;
mEnemyFormation[j].numberOfEnemies = 3;
incX = BALLOON_WIDTH_4 + 1;
incTime = 15;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x4_0 + (i * incX);
mEnemyFormation[j].init[i].y = y4;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_4;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #11 - Tres enemigos BALLOON4 seguidos desde la derecha
j = 11;
mEnemyFormation[j].numberOfEnemies = 3;
incX = BALLOON_WIDTH_4 + 1;
incTime = 15;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x4_100 - (i * incX);
mEnemyFormation[j].init[i].y = y4;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_4;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #12 - Seis enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro
j = 12;
mEnemyFormation[j].numberOfEnemies = 6;
incX = BALLOON_WIDTH_2 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x2_0 + (i * incX);
mEnemyFormation[j].init[i].y = y2;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_2;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #13 - Seis enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro
j = 13;
mEnemyFormation[j].numberOfEnemies = 6;
incX = BALLOON_WIDTH_2 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x2_100 - (i * incX);
mEnemyFormation[j].init[i].y = y2;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_2;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados
j = 14;
mEnemyFormation[j].numberOfEnemies = 5;
incX = BALLOON_WIDTH_3 * 2;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_0 + (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados
j = 15;
mEnemyFormation[j].numberOfEnemies = 5;
incX = BALLOON_WIDTH_3 * 2;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_100 - (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos
j = 16;
mEnemyFormation[j].numberOfEnemies = 5;
incX = BALLOON_WIDTH_3 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_0 + (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos
j = 17;
mEnemyFormation[j].numberOfEnemies = 5;
incX = BALLOON_WIDTH_3 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x3_100 - (i * incX);
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_3;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos
j = 18;
mEnemyFormation[j].numberOfEnemies = 12;
incX = BALLOON_WIDTH_1 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x1_0 + (i * incX);
mEnemyFormation[j].init[i].y = y1;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].kind = BALLOON_1;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos
j = 19;
mEnemyFormation[j].numberOfEnemies = 12;
incX = BALLOON_WIDTH_1 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
mEnemyFormation[j].init[i].x = x1_100 - (i * incX);
mEnemyFormation[j].init[i].y = y1;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].kind = BALLOON_1;
mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i);
}
// #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simetricos
j = 20;
mEnemyFormation[j].numberOfEnemies = 4;
incX = BALLOON_WIDTH_4 + 1;
incTime = 0;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
Uint8 half = mEnemyFormation[j].numberOfEnemies / 2;
if (i < half)
{
mEnemyFormation[j].init[i].x = x4_0 + (i * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
}
else
{
mEnemyFormation[j].init[i].x = x4_100 - ((i - half) * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
}
mEnemyFormation[j].init[i].y = y4;
mEnemyFormation[j].init[i].kind = BALLOON_4;
mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i);
}
// #21 - Diez enemigos BALLOON2 uno detras del otro. Izquierda/derecha. Simetricos
j = 21;
mEnemyFormation[j].numberOfEnemies = 10;
incX = BALLOON_WIDTH_2 + 1;
incTime = 3;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
Uint8 half = mEnemyFormation[j].numberOfEnemies / 2;
if (i < half)
{
mEnemyFormation[j].init[i].x = x2_0 + (i * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i);
}
else
{
mEnemyFormation[j].init[i].x = x2_100 - ((i - half) * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half));
}
mEnemyFormation[j].init[i].y = y2;
mEnemyFormation[j].init[i].kind = BALLOON_2;
}
// #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simetricos
j = 22;
mEnemyFormation[j].numberOfEnemies = 10;
incX = BALLOON_WIDTH_3 * 2;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
Uint8 half = mEnemyFormation[j].numberOfEnemies / 2;
if (i < half)
{
mEnemyFormation[j].init[i].x = x3_0 + (i * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i);
}
else
{
mEnemyFormation[j].init[i].x = x3_100 - ((i - half) * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half));
}
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].kind = BALLOON_3;
}
// #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simetricos
j = 23;
mEnemyFormation[j].numberOfEnemies = 10;
incX = BALLOON_WIDTH_3 + 1;
incTime = 10;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
Uint8 half = mEnemyFormation[j].numberOfEnemies / 2;
if (i < half)
{
mEnemyFormation[j].init[i].x = x3_0 + (i * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i);
}
else
{
mEnemyFormation[j].init[i].x = x3_100 - ((i - half) * incX);
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half));
}
mEnemyFormation[j].init[i].y = y3;
mEnemyFormation[j].init[i].kind = BALLOON_3;
}
// #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos
j = 24;
mEnemyFormation[j].numberOfEnemies = 30;
incX = 0;
incTime = 5;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
Uint8 half = mEnemyFormation[j].numberOfEnemies / 2;
if (i < half)
{
mEnemyFormation[j].init[i].x = x1_50;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * i);
}
else
{
mEnemyFormation[j].init[i].x = x1_50;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * (i - half));
}
mEnemyFormation[j].init[i].y = y1;
mEnemyFormation[j].init[i].kind = BALLOON_1;
}
// #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos
j = 25;
mEnemyFormation[j].numberOfEnemies = 30;
incX = BALLOON_WIDTH_1 + 1;
incTime = 5;
for (int i = 0; i < mEnemyFormation[j].numberOfEnemies; i++)
{
Uint8 half = mEnemyFormation[j].numberOfEnemies / 2;
if (i < half)
{
mEnemyFormation[j].init[i].x = x1_50 + 20;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i);
}
else
{
mEnemyFormation[j].init[i].x = x1_50 - 20;
mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE;
mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half));
}
mEnemyFormation[j].init[i].y = y1;
mEnemyFormation[j].init[i].kind = BALLOON_1;
}
// Crea las mismas formaciones pero con hexagonos a partir de la posición 50 del vector
for (int k = 0; k < j + 1; k++)
{
mEnemyFormation[k + 50].numberOfEnemies = mEnemyFormation[k].numberOfEnemies;
for (int i = 0; i < mEnemyFormation[k + 50].numberOfEnemies; i++)
{
mEnemyFormation[k + 50].init[i].x = mEnemyFormation[k].init[i].x;
mEnemyFormation[k + 50].init[i].y = mEnemyFormation[k].init[i].y;
mEnemyFormation[k + 50].init[i].velX = mEnemyFormation[k].init[i].velX;
mEnemyFormation[k + 50].init[i].creationCounter = mEnemyFormation[k].init[i].creationCounter;
mEnemyFormation[k + 50].init[i].kind = mEnemyFormation[k].init[i].kind + 4;
}
}
// TEST
mEnemyFormation[99].numberOfEnemies = 4;
mEnemyFormation[99].init[0].x = 10;
mEnemyFormation[99].init[0].y = y1;
mEnemyFormation[99].init[0].velX = 0;
mEnemyFormation[99].init[0].kind = BALLOON_1;
mEnemyFormation[99].init[0].creationCounter = 200;
mEnemyFormation[99].init[1].x = 50;
mEnemyFormation[99].init[1].y = y1;
mEnemyFormation[99].init[1].velX = 0;
mEnemyFormation[99].init[1].kind = BALLOON_2;
mEnemyFormation[99].init[1].creationCounter = 200;
mEnemyFormation[99].init[2].x = 90;
mEnemyFormation[99].init[2].y = y1;
mEnemyFormation[99].init[2].velX = 0;
mEnemyFormation[99].init[2].kind = BALLOON_3;
mEnemyFormation[99].init[2].creationCounter = 200;
mEnemyFormation[99].init[3].x = 140;
mEnemyFormation[99].init[3].y = y1;
mEnemyFormation[99].init[3].velX = 0;
mEnemyFormation[99].init[3].kind = BALLOON_4;
mEnemyFormation[99].init[3].creationCounter = 200;
}
// Inicializa los conjuntos de formaciones
void Game::initEnemyPools()
{
// EnemyPool #0
mEnemyPool[0].set[0] = &mEnemyFormation[0];
mEnemyPool[0].set[1] = &mEnemyFormation[1];
mEnemyPool[0].set[2] = &mEnemyFormation[2];
mEnemyPool[0].set[3] = &mEnemyFormation[3];
mEnemyPool[0].set[4] = &mEnemyFormation[4];
mEnemyPool[0].set[5] = &mEnemyFormation[5];
mEnemyPool[0].set[6] = &mEnemyFormation[6];
mEnemyPool[0].set[7] = &mEnemyFormation[7];
mEnemyPool[0].set[8] = &mEnemyFormation[8];
mEnemyPool[0].set[9] = &mEnemyFormation[9];
// EnemyPool #1
mEnemyPool[1].set[0] = &mEnemyFormation[10];
mEnemyPool[1].set[1] = &mEnemyFormation[11];
mEnemyPool[1].set[2] = &mEnemyFormation[12];
mEnemyPool[1].set[3] = &mEnemyFormation[13];
mEnemyPool[1].set[4] = &mEnemyFormation[14];
mEnemyPool[1].set[5] = &mEnemyFormation[15];
mEnemyPool[1].set[6] = &mEnemyFormation[16];
mEnemyPool[1].set[7] = &mEnemyFormation[17];
mEnemyPool[1].set[8] = &mEnemyFormation[18];
mEnemyPool[1].set[9] = &mEnemyFormation[19];
// EnemyPool #2
mEnemyPool[2].set[0] = &mEnemyFormation[0];
mEnemyPool[2].set[1] = &mEnemyFormation[1];
mEnemyPool[2].set[2] = &mEnemyFormation[2];
mEnemyPool[2].set[3] = &mEnemyFormation[3];
mEnemyPool[2].set[4] = &mEnemyFormation[4];
mEnemyPool[2].set[5] = &mEnemyFormation[55];
mEnemyPool[2].set[6] = &mEnemyFormation[56];
mEnemyPool[2].set[7] = &mEnemyFormation[57];
mEnemyPool[2].set[8] = &mEnemyFormation[58];
mEnemyPool[2].set[9] = &mEnemyFormation[59];
// EnemyPool #3
mEnemyPool[3].set[0] = &mEnemyFormation[50];
mEnemyPool[3].set[1] = &mEnemyFormation[51];
mEnemyPool[3].set[2] = &mEnemyFormation[52];
mEnemyPool[3].set[3] = &mEnemyFormation[53];
mEnemyPool[3].set[4] = &mEnemyFormation[54];
mEnemyPool[3].set[5] = &mEnemyFormation[5];
mEnemyPool[3].set[6] = &mEnemyFormation[6];
mEnemyPool[3].set[7] = &mEnemyFormation[7];
mEnemyPool[3].set[8] = &mEnemyFormation[8];
mEnemyPool[3].set[9] = &mEnemyFormation[9];
// EnemyPool #4
mEnemyPool[4].set[0] = &mEnemyFormation[60];
mEnemyPool[4].set[1] = &mEnemyFormation[61];
mEnemyPool[4].set[2] = &mEnemyFormation[62];
mEnemyPool[4].set[3] = &mEnemyFormation[63];
mEnemyPool[4].set[4] = &mEnemyFormation[64];
mEnemyPool[4].set[5] = &mEnemyFormation[65];
mEnemyPool[4].set[6] = &mEnemyFormation[66];
mEnemyPool[4].set[7] = &mEnemyFormation[67];
mEnemyPool[4].set[8] = &mEnemyFormation[68];
mEnemyPool[4].set[9] = &mEnemyFormation[69];
// EnemyPool #5
mEnemyPool[5].set[0] = &mEnemyFormation[10];
mEnemyPool[5].set[1] = &mEnemyFormation[61];
mEnemyPool[5].set[2] = &mEnemyFormation[12];
mEnemyPool[5].set[3] = &mEnemyFormation[63];
mEnemyPool[5].set[4] = &mEnemyFormation[14];
mEnemyPool[5].set[5] = &mEnemyFormation[65];
mEnemyPool[5].set[6] = &mEnemyFormation[16];
mEnemyPool[5].set[7] = &mEnemyFormation[67];
mEnemyPool[5].set[8] = &mEnemyFormation[18];
mEnemyPool[5].set[9] = &mEnemyFormation[69];
// EnemyPool #6
mEnemyPool[6].set[0] = &mEnemyFormation[60];
mEnemyPool[6].set[1] = &mEnemyFormation[11];
mEnemyPool[6].set[2] = &mEnemyFormation[62];
mEnemyPool[6].set[3] = &mEnemyFormation[13];
mEnemyPool[6].set[4] = &mEnemyFormation[64];
mEnemyPool[6].set[5] = &mEnemyFormation[15];
mEnemyPool[6].set[6] = &mEnemyFormation[66];
mEnemyPool[6].set[7] = &mEnemyFormation[17];
mEnemyPool[6].set[8] = &mEnemyFormation[68];
mEnemyPool[6].set[9] = &mEnemyFormation[19];
// EnemyPool #7
mEnemyPool[7].set[0] = &mEnemyFormation[20];
mEnemyPool[7].set[1] = &mEnemyFormation[21];
mEnemyPool[7].set[2] = &mEnemyFormation[22];
mEnemyPool[7].set[3] = &mEnemyFormation[23];
mEnemyPool[7].set[4] = &mEnemyFormation[24];
mEnemyPool[7].set[5] = &mEnemyFormation[65];
mEnemyPool[7].set[6] = &mEnemyFormation[66];
mEnemyPool[7].set[7] = &mEnemyFormation[67];
mEnemyPool[7].set[8] = &mEnemyFormation[68];
mEnemyPool[7].set[9] = &mEnemyFormation[69];
// EnemyPool #8
mEnemyPool[8].set[0] = &mEnemyFormation[70];
mEnemyPool[8].set[1] = &mEnemyFormation[71];
mEnemyPool[8].set[2] = &mEnemyFormation[72];
mEnemyPool[8].set[3] = &mEnemyFormation[73];
mEnemyPool[8].set[4] = &mEnemyFormation[74];
mEnemyPool[8].set[5] = &mEnemyFormation[15];
mEnemyPool[8].set[6] = &mEnemyFormation[16];
mEnemyPool[8].set[7] = &mEnemyFormation[17];
mEnemyPool[8].set[8] = &mEnemyFormation[18];
mEnemyPool[8].set[9] = &mEnemyFormation[19];
// EnemyPool #9
mEnemyPool[9].set[0] = &mEnemyFormation[20];
mEnemyPool[9].set[1] = &mEnemyFormation[21];
mEnemyPool[9].set[2] = &mEnemyFormation[22];
mEnemyPool[9].set[3] = &mEnemyFormation[23];
mEnemyPool[9].set[4] = &mEnemyFormation[24];
mEnemyPool[9].set[5] = &mEnemyFormation[70];
mEnemyPool[9].set[6] = &mEnemyFormation[71];
mEnemyPool[9].set[7] = &mEnemyFormation[72];
mEnemyPool[9].set[8] = &mEnemyFormation[73];
mEnemyPool[9].set[9] = &mEnemyFormation[74];
}
// Inicializa las fases del juego
void Game::initGameStages()
{
// STAGE 1
mStage[0].number = 1;
mStage[0].currentPower = 0;
mStage[0].powerToComplete = 200;
mStage[0].minMenace = 7 + (4 * 1);
mStage[0].maxMenace = 7 + (4 * 3);
mStage[0].enemyPool = &mEnemyPool[0];
// STAGE 2
mStage[1].number = 2;
mStage[1].currentPower = 0;
mStage[1].powerToComplete = 300;
mStage[1].minMenace = 7 + (4 * 2);
mStage[1].maxMenace = 7 + (4 * 4);
mStage[1].enemyPool = &mEnemyPool[1];
// STAGE 3
mStage[2].number = 3;
mStage[2].currentPower = 0;
mStage[2].powerToComplete = 600;
mStage[2].minMenace = 7 + (4 * 3);
mStage[2].maxMenace = 7 + (4 * 5);
mStage[2].enemyPool = &mEnemyPool[2];
// STAGE 4
mStage[3].number = 4;
mStage[3].currentPower = 0;
mStage[3].powerToComplete = 600;
mStage[3].minMenace = 7 + (4 * 3);
mStage[3].maxMenace = 7 + (4 * 5);
mStage[3].enemyPool = &mEnemyPool[3];
// STAGE 5
mStage[4].number = 5;
mStage[4].currentPower = 0;
mStage[4].powerToComplete = 600;
mStage[4].minMenace = 7 + (4 * 4);
mStage[4].maxMenace = 7 + (4 * 6);
mStage[4].enemyPool = &mEnemyPool[4];
// STAGE 6
mStage[5].number = 6;
mStage[5].currentPower = 0;
mStage[5].powerToComplete = 600;
mStage[5].minMenace = 7 + (4 * 4);
mStage[5].maxMenace = 7 + (4 * 6);
mStage[5].enemyPool = &mEnemyPool[5];
// STAGE 7
mStage[6].number = 7;
mStage[6].currentPower = 0;
mStage[6].powerToComplete = 650;
mStage[6].minMenace = 7 + (4 * 5);
mStage[6].maxMenace = 7 + (4 * 7);
mStage[6].enemyPool = &mEnemyPool[6];
// STAGE 8
mStage[7].number = 8;
mStage[7].currentPower = 0;
mStage[7].powerToComplete = 750;
mStage[7].minMenace = 7 + (4 * 5);
mStage[7].maxMenace = 7 + (4 * 7);
mStage[7].enemyPool = &mEnemyPool[7];
// STAGE 9
mStage[8].number = 9;
mStage[8].currentPower = 0;
mStage[8].powerToComplete = 850;
mStage[8].minMenace = 7 + (4 * 6);
mStage[8].maxMenace = 7 + (4 * 8);
mStage[8].enemyPool = &mEnemyPool[8];
// STAGE 10
mStage[9].number = 10;
mStage[9].currentPower = 0;
mStage[9].powerToComplete = 950;
mStage[9].minMenace = 7 + (4 * 7);
mStage[9].maxMenace = 7 + (4 * 10);
mStage[9].enemyPool = &mEnemyPool[9];
}
// Crea una formación de enemigos
void Game::deployEnemyFormation()
{
// Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última
if (mEnemyDeployCounter == 0)
{
// En este punto se decide entre crear una powerball o una formación enemiga
if ((rand() % 100 < 15) && (canPowerBallBeCreated()))
{
// Crea una powerball
createPowerBall();
// Da un poco de margen para que se creen mas enemigos
mEnemyDeployCounter = 50;
}
else
{
// Decrementa el contador de despliegues enemigos de la PowerBall
mPowerBallCounter > 0 ? mPowerBallCounter-- : mPowerBallCounter = 0;
// Elige una formación enemiga la azar
Uint8 set = (rand() % 10);
// Evita repetir la ultima formación enemiga desplegada
if (set == mLastEnemyDeploy)
++set %= 10;
mLastEnemyDeploy = set;
if (mDebug.enabled)
set = mDebug.enemySet;
const Uint8 numEnemies = mStage[mCurrentStage].enemyPool->set[set]->numberOfEnemies;
for (int i = 0; i < numEnemies; ++i)
{
createBalloon(mStage[mCurrentStage].enemyPool->set[set]->init[i].x,
mStage[mCurrentStage].enemyPool->set[set]->init[i].y,
mStage[mCurrentStage].enemyPool->set[set]->init[i].kind,
mStage[mCurrentStage].enemyPool->set[set]->init[i].velX,
mEnemySpeed,
mStage[mCurrentStage].enemyPool->set[set]->init[i].creationCounter);
}
mEnemyDeployCounter = 300;
}
}
}
// Aumenta el poder de la fase
void Game::increaseStageCurrentPower(Uint8 power)
{
mStage[mCurrentStage].currentPower += power;
}
// Establece el valor de la variable
void Game::setHiScore(Uint32 score)
{
mHiScore = score;
}
// Actualiza el valor de mHiScore en caso necesario
void Game::updateHiScore()
{
// Si la puntuación actual es mayor que la máxima puntuación
for (auto player : players)
// for (int i = 0; i < mNumPlayers; i++)
if (player->getScore() > mHiScore)
{
// Actualiza la máxima puntuación
mHiScore = player->getScore();
// Almacena la máxima puntuación en el fichero junto con un checksum
mScoreDataFile[0] = mHiScore;
mScoreDataFile[1] = mHiScore % 43;
// Si se supera la máxima puntuación emite sonido
if (mHiScoreAchieved == false)
{
mHiScoreAchieved = true;
JA_PlaySound(mSoundHiScore);
}
}
}
// Transforma un valor numérico en una cadena de 6 cifras
std::string Game::updateScoreText(Uint32 num)
{
// return (std::to_string(num));
if ((num >= 0) && (num <= 9))
{
return ("000000" + std::to_string(num));
}
if ((num >= 10) && (num <= 99))
{
return ("00000" + std::to_string(num));
}
if ((num >= 100) && (num <= 999))
{
return ("0000" + std::to_string(num));
}
if ((num >= 1000) && (num <= 9999))
{
return ("000" + std::to_string(num));
}
if ((num >= 010000) && (num <= 99999))
{
return ("00" + std::to_string(num));
}
if ((num >= 100000) && (num <= 999999))
{
return ("0" + std::to_string(num));
}
if ((num >= 1000000) && (num <= 9999999))
{
return (std::to_string(num));
}
return (std::to_string(num));
}
// Pinta el marcador en pantalla usando un objeto texto
void Game::renderScoreBoard()
{
mSpriteScoreBoard->render();
const int offset1 = 162;
const int offset2 = offset1 + 7;
const int offset3 = offset2 + 7;
const int offset4 = offset3 + 7;
const int offsetLeft = PLAY_AREA_LEFT + 45;
const int offsetRight = PLAY_AREA_RIGHT - 45;
// PLAYER1 - SCORE
mTextScoreBoard->writeCentered(offsetLeft, offset1, mLang->getText(53));
mTextScoreBoard->writeCentered(offsetLeft, offset2, updateScoreText(players[0]->getScore()));
// PLAYER1 - MULT
mTextScoreBoard->writeCentered(offsetLeft, offset3, mLang->getText(55));
mTextScoreBoard->writeCentered(offsetLeft, offset4, std::to_string(players[0]->getScoreMultiplier()).substr(0, 3));
if (mNumPlayers == 2)
{
// PLAYER2 - SCORE
mTextScoreBoard->writeCentered(offsetRight, offset1, mLang->getText(54));
mTextScoreBoard->writeCentered(offsetRight, offset2, updateScoreText(players[1]->getScore()));
// PLAYER2 - MULT
mTextScoreBoard->writeCentered(offsetRight, offset3, mLang->getText(55));
mTextScoreBoard->writeCentered(offsetRight, offset4, std::to_string(players[1]->getScoreMultiplier()).substr(0, 3));
}
else
{
// PLAYER2 - SCORE
mTextScoreBoard->writeCentered(offsetRight, offset1, mLang->getText(54));
mTextScoreBoard->writeCentered(offsetRight, offset2, "0000000");
// PLAYER2 - MULT
mTextScoreBoard->writeCentered(offsetRight, offset3, mLang->getText(55));
mTextScoreBoard->writeCentered(offsetRight, offset4, "1.0");
}
// STAGE
mTextScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset1, mLang->getText(57) + std::to_string(mStage[mCurrentStage].number));
// POWER
mSpritePowerMeter->setPosY(offset2);
mSpritePowerMeter->setSpriteClip(256, 184, 40, 8);
mSpritePowerMeter->render();
const float percent = (mStage[mCurrentStage].currentPower * 40.0f) / mStage[mCurrentStage].powerToComplete;
mSpritePowerMeter->setSpriteClip(296, 184, (int)percent, 8);
mSpritePowerMeter->render();
// HI-SCORE
mTextScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset3, mLang->getText(56));
mTextScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset4, updateScoreText(mHiScore));
}
// Actualiza las variables del jugador
void Game::updatePlayer()
{
for (auto player : players)
// for (int i = 0; i < mNumPlayers; i++)
{
player->update();
// Comprueba la colisión entre el jugador y los globos
if (checkPlayerBalloonCollision(player))
{
if (player->isAlive())
{
if (mDemo.enabled)
{
mSection = {PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS};
}
else
{
killPlayer(player);
}
}
}
// Comprueba las colisiones entre el jugador y los items
checkPlayerItemCollision(player);
}
}
// Actualiza las variables de la fase
void Game::updateStage()
{
if (mStage[mCurrentStage].currentPower >= mStage[mCurrentStage].powerToComplete)
{
// Cambio de fase
mCurrentStage++;
mLastStageReached = mCurrentStage;
if (mCurrentStage == 10)
{ // Ha llegado al final el juego
mGameCompleted = true; // Marca el juego como completado
mCurrentStage = 9; // Deja el valor dentro de los limites
mStage[mCurrentStage].currentPower = 0; // Deja el poder a cero para que no vuelva a entrar en esta condición
destroyAllBalloons(); // Destruye a todos los enemigos
mStage[mCurrentStage].currentPower = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos lo globos
mMenaceCurrent = 255; // Sube el nivel de amenaza para que no cree mas globos
for (auto player : players)
// for (int i = 0; i < mNumPlayers; i++) // Añade un millon de puntos a los jugadores que queden vivos
{
if (player->isAlive())
{
player->addScore(1000000);
}
}
updateHiScore();
JA_StopMusic();
}
JA_PlaySound(mSoundStageChange);
mStageBitmapCounter = 0;
mEnemySpeed = mDefaultEnemySpeed;
setBalloonSpeed(mEnemySpeed);
mEffect.flash = true;
mEffect.shake = true;
}
// Incrementa el contador del bitmap que aparece mostrando el cambio de fase
if (mStageBitmapCounter < STAGE_COUNTER)
{
mStageBitmapCounter++;
}
// Si el juego se ha completado, el bitmap se detiene en el centro de la pantalla
if (mGameCompleted)
{
if (mStageBitmapCounter > 100)
{
mStageBitmapCounter = 100;
}
}
}
// Actualiza el estado de muerte
void Game::updateDeath()
{
// Comprueba si todos los jugadores estan muertos
bool allAreDead = true;
for (auto player : players)
// for (int i = 0; i < mNumPlayers; i++)
{
allAreDead &= (!player->isAlive());
/*if (!player->isAlive())
{
// Animación
if ((player->getDeathCounter() / 5) % 4 == 0)
{
smartSprites[player->deathIndex]->setSpriteClip(24 * 0, 24, 24, 24);
}
else if ((player->getDeathCounter() / 5) % 4 == 1)
{
smartSprites[player->deathIndex]->setSpriteClip(24 * 1, 24, 24, 24);
}
else if ((player->getDeathCounter() / 5) % 4 == 2)
{
smartSprites[player->deathIndex]->setSpriteClip(24 * 2, 24, 24, 24);
}
else if ((player->getDeathCounter() / 5) % 4 == 3)
{
smartSprites[player->deathIndex]->setSpriteClip(24 * 3, 24, 24, 24);
}
// Rebote en los laterales
if (smartSprites[player->deathIndex]->getVelX() > 0)
{
if (smartSprites[player->deathIndex]->getPosX() > (GAME_WIDTH - smartSprites[player->deathIndex]->getWidth()))
{
smartSprites[player->deathIndex]->setPosX(GAME_WIDTH - smartSprites[player->deathIndex]->getWidth());
smartSprites[player->deathIndex]->setVelX(smartSprites[player->deathIndex]->getVelX() * (-1));
smartSprites[player->deathIndex]->setDestX(smartSprites[player->deathIndex]->getDestX() * (-1));
}
}
else
{
if (smartSprites[player->deathIndex]->getPosX() < 0)
{
smartSprites[player->deathIndex]->setPosX(0);
smartSprites[player->deathIndex]->setVelX(smartSprites[player->deathIndex]->getVelX() * (-1));
smartSprites[player->deathIndex]->setDestX(smartSprites[player->deathIndex]->getDestX() * (-1));
}
}
}*/
}
if (allAreDead)
{
if (mDeathCounter > 0)
{
mDeathCounter--;
if ((mDeathCounter == 250) || (mDeathCounter == 200) || (mDeathCounter == 180) || (mDeathCounter == 120) || (mDeathCounter == 60))
{
const Uint8 sound = rand() % 4;
switch (sound)
{
case 0:
JA_PlaySound(mSoundBubble1, 0);
break;
case 1:
JA_PlaySound(mSoundBubble2, 0);
break;
case 2:
JA_PlaySound(mSoundBubble3, 0);
break;
case 3:
JA_PlaySound(mSoundBubble4, 0);
break;
default:
break;
}
}
}
else
{
mSection.subsection = GAME_SECTION_GAMEOVER;
}
}
}
// Renderiza el fade final cuando se acaba la partida
void Game::renderDeathFade(int counter)
// Counter debe ir de 0 a 150
{
SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 255);
if (counter < 150)
{
// 192 / 6 = 32, 6 cuadrados de 32 pixeles
SDL_Rect rect[12];
Uint8 h = counter / 3;
for (int i = 0; i < 12; i++)
{
rect[i].x = 0;
rect[i].y = i * 16;
rect[i].w = GAME_WIDTH;
if (i == 0)
rect[i].h = h;
else
rect[i].h = std::max(rect[i - 1].h - 3, 0);
SDL_RenderFillRect(mRenderer, &rect[i]);
}
}
else
{
SDL_Rect rect = {0, 0, GAME_WIDTH, GAME_HEIGHT};
SDL_RenderFillRect(mRenderer, &rect);
}
}
// Actualiza los globos
void Game::updateBalloons()
{
for (auto balloon : balloons)
{
balloon->update();
}
}
// Pinta en pantalla todos los globos activos
void Game::renderBalloons()
{
for (auto balloon : balloons)
{
balloon->render();
}
}
// Crea un globo nuevo en el vector de globos
Uint8 Game::createBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 creationtimer)
{
Balloon *b = new Balloon(x, y, kind, velx, speed, creationtimer, balloonTexture(kind), balloonStreamAnimation(kind), mRenderer);
balloons.push_back(b);
return (Uint8)(balloons.size() - 1);
}
// Crea una PowerBall
void Game::createPowerBall()
{
const int posY = PLAY_AREA_TOP;
const int left = PLAY_AREA_LEFT;
const int center = PLAY_AREA_CENTER_X - (BALLOON_WIDTH_4 / 2);
const int right = PLAY_AREA_RIGHT - BALLOON_WIDTH_4;
const int x[3] = {left, center, right};
const int posX = x[rand() % 3];
Balloon *b = new Balloon(posX, posY, POWER_BALL, BALLOON_VELX_POSITIVE * (((rand() % 2) * 2) - 1), mEnemySpeed, 100, balloon4Texture, balloon4Animation, mRenderer);
balloons.push_back(b);
mPowerBallEnabled = true;
mPowerBallCounter = POWERBALL_COUNTER;
}
// Establece la velocidad de los globos
void Game::setBalloonSpeed(float speed)
{
for (auto balloon : balloons)
// for (int i = 0; i < MAX_BALLOONS; i++)
{
if (balloon->isEnabled())
{
balloon->setSpeed(speed);
}
}
}
// Incrementa la velocidad de los globos
void Game::incBalloonSpeed()
{
// La velocidad solo se incrementa en el modo normal
if (mDifficulty == DIFFICULTY_NORMAL)
{
if (mEnemySpeed == BALLOON_SPEED_1)
mEnemySpeed = BALLOON_SPEED_2;
else if (mEnemySpeed == BALLOON_SPEED_2)
mEnemySpeed = BALLOON_SPEED_3;
else if (mEnemySpeed == BALLOON_SPEED_3)
mEnemySpeed = BALLOON_SPEED_4;
else if (mEnemySpeed == BALLOON_SPEED_4)
mEnemySpeed = BALLOON_SPEED_5;
setBalloonSpeed(mEnemySpeed);
}
}
// Decrementa la velocidad de los globos
void Game::decBalloonSpeed()
{
// La velocidad solo se decrementa en el modo normal
if (mDifficulty == DIFFICULTY_NORMAL)
{
if (mEnemySpeed == BALLOON_SPEED_5)
mEnemySpeed = BALLOON_SPEED_4;
else if (mEnemySpeed == BALLOON_SPEED_4)
mEnemySpeed = BALLOON_SPEED_3;
else if (mEnemySpeed == BALLOON_SPEED_3)
mEnemySpeed = BALLOON_SPEED_2;
else if (mEnemySpeed == BALLOON_SPEED_2)
mEnemySpeed = BALLOON_SPEED_1;
setBalloonSpeed(mEnemySpeed);
}
}
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
void Game::updateBalloonSpeed()
{
const float percent = (float)mStage[mCurrentStage].currentPower / (float)mStage[mCurrentStage].powerToComplete;
if (mEnemySpeed == BALLOON_SPEED_1)
{
if (percent > 0.2f)
{
incBalloonSpeed();
}
}
else if (mEnemySpeed == BALLOON_SPEED_2)
{
if (percent > 0.4f)
{
incBalloonSpeed();
}
}
else if (mEnemySpeed == BALLOON_SPEED_3)
{
if (percent > 0.6f)
{
incBalloonSpeed();
}
}
else if (mEnemySpeed == BALLOON_SPEED_4)
{
if (percent > 0.8f)
{
incBalloonSpeed();
}
}
}
// Explosiona un globo. Lo destruye y crea otros dos si es el caso
void Game::popBalloon(Balloon *balloon)
{
// Aumenta el poder de la fase
increaseStageCurrentPower(1);
mBalloonsPopped++;
const Uint8 kind = balloon->getKind();
switch (kind)
{
// Si es del tipo más pequeño, simplemente elimina el globo
case BALLOON_1:
balloon->pop();
break;
case HEXAGON_1:
balloon->pop();
break;
// Si es del tipo PowerBall, destruye todos los globos
case POWER_BALL:
destroyAllBalloons();
mPowerBallEnabled = false;
mEnemyDeployCounter = 20;
break;
// En cualquier otro caso, crea dos globos de un tipo inferior
default:
const int index = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, BALLOON_VELX_NEGATIVE, mEnemySpeed, 0);
balloons.at(index)->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
if (balloons.at(index)->getClass() == BALLOON_CLASS)
{
balloons.at(index)->setVelY(-2.50f);
}
else
{
balloons.at(index)->setVelY(BALLOON_VELX_NEGATIVE);
}
const int index2 = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, BALLOON_VELX_POSITIVE, mEnemySpeed, 0);
balloons.at(index2)->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
if (balloons.at(index2)->getClass() == BALLOON_CLASS)
{
balloons.at(index2)->setVelY(-2.50f);
}
else
{
balloons.at(index2)->setVelY(BALLOON_VELX_NEGATIVE);
}
// Elimina el globo
balloon->pop();
break;
}
// Recalcula el nivel de amenaza
evaluateAndSetMenace();
}
// Explosiona un globo. Lo destruye
void Game::destroyBalloon(Balloon *balloon)
{
int score = 0;
Uint8 power = 0;
// Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos
switch (balloon->getSize())
{
case BALLOON_SIZE_4:
score = BALLOON_SCORE_4 + (2 * BALLOON_SCORE_3) + (4 * BALLOON_SCORE_2) + (8 * BALLOON_SCORE_1);
power = 15;
break;
case BALLOON_SIZE_3:
score = BALLOON_SCORE_3 + (2 * BALLOON_SCORE_2) + (4 * BALLOON_SCORE_1);
power = 7;
break;
case BALLOON_SIZE_2:
score = BALLOON_SCORE_2 + (2 * BALLOON_SCORE_1);
power = 3;
break;
case BALLOON_SIZE_1:
score = BALLOON_SCORE_1;
power = 1;
break;
default:
score = 0;
power = 0;
break;
}
// Otorga los puntos correspondientes al globo
for (auto player : players)
{
player->addScore(Uint32(score * player->getScoreMultiplier() * mDifficultyScoreMultiplier));
}
updateHiScore();
// Aumenta el poder de la fase
increaseStageCurrentPower(power);
mBalloonsPopped += power;
// Destruye el globo
balloon->pop();
// Recalcula el nivel de amenaza
evaluateAndSetMenace();
}
// Explosiona todos los globos
void Game::popAllBalloons()
{
for (auto balloon : balloons)
{
if ((balloon->isEnabled()) && (!balloon->isPopping()) && (!balloon->isBeingCreated()))
{
popBalloon(balloon);
}
}
JA_PlaySound(mSoundBalloon);
}
// Destruye todos los globos
void Game::destroyAllBalloons()
{
for (auto balloon : balloons)
{
if ((balloon->isEnabled()) && (!balloon->isPopping()))
{
destroyBalloon(balloon);
}
}
mEnemyDeployCounter = 255;
JA_PlaySound(mSoundPowerBall);
mEffect.flash = true;
mEffect.shake = true;
}
// Detiene todos los globos
void Game::stopAllBalloons(Uint16 time)
{
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
balloon->setStop(true);
balloon->setStoppedTimer(time);
}
}
}
// Pone en marcha todos los globos
void Game::startAllBalloons()
{
for (auto balloon : balloons)
{
if ((balloon->isEnabled()) && (!balloon->isBeingCreated()))
{
balloon->setStop(false);
balloon->setStoppedTimer(0);
}
}
}
// Obtiene el numero de globos activos
Uint8 Game::countBalloons()
{
Uint8 num = 0;
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
if (!balloon->isPopping())
{
num++;
}
}
}
return num;
}
// Obtiene la textura correspondiente en funcion del tipo
LTexture *Game::balloonTexture(int kind)
{
if (kind == 1 || kind == 5)
{
return balloon1Texture;
}
else if (kind == 2 || kind == 6)
{
return balloon2Texture;
}
else if (kind == 3 || kind == 7)
{
return balloon3Texture;
}
else if (kind == 4 || kind == 8 || kind == 9)
{
return balloon4Texture;
}
return balloon1Texture;
}
// Obtiene la animacion correspondiente en funcion del tipo
std::vector<std::string> *Game::balloonStreamAnimation(int kind)
{
if (kind == 1 || kind == 5)
{
return balloon1Animation;
}
else if (kind == 2 || kind == 6)
{
return balloon2Animation;
}
else if (kind == 3 || kind == 7)
{
return balloon3Animation;
}
else if (kind == 4 || kind == 8 || kind == 9)
{
return balloon4Animation;
}
return balloon1Animation;
}
// Vacia el vector de globos
void Game::freeBalloons()
{
if (balloons.empty() == false)
{
for (int i = balloons.size() - 1; i >= 0; --i)
{
if (balloons.at(i)->isEnabled() == false)
{
delete balloons.at(i);
balloons.erase(balloons.begin() + i);
}
}
}
}
// Comprueba la colisión entre el jugador y los globos activos
bool Game::checkPlayerBalloonCollision(Player *player)
{
for (auto balloon : balloons)
{
if ((balloon->isEnabled()) && !(balloon->isStopped()) && !(balloon->isInvulnerable()))
{
if (checkCollision(player->getCollider(), balloon->getCollider()))
{
return true;
}
}
}
return false;
}
// Comprueba la colisión entre el jugador y los items
void Game::checkPlayerItemCollision(Player *player)
{
if (!player->isAlive())
{
return;
}
for (auto item : items)
{
if (item->isEnabled())
{
if (checkCollision(player->getCollider(), item->getCollider()))
{
switch (item->getClass())
{
case ITEM_POINTS_1_DISK:
player->addScore(1000);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (m1000Bitmap->getWidth() / 2), player->getPosY(), m1000Bitmap);
JA_PlaySound(mSoundItemPickup);
break;
case ITEM_POINTS_2_GAVINA:
player->addScore(2500);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (m2500Bitmap->getWidth() / 2), player->getPosY(), m2500Bitmap);
JA_PlaySound(mSoundItemPickup);
break;
case ITEM_POINTS_3_PACMAR:
player->addScore(5000);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), player->getPosY(), m5000Bitmap);
JA_PlaySound(mSoundItemPickup);
break;
case ITEM_CLOCK:
enableTimeStopItem();
JA_PlaySound(mSoundItemPickup);
break;
case ITEM_COFFEE:
if (player->getCoffees() == 2)
{
player->addScore(5000);
updateHiScore();
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), player->getPosY(), m5000Bitmap);
}
player->giveExtraHit();
JA_PlaySound(mSoundItemPickup);
break;
case ITEM_COFFEE_MACHINE:
player->setPowerUp(true);
JA_PlaySound(mSoundItemPickup);
mCoffeeMachineEnabled = false;
break;
default:
break;
}
item->disable();
}
}
}
}
// Comprueba y procesa la colisión entre las balas y los globos
void Game::checkBulletBalloonCollision()
{
for (auto bullet : bullets)
{
for (auto balloon : balloons)
{
if (balloon->isEnabled() && (!balloon->isInvulnerable()) && bullet->isEnabled())
{
if (checkCollision(balloon->getCollider(), bullet->getCollider()))
{
// Otorga los puntos correspondientes al globo al jugador que disparó la bala
int index = bullet->getOwner();
players.at(index)->incScoreMultiplier();
players.at(index)->addScore(Uint32(balloon->getScore() * players.at(index)->getScoreMultiplier() * mDifficultyScoreMultiplier));
updateHiScore();
// Explota el globo
popBalloon(balloon);
// Si no es el modo demo, genera un sonido
if (!mDemo.enabled)
{
JA_PlaySound(mSoundBalloon);
}
// Deshabilita la bala
bullet->disable();
// Suelta el item en caso de que salga uno
const Uint8 droppeditem = dropItem();
if ((droppeditem != NO_KIND) && !(mDemo.enabled) && !(mDemo.recording))
{
if (droppeditem != ITEM_COFFEE_MACHINE)
{
createItem(droppeditem, balloon->getPosX(), balloon->getPosY());
JA_PlaySound(mSoundItemDrop);
}
else
{
createItem(droppeditem, players.at(index)->getPosX(), 0);
mCoffeeMachineEnabled = true;
}
}
break;
}
}
}
}
}
// Mueve las balas activas
void Game::moveBullets()
{
for (auto bullet : bullets)
{
if (bullet->isEnabled())
{
if (bullet->move() == BULLET_MOVE_OUT)
{
players.at(bullet->getOwner())->decScoreMultiplier();
}
}
}
}
// Pinta las balas activas
void Game::renderBullets()
{
for (auto bullet : bullets)
{
if (bullet->isEnabled())
{
bullet->render();
}
}
}
// Crea un objeto bala
void Game::createBullet(int x, int y, Uint8 kind, bool poweredUp, int owner)
{
Bullet *b = new Bullet(x, y, kind, poweredUp, owner, mTextureBullet, mRenderer);
bullets.push_back(b);
}
// Vacia el vector de balas
void Game::freeBullets()
{
if (bullets.empty() == false)
{
for (int i = bullets.size() - 1; i >= 0; --i)
{
if (bullets.at(i)->isEnabled() == false)
{
delete bullets.at(i);
bullets.erase(bullets.begin() + i);
}
}
}
}
// Actualiza los items
void Game::updateItems()
{
for (auto item : items)
{
if (item->isEnabled())
{
item->update();
if (item->isOnFloor())
{
JA_PlaySound(mSoundCollision);
mEffect.shake = true;
}
}
}
}
// Pinta los items activos
void Game::renderItems()
{
for (auto item : items)
{
item->render();
}
}
// Devuelve un item en función del azar
Uint8 Game::dropItem()
{
const Uint8 luckyNumber = rand() % 100;
const Uint8 item = rand() % 6;
switch (item)
{
case 0:
if (luckyNumber < mHelper.itemPoints1Odds)
{
return ITEM_POINTS_1_DISK;
}
break;
case 1:
if (luckyNumber < mHelper.itemPoints2Odds)
{
return ITEM_POINTS_2_GAVINA;
}
break;
case 2:
if (luckyNumber < mHelper.itemPoints3Odds)
{
return ITEM_POINTS_3_PACMAR;
}
break;
case 3:
if (luckyNumber < mHelper.itemClockOdds)
{
return ITEM_CLOCK;
}
break;
case 4:
if (luckyNumber < mHelper.itemCoffeeOdds)
{
mHelper.itemCoffeeOdds = ITEM_COFFEE_ODDS;
return ITEM_COFFEE;
}
else
{
if (mHelper.needCoffee)
{
mHelper.itemCoffeeOdds++;
}
}
break;
case 5:
if (luckyNumber < mHelper.itemCoffeeMachineOdds)
{
mHelper.itemCoffeeMachineOdds = ITEM_COFFEE_MACHINE_ODDS;
if ((!mCoffeeMachineEnabled) && (mHelper.needCoffeeMachine))
{
return ITEM_COFFEE_MACHINE;
}
}
else
{
if (mHelper.needCoffeeMachine)
{
mHelper.itemCoffeeMachineOdds++;
}
}
break;
default:
break;
}
return NO_KIND;
}
// Crea un objeto item
void Game::createItem(Uint8 type, float x, float y)
{
Item *item = new Item(mRenderer, mAsset, type, x, y);
items.push_back(item);
}
// Vacia el vector de items
void Game::freeItems()
{
if (items.empty() == false)
{
for (int i = items.size() - 1; i >= 0; --i)
{
if (items.at(i)->isEnabled() == false)
{
delete items.at(i);
items.erase(items.begin() + i);
}
}
}
}
// Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto
void Game::createItemScoreSprite(int x, int y, SmartSprite *sprite)
{
SmartSprite *ss = new SmartSprite(nullptr, mRenderer);
smartSprites.push_back(ss);
// Crea una copia del objeto
*ss = *sprite;
ss->setPosX(x);
ss->setPosY(y);
ss->setDestX(x);
ss->setDestY(y - 15);
ss->setEnabled(true);
ss->setEnabledCounter(100);
}
// Vacia el vector de smartsprites
void Game::freeSmartSprites()
{
if (smartSprites.empty() == false)
{
for (int i = smartSprites.size() - 1; i >= 0; --i)
{
if (smartSprites.at(i)->hasFinished())
{
delete smartSprites.at(i);
smartSprites.erase(smartSprites.begin() + i);
}
}
}
}
// Dibuja el efecto de flash
void Game::renderFlashEffect()
{
if (mEffect.flash)
{
// Pantallazo blanco
SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(mRenderer);
mEffect.flash = false;
}
}
// Actualiza el efecto de agitar la pantalla
void Game::updateShakeEffect()
{
if (mEffect.shake)
{
if (mEffect.shakeCounter > 0)
{
mEffect.shakeCounter--;
}
else
{
mEffect.shake = false;
mEffect.shakeCounter = SHAKE_COUNTER;
}
}
}
// Crea un SmartSprite para arrojar el item café al recibir un impacto
void Game::throwCoffee(int x, int y)
{
SmartSprite *ss = new SmartSprite(mTextureItems, mRenderer);
smartSprites.push_back(ss);
ss->setPosX(x - 8);
ss->setPosY(y - 8);
ss->setWidth(16);
ss->setHeight(16);
ss->setVelX(-1.0f + ((rand() % 5) * 0.5f));
ss->setVelY(-4.0f);
ss->setAccelX(0.0f);
ss->setAccelY(0.2f);
ss->setDestX(x + (ss->getVelX() * 50));
ss->setDestY(GAME_HEIGHT + 1);
ss->setEnabled(true);
ss->setEnabledCounter(1);
ss->setSpriteClip(80, 16, 16, 16);
ss->setRotate(true);
ss->setRotateSpeed(10);
ss->setRotateAmount(90.0);
}
// Crea un SmartSprite para arrojar al jugador al morir
void Game::throwPlayer(int x, int y, Player *player)
{
const int sentit = ((rand() % 2) ? 1 : -1);
// player->deathIndex = getSmartSpriteFreeIndex();
SmartSprite *ss = new SmartSprite(nullptr, mRenderer);
smartSprites.push_back(ss);
ss->setTexture(player->getDeadTexture());
ss->setPosX(x);
ss->setPosY(y);
ss->setWidth(24);
ss->setHeight(24);
ss->setVelX(2.0f * sentit);
ss->setVelY(-5.0f);
ss->setAccelX(0.0f);
ss->setAccelY(0.2f);
ss->setDestX(GAME_WIDTH * sentit);
ss->setDestY(GAME_HEIGHT + 1);
ss->setEnabled(true);
ss->setEnabledCounter(1);
ss->setSpriteClip(0, 0, 24, 24);
}
// Actualiza los SmartSprites
void Game::updateSmartSprites()
{
for (auto ss : smartSprites)
{
ss->update();
}
}
// Pinta los SmartSprites activos
void Game::renderSmartSprites()
{
for (auto ss : smartSprites)
{
ss->render();
}
}
// Acciones a realizar cuando el jugador muere
void Game::killPlayer(Player *player)
{
if (!player->isInvulnerable())
{
if (player->hasExtraHit())
{
player->removeExtraHit();
throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2));
JA_PlaySound(mSoundCoffeeOut);
}
else
{
JA_PauseMusic();
stopAllBalloons(10);
JA_PlaySound(mSoundPlayerCollision);
shakeScreen();
SDL_Delay(500);
JA_PlaySound(mSoundCoffeeOut);
throwPlayer(player->getPosX(), player->getPosY(), player);
player->setAlive(false);
if (allPlayersAreDead())
{
JA_StopMusic();
}
else
{
JA_ResumeMusic();
}
}
}
}
// Calcula y establece el valor de amenaza en funcion de los globos activos
void Game::evaluateAndSetMenace()
{
mMenaceCurrent = 0;
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
mMenaceCurrent += balloon->getMenace();
}
}
}
// Obtiene el valor de la variable
Uint8 Game::getMenace()
{
return mMenaceCurrent;
}
// Establece el valor de la variable
void Game::setTimeStopped(bool value)
{
mTimeStopped = value;
}
// Obtiene el valor de la variable
bool Game::isTimeStopped()
{
return mTimeStopped;
}
// Establece el valor de la variable
void Game::setTimeStoppedCounter(Uint16 value)
{
mTimeStoppedCounter = value;
}
// Incrementa el valor de la variable
void Game::incTimeStoppedCounter(Uint16 value)
{
mTimeStoppedCounter += value;
}
// Actualiza y comprueba el valor de la variable
void Game::updateTimeStoppedCounter()
{
if (isTimeStopped())
{
if (mTimeStoppedCounter > 0)
{
mTimeStoppedCounter--;
stopAllBalloons(TIME_STOPPED_COUNTER);
}
else
{
disableTimeStopItem();
}
}
}
// Actualiza la variable mEnemyDeployCounter
void Game::updateEnemyDeployCounter()
{
if (mEnemyDeployCounter > 0)
{
mEnemyDeployCounter--;
}
}
// Actualiza el juego
void Game::update()
{
// Comprueba los eventos que hay en cola
checkEventHandler();
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - mTicks > mTicksSpeed)
{
// Actualiza el contador de ticks
mTicks = SDL_GetTicks();
// Actualiza el contador de juego
mCounter++;
// Comprueba el teclado/mando
checkGameInput();
// Actualiza las variables del jugador
updatePlayer();
// Actualiza el fondo
updateBackground();
// Mueve los globos
updateBalloons();
// Mueve las balas
moveBullets();
// Actualiza los items
updateItems();
// Actualiza el valor de mCurrentStage
updateStage();
// Actualiza el estado de muerte
updateDeath();
// Actualiza los SmartSprites
updateSmartSprites();
// Actualiza los contadores de estado y efectos
updateTimeStoppedCounter();
updateEnemyDeployCounter();
updateShakeEffect();
// Actualiza el ayudante
updateHelper();
// Comprueba las colisiones entre globos y balas
checkBulletBalloonCollision();
// Comprueba el nivel de amenaza para ver si se han de crear nuevos enemigos
if (!mGameCompleted)
{
updateMenace();
}
// Actualiza la velocidad de los enemigos
updateBalloonSpeed();
// Actualiza el tramo final de juego, una vez completado
updateGameCompleted();
// Vacia los vectores
freeBullets();
freeBalloons();
freeItems();
freeSmartSprites();
}
}
// Actualiza el fondo
void Game::updateBackground()
{
mClouds1a->move();
mClouds1b->move();
mClouds2a->move();
mClouds2b->move();
if (mClouds1a->getPosX() < -mClouds1a->getWidth())
{
mClouds1a->setPosX(mClouds1a->getWidth());
}
if (mClouds1b->getPosX() < -mClouds1b->getWidth())
{
mClouds1b->setPosX(mClouds1b->getWidth());
}
if (mClouds2a->getPosX() < -mClouds2a->getWidth())
{
mClouds2a->setPosX(mClouds2a->getWidth());
}
if (mClouds2b->getPosX() < -mClouds2b->getWidth())
{
mClouds2b->setPosX(mClouds2b->getWidth());
}
mSpriteGrass->setSpriteClip(256, 85 + (6 * (mCounter / 20 % 2)), GAME_WIDTH, 6);
if (mEffect.shake)
{
mSpriteBackground->setPosX(((mEffect.shakeCounter % 2) * 2) - 1);
}
else
{
mSpriteBackground->setPosX(0);
}
}
// Dibuja el fondo
void Game::renderBackground()
{
const float gradientNumber = std::min(((float)mBalloonsPopped / 1000.0f), 3.0f);
const float percent = gradientNumber - (int)gradientNumber;
const int alpha = std::max((255 - (int)(255 * percent)), 0);
// Dibuja el gradiente 2
mSpriteGradient->setSpriteClip(mGradientRect[((int)gradientNumber + 1) % 4]);
mSpriteGradient->render();
// Dibuja el gradiente 1 con una opacidad cada vez menor
mSpriteGradient->setSpriteClip(mGradientRect[(int)gradientNumber]);
mTextureGameBG->setAlpha(alpha);
mSpriteGradient->render();
mTextureGameBG->setAlpha(255);
mClouds1a->render();
mClouds1b->render();
mClouds2a->render();
mClouds2b->render();
mSpriteBackground->render();
mSpriteGrass->render();
}
// Dibuja el juego
void Game::render()
{
// Prepara para empezar a dibujar en la textura de juego
mScreen->start();
// Limpia la pantalla
mScreen->clean(bgColor);
// Dibuja los objetos
renderBackground();
renderBalloons();
renderBullets();
renderMessages();
renderItems();
renderSmartSprites();
renderScoreBoard();
for (auto player : players)
{
player->render();
}
if ((mDeathCounter <= 150) && !players.at(0)->isAlive())
{
renderDeathFade(150 - mDeathCounter);
}
if ((mGameCompleted) && (mGameCompletedCounter >= 300))
{
renderDeathFade(mGameCompletedCounter - 300);
}
renderFlashEffect();
mText->write(0, 0, std::to_string(balloons.size()));
// Pinta la informacion de debug
renderDebugInfo();
// Vuelca el contenido del renderizador en pantalla
mScreen->blit();
}
// Gestiona el nivel de amenaza
void Game::updateMenace()
{
const float percent = mStage[mCurrentStage].currentPower / mStage[mCurrentStage].powerToComplete;
const Uint8 difference = mStage[mCurrentStage].maxMenace - mStage[mCurrentStage].minMenace;
// Aumenta el nivel de amenaza en función de la puntuación
mMenaceThreshold = mStage[mCurrentStage].minMenace + (difference * percent);
// Si el nivel de amenza es inferior al umbral
if (mMenaceCurrent < mMenaceThreshold)
{
// Crea una formación de enemigos
deployEnemyFormation();
// Recalcula el nivel de amenaza con el nuevo globo
evaluateAndSetMenace();
}
}
// Gestiona la entrada durante el juego
void Game::checkGameInput()
{
mDemo.keys.left = 0;
mDemo.keys.right = 0;
mDemo.keys.noInput = 0;
mDemo.keys.fire = 0;
mDemo.keys.fireLeft = 0;
mDemo.keys.fireRight = 0;
// Modo Demo activo
if (mDemo.enabled)
{
const int index = 0;
if (mDemo.dataFile[mDemo.counter].left == 1)
{
players.at(index)->setInput(INPUT_LEFT);
}
if (mDemo.dataFile[mDemo.counter].right == 1)
{
players.at(index)->setInput(INPUT_RIGHT);
}
if (mDemo.dataFile[mDemo.counter].noInput == 1)
{
players.at(index)->setInput(INPUT_NULL);
}
if (mDemo.dataFile[mDemo.counter].fire == 1)
{
if (players.at(index)->canFire())
{
players.at(index)->setInput(INPUT_BUTTON_2);
createBullet(players.at(index)->getPosX() + (players.at(index)->getWidth() / 2) - 4, players.at(index)->getPosY() + (players.at(index)->getHeight() / 2), BULLET_UP, players.at(index)->isPowerUp(), index);
players.at(index)->setFireCooldown(10);
}
}
if (mDemo.dataFile[mDemo.counter].fireLeft == 1)
{
if (players.at(index)->canFire())
{
players.at(index)->setInput(INPUT_BUTTON_1);
createBullet(players.at(index)->getPosX() + (players.at(index)->getWidth() / 2) - 4, players.at(index)->getPosY() + (players.at(index)->getHeight() / 2), BULLET_UP, players.at(index)->isPowerUp(), index);
players.at(index)->setFireCooldown(10);
}
}
if (mDemo.dataFile[mDemo.counter].fireRight == 1)
{
if (players.at(index)->canFire())
{
players.at(index)->setInput(INPUT_BUTTON_3);
createBullet(players.at(index)->getPosX() + (players.at(index)->getWidth() / 2) - 4, players.at(index)->getPosY() + (players.at(index)->getHeight() / 2), BULLET_UP, players.at(index)->isPowerUp(), index);
players.at(index)->setFireCooldown(10);
}
}
// Comprueba el input de pausa
if (mInput->checkInput(INPUT_BUTTON_PAUSE, REPEAT_FALSE))
{
mSection.name = PROG_SECTION_TITLE;
}
// Incrementa el contador de la demo
if (mDemo.counter < TOTAL_DEMO_DATA)
{
mDemo.counter++;
}
else
{
mSection = {PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS};
}
}
// Modo Demo no activo
else
{
int i = 0;
for (auto player : players)
{
if (player->isAlive())
{
// Input a la izquierda
if (mInput->checkInput(INPUT_LEFT, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id))
{
player->setInput(INPUT_LEFT);
mDemo.keys.left = 1;
}
else
{
// Input a la derecha
if (mInput->checkInput(INPUT_RIGHT, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id))
{
player->setInput(INPUT_RIGHT);
mDemo.keys.right = 1;
}
else
{
// Ninguno de los dos inputs anteriores
player->setInput(INPUT_NULL);
mDemo.keys.noInput = 1;
}
}
// Comprueba el input de disparar al centro
if (mInput->checkInput(INPUT_BUTTON_2, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id))
{
if (player->canFire())
{
player->setInput(INPUT_BUTTON_2);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_UP, player->isPowerUp(), i);
player->setFireCooldown(10);
// Reproduce el sonido de disparo
JA_PlaySound(mSoundBullet);
mDemo.keys.fire = 1;
}
}
// Comprueba el input de disparar a la izquierda
if (mInput->checkInput(INPUT_BUTTON_1, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id))
{
if (player->canFire())
{
player->setInput(INPUT_BUTTON_1);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_LEFT, player->isPowerUp(), i);
player->setFireCooldown(10);
// Reproduce el sonido de disparo
JA_PlaySound(mSoundBullet);
mDemo.keys.fireLeft = 1;
}
}
// Comprueba el input de disparar a la derecha
if (mInput->checkInput(INPUT_BUTTON_3, REPEAT_TRUE, mOptions->input[i].deviceType, mOptions->input[i].id))
{
if (player->canFire())
{
player->setInput(INPUT_BUTTON_3);
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_RIGHT, player->isPowerUp(), i);
player->setFireCooldown(10);
// Reproduce el sonido de disparo
JA_PlaySound(mSoundBullet);
mDemo.keys.fireRight = 1;
}
}
// Comprueba el input de pausa
if (mInput->checkInput(INPUT_CANCEL, REPEAT_FALSE, mOptions->input[i].deviceType, mOptions->input[i].id))
{
mSection.subsection = GAME_SECTION_PAUSE;
if (JA_GetMusicState() == JA_MUSIC_PLAYING)
{
JA_PauseMusic();
}
}
if (mDemo.counter < TOTAL_DEMO_DATA)
{
if (mDemo.recording)
{
mDemo.dataFile[mDemo.counter] = mDemo.keys;
}
mDemo.counter++;
}
else if (mDemo.recording)
{
mSection.name = PROG_SECTION_QUIT;
}
i++;
}
}
}
}
// Pinta diferentes mensajes en la pantalla
void Game::renderMessages()
{
// GetReady
if ((mCounter < STAGE_COUNTER) && (!mDemo.enabled))
{
mSpriteGetReady->setPosX((int)mGetReadyBitmapPath[mCounter]);
mTextNokiaBig2->write((int)mGetReadyBitmapPath[mCounter], PLAY_AREA_CENTER_Y - 8, mLang->getText(75), -2);
}
// Time Stopped
if (mTimeStopped)
{
if ((mTimeStoppedCounter > 100) || (mTimeStoppedCounter % 10 > 4))
{
mTextNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, mLang->getText(36) + std::to_string(mTimeStoppedCounter / 10), -1, noColor, 1, shdwTxtColor);
}
if (mTimeStoppedCounter > 100)
{
if (mTimeStoppedCounter % 30 == 0)
{
JA_PlaySound(mSoundClock, false);
}
}
else
{
if (mTimeStoppedCounter % 15 == 0)
{
JA_PlaySound(mSoundClock, false);
}
}
}
// D E M O
if (mDemo.enabled)
{
if (mDemo.counter % 30 > 14)
{
mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, mLang->getText(37), 0, noColor, 2, shdwTxtColor);
}
}
// STAGE NUMBER
if (mStageBitmapCounter < STAGE_COUNTER)
{
const int stage = mStage[mCurrentStage].number;
std::string text;
if (stage == 10)
{ // Ultima fase
text = mLang->getText(79);
}
else
{ // X fases restantes
text = std::to_string(11 - mStage[mCurrentStage].number) + mLang->getText(38);
}
if (!mGameCompleted)
{ // Escribe el numero de fases restantes
mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, mStageBitmapPath[mStageBitmapCounter], text, -2, noColor, 2, shdwTxtColor);
}
else
{ // Escribe el texto de juego completado
text = mLang->getText(50);
mTextNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, mStageBitmapPath[mStageBitmapCounter], text, -2, noColor, 1, shdwTxtColor);
mTextNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, mStageBitmapPath[mStageBitmapCounter] + mTextNokiaBig2->getCharacterSize() + 2, mLang->getText(76), -1, noColor, 1, shdwTxtColor);
}
}
}
// Habilita el efecto del item de detener el tiempo
void Game::enableTimeStopItem()
{
stopAllBalloons(TIME_STOPPED_COUNTER);
setTimeStopped(true);
incTimeStoppedCounter(TIME_STOPPED_COUNTER);
if (JA_GetMusicState() == JA_MUSIC_PLAYING)
{
JA_PauseMusic();
}
}
// Deshabilita el efecto del item de detener el tiempo
void Game::disableTimeStopItem()
{
mTimeStopped = false;
setTimeStoppedCounter(0);
startAllBalloons();
if (JA_GetMusicState() == JA_MUSIC_PAUSED)
{
JA_ResumeMusic();
}
}
// Agita la pantalla
void Game::shakeScreen()
{
const int v[] = {-1, 1, -1, 1, -1, 1, -1, 0};
for (int n = 0; n < 8; n++)
{
// Prepara para empezar a dibujar en la textura de juego
mScreen->start();
// Limpia la pantalla
mScreen->clean(bgColor);
// Dibuja los objetos
mSpriteBackground->setPosX(0);
mSpriteBackground->setWidth(1);
mSpriteBackground->setSpriteClip(0, 0, 1, 192);
renderBackground();
mSpriteBackground->setPosX(255);
mSpriteBackground->setSpriteClip(255, 0, 1, 192);
mSpriteBackground->render();
mSpriteBackground->setPosX(v[n]);
mSpriteBackground->setWidth(256);
mSpriteBackground->setSpriteClip(0, 0, 256, 192);
mSpriteBackground->render();
mSpriteGrass->render();
renderBalloons();
renderBullets();
renderItems();
for (auto player : players)
{
player->render();
}
renderScoreBoard();
// Vuelca el contenido del renderizador en pantalla
mScreen->blit();
SDL_Delay(50);
}
}
// Bucle para el juego
section_t Game::run()
{
init();
while (mSection.name == PROG_SECTION_GAME)
{
// Sección juego en pausa
if (mSection.subsection == GAME_SECTION_PAUSE)
{
runPausedGame();
}
// Sección Game Over
if (mSection.subsection == GAME_SECTION_GAMEOVER)
{
runGameOverScreen();
}
// Sección juego jugando
if ((mSection.subsection == GAME_SECTION_PLAY_1P) || (mSection.subsection == GAME_SECTION_PLAY_2P))
{
// Si la música no está sonando
if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED))
{
// Reproduce la música
if (!mGameCompleted)
{
if (players.at(0)->isAlive())
{
JA_PlayMusic(mMusicPlaying);
}
}
}
// Actualiza la lógica del juego
update();
// Dibuja los objetos
render();
}
}
return mSection;
}
// Bucle para el menu de pausa del juego
void Game::runPausedGame()
{
// Reinicia el menu
mMenuPause->reset();
while ((mSection.subsection == GAME_SECTION_PAUSE) && (mSection.name == PROG_SECTION_GAME))
{
// Comprueba los eventos que hay en la cola
checkEventHandler();
// Calcula la lógica de los objetos
if (SDL_GetTicks() - mTicks > mTicksSpeed)
{
// Actualiza el contador de ticks
mTicks = SDL_GetTicks();
// Actualiza la lógica del menu
mMenuPause->update();
mFade->update();
if (mFade->hasEnded())
{
mSection.name = PROG_SECTION_TITLE;
mSection.subsection = TITLE_SECTION_1;
JA_StopMusic();
break;
}
}
// Prepara para empezar a dibujar en la textura de juego
mScreen->start();
// Limpia la pantalla
mScreen->clean(bgColor);
// Pinta el escenario
{
renderBackground();
renderBalloons();
renderBullets();
renderMessages();
renderItems();
renderSmartSprites();
renderScoreBoard();
for (auto player : players)
{
player->render();
}
if ((mDeathCounter <= 150) && !players.at(0)->isAlive())
{
renderDeathFade(150 - mDeathCounter);
}
if ((mGameCompleted) && (mGameCompletedCounter >= 300))
{
renderDeathFade(mGameCompletedCounter - 300);
}
renderFlashEffect();
mText->write(0, 0, std::to_string(balloons.size()));
// Pinta la informacion de debug
renderDebugInfo();
}
mMenuPause->render();
mFade->render();
// Vuelca el contenido del renderizador en pantalla
mScreen->blit();
// Comprueba las entradas para el menu
mMenuPause->checkInput();
// Comprueba si se ha seleccionado algún item del menú
switch (mMenuPause->getItemSelected())
{
case 0:
mSection.name = PROG_SECTION_GAME;
if (mNumPlayers == 1)
{
mSection.subsection = GAME_SECTION_PLAY_1P;
}
else
{
mSection.subsection = GAME_SECTION_PLAY_2P;
}
if (JA_GetMusicState() == JA_MUSIC_PAUSED)
{
JA_ResumeMusic();
}
break;
case 1:
mFade->setFadeType(FADE_CENTER);
mFade->activateFade();
break;
default:
break;
}
}
}
// Bucle para la pantalla de game over
void Game::runGameOverScreen()
{
// Guarda los puntos
saveScoreFile();
// Reinicia el menu
mMenuGameOver->reset();
while ((mSection.subsection == GAME_SECTION_GAMEOVER) && (mSection.name == PROG_SECTION_GAME))
{
// Comprueba los eventos que hay en la cola
while (SDL_PollEvent(mEventHandler) != 0)
{
// Evento de salida de la aplicación
if (mEventHandler->type == SDL_QUIT)
{
mSection.name = PROG_SECTION_QUIT;
break;
}
}
// Calcula la lógica de los objetos
if (SDL_GetTicks() - mTicks > mTicksSpeed)
{
// Actualiza el contador de ticks
mTicks = SDL_GetTicks();
// Actualiza la lógica del menu
mMenuGameOver->update();
mFade->update();
if (mFade->hasEnded())
{
switch (mPostFade)
{
case 0: // YES
mSection.name = PROG_SECTION_GAME;
if (mNumPlayers == 1)
{
mSection.subsection = GAME_SECTION_PLAY_1P;
}
else
{
mSection.subsection = GAME_SECTION_PLAY_2P;
}
init();
break;
case 1: // NO
mSection.name = PROG_SECTION_TITLE;
mSection.subsection = TITLE_SECTION_1;
break;
default:
break;
}
break;
}
}
// Prepara para empezar a dibujar en la textura de juego
mScreen->start();
// Limpia la pantalla
mScreen->clean(bgColor);
// Dibuja los objetos
if (mNumPlayers == 1)
{
mTextBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), mLang->getText(43));
mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 1), mLang->getText(44) + std::to_string(players[0]->getScore()));
}
else
{
mTextBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - 36, mLang->getText(43));
mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - 12, mLang->getText(77) + std::to_string(players[0]->getScore()));
mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y, mLang->getText(78) + std::to_string(players[1]->getScore()));
}
mText->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y + BLOCK * 2, mLang->getText(45));
mMenuGameOver->render();
mFade->render();
// Vuelca el contenido del renderizador en pantalla
mScreen->blit();
// Comprueba las entradas para el menu
mMenuGameOver->checkInput();
// Comprueba si se ha seleccionado algún item del menú
switch (mMenuGameOver->getItemSelected())
{
case 0: // YES
mPostFade = 0;
mFade->activateFade();
break;
case 1: // NO
mPostFade = 1;
mFade->activateFade();
break;
default:
break;
}
}
}
// Dibuja la informacion de debug en pantalla
void Game::renderDebugInfo()
{
const color_t color = {0xFF, 0x20, 0x20};
if (mDebug.enabled)
{
mText->writeShadowed(2, 2 + 0 * BLOCK, "menace(umb): " + std::to_string(mMenaceCurrent) + "(" + std::to_string(mMenaceThreshold) + ")", color);
mText->writeShadowed(2, 2 + 1 * BLOCK, "currentPower: " + std::to_string(mStage[mCurrentStage].currentPower), color);
mText->writeShadowed(2, 2 + 2 * BLOCK, "mCurrentStage:" + std::to_string(mCurrentStage), color);
mText->writeShadowed(2, 2 + 3 * BLOCK, "mCounter: " + std::to_string(mCounter), color);
mText->writeShadowed(2, 2 + 4 * BLOCK, "(R)enemyset: " + std::to_string(mDebug.enemySet), color);
mText->writeShadowed(2, 2 + 5 * BLOCK, "RGB: " + std::to_string(mDebug.gradR) + "," + std::to_string(mDebug.gradG) + "," + std::to_string(mDebug.gradB), color);
mText->writeShadowed(2, 2 + 6 * BLOCK, "(I)invuln: " + std::to_string(players[0]->getInvulnerableCounter()), color);
mText->writeShadowed(2, 2 + 7 * BLOCK, "balloons: " + std::to_string(countBalloons()), color);
mText->writeShadowed(2, 2 + 8 * BLOCK, "balloonsPop: " + std::to_string(mBalloonsPopped), color);
mText->writeShadowed(2, 2 + 9 * BLOCK, "(Z-X)ballSped:" + std::to_string(mEnemySpeed), color);
mText->writeShadowed(2, 2 + 10 * BLOCK, "EGcounter: " + std::to_string(mGameCompletedCounter), color);
}
}
// Indica si se puede crear una powerball
bool Game::canPowerBallBeCreated()
{
if ((!mPowerBallEnabled) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (mPowerBallCounter == 0))
{
return true;
}
return false;
}
// Calcula el poder actual de los globos en pantalla
int Game::calculateScreenPower()
{
int power = 0;
for (auto balloon : balloons)
{
if (balloon->isEnabled())
{
power += balloon->getPower();
}
}
return power;
}
// Inicializa las variables que contienen puntos de ruta para mover objetos
void Game::initPaths()
{
// Letrero de STAGE #
const int firstPart = STAGE_COUNTER / 4; // 50
const int secondPart = firstPart * 3; // 150
const int centerPoint = PLAY_AREA_CENTER_Y - (BLOCK * 2);
const int distance = (PLAY_AREA_BOTTOM) - (PLAY_AREA_CENTER_Y - 16);
for (int i = 0; i < STAGE_COUNTER; i++)
{
if (i < firstPart)
{
mStageBitmapPath[i] = (mSin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint);
}
else if (i < secondPart)
{
mStageBitmapPath[i] = (int)centerPoint;
}
else
{
mStageBitmapPath[i] = (mSin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17);
}
}
// Letrero de GetReady
const int size = mTextNokiaBig2->lenght(mLang->getText(75), -2);
const float start1 = PLAY_AREA_LEFT - size;
const float finish1 = PLAY_AREA_CENTER_X - (size / 2);
const float start2 = finish1;
const float finish2 = PLAY_AREA_RIGHT;
const float distance1 = finish1 - start1;
const float distance2 = finish2 - start2;
for (int i = 0; i < STAGE_COUNTER; i++)
{
if (i < firstPart)
{
mGetReadyBitmapPath[i] = mSin[(int)(i * 1.8f)];
mGetReadyBitmapPath[i] *= distance1;
mGetReadyBitmapPath[i] -= size;
}
else if (i < secondPart)
{
mGetReadyBitmapPath[i] = (int)finish1;
}
else
{
mGetReadyBitmapPath[i] = mSin[(int)((i - 150) * 1.8f)];
mGetReadyBitmapPath[i] *= distance2;
mGetReadyBitmapPath[i] += finish1;
}
}
}
// Actualiza el tramo final de juego, una vez completado
void Game::updateGameCompleted()
{
if (mGameCompleted)
{
mGameCompletedCounter++;
}
if (mGameCompletedCounter == 500)
{
mSection.subsection = GAME_SECTION_GAMEOVER;
}
}
// Actualiza las variables de ayuda
void Game::updateHelper()
{
// Solo ofrece ayuda cuando la amenaza es elevada
if (mMenaceCurrent > 15)
{
for (auto player : players)
{
if (player->getCoffees() == 0)
{
mHelper.needCoffee = true;
}
else
{
mHelper.needCoffee = false;
}
if (!player->isPowerUp())
{
mHelper.needCoffeeMachine = true;
}
else
{
mHelper.needCoffeeMachine = false;
}
}
}
else
{
mHelper.needCoffee = false;
mHelper.needCoffeeMachine = false;
}
}
// Comprueba si todos los jugadores han muerto
bool Game::allPlayersAreDead()
{
bool success = true;
for (auto player : players)
{
success &= (!player->isAlive());
}
return success;
}
// Comprueba los eventos que hay en cola
void Game::checkEventHandler()
{
while (SDL_PollEvent(mEventHandler) != 0)
{
// Evento de salida de la aplicación
if (mEventHandler->type == SDL_QUIT)
{
mSection.name = PROG_SECTION_QUIT;
break;
}
else if (mEventHandler->type == SDL_KEYDOWN && mEventHandler->key.repeat == 0)
{
switch (mEventHandler->key.keysym.scancode)
{
case SDL_SCANCODE_P:
createPowerBall();
break;
default:
break;
}
}
}
}
// Carga las animaciones
void Game::loadAnimations(std::string filePath, std::vector<std::string> *buffer)
{
std::ifstream file(filePath);
std::string line;
if (file)
{
std::cout << "Animation loaded: " << filePath.substr(filePath.find_last_of("\\/") + 1).c_str() << std::endl;
while (std::getline(file, line))
{
buffer->push_back(line);
}
file.close();
}
}