diff --git a/media/.DS_Store b/media/.DS_Store index ef9c65c..0df0db1 100644 Binary files a/media/.DS_Store and b/media/.DS_Store differ diff --git a/media/gfx/balloon.png b/media/gfx/balloon.png index 109efde..c05e0be 100644 Binary files a/media/gfx/balloon.png and b/media/gfx/balloon.png differ diff --git a/media/gfx/game_bg.png b/media/gfx/game_bg.png index 691bd54..5dc42f7 100644 Binary files a/media/gfx/game_bg.png and b/media/gfx/game_bg.png differ diff --git a/media/gfx/player_body.png b/media/gfx/player_body.png index a082434..b13e599 100644 Binary files a/media/gfx/player_body.png and b/media/gfx/player_body.png differ diff --git a/source/animatedsprite.cpp b/source/animatedsprite.cpp index f39ea0f..a6d1985 100644 --- a/source/animatedsprite.cpp +++ b/source/animatedsprite.cpp @@ -24,6 +24,7 @@ void AnimatedSprite::init(LTexture *texture, SDL_Renderer *renderer) mAnimation[i].numFrames = 0; mAnimation[i].speed = 0; mAnimation[i].loop = true; + mAnimation[i].completed = false; for (Uint8 j = 0; i < 20; i++) { mAnimation[i].frames[j].x = 0; @@ -105,6 +106,18 @@ void AnimatedSprite::setAnimationLoop(Uint8 index, bool loop) mAnimation[index].loop = loop; } +// Establece el valor de la variable +void AnimatedSprite::setCompleted(Uint8 index, bool value) +{ + mAnimation[index].completed = value; +} + +// Comprueba si ha terminado la animación +bool AnimatedSprite::isCompleted(Uint8 index) +{ + return mAnimation[index].completed; +} + // Devuelve el rectangulo de una animación y frame concreto SDL_Rect AnimatedSprite::getAnimationClip(Uint8 index_animation, Uint8 index_frame) { diff --git a/source/animatedsprite.h b/source/animatedsprite.h index 380089c..f1c84bc 100644 --- a/source/animatedsprite.h +++ b/source/animatedsprite.h @@ -5,9 +5,26 @@ #ifndef ANIMATEDSPRITE_H #define ANIMATEDSPRITE_H +#define MAX_FRAMES 30 +#define MAX_ANIMATIONS 20 + // Clase AnimatedSprite class AnimatedSprite : public MovingSprite { +private: + struct sAnimation + { + SDL_Rect frames[MAX_FRAMES]; // Cada uno de los frames que componen la animación + Uint8 numFrames; // Numero de frames que componen la animación + Uint8 speed; // Velocidad de la animación + bool loop; // Indica si la animación se reproduce en bucle + bool completed; // Indica si ha finalizado la animación + }; + sAnimation mAnimation[MAX_ANIMATIONS]; // Vector con las diferentes animaciones + + Uint8 mCurrentFrame; // Frame actual + Uint16 mAnimationCounter; // Contador para las animaciones + public: // Constructor AnimatedSprite(); @@ -39,26 +56,14 @@ public: // Establece si la animación se reproduce en bucle void setAnimationLoop(Uint8 index, bool loop); + // Establece el valor de la variable + void setCompleted(Uint8 index, bool value); + + // Comprueba si ha terminado la animación + bool isCompleted(Uint8 index); + // Devuelve el rectangulo de una animación y frame concreto SDL_Rect getAnimationClip(Uint8 index_animation, Uint8 index_frame); - -private: - struct sAnimation - { - SDL_Rect frames[20]; // Hasta 20 frames - Uint8 numFrames; - Uint8 speed; - bool loop; - }; - - // Vector con las diferentes animaciones y los diferentes frames de cada animación - sAnimation mAnimation[20]; // Hasta 20 animaciones - - // Frame actual - Uint8 mCurrentFrame; - - // Contador para las animaciones - Uint16 mAnimationCounter; }; -#endif +#endif \ No newline at end of file diff --git a/source/balloon.cpp b/source/balloon.cpp index 01a3e2e..e6c84e7 100644 --- a/source/balloon.cpp +++ b/source/balloon.cpp @@ -5,7 +5,7 @@ Balloon::Balloon() { mSprite = new AnimatedSprite(); - init(0, 0, NO_KIND, BALLOON_VELX_POSITIVE, 0, nullptr, nullptr); + disable(); } // Destructor @@ -15,29 +15,34 @@ Balloon::~Balloon() } // Inicializador -void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture, SDL_Renderer *renderer) +void Balloon::init(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer, LTexture *texture, SDL_Renderer *renderer) { const Uint8 NUM_FRAMES_BALLON = 10; const Uint8 NUM_FRAMES_BALLON_POP = 12; const Uint8 NUM_FRAMES_BALLON_BORN = 10; + const Uint8 OFFSET_ORANGE_BALLOONS = 58 * 0; + const Uint8 OFFSET_BLUE_BALLOONS = 58 * 1; + const Uint8 OFFSET_GREEN_BALLOONS = 58 * 2; + const Uint8 OFFSET_PURPLE_BALLOONS = 58 * 3; + const Uint8 OFFSET_POWER_BALL = 58 * 4; + const int OFFSET_EXPLOSIONS = 58 * 5; + switch (kind) { case BALLOON_1: - // Posición inicial - mPosX = x; - mPosY = y; + mEnabled = true; // Alto y ancho del objeto - mWidth = 8; - mHeight = mWidth; + mWidth = BALLOON_SIZE_1; + mHeight = BALLOON_SIZE_1; // Inicializa los valores de velocidad y gravedad mVelX = velx; mVelY = 0; - mMaxVelY = 3; + mMaxVelY = 3.0f; mGravity = 0.09f; - mDefaultVelY = 3; + mDefaultVelY = 2.6f; // Puntos que da el globo al ser destruido mScore = 50; @@ -47,35 +52,29 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, // Establece los frames de cada animación for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) - { - mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 50, 21 + (37 * i), getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 50 + OFFSET_ORANGE_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) - { - mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 50 + 58, 21 + (37 * i), getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 50 + OFFSET_BLUE_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) - { - mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 50 + 58 + 58, 21 + (37 * i), getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 50 + OFFSET_EXPLOSIONS, 21 + (37 * i), getWidth(), getHeight()); break; case BALLOON_2: - // Posición inicial - mPosX = x; - mPosY = y; + mEnabled = true; // Alto y ancho del objeto - mWidth = 13; - mHeight = mWidth; + mWidth = BALLOON_SIZE_2; + mHeight = BALLOON_SIZE_2; // Inicializa los valores de velocidad y gravedad mVelX = velx; mVelY = 0; - mMaxVelY = 3; + mMaxVelY = 3.0f; mGravity = 0.10f; - mDefaultVelY = 4; + mDefaultVelY = 3.5f; // Puntos que da el globo al ser destruido mScore = 100; @@ -85,35 +84,29 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, // Establece los frames de cada animación for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) - { - mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 37, 21 + (37 * i), getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 37 + OFFSET_ORANGE_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) - { - mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 37 + 58, 21 + (37 * i), getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 37 + OFFSET_BLUE_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) - { - mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 37 + 58 + 58, 21 + (37 * i), getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 37 + OFFSET_EXPLOSIONS, 21 + (37 * i), getWidth(), getHeight()); break; case BALLOON_3: - // Posición inicial - mPosX = x; - mPosY = y; + mEnabled = true; // Alto y ancho del objeto - mWidth = 21; - mHeight = mWidth; + mWidth = BALLOON_SIZE_3; + mHeight = BALLOON_SIZE_3; // Inicializa los valores de velocidad y gravedad mVelX = velx; mVelY = 0; - mMaxVelY = 3; + mMaxVelY = 3.0f; mGravity = 0.10f; - mDefaultVelY = 4.5; + mDefaultVelY = 4.50f; // Puntos que da el globo al ser destruido mScore = 200; @@ -123,35 +116,29 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, // Establece los frames de cada animación for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) - { - mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 37, 37 * i, getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 37 + OFFSET_ORANGE_BALLOONS, 37 * i, getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) - { - mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 37 + 58, 37 * i, getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 37 + OFFSET_BLUE_BALLOONS, 37 * i, getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) - { - mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 37 + 58 + 58, 37 * i, getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 37 + OFFSET_EXPLOSIONS, 37 * i, getWidth(), getHeight()); break; case BALLOON_4: - // Posición inicial - mPosX = x; - mPosY = y; + mEnabled = true; // Alto y ancho del objeto - mWidth = 37; - mHeight = mWidth; + mWidth = BALLOON_SIZE_4; + mHeight = BALLOON_SIZE_4; // Inicializa los valores de velocidad y gravedad mVelX = velx; mVelY = 0; - mMaxVelY = 3; + mMaxVelY = 3.0f; mGravity = 0.10f; - mDefaultVelY = 5; + mDefaultVelY = 4.95f; // Puntos que da el globo al ser destruido mScore = 400; @@ -161,35 +148,157 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, // Establece los frames de cada animación for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) - { - mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 0, 37 * i, getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, OFFSET_ORANGE_BALLOONS, 37 * i, getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) - { - mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 58, 37 * i, getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, OFFSET_BLUE_BALLOONS, 37 * i, getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) - { - mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 58 + 58, 37 * i, getWidth(), getHeight()); - } + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, OFFSET_EXPLOSIONS, 37 * i, getWidth(), getHeight()); break; - default: - // Posición inicial - mPosX = x; - mPosY = y; + case HEXAGON_1: + mEnabled = true; // Alto y ancho del objeto - mWidth = 0; - mHeight = mWidth; + mWidth = BALLOON_SIZE_1; + mHeight = BALLOON_SIZE_1; + + // Inicializa los valores de velocidad y gravedad + mVelX = velx; + mVelY = abs(velx) * 2; + mMaxVelY = abs(velx) * 2; + mGravity = 0.00f; + mDefaultVelY = abs(velx) * 2; + + // Puntos que da el globo al ser destruido + mScore = 50; + + // Amenaza que genera el globo + mMenace = 1; + + // Establece los frames de cada animación + for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 50 + OFFSET_GREEN_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 50 + OFFSET_PURPLE_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 50 + OFFSET_EXPLOSIONS, 21 + (37 * i), getWidth(), getHeight()); + + break; + + case HEXAGON_2: + mEnabled = true; + + // Alto y ancho del objeto + mWidth = BALLOON_SIZE_2; + mHeight = BALLOON_SIZE_2; + + // Inicializa los valores de velocidad y gravedad + mVelX = velx; + mVelY = abs(velx) * 2; + mMaxVelY = abs(velx) * 2; + mGravity = 0.00f; + mDefaultVelY = abs(velx) * 2; + + // Puntos que da el globo al ser destruido + mScore = 100; + + // Amenaza que genera el globo + mMenace = 2; + + // Establece los frames de cada animación + for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 37 + OFFSET_GREEN_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 37 + OFFSET_PURPLE_BALLOONS, 21 + (37 * i), getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 37 + OFFSET_EXPLOSIONS, 21 + (37 * i), getWidth(), getHeight()); + + break; + + case HEXAGON_3: + mEnabled = true; + + // Alto y ancho del objeto + mWidth = BALLOON_SIZE_3; + mHeight = BALLOON_SIZE_3; + + // Inicializa los valores de velocidad y gravedad + mVelX = velx; + mVelY = abs(velx) * 2; + mMaxVelY = abs(velx) * 2; + mGravity = 0.00f; + mDefaultVelY = abs(velx) * 2; + + // Puntos que da el globo al ser destruido + mScore = 200; + + // Amenaza que genera el globo + mMenace = 4; + + // Establece los frames de cada animación + for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, 37 + OFFSET_GREEN_BALLOONS, 37 * i, getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, 37 + OFFSET_PURPLE_BALLOONS, 37 * i, getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, 37 + OFFSET_EXPLOSIONS, 37 * i, getWidth(), getHeight()); + + break; + + case HEXAGON_4: + mEnabled = true; + + // Alto y ancho del objeto + mWidth = BALLOON_SIZE_4; + mHeight = BALLOON_SIZE_4; + + // Inicializa los valores de velocidad y gravedad + mVelX = velx; + mVelY = abs(velx) * 2; + mMaxVelY = abs(velx) * 2; + mGravity = 0.00f; + mDefaultVelY = abs(velx) * 2; + + // Puntos que da el globo al ser destruido + mScore = 400; + + // Amenaza que genera el globo + mMenace = 8; + + // Establece los frames de cada animación + for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, OFFSET_GREEN_BALLOONS, 37 * i, getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, OFFSET_PURPLE_BALLOONS, 37 * i, getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, OFFSET_EXPLOSIONS, 37 * i, getWidth(), getHeight()); + + break; + + case POWER_BALL: + mEnabled = true; + + // Alto y ancho del objeto + mWidth = BALLOON_SIZE_4; + mHeight = BALLOON_SIZE_4; // Inicializa los valores de velocidad y gravedad mVelX = velx; mVelY = 0; - mMaxVelY = 0; - mGravity = 0; - mDefaultVelY = 0; + mMaxVelY = 3.0f; + mGravity = 0.10f; + mDefaultVelY = 4.95f; // Puntos que da el globo al ser destruido mScore = 0; @@ -198,13 +307,35 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, mMenace = 0; // Establece los frames de cada animación - mSprite->setAnimationFrames(0, 0, 0, 0, getWidth(), getHeight()); - mSprite->setAnimationFrames(1, 0, 0, 0, getWidth(), getHeight()); - mSprite->setAnimationFrames(2, 0, 0, 0, getWidth(), getHeight()); + for (Uint8 i = 0; i < NUM_FRAMES_BALLON; i++) + mSprite->setAnimationFrames(BALLOON_MOVING_ANIMATION, i, OFFSET_POWER_BALL, 37 * i, getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_BORN; i++) + mSprite->setAnimationFrames(BALLOON_BORN_ANIMATION, i, OFFSET_BLUE_BALLOONS, 37 * i, getWidth(), getHeight()); + + for (Uint8 i = 0; i < NUM_FRAMES_BALLON_POP; i++) + mSprite->setAnimationFrames(BALLOON_POP_ANIMATION, i, OFFSET_EXPLOSIONS, 37 * i, getWidth(), getHeight()); break; + + default: + mEnabled = false; + mMenace = 0; + break; } + // Posición inicial + mPosX = x; + mPosY = y; + + mBouncing.enabled = false; // Si el efecto está activo + mBouncing.counter = 0; // Countador para el efecto + mBouncing.speed = 0; // Velocidad a la que transcurre el efecto + mBouncing.zoomW = 1.0f; // Zoom aplicado a la anchura + mBouncing.zoomH = 1.0f; // Zoom aplicado a la altura + mBouncing.despX = 0.0f; // Desplazamiento de pixeles en el eje X antes de pintar el objeto con zoom + mBouncing.despY = 0.0f; + // Textura con los gráficos del sprite mSprite->setTexture(texture); @@ -216,14 +347,14 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, mSprite->setHeight(mHeight); // Posición X,Y del sprite - mSprite->setPosX(int(mPosX)); - mSprite->setPosY(mPosY); + mSprite->setPosX((int)mPosX); + mSprite->setPosY((int)mPosY); // Tamaño del circulo de colisión mCollider.r = mWidth / 2; // Alinea el circulo de colisión con el objeto - shiftColliders(); + updateColliders(); // Inicializa variables mStopped = true; @@ -242,6 +373,9 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, mBouncing.zoomH = 1; mBouncing.despX = 0; mBouncing.despY = 0; + mCounter = 0; + mTravelY = 1.0f; + mSpeed = speed; // Tipo mKind = kind; @@ -288,16 +422,17 @@ void Balloon::allignTo(int x) mSprite->setPosY(getPosY()); // Alinea el circulo de colisión con el objeto - shiftColliders(); + updateColliders(); } // Pinta el globo en la pantalla void Balloon::render() { - if (mVisible) + if ((mVisible) && (mEnabled)) { if (mBouncing.enabled) { + // Aplica desplazamiento para el zoom mSprite->setPosX(getPosX() + mBouncing.despX); mSprite->setPosY(getPosY() + mBouncing.despY); mSprite->render(); @@ -306,7 +441,8 @@ void Balloon::render() } else if (isBeingCreated()) { - mSprite->getTexture()->setAlpha(255 - (int)((float)mCreationCounter * (255.0f/(float)mCreationCounterIni))); + // Aplica alpha blending + mSprite->getTexture()->setAlpha(255 - (int)((float)mCreationCounter * (255.0f / (float)mCreationCounterIni))); mSprite->render(); mSprite->getTexture()->setAlpha(255); } @@ -320,127 +456,116 @@ void Balloon::render() // Actualiza la posición y estados del globo void Balloon::move() { - // Comprobamos si se puede mover - if (isStopped() == false) + // Comprueba si se puede mover + if (!isStopped()) { - // Lo movemos a izquierda o derecha - mPosX += mVelX; + // Lo mueve a izquierda o derecha + mPosX += (mVelX * mSpeed); // Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido if ((mPosX < PLAY_AREA_LEFT) || (mPosX + mWidth > PLAY_AREA_RIGHT)) { - // Corregir posición - mPosX -= mVelX; + // Corrige posición + mPosX -= (mVelX * mSpeed); - // Invertir sentido + // Invierte sentido mVelX = -mVelX; + + // Activa el efecto de rebote + bounceStart(); } // Mueve el globo hacia arriba o hacia abajo - mPosY += int(mVelY); + mPosY += (mVelY * mSpeed); // Si se sale por arriba if (mPosY < PLAY_AREA_TOP) { - // Corregimos - mPosY -= int(mVelY); + // Corrige + mPosY = PLAY_AREA_TOP; - // Invertimos sentido + // Invierte sentido mVelY = -mVelY; + + // Activa el efecto de rebote + bounceStart(); } // Si el globo se sale por la parte inferior if (mPosY + mHeight > PLAY_AREA_BOTTOM) { - // Corregimos - //mPosY -= int(mVelY); + // Corrige mPosY = PLAY_AREA_BOTTOM - mHeight; - // Invertimos colocando una velocidad por defecto + // Invierte colocando una velocidad por defecto mVelY = -mDefaultVelY; + // Activa el efecto de rebote bounceStart(); } - // Aplica gravedad al objeto, sin pasarse de un limite establecido - if (int(mVelY) > mMaxVelY) - { - mVelY = float(mMaxVelY); - } - else + /* + Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle + Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por + tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya + recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial + */ + + // Incrementa la variable que calcula la distancia acumulada en Y + mTravelY += mSpeed; + + // Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad + if (mTravelY >= 1.0f) { + // Quita el excedente + mTravelY -= 1.0f; + + // Aplica la gravedad al objeto sin pasarse de una velocidad máxima mVelY += mGravity; + std::min(mVelY, mMaxVelY); } + // Aplica la gravedad al objeto + //if (mVelY > mMaxVelY) + // mVelY = mMaxVelY; + //else if (mCounter % 1 == 0) + // mVelY += mGravity; // Actualiza la posición del sprite mSprite->setPosX(getPosX()); - mSprite->setPosY(mPosY); - } - // Si no se puede mover: - // Comprobar si se está creando - else if (isBeingCreated() == true) - { - // Actualiza el valor de las variables - setStop(true); - setInvulnerable(true); - - // Todavia tiene tiempo en el contador - if (mCreationCounter > 0) - { - // Desplaza lentamente el globo hacia abajo y hacia un lado - if (mCreationCounter % 10 == 0) - { - ++mPosY; - mPosX += mVelX; - - // Actualiza la posición del sprite - mSprite->setPosX(getPosX()); - mSprite->setPosY(mPosY); - - // Actualiza la posición del circulo de colisión - shiftColliders(); - } - - // Hace visible el globo de forma intermitente - if (mCreationCounter > 100) - { - setVisible(mCreationCounter / 10 % 2 == 0); - } - else - { - setVisible(mCreationCounter / 5 % 2 == 0); - } - - --mCreationCounter; - } - // El contador ha llegado a cero - else - { - setBeingCreated(false); - setStop(false); - setVisible(true); - setInvulnerable(false); - } - } - // Comprobar si está detenido - else if (isStopped() == true) - { - // Si todavía está detenido, reduce el contador - if (mStoppedCounter > 0) - { - --mStoppedCounter; - } // Si el contador ha llegado a cero, ya no está detenido - else - { - setStop(false); - } + mSprite->setPosY(getPosY()); } } -// Pone a cero todos los valores del globo -void Balloon::erase() +// Deshabilita el globo y pone a cero todos los valores +void Balloon::disable() { - init(0, 0, NO_KIND, 0, 0, nullptr, nullptr); + mEnabled = false; + + mPosX = 0.0f; + mPosY = 0.0f; + mWidth = 0; + mHeight = 0; + mVelX = 0.0f; + mVelY = 0.0f; + mGravity = 0.0f; + mDefaultVelY = 0.0f; + mMaxVelY = 0.0f; + mBeingCreated = false; + mBlinking = false; + mInvulnerable = false; + mPopping = false; + mStopped = false; + mVisible = false; + mCollider.x = 0; + mCollider.y = 0; + mCollider.r = 0; + mCreationCounter = 0; + mCreationCounterIni = 0; + mScore = 0; + mStoppedCounter = 0; + mTimeToLive = 0; + mKind = 0; + mMenace = 0; } // Explosiona el globo @@ -458,64 +583,124 @@ void Balloon::pop() // Actualiza al globo a su posicion, animación y controla los contadores void Balloon::update() { - move(); - setAnimation(); - shiftColliders(); - bounceUpdate(); + if (mEnabled) + { + move(); + updateAnimation(); + updateColliders(); + updateState(); + updateBounce(); + mCounter++; + } +} + +// Actualiza los estados del globo +void Balloon::updateState() +{ + // Si está explotando if (isPopping()) { setInvulnerable(true); setStop(true); - if (mTimeToLive > 0) + if (mSprite->isCompleted(BALLOON_POP_ANIMATION)) { - --mTimeToLive; + mSprite->setCompleted(BALLOON_POP_ANIMATION, false); + mTimeToLive = 0; + disable(); } + else if (mTimeToLive > 0) + mTimeToLive--; + else + disable(); + } + + // Si se está creando + if (isBeingCreated()) + { + // Actualiza el valor de las variables + setStop(true); + setInvulnerable(true); + + // Todavia tiene tiempo en el contador + if (mCreationCounter > 0) + { + // Desplaza lentamente el globo hacia abajo y hacia un lado + if (mCreationCounter % 10 == 0) + { + mPosY++; + mPosX += mVelX; + + // Comprueba no se salga por los laterales + if ((mPosX < PLAY_AREA_LEFT) || (mPosX > (PLAY_AREA_RIGHT - mWidth))) + { + // Corrige y cambia el sentido de la velocidad + mPosX -= mVelX; + mVelX = -mVelX; + } + + // Actualiza la posición del sprite + mSprite->setPosX(getPosX()); + mSprite->setPosY(getPosY()); + + // Actualiza la posición del circulo de colisión + updateColliders(); + } + + // Hace visible el globo de forma intermitente + //if (mCreationCounter > 100) + // setVisible(mCreationCounter / 10 % 2 == 0); + //else + // setVisible(mCreationCounter / 5 % 2 == 0); + + mCreationCounter--; + } + // El contador ha llegado a cero else { - erase(); + setBeingCreated(false); + setStop(false); + setVisible(true); + setInvulnerable(false); } } + // Solo comprueba el estado detenido cuando no se está creando + else if (isStopped()) + { + // Si está detenido, reduce el contador + if (mStoppedCounter > 0) + mStoppedCounter--; + // Si el contador ha llegado a cero, ya no está detenido + else if (!isPopping()) // Quitarles el estado "detenido" si no estan explosionandoxq + setStop(false); + } } // Establece la animación correspondiente al estado -void Balloon::setAnimation() +void Balloon::updateAnimation() { // Establece el frame de animación if (isPopping()) - { mSprite->animate(BALLOON_POP_ANIMATION); - } else if (isBeingCreated()) - { mSprite->animate(BALLOON_BORN_ANIMATION); - } else - { mSprite->animate(BALLOON_MOVING_ANIMATION); - } } -// Comprueba si el globo tiene algun tipo asignado -bool Balloon::isActive() +// Comprueba si el globo está habilitado +bool Balloon::isEnabled() { - if (mKind == NO_KIND) - { - return false; - } - else - { - return true; - } + return mEnabled; } // Obtiene del valor de la variable -int Balloon::getPosX() +float Balloon::getPosX() { - return int(mPosX); + return mPosX; } // Obtiene del valor de la variable -int Balloon::getPosY() +float Balloon::getPosY() { return mPosY; } @@ -544,12 +729,29 @@ void Balloon::setVelY(float velY) mVelY = velY; } +// Establece el valor de la variable +void Balloon::setSpeed(float speed) +{ + mSpeed = speed; +} + // Obtiene del valor de la variable int Balloon::getKind() { return mKind; } +// Obtiene la clase a la que pertenece el globo +Uint8 Balloon::getClass() +{ + if ((mKind >= BALLOON_1) && (mKind <= BALLOON_4)) + return BALLOON_CLASS; + else if ((mKind >= HEXAGON_1) && (mKind <= HEXAGON_4)) + return HEXAGON_CLASS; + else + return 0; +} + // Establece el valor de la variable void Balloon::setStop(bool state) { @@ -653,13 +855,13 @@ Uint16 Balloon::getScore() } // Obtiene el circulo de colisión -Circle &Balloon::getCollider() +circle_t &Balloon::getCollider() { return mCollider; } // Alinea el circulo de colisión con la posición del objeto globo -void Balloon::shiftColliders() +void Balloon::updateColliders() { // Align collider to center of balloon mCollider.x = Uint16(mPosX + mCollider.r); @@ -669,7 +871,10 @@ void Balloon::shiftColliders() // Obtiene le valor de la variable Uint8 Balloon::getMenace() { - return mMenace; + if (isEnabled()) + return mMenace; + else + return 0; } void Balloon::bounceStart() @@ -686,14 +891,14 @@ void Balloon::bounceStop() { mBouncing.enabled = false; mBouncing.counter = 0; - mBouncing.zoomW = 1; - mBouncing.zoomH = 1; + mBouncing.zoomW = 1.0f; + mBouncing.zoomH = 1.0f; mSprite->setZoomW(mBouncing.zoomW); mSprite->setZoomH(mBouncing.zoomH); - mBouncing.despX = 0; - mBouncing.despY = 0; + mBouncing.despX = 0.0f; + mBouncing.despY = 0.0f; } -void Balloon::bounceUpdate() +void Balloon::updateBounce() { if (mBouncing.enabled) { diff --git a/source/balloon.h b/source/balloon.h index b46531d..de6db8a 100644 --- a/source/balloon.h +++ b/source/balloon.h @@ -11,33 +11,34 @@ class Balloon { private: - float mPosX; // Posición en el eje X - int mPosY; // Posición en el eje Y - - Uint8 mWidth; // Ancho - Uint8 mHeight; // Alto - - float mVelX; // Velocidad en el eje X. Cantidad de pixeles a desplazarse - float mVelY; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse - float mGravity; // Aceleración en el eje Y. Modifica la velocidad - float mDefaultVelY; // Velocidad inicial que tienen al rebotar contra el suelo - int mMaxVelY; // Máxima velocidad que puede alcanzar el objeto en el eje Y - + float mPosX; // Posición en el eje X + float mPosY; // Posición en el eje Y + Uint8 mWidth; // Ancho + Uint8 mHeight; // Alto + float mVelX; // Velocidad en el eje X. Cantidad de pixeles a desplazarse + float mVelY; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse + float mGravity; // Aceleración en el eje Y. Modifica la velocidad + float mDefaultVelY; // Velocidad inicial que tienen al rebotar contra el suelo + float mMaxVelY; // Máxima velocidad que puede alcanzar el objeto en el eje Y AnimatedSprite *mSprite; // Sprite del objeto globo bool mBeingCreated; // Indica si el globo se está creando bool mBlinking; // Indica si el globo está intermitente + bool mEnabled; // Indica si el globo esta activo bool mInvulnerable; // Indica si el globo es invulnerable bool mPopping; // Indica si el globo está explotando bool mStopped; // Indica si el globo está parado bool mVisible; // Indica si el globo es visible - Circle mCollider; // Circulo de colisión del objeto + circle_t mCollider; // Circulo de colisión del objeto Uint16 mCreationCounter; // Temporizador para controlar el estado "creandose" Uint16 mCreationCounterIni; // Valor inicial para el temporizador para controlar el estado "creandose" Uint16 mScore; // Puntos que da el globo al ser destruido Uint16 mStoppedCounter; // Contador para controlar el estado "parado" Uint16 mTimeToLive; // Indica el tiempo de vida que le queda al globo Uint8 mKind; // Tipo de globo - Uint8 mMenace; // Cantidad de amenaza que genera el globo + Uint8 mMenace; // Cantidad de amenaza que genera el globo + Uint32 mCounter; // Contador interno + float mTravelY; // Distancia que ha de recorrer el globo en el eje Y antes de que se le aplique la gravedad + float mSpeed; // Velocidad a la que se mueven los globos struct bouncing // Estructura para las variables para el efecto de los rebotes { @@ -53,13 +54,17 @@ private: float w[MAX_BOUNCE] = {1.10f, 1.05f, 1.00f, 0.95f, 0.90f, 0.95f, 1.00f, 1.02f, 1.05f, 1.02f}; float h[MAX_BOUNCE] = {0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.05f, 1.00f, 0.98f, 0.95f, 0.98f}; }; - bouncing mBouncing; - void shiftColliders(); // Alinea el circulo de colisión con la posición del objeto globo - void bounceStart(); // Activa el efecto - void bounceStop(); // Detiene el efecto - void bounceUpdate(); // Aplica el efecto + void updateColliders(); // Alinea el circulo de colisión con la posición del objeto globo + void bounceStart(); // Activa el efecto + void bounceStop(); // Detiene el efecto + void updateBounce(); // Aplica el efecto + void updateState(); // Actualiza los estados del globo + void updateAnimation(); // Establece la animación correspondiente + void setBeingCreated(bool state); // Establece el valor de la variable + void setTimeToLive(Uint16 time); // Establece el valor de la variable + Uint16 getTimeToLive(); // Obtiene del valor de la variable public: // Constructor @@ -69,7 +74,7 @@ public: ~Balloon(); // Inicializador - void init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture, SDL_Renderer *renderer); + void init(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer, LTexture *texture, SDL_Renderer *renderer); // Centra el globo en la posición X void allignTo(int x); @@ -80,8 +85,8 @@ public: // Actualiza la posición y estados del globo void move(); - // Pone a cero todos los valores del globo - void erase(); + // Deshabilita el globo y pone a cero todos los valores + void disable(); // Explosiona el globo void pop(); @@ -89,17 +94,14 @@ public: // Actualiza al globo a su posicion, animación y controla los contadores void update(); - // Establece la animación correspondiente - void setAnimation(); - - // Comprueba si el globo tiene algun tipo asignado - bool isActive(); + // Comprueba si el globo está habilitado + bool isEnabled(); // Obtiene del valor de la variable - int getPosX(); + float getPosX(); // Obtiene del valor de la variable - int getPosY(); + float getPosY(); // Obtiene del valor de la variable float getVelY(); @@ -113,9 +115,15 @@ public: // Establece el valor de la variable void setVelY(float velY); + // Establece el valor de la variable + void setSpeed(float speed); + // Obtiene del valor de la variable int getKind(); + // Obtiene la clase a la que pertenece el globo + Uint8 getClass(); + // Establece el valor de la variable void setStop(bool state); @@ -140,9 +148,6 @@ public: // Obtiene del valor de la variable bool isInvulnerable(); - // Establece el valor de la variable - void setBeingCreated(bool state); - // Obtiene del valor de la variable bool isBeingCreated(); @@ -152,12 +157,6 @@ public: // Obtiene del valor de la variable bool isPopping(); - // Establece el valor de la variable - void setTimeToLive(Uint16 time); - - // Obtiene del valor de la variable - Uint16 getTimeToLive(); - // Establece el valor de la variable void setStoppedTimer(Uint16 time); @@ -168,7 +167,7 @@ public: Uint16 getScore(); // Obtiene el circulo de colisión - Circle &getCollider(); + circle_t &getCollider(); // Obtiene le valor de la variable Uint8 getMenace(); diff --git a/source/bullet.cpp b/source/bullet.cpp index f6b078d..f2dbf6e 100644 --- a/source/bullet.cpp +++ b/source/bullet.cpp @@ -200,7 +200,7 @@ int Bullet::getKind() } // Obtiene el circulo de colisión -Circle &Bullet::getCollider() +circle_t &Bullet::getCollider() { return mCollider; } diff --git a/source/bullet.h b/source/bullet.h index 26c5d24..add26ce 100644 --- a/source/bullet.h +++ b/source/bullet.h @@ -49,7 +49,7 @@ public: int getKind(); // Obtiene el circulo de colisión - Circle &getCollider(); + circle_t &getCollider(); private: // Posición X/Y del objeto @@ -71,7 +71,7 @@ private: Sprite *mSprite; // Balloon's collision circle - Circle mCollider; + circle_t mCollider; // Alinea el circulo de colisión con el objeto void shiftColliders(); diff --git a/source/const.h b/source/const.h index c547b54..5c38e87 100644 --- a/source/const.h +++ b/source/const.h @@ -75,7 +75,7 @@ const int VIEW_HEIGHT = SCREEN_HEIGHT * 3; // Cantidad de enteros a escribir en los ficheros de datos #define TOTAL_SCORE_DATA 3 -#define TOTAL_DEMO_DATA 2000 +#define TOTAL_DEMO_DATA 2000 // Zona de juego const int PLAY_AREA_TOP = (0 * BLOCK); @@ -85,6 +85,8 @@ const int PLAY_AREA_RIGHT = SCREEN_WIDTH - (0 * BLOCK); const int PLAY_AREA_WIDTH = PLAY_AREA_RIGHT - PLAY_AREA_LEFT; const int PLAY_AREA_HEIGHT = PLAY_AREA_BOTTOM - PLAY_AREA_TOP; const int PLAY_AREA_CENTER_X = PLAY_AREA_LEFT + (PLAY_AREA_WIDTH / 2); +const int PLAY_AREA_CENTER_FIRST_QUARTER_X = (PLAY_AREA_WIDTH / 4); +const int PLAY_AREA_CENTER_THIRD_QUARTER_X = (PLAY_AREA_WIDTH / 4) * 3; const int PLAY_AREA_CENTER_Y = PLAY_AREA_TOP + (PLAY_AREA_HEIGHT / 2); const int PLAY_AREA_FIRST_QUARTER_Y = PLAY_AREA_HEIGHT / 4; const int PLAY_AREA_THIRD_QUARTER_Y = (PLAY_AREA_HEIGHT / 4) * 3; @@ -127,12 +129,6 @@ const int SCREEN_THIRD_QUARTER_Y = (SCREEN_HEIGHT / 4) * 3; #define PLAYER_ANIMATION_BODY_FIRING_RIGHT 3 #define PLAYER_ANIMATION_BODY_WALKING_STOP 4 #define PLAYER_ANIMATION_BODY_FIRING_UP 5 -#define PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT 6 -#define PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT 7 -#define PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT 8 -#define PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT 9 -#define PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT 10 -#define PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT 11 // Variables del jugador #define PLAYER_INVULNERABLE_COUNTER 200 @@ -208,6 +204,15 @@ const int MULTIPLIER_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 2; #define BALLOON_2 2 #define BALLOON_3 3 #define BALLOON_4 4 +#define HEXAGON_1 5 +#define HEXAGON_2 6 +#define HEXAGON_3 7 +#define HEXAGON_4 8 +#define POWER_BALL 9 + +// Clases de globo +#define BALLOON_CLASS 0 +#define HEXAGON_CLASS 1 // Velocidad del globo #define BALLOON_VELX_POSITIVE 0.7f @@ -219,7 +224,20 @@ const int MULTIPLIER_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 2; #define BALLOON_BORN_ANIMATION 2 // Cantidad posible de globos -#define MAX_BALLOONS 75 +#define MAX_BALLOONS 100 + +// Velocidades a las que se mueven los enemigos +#define BALLOON_SPEED_1 0.60f +#define BALLOON_SPEED_2 0.70f +#define BALLOON_SPEED_3 0.80f +#define BALLOON_SPEED_4 0.90f +#define BALLOON_SPEED_5 1.00f + +// Tamaño de los globos +#define BALLOON_SIZE_1 8 +#define BALLOON_SIZE_2 13 +#define BALLOON_SIZE_3 21 +#define BALLOON_SIZE_4 37 // Tipos de bala #define BULLET_UP 1 @@ -236,6 +254,7 @@ const int MULTIPLIER_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 2; #define ITEM_CLOCK 4 #define ITEM_TNT 5 #define ITEM_COFFEE 6 +#define ITEM_POWER_BALL 7 // Cantidad de objetos simultaneos #define MAX_ITEMS 5 @@ -280,5 +299,6 @@ const int MULTIPLIER_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 2; #define STAGE_COUNTER 200 #define INSTRUCTIONS_COUNTER 600 #define DEATH_COUNTER 350 +#define SHAKE_COUNTER 10 #endif \ No newline at end of file diff --git a/source/gamedirector.cpp b/source/gamedirector.cpp index 3dfa035..00c6c73 100644 --- a/source/gamedirector.cpp +++ b/source/gamedirector.cpp @@ -98,14 +98,20 @@ GameDirector::~GameDirector() // Inicia las variables necesarias para arrancar el programa void GameDirector::initProg() { + // Debug mode + mDebug.enabled = false; + mDebug.enemySet = 0; + mDebug.gradR = mDebug.gradG = mDebug.gradB = 0; + // Variables mProg.quit = false; - mProg.debug = false; mProg.ticks = 0; mProg.ticksSpeed = 15; mProg.section = PROG_SECTION_LOGO; initSin(); initPaths(); + initEnemyFormations(); + initEnemyPools(); mOptions.displayCoffeeDrops = false; @@ -182,6 +188,7 @@ void GameDirector::initProg() mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 0); mMenu.gameOver->setSelectorTextColor(0xFF, 0xFF, 0xFF); mMenu.gameOver->setSelectorTextColor(0xFF, 0xF1, 0x76); + mMenu.gameOver->setSelectorTextColor(0xFF, 0x7A, 0x00); mMenu.gameOver->centerMenu(SCREEN_CENTER_X); mMenu.gameOver->centerMenuElements(); @@ -373,6 +380,7 @@ void GameDirector::initSin() // Inicializa las variables que contienen puntos de ruta para mover objetos void GameDirector::initPaths() { + // Letrero de STAGE # int firstPart = STAGE_COUNTER / 4; // 50 int secondPart = firstPart * 3; // 150 int centerPoint = PLAY_AREA_CENTER_Y - (BLOCK * 2); @@ -381,11 +389,39 @@ void GameDirector::initPaths() for (int i = 0; i < STAGE_COUNTER; i++) { if (i < firstPart) - mGame.stagePath[i] = (mSin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint); + mGame.stageBitmapPath[i] = (mSin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint); else if (i < secondPart) - mGame.stagePath[i] = (int)centerPoint; + mGame.stageBitmapPath[i] = (int)centerPoint; else - mGame.stagePath[i] = (mSin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17); + mGame.stageBitmapPath[i] = (mSin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17); + } + + float start1 = PLAY_AREA_LEFT - 130; + float finish1 = PLAY_AREA_CENTER_X - 55; + + float start2 = finish1; + float finish2 = PLAY_AREA_RIGHT + 20; + + float distance1 = finish1 - start1; + float distance2 = finish2 - start2; + + // Letrero de GetReady + for (int i = 0; i < STAGE_COUNTER; i++) + { + if (i < firstPart) + { + mGame.getReadyBitmapPath[i] = mSin[(int)(i * 1.8f)]; + mGame.getReadyBitmapPath[i] *= distance1; + mGame.getReadyBitmapPath[i] -= 130; + } + else if (i < secondPart) + mGame.getReadyBitmapPath[i] = (int)finish1; + else + { + mGame.getReadyBitmapPath[i] = mSin[(int)((i - 150) * 1.8f)]; + mGame.getReadyBitmapPath[i] *= distance2; + mGame.getReadyBitmapPath[i] += finish1; + } } } @@ -813,9 +849,11 @@ void GameDirector::initGame() mGame.clouds1b = new MovingSprite(); mGame.clouds2a = new MovingSprite(); mGame.clouds2b = new MovingSprite(); - mGame.getReadyBitmap = new SmartSprite(); + mGame.getReadyBitmap = new Sprite(); mGame.gradient = new Sprite(); mGame.grass = new Sprite(); + mGame.scoreBoard = new Sprite(); + mGame.powerMeter = new Sprite(); // Carga los recursos loadMediaGame(); @@ -829,22 +867,29 @@ void GameDirector::resetGame() { // Inicializa las variables mGame.section = GAME_SECTION_PLAY; - mGame.menaceLevelCurrent = 0; - mGame.menaceLevelThreshold = 0; + mGame.menaceCurrent = 0; + mGame.menaceThreshold = 0; mGame.score = 0; mGame.hiScoreAchieved = false; - mGame.stage = 1; - mGame.stageCounter = STAGE_COUNTER; + mGame.currentStage = 0; + mGame.stageBitmapCounter = STAGE_COUNTER; mGame.deathCounter = DEATH_COUNTER; mGame.explosionTime = false; mGame.remainingExplosions = REMAINING_EXPLOSIONS; mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; mGame.timeStopped = false; mGame.timeStoppedCounter = TIME_STOPPED_COUNTER; - mProg.debug = false; mGame.counter = 0; + mGame.balloonsPopped = 0; + mGame.lastEnemyDeploy = 0; + mGame.enemyDeployCounter = 0; + mGame.enemySpeed = BALLOON_SPEED_1; + mGame.effect.flash = false; + mGame.effect.shake = false; + mGame.effect.shakeCounter = SHAKE_COUNTER; + initGameStages(); - // Fondo animado + // Sprites mGame.clouds1a->init(0, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.clouds1a->setSpriteClip(256, 0, 256, 52); @@ -860,6 +905,12 @@ void GameDirector::resetGame() mGame.grass->init(0, 85, SCREEN_WIDTH, 6, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.grass->setPosY(154); + mGame.scoreBoard->init(0, 160, SCREEN_WIDTH, 32, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.scoreBoard->setSpriteClip(0, 160, 256, 32); + + mGame.powerMeter->init(PLAY_AREA_CENTER_THIRD_QUARTER_X - 20, HISCORE_NUMBER_Y + 4, 40, 8, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.powerMeter->setSpriteClip(256, 192 - 8, 40, 8); + // Objeto jugador mGame.player->init(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_BODY].texture, mRenderer); @@ -867,11 +918,11 @@ void GameDirector::resetGame() resetBalloons(); // Crea objetos globo y los centra en el area de juego - mGame.balloon[0]->init(0, BLOCK, BALLOON_4, BALLOON_VELX_POSITIVE, 100, mTexture[TEXTURE_BALLOON].texture, mRenderer); - mGame.balloon[0]->allignTo(PLAY_AREA_CENTER_X); + //mGame.balloon[0]->init(0, -BLOCK, POWER_BALL, BALLOON_VELX_POSITIVE, mGame.enemySpeed, 300, mTexture[TEXTURE_BALLOON].texture, mRenderer); + //mGame.balloon[0]->allignTo(PLAY_AREA_CENTER_X); // Con los globos creados, calcula el nivel de amenaza - setMenaceLevel(); + setMenace(); // Establece a cero todos los valores del vector de objetos bala resetBullets(); @@ -882,21 +933,9 @@ void GameDirector::resetGame() // Establece a cero todos los valores del vector de objetos SmafrtSprite resetSmartSprites(); - // Inicializa el bitmap de GetReady! - mGame.getReadyBitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); - mGame.getReadyBitmap->setPosX(-113); - mGame.getReadyBitmap->setPosY(PLAY_AREA_CENTER_Y - 10); - mGame.getReadyBitmap->setWidth(109); - mGame.getReadyBitmap->setHeight(20); - mGame.getReadyBitmap->setVelX(2.0f); - mGame.getReadyBitmap->setVelY(0.0f); - mGame.getReadyBitmap->setAccelX(0.00f); - mGame.getReadyBitmap->setAccelY(0.0f); + // Inicializa el bitmap de GetLeady! + mGame.getReadyBitmap->init(0, PLAY_AREA_CENTER_Y - 10, 109, 20, mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); mGame.getReadyBitmap->setSpriteClip(0, 0, 109, 20); - mGame.getReadyBitmap->setEnabled(!mDemo.enabled); - mGame.getReadyBitmap->setEnabledTimer(0); - mGame.getReadyBitmap->setDestX(PLAY_AREA_RIGHT); - mGame.getReadyBitmap->setDestY(PLAY_AREA_CENTER_Y - 10); // Inicializa el bitmap de 1000 puntos mGame._1000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); @@ -949,6 +988,10 @@ void GameDirector::resetGame() // Los fondos mGame.background->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); mGame.gradient->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.gradientRect[0] = {0, 192, SCREEN_WIDTH, SCREEN_HEIGHT}; + mGame.gradientRect[1] = {256, 192, SCREEN_WIDTH, SCREEN_HEIGHT}; + mGame.gradientRect[2] = {0, 384, SCREEN_WIDTH, SCREEN_HEIGHT}; + mGame.gradientRect[3] = {256, 384, SCREEN_WIDTH, SCREEN_HEIGHT}; } // Carga los recursos necesarios para la sección 'Game' @@ -1059,6 +1102,8 @@ void GameDirector::quitGame() delete mGame.getReadyBitmap; delete mGame.gradient; delete mGame.grass; + delete mGame.scoreBoard; + delete mGame.powerMeter; mTexture[TEXTURE_PLAYER_LEGS].texture = nullptr; mTexture[TEXTURE_PLAYER_BODY].texture = nullptr; mTexture[TEXTURE_PLAYER_DEATH].texture = nullptr; @@ -1079,6 +1124,8 @@ void GameDirector::quitGame() mGame.getReadyBitmap = nullptr; mGame.gradient = nullptr; mGame.grass = nullptr; + mGame.scoreBoard = nullptr; + mGame.powerMeter = nullptr; } // Crea el indice de ficheros @@ -1358,7 +1405,7 @@ bool GameDirector::loadDemoFile() mDemo.keys.fireLeft = 0; mDemo.keys.fireRight = 0; mDemo.dataFile[i] = mDemo.keys; - SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(DemoKeys), 1); + SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1); } // Cerramos el fichero @@ -1376,7 +1423,7 @@ bool GameDirector::loadDemoFile() // 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), 1); + SDL_RWread(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1); // Cierra el fichero SDL_RWclose(file); @@ -1454,7 +1501,7 @@ bool GameDirector::saveDemoFile() // Guardamos los datos for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { - SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(DemoKeys), 1); + SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(demoKeys_t), 1); } printf("Writing file %s\n", filename.c_str()); @@ -1494,6 +1541,745 @@ void GameDirector::setExecutablePath(std::string path) mProg.executablePath = path.substr(0, path.find_last_of("\\/")); } +// Inicializa las formaciones enemigas +void GameDirector::initEnemyFormations() +{ + const int y4 = (PLAY_AREA_TOP - BLOCK); + const int x4_0 = PLAY_AREA_LEFT; + //const int x4_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_4 / 2); + const int x4_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_4; + + const int y3 = (PLAY_AREA_TOP - BLOCK); + const int x3_0 = PLAY_AREA_LEFT; + //const int x3_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_3 / 2); + const int x3_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_3; + + const int y2 = (PLAY_AREA_TOP - BLOCK); + const int x2_0 = PLAY_AREA_LEFT; + //const int x2_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_2 / 2); + const int x2_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_2; + + const int y1 = (PLAY_AREA_TOP - BLOCK); + const int x1_0 = PLAY_AREA_LEFT; + const int x1_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_1 / 2); + const int x1_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_1; + + const Uint16 creationTime = 300; + int incX = 0; + Uint8 incTime = 0; + Uint8 j = 0; + + // #00 - Dos enemigos BALLOON4 uno a cada extremo + j = 0; + mEnemyFormation[j].numberOfEnemies = 2; + incX = x4_100; + incTime = 0; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x4_0 + (i * incX); + mEnemyFormation[j].init[i].y = y4; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1); + mEnemyFormation[j].init[i].kind = BALLOON_4; + mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); + } + + // #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro + j = 1; + mEnemyFormation[j].numberOfEnemies = 2; + incX = PLAY_AREA_CENTER_X; + incTime = 0; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = PLAY_AREA_CENTER_FIRST_QUARTER_X - (BALLOON_SIZE_4 / 2) + (i * incX); + mEnemyFormation[j].init[i].y = y4; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE * (((i % 2) * 2) - 1); + mEnemyFormation[j].init[i].kind = BALLOON_4; + mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); + } + + // #02 - Cuatro enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro + j = 2; + mEnemyFormation[j].numberOfEnemies = 4; + incX = BALLOON_SIZE_2 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x2_0 + (i * incX); + mEnemyFormation[j].init[i].y = y2; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_2; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #03 - Cuatro enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro + j = 3; + mEnemyFormation[j].numberOfEnemies = 4; + incX = BALLOON_SIZE_2 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x2_100 - (i * incX); + mEnemyFormation[j].init[i].y = y2; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_2; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha + j = 4; + mEnemyFormation[j].numberOfEnemies = 3; + incX = BALLOON_SIZE_3 * 2; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_0 + (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda + j = 5; + mEnemyFormation[j].numberOfEnemies = 3; + incX = BALLOON_SIZE_3 * 2; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_100 - (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha + j = 6; + mEnemyFormation[j].numberOfEnemies = 3; + incX = BALLOON_SIZE_3 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_0 + (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda + j = 7; + mEnemyFormation[j].numberOfEnemies = 3; + incX = BALLOON_SIZE_3 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_100 - (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha + j = 8; + mEnemyFormation[j].numberOfEnemies = 6; + incX = BALLOON_SIZE_1 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x1_0 + (i * incX); + mEnemyFormation[j].init[i].y = 13; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_1; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda + j = 9; + mEnemyFormation[j].numberOfEnemies = 6; + incX = BALLOON_SIZE_1 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x1_100 - (i * incX); + mEnemyFormation[j].init[i].y = 13; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_1; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #10 - Tres enemigos BALLOON4 seguidos desde la izquierda + j = 10; + mEnemyFormation[j].numberOfEnemies = 3; + incX = BALLOON_SIZE_4 + 1; + incTime = 15; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x4_0 + (i * incX); + mEnemyFormation[j].init[i].y = y4; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_4; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #11 - Tres enemigos BALLOON4 seguidos desde la derecha + j = 11; + mEnemyFormation[j].numberOfEnemies = 3; + incX = BALLOON_SIZE_4 + 1; + incTime = 15; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x4_100 - (i * incX); + mEnemyFormation[j].init[i].y = y4; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_4; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #12 - Seis enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro + j = 12; + mEnemyFormation[j].numberOfEnemies = 6; + incX = BALLOON_SIZE_2 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x2_0 + (i * incX); + mEnemyFormation[j].init[i].y = y2; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_2; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #13 - Seis enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro + j = 13; + mEnemyFormation[j].numberOfEnemies = 6; + incX = BALLOON_SIZE_2 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x2_100 - (i * incX); + mEnemyFormation[j].init[i].y = y2; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_2; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados + j = 14; + mEnemyFormation[j].numberOfEnemies = 5; + incX = BALLOON_SIZE_3 * 2; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_0 + (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados + j = 15; + mEnemyFormation[j].numberOfEnemies = 5; + incX = BALLOON_SIZE_3 * 2; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_100 - (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos + j = 16; + mEnemyFormation[j].numberOfEnemies = 5; + incX = BALLOON_SIZE_3 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_0 + (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos + j = 17; + mEnemyFormation[j].numberOfEnemies = 5; + incX = BALLOON_SIZE_3 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x3_100 - (i * incX); + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_3; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos + j = 18; + mEnemyFormation[j].numberOfEnemies = 12; + incX = BALLOON_SIZE_1 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x1_0 + (i * incX); + mEnemyFormation[j].init[i].y = y1; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].kind = BALLOON_1; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos + j = 19; + mEnemyFormation[j].numberOfEnemies = 12; + incX = BALLOON_SIZE_1 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + mEnemyFormation[j].init[i].x = x1_100 - (i * incX); + mEnemyFormation[j].init[i].y = y1; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].kind = BALLOON_1; + mEnemyFormation[j].init[i].creationCounter = creationTime - (incTime * i); + } + + // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simetricos + j = 20; + mEnemyFormation[j].numberOfEnemies = 4; + incX = BALLOON_SIZE_4 + 1; + incTime = 0; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; + if (i < half) + { + mEnemyFormation[j].init[i].x = x4_0 + (i * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + } + else + { + mEnemyFormation[j].init[i].x = x4_100 - ((i - half) * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + } + mEnemyFormation[j].init[i].y = y4; + mEnemyFormation[j].init[i].kind = BALLOON_4; + mEnemyFormation[j].init[i].creationCounter = creationTime + (incTime * i); + } + + // #21 - Diez enemigos BALLOON2 uno detras del otro. Izquierda/derecha. Simetricos + j = 21; + mEnemyFormation[j].numberOfEnemies = 10; + incX = BALLOON_SIZE_2 + 1; + incTime = 3; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; + if (i < half) + { + mEnemyFormation[j].init[i].x = x2_0 + (i * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); + } + else + { + mEnemyFormation[j].init[i].x = x2_100 - ((i - half) * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); + } + mEnemyFormation[j].init[i].y = y2; + mEnemyFormation[j].init[i].kind = BALLOON_2; + } + + // #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simetricos + j = 22; + mEnemyFormation[j].numberOfEnemies = 10; + incX = BALLOON_SIZE_3 * 2; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; + if (i < half) + { + mEnemyFormation[j].init[i].x = x3_0 + (i * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); + } + else + { + mEnemyFormation[j].init[i].x = x3_100 - ((i - half) * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); + } + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].kind = BALLOON_3; + } + + // #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simetricos + j = 23; + mEnemyFormation[j].numberOfEnemies = 10; + incX = BALLOON_SIZE_3 + 1; + incTime = 10; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; + if (i < half) + { + mEnemyFormation[j].init[i].x = x3_0 + (i * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); + } + else + { + mEnemyFormation[j].init[i].x = x3_100 - ((i - half) * incX); + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); + } + mEnemyFormation[j].init[i].y = y3; + mEnemyFormation[j].init[i].kind = BALLOON_3; + } + + // #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos + j = 24; + mEnemyFormation[j].numberOfEnemies = 30; + incX = 0; + incTime = 5; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; + if (i < half) + { + mEnemyFormation[j].init[i].x = x1_50; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * i); + } + else + { + mEnemyFormation[j].init[i].x = x1_50; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) + (incTime * (i - half)); + } + mEnemyFormation[j].init[i].y = y1; + mEnemyFormation[j].init[i].kind = BALLOON_1; + } + + // #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos + j = 25; + mEnemyFormation[j].numberOfEnemies = 30; + incX = BALLOON_SIZE_1 + 1; + incTime = 5; + for (Uint8 i = 0; i < mEnemyFormation[j].numberOfEnemies; i++) + { + Uint8 half = mEnemyFormation[j].numberOfEnemies / 2; + if (i < half) + { + mEnemyFormation[j].init[i].x = x1_50 + 20; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_NEGATIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * i); + } + else + { + mEnemyFormation[j].init[i].x = x1_50 - 20; + mEnemyFormation[j].init[i].velX = BALLOON_VELX_POSITIVE; + mEnemyFormation[j].init[i].creationCounter = (creationTime) - (incTime * (i - half)); + } + mEnemyFormation[j].init[i].y = y1; + mEnemyFormation[j].init[i].kind = BALLOON_1; + } + + // Crea las mismas formaciones pero con hexagonos a partir de la posición 50 del vector + for (Uint8 k = 0; k < 50; k++) + { + mEnemyFormation[k + 50].numberOfEnemies = mEnemyFormation[k].numberOfEnemies; + for (Uint8 i = 0; i < mEnemyFormation[k + 50].numberOfEnemies; i++) + { + mEnemyFormation[k + 50].init[i].x = mEnemyFormation[k].init[i].x; + mEnemyFormation[k + 50].init[i].y = mEnemyFormation[k].init[i].y; + mEnemyFormation[k + 50].init[i].velX = mEnemyFormation[k].init[i].velX; + mEnemyFormation[k + 50].init[i].creationCounter = mEnemyFormation[k].init[i].creationCounter; + mEnemyFormation[k + 50].init[i].kind = mEnemyFormation[k].init[i].kind + 4; + } + } + + // TEST + mEnemyFormation[99].numberOfEnemies = 4; + + mEnemyFormation[99].init[0].x = 10; + mEnemyFormation[99].init[0].y = y1; + mEnemyFormation[99].init[0].velX = 0; + mEnemyFormation[99].init[0].kind = BALLOON_1; + mEnemyFormation[99].init[0].creationCounter = 200; + + mEnemyFormation[99].init[1].x = 50; + mEnemyFormation[99].init[1].y = y1; + mEnemyFormation[99].init[1].velX = 0; + mEnemyFormation[99].init[1].kind = BALLOON_2; + mEnemyFormation[99].init[1].creationCounter = 200; + + mEnemyFormation[99].init[2].x = 90; + mEnemyFormation[99].init[2].y = y1; + mEnemyFormation[99].init[2].velX = 0; + mEnemyFormation[99].init[2].kind = BALLOON_3; + mEnemyFormation[99].init[2].creationCounter = 200; + + mEnemyFormation[99].init[3].x = 140; + mEnemyFormation[99].init[3].y = y1; + mEnemyFormation[99].init[3].velX = 0; + mEnemyFormation[99].init[3].kind = BALLOON_4; + mEnemyFormation[99].init[3].creationCounter = 200; +} + +// Inicializa los conjuntos de formaciones +void GameDirector::initEnemyPools() +{ + // EnemyPool #0 + mEnemyPool[0].set[0] = &mEnemyFormation[0]; + mEnemyPool[0].set[1] = &mEnemyFormation[1]; + mEnemyPool[0].set[2] = &mEnemyFormation[2]; + mEnemyPool[0].set[3] = &mEnemyFormation[3]; + mEnemyPool[0].set[4] = &mEnemyFormation[4]; + mEnemyPool[0].set[5] = &mEnemyFormation[5]; + mEnemyPool[0].set[6] = &mEnemyFormation[6]; + mEnemyPool[0].set[7] = &mEnemyFormation[7]; + mEnemyPool[0].set[8] = &mEnemyFormation[8]; + mEnemyPool[0].set[9] = &mEnemyFormation[9]; + + // EnemyPool #1 + mEnemyPool[1].set[0] = &mEnemyFormation[10]; + mEnemyPool[1].set[1] = &mEnemyFormation[11]; + mEnemyPool[1].set[2] = &mEnemyFormation[12]; + mEnemyPool[1].set[3] = &mEnemyFormation[13]; + mEnemyPool[1].set[4] = &mEnemyFormation[14]; + mEnemyPool[1].set[5] = &mEnemyFormation[15]; + mEnemyPool[1].set[6] = &mEnemyFormation[16]; + mEnemyPool[1].set[7] = &mEnemyFormation[17]; + mEnemyPool[1].set[8] = &mEnemyFormation[18]; + mEnemyPool[1].set[9] = &mEnemyFormation[19]; + + // EnemyPool #2 + mEnemyPool[2].set[0] = &mEnemyFormation[0]; + mEnemyPool[2].set[1] = &mEnemyFormation[1]; + mEnemyPool[2].set[2] = &mEnemyFormation[2]; + mEnemyPool[2].set[3] = &mEnemyFormation[3]; + mEnemyPool[2].set[4] = &mEnemyFormation[4]; + mEnemyPool[2].set[5] = &mEnemyFormation[55]; + mEnemyPool[2].set[6] = &mEnemyFormation[56]; + mEnemyPool[2].set[7] = &mEnemyFormation[57]; + mEnemyPool[2].set[8] = &mEnemyFormation[58]; + mEnemyPool[2].set[9] = &mEnemyFormation[59]; + + // EnemyPool #3 + mEnemyPool[3].set[0] = &mEnemyFormation[50]; + mEnemyPool[3].set[1] = &mEnemyFormation[51]; + mEnemyPool[3].set[2] = &mEnemyFormation[52]; + mEnemyPool[3].set[3] = &mEnemyFormation[53]; + mEnemyPool[3].set[4] = &mEnemyFormation[54]; + mEnemyPool[3].set[5] = &mEnemyFormation[5]; + mEnemyPool[3].set[6] = &mEnemyFormation[6]; + mEnemyPool[3].set[7] = &mEnemyFormation[7]; + mEnemyPool[3].set[8] = &mEnemyFormation[8]; + mEnemyPool[3].set[9] = &mEnemyFormation[9]; + + // EnemyPool #4 + mEnemyPool[4].set[0] = &mEnemyFormation[60]; + mEnemyPool[4].set[1] = &mEnemyFormation[61]; + mEnemyPool[4].set[2] = &mEnemyFormation[62]; + mEnemyPool[4].set[3] = &mEnemyFormation[63]; + mEnemyPool[4].set[4] = &mEnemyFormation[64]; + mEnemyPool[4].set[5] = &mEnemyFormation[65]; + mEnemyPool[4].set[6] = &mEnemyFormation[66]; + mEnemyPool[4].set[7] = &mEnemyFormation[67]; + mEnemyPool[4].set[8] = &mEnemyFormation[68]; + mEnemyPool[4].set[9] = &mEnemyFormation[69]; + + // EnemyPool #5 + mEnemyPool[5].set[0] = &mEnemyFormation[10]; + mEnemyPool[5].set[1] = &mEnemyFormation[61]; + mEnemyPool[5].set[2] = &mEnemyFormation[12]; + mEnemyPool[5].set[3] = &mEnemyFormation[63]; + mEnemyPool[5].set[4] = &mEnemyFormation[14]; + mEnemyPool[5].set[5] = &mEnemyFormation[65]; + mEnemyPool[5].set[6] = &mEnemyFormation[16]; + mEnemyPool[5].set[7] = &mEnemyFormation[67]; + mEnemyPool[5].set[8] = &mEnemyFormation[18]; + mEnemyPool[5].set[9] = &mEnemyFormation[69]; + + // EnemyPool #6 + mEnemyPool[6].set[0] = &mEnemyFormation[60]; + mEnemyPool[6].set[1] = &mEnemyFormation[11]; + mEnemyPool[6].set[2] = &mEnemyFormation[62]; + mEnemyPool[6].set[3] = &mEnemyFormation[13]; + mEnemyPool[6].set[4] = &mEnemyFormation[64]; + mEnemyPool[6].set[5] = &mEnemyFormation[15]; + mEnemyPool[6].set[6] = &mEnemyFormation[66]; + mEnemyPool[6].set[7] = &mEnemyFormation[17]; + mEnemyPool[6].set[8] = &mEnemyFormation[68]; + mEnemyPool[6].set[9] = &mEnemyFormation[19]; + + // EnemyPool #7 + mEnemyPool[7].set[0] = &mEnemyFormation[20]; + mEnemyPool[7].set[1] = &mEnemyFormation[21]; + mEnemyPool[7].set[2] = &mEnemyFormation[22]; + mEnemyPool[7].set[3] = &mEnemyFormation[23]; + mEnemyPool[7].set[4] = &mEnemyFormation[24]; + mEnemyPool[7].set[5] = &mEnemyFormation[65]; + mEnemyPool[7].set[6] = &mEnemyFormation[66]; + mEnemyPool[7].set[7] = &mEnemyFormation[67]; + mEnemyPool[7].set[8] = &mEnemyFormation[68]; + mEnemyPool[7].set[9] = &mEnemyFormation[69]; + + // EnemyPool #8 + mEnemyPool[8].set[0] = &mEnemyFormation[70]; + mEnemyPool[8].set[1] = &mEnemyFormation[71]; + mEnemyPool[8].set[2] = &mEnemyFormation[72]; + mEnemyPool[8].set[3] = &mEnemyFormation[73]; + mEnemyPool[8].set[4] = &mEnemyFormation[74]; + mEnemyPool[8].set[5] = &mEnemyFormation[15]; + mEnemyPool[8].set[6] = &mEnemyFormation[16]; + mEnemyPool[8].set[7] = &mEnemyFormation[17]; + mEnemyPool[8].set[8] = &mEnemyFormation[18]; + mEnemyPool[8].set[9] = &mEnemyFormation[19]; + + // EnemyPool #9 + mEnemyPool[9].set[0] = &mEnemyFormation[20]; + mEnemyPool[9].set[1] = &mEnemyFormation[21]; + mEnemyPool[9].set[2] = &mEnemyFormation[22]; + mEnemyPool[9].set[3] = &mEnemyFormation[23]; + mEnemyPool[9].set[4] = &mEnemyFormation[24]; + mEnemyPool[9].set[5] = &mEnemyFormation[70]; + mEnemyPool[9].set[6] = &mEnemyFormation[71]; + mEnemyPool[9].set[7] = &mEnemyFormation[72]; + mEnemyPool[9].set[8] = &mEnemyFormation[73]; + mEnemyPool[9].set[9] = &mEnemyFormation[74]; +} + +// Inicializa las fases del juego +void GameDirector::initGameStages() +{ + // STAGE 1 + mGame.stage[0].number = 1; + mGame.stage[0].currentPower = 0; + mGame.stage[0].powerToComplete = 200; + mGame.stage[0].minMenace = 7 + (4 * 1); + mGame.stage[0].maxMenace = 7 + (4 * 3); + mGame.stage[0].enemyPool = &mEnemyPool[0]; + + // STAGE 2 + mGame.stage[1].number = 2; + mGame.stage[1].currentPower = 0; + mGame.stage[1].powerToComplete = 300; + mGame.stage[1].minMenace = 7 + (4 * 2); + mGame.stage[1].maxMenace = 7 + (4 * 4); + mGame.stage[1].enemyPool = &mEnemyPool[1]; + + // STAGE 3 + mGame.stage[2].number = 3; + mGame.stage[2].currentPower = 0; + mGame.stage[2].powerToComplete = 600; + mGame.stage[2].minMenace = 7 + (4 * 3); + mGame.stage[2].maxMenace = 7 + (4 * 6); + mGame.stage[2].enemyPool = &mEnemyPool[2]; + + // STAGE 4 + mGame.stage[3].number = 4; + mGame.stage[3].currentPower = 0; + mGame.stage[3].powerToComplete = 600; + mGame.stage[3].minMenace = 7 + (4 * 3); + mGame.stage[3].maxMenace = 7 + (4 * 7); + mGame.stage[3].enemyPool = &mEnemyPool[3]; + + // STAGE 5 + mGame.stage[4].number = 5; + mGame.stage[4].currentPower = 0; + mGame.stage[4].powerToComplete = 600; + mGame.stage[4].minMenace = 7 + (4 * 4); + mGame.stage[4].maxMenace = 7 + (4 * 8); + mGame.stage[4].enemyPool = &mEnemyPool[4]; + + // STAGE 6 + mGame.stage[5].number = 6; + mGame.stage[5].currentPower = 0; + mGame.stage[5].powerToComplete = 600; + mGame.stage[5].minMenace = 7 + (4 * 5); + mGame.stage[5].maxMenace = 7 + (4 * 10); + mGame.stage[5].enemyPool = &mEnemyPool[5]; + + // STAGE 7 + mGame.stage[6].number = 7; + mGame.stage[6].currentPower = 0; + mGame.stage[6].powerToComplete = 650; + mGame.stage[6].minMenace = 7 + (4 * 6); + mGame.stage[6].maxMenace = 7 + (4 * 11); + mGame.stage[6].enemyPool = &mEnemyPool[6]; + + // STAGE 8 + mGame.stage[7].number = 8; + mGame.stage[7].currentPower = 0; + mGame.stage[7].powerToComplete = 750; + mGame.stage[7].minMenace = 7 + (4 * 7); + mGame.stage[7].maxMenace = 7 + (4 * 12); + mGame.stage[7].enemyPool = &mEnemyPool[7]; + + // STAGE 9 + mGame.stage[8].number = 9; + mGame.stage[8].currentPower = 0; + mGame.stage[8].powerToComplete = 850; + mGame.stage[8].minMenace = 7 + (4 * 8); + mGame.stage[8].maxMenace = 7 + (4 * 13); + mGame.stage[8].enemyPool = &mEnemyPool[8]; + + // STAGE 10 + mGame.stage[9].number = 10; + mGame.stage[9].currentPower = 0; + mGame.stage[9].powerToComplete = 950; + mGame.stage[9].minMenace = 7 + (4 * 9); + mGame.stage[9].maxMenace = 7 + (4 * 15); + mGame.stage[9].enemyPool = &mEnemyPool[9]; +} + +// Crea una formación de enemigos +void GameDirector::deployEnemyFormation() +{ + // Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última + if (mGame.enemyDeployCounter == 0) + { + mGame.enemyDeployCounter = 255; + + Uint8 set = (rand() % 10); + + if (set == mGame.lastEnemyDeploy) + { + set++; + set %= 10; + } + mGame.lastEnemyDeploy = set; + + if (mDebug.enabled) + set = mDebug.enemySet; + + Uint8 numEnemies = mGame.stage[mGame.currentStage].enemyPool->set[set]->numberOfEnemies; + for (Uint8 i = 0; i < numEnemies; i++) + createNewBalloon(mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].x, + mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].y, + mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].kind, + mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].velX, + mGame.enemySpeed, + mGame.stage[mGame.currentStage].enemyPool->set[set]->init[i].creationCounter, + mTexture[TEXTURE_BALLOON].texture); + } +} + +// Aumenta el poder de la fase +void GameDirector::increaseStageCurrentPower() +{ + mGame.stage[mGame.currentStage].currentPower++; +} + // Establece el valor de la variable void GameDirector::setScore(Uint32 score) { @@ -1531,32 +2317,39 @@ void GameDirector::updateHiScore() // Transforma un valor numérico en una cadena de 6 cifras std::string GameDirector::updateScoreText(Uint32 num) { + return (std::to_string(num)); + if ((num >= 0) && (num <= 9)) { - return ("00000" + std::to_string(num)); + return ("000000" + std::to_string(num)); } if ((num >= 10) && (num <= 99)) { - return ("0000" + std::to_string(num)); + return ("00000" + std::to_string(num)); } if ((num >= 100) && (num <= 999)) { - return ("000" + std::to_string(num)); + return ("0000" + std::to_string(num)); } if ((num >= 1000) && (num <= 9999)) { - return ("00" + std::to_string(num)); + return ("000" + std::to_string(num)); } if ((num >= 010000) && (num <= 99999)) { - return ("0" + std::to_string(num)); + 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)); } @@ -1567,31 +2360,81 @@ std::string GameDirector::updateScoreText(Uint32 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); + // Si el jugador esta muerto, no pintes el fondo del marcador, así que pase por encima cuando sale despedido + if (mGame.player->isAlive()) + mGame.scoreBoard->render(); - mText.white->write(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y, "MULT", 0); - mText.white->write(MULTIPLIER_NUMBER_X, MULTIPLIER_NUMBER_Y, std::to_string(mGame.player->getScoreMultiplier()), 0, 3); + if (!mDemo.enabled) + { + // SCORE + mText.white->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_WORD_Y - 6, "SCORE", 0); + mText.white->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_NUMBER_Y - 6, updateScoreText(mGame.score), 0); - 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); + // HI-SCORE + mText.white->writeCentered(PLAY_AREA_CENTER_THIRD_QUARTER_X, HISCORE_WORD_Y - 6, "HI-SCORE", 0); + mText.white->writeCentered(PLAY_AREA_CENTER_THIRD_QUARTER_X, HISCORE_NUMBER_Y - 6, updateScoreText(mGame.hiScore), 0); + + // MULT + mText.white->writeColored(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y - 6, "MULT", 0, 255, 0); + mText.whiteX2->writeShadowed(PLAY_AREA_CENTER_X - 16, SCORE_WORD_Y + 5, std::to_string(mGame.player->getScoreMultiplier()).substr(0, 1), 0, 192, 0); + mText.white->writeShadowed(PLAY_AREA_CENTER_X - 2, SCORE_WORD_Y + 12, ".", 0, 192, 0); + mText.white->writeShadowed(PLAY_AREA_CENTER_X + 4, SCORE_WORD_Y + 12, std::to_string(mGame.player->getScoreMultiplier()).substr(2, 1), 0, 192, 0); + + // STAGE + mText.white->writeCentered(PLAY_AREA_CENTER_FIRST_QUARTER_X, SCORE_NUMBER_Y + 4, "STAGE " + std::to_string(mGame.stage[mGame.currentStage].number), 0); + + // POWER + mGame.powerMeter->setSpriteClip(256, 184, 40, 8); + mGame.powerMeter->render(); + float percent = (mGame.stage[mGame.currentStage].currentPower * 40.0f) / mGame.stage[mGame.currentStage].powerToComplete; + mGame.powerMeter->setSpriteClip(296, 184, (int)percent, 8); + mGame.powerMeter->render(); + } + else + { + mText.nokia->writeCentered(SCREEN_FIRST_QUARTER_X, SCORE_WORD_Y - 3, "A game by", -1); + mText.nokia->writeCentered(SCREEN_FIRST_QUARTER_X, SCORE_NUMBER_Y, "JailDesigner", -1); + mText.nokia->writeCentered(SCREEN_THIRD_QUARTER_X, HISCORE_WORD_Y - 3, "Intro music by", -1); + mText.nokia->writeCentered(SCREEN_THIRD_QUARTER_X, HISCORE_NUMBER_Y, "JailDoctor", -1); + } } -// Actualiza el valor de la variable mGame.stage +// Actualiza las variables del jugador +void GameDirector::updatePlayer() +{ + mGame.player->update(); + + // Comprueba la colisión entre el jugador y los globos + if (checkPlayerBalloonCollision()) + { + if (mGame.player->isAlive()) + { + if (mDemo.enabled) + setProgSection(PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS); + else + killPlayer(); + } + } +} + +// Actualiza las variables de la fase void GameDirector::updateStage() { - Uint8 realStage; - realStage = (mGame.score / 10000) + 1; - if (realStage > mGame.stage) + if (mGame.stage[mGame.currentStage].currentPower >= mGame.stage[mGame.currentStage].powerToComplete) { - mGame.stage = realStage; + // Cambio de fase + mGame.currentStage++; JA_PlaySound(mSound[SOUND_STAGE_CHANGE].sound); - mGame.stageCounter = 0; + mGame.stageBitmapCounter = 0; + mGame.enemySpeed = BALLOON_SPEED_1; + setBalloonSpeed(mGame.enemySpeed); + mGame.effect.flash = true; + mGame.effect.shake = true; } - if (mGame.stageCounter < STAGE_COUNTER) + if (mGame.stageBitmapCounter < STAGE_COUNTER) { - mGame.stageCounter++; + mGame.stageBitmapCounter++; } } @@ -1686,16 +2529,11 @@ void GameDirector::renderDeathFade() } } -// Mueve todos los globos activos -void GameDirector::moveBalloons() +// Actualiza los globos +void GameDirector::updateBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - if (mGame.balloon[i]->isActive()) - { - mGame.balloon[i]->update(); - } - } + mGame.balloon[i]->update(); } // Pinta en pantalla todos los globos activos @@ -1703,59 +2541,133 @@ void GameDirector::renderBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if (mGame.balloon[i]->isActive()) - { - mGame.balloon[i]->render(); - if ((mProg.debug) && (mGame.balloon[i]->isPopping() == false)) - mText.white->write(mGame.balloon[i]->getPosX() + (mGame.balloon[i]->getWidth() / 2) - 4, mGame.balloon[i]->getPosY() - 8, std::to_string(i)); - } + mGame.balloon[i]->render(); + if ((mDebug.enabled) && (mGame.balloon[i]->isPopping() == false)) + mText.white->writeCentered(mGame.balloon[i]->getPosX() + (mGame.balloon[i]->getWidth() / 2), mGame.balloon[i]->getPosY() - 8, std::to_string(i), 0); } } // Devuelve el primer indice no activo del vector de globos Uint8 GameDirector::getBalloonFreeIndex() { - int index = 0; - + Uint8 index = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - if (mGame.balloon[i]->isActive() == false) + if (!mGame.balloon[i]->isEnabled()) { index = i; break; } - } - return index; } // Crea un globo nuevo en el vector de globos -Uint8 GameDirector::createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture) +Uint8 GameDirector::createNewBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 creationtimer, LTexture *texture) { Uint8 index = getBalloonFreeIndex(); - mGame.balloon[index]->init(x, y, kind, velx, creationtimer, texture, mRenderer); + mGame.balloon[index]->init(x, y, kind, velx, speed, creationtimer, texture, mRenderer); return index; } +// Crea una PowerBall +void GameDirector::createPowerBall() +{ + const int y4 = (PLAY_AREA_TOP - BLOCK); + const int x4_0 = PLAY_AREA_LEFT; + const int x4_50 = PLAY_AREA_CENTER_X - (BALLOON_SIZE_4 / 2); + const int x4_100 = (PLAY_AREA_RIGHT)-BALLOON_SIZE_4; + const int x[3] = {x4_0, x4_50, x4_100}; + int x4 = x[rand() % 3]; + + mGame.balloon[getBalloonFreeIndex()]->init(x4, y4, POWER_BALL, BALLOON_VELX_POSITIVE * (((rand() % 2) * 2) - 1), mGame.enemySpeed, 350, mTexture[TEXTURE_BALLOON].texture, mRenderer); +} + // Establece a cero todos los valores del vector de objetos globo void GameDirector::resetBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - mGame.balloon[i]->erase(); + mGame.balloon[i]->disable(); + } +} + +// Establece la velocidad de los globos +void GameDirector::setBalloonSpeed(float speed) +{ + for (Uint8 i = 0; i < MAX_BALLOONS; i++) + { + if (mGame.balloon[i]->isEnabled()) + mGame.balloon[i]->setSpeed(speed); + } +} + +// Incrementa la velocidad de los globos +void GameDirector::incBalloonSpeed() +{ + if (mGame.enemySpeed == BALLOON_SPEED_1) + mGame.enemySpeed = BALLOON_SPEED_2; + else if (mGame.enemySpeed == BALLOON_SPEED_2) + mGame.enemySpeed = BALLOON_SPEED_3; + else if (mGame.enemySpeed == BALLOON_SPEED_3) + mGame.enemySpeed = BALLOON_SPEED_4; + else if (mGame.enemySpeed == BALLOON_SPEED_4) + mGame.enemySpeed = BALLOON_SPEED_5; + setBalloonSpeed(mGame.enemySpeed); +} + +// Decrementa la velocidad de los globos +void GameDirector::decBalloonSpeed() +{ + if (mGame.enemySpeed == BALLOON_SPEED_5) + mGame.enemySpeed = BALLOON_SPEED_4; + else if (mGame.enemySpeed == BALLOON_SPEED_4) + mGame.enemySpeed = BALLOON_SPEED_3; + else if (mGame.enemySpeed == BALLOON_SPEED_3) + mGame.enemySpeed = BALLOON_SPEED_2; + else if (mGame.enemySpeed == BALLOON_SPEED_2) + mGame.enemySpeed = BALLOON_SPEED_1; + setBalloonSpeed(mGame.enemySpeed); +} + +// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase +void GameDirector::updateBalloonSpeed() +{ + float percent = (float)mGame.stage[mGame.currentStage].currentPower / (float)mGame.stage[mGame.currentStage].powerToComplete; + if (mGame.enemySpeed == BALLOON_SPEED_1) + { + if (percent > 0.2f) + incBalloonSpeed(); + } + else if (mGame.enemySpeed == BALLOON_SPEED_2) + { + if (percent > 0.4f) + incBalloonSpeed(); + } + else if (mGame.enemySpeed == BALLOON_SPEED_3) + { + if (percent > 0.6f) + incBalloonSpeed(); + } + else if (mGame.enemySpeed == BALLOON_SPEED_4) + { + if (percent > 0.8f) + incBalloonSpeed(); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso void GameDirector::popBalloon(Uint8 index) { - if (mGame.balloon[index]->isActive()) + //if (mGame.balloon[index]->isActive()) { // Otorga los puntos correspondientes al globo mGame.player->addScore(Uint32(mGame.balloon[index]->getScore() * mGame.player->getScoreMultiplier())); setScore(mGame.player->getScore()); updateHiScore(); + // Aumenta el poder de la fase + increaseStageCurrentPower(); + mGame.balloonsPopped++; + Uint8 kind = mGame.balloon[index]->getKind(); Uint8 freeIndex = 0; switch (kind) @@ -1765,29 +2677,62 @@ void GameDirector::popBalloon(Uint8 index) mGame.balloon[index]->pop(); break; + case HEXAGON_1: + mGame.balloon[index]->pop(); + break; + + // Si es del tipo PowerBall, destruye todos los globos + case POWER_BALL: + destroyAllBalloons(); + break; + // En cualquier otro caso, crea dos globos de un tipo inferior default: freeIndex = getBalloonFreeIndex(); - mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_NEGATIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); + mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_NEGATIVE, mGame.enemySpeed, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mGame.balloon[freeIndex]->allignTo(mGame.balloon[index]->getPosX() + (mGame.balloon[index]->getWidth() / 2)); - mGame.balloon[freeIndex]->setVelY(-2.5); - // if (isTimeStopped()) mGame.balloon[freeIndex]->setStop(true); + if (mGame.balloon[freeIndex]->getClass() == BALLOON_CLASS) + mGame.balloon[freeIndex]->setVelY(-2.50f); + else if (mGame.balloon[freeIndex]->getClass() == HEXAGON_CLASS) + mGame.balloon[freeIndex]->setVelY(BALLOON_VELX_NEGATIVE); freeIndex = getBalloonFreeIndex(); - mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); + mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_POSITIVE, mGame.enemySpeed, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); mGame.balloon[freeIndex]->allignTo(mGame.balloon[index]->getPosX() + (mGame.balloon[index]->getWidth() / 2)); - mGame.balloon[freeIndex]->setVelY(-2.5); - // if (isTimeStopped()) mGame.balloon[freeIndex]->setStop(true); + if (mGame.balloon[freeIndex]->getClass() == BALLOON_CLASS) + mGame.balloon[freeIndex]->setVelY(-2.50f); + else if (mGame.balloon[freeIndex]->getClass() == HEXAGON_CLASS) + mGame.balloon[freeIndex]->setVelY(BALLOON_VELX_NEGATIVE); // Elimina el globo mGame.balloon[index]->pop(); break; } - // Reproduce el sonido de explotar el globo - // JA_PlaySound(mFX.popBalloon); // Recalcula el nivel de amenaza - setMenaceLevel(); + setMenace(); + } +} + +// Explosiona un globo. Lo destruye +void GameDirector::destroyBalloon(Uint8 index) +{ + //if (mGame.balloon[index]->isActive()) + { + // Otorga los puntos correspondientes al globo + mGame.player->addScore(Uint32(mGame.balloon[index]->getScore() * mGame.player->getScoreMultiplier())); + setScore(mGame.player->getScore()); + updateHiScore(); + + // Aumenta el poder de la fase + increaseStageCurrentPower(); + mGame.balloonsPopped++; + + // Destruye el globo + mGame.balloon[index]->pop(); + + // Recalcula el nivel de amenaza + setMenace(); } } @@ -1798,50 +2743,59 @@ void GameDirector::popAllBalloons() Uint8 j = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if ((mGame.balloon[i]->isActive()) && (mGame.balloon[i]->isPopping() == false) && (mGame.balloon[i]->isBeingCreated() == false)) - { + if ((mGame.balloon[i]->isEnabled()) && (!mGame.balloon[i]->isPopping()) && (!mGame.balloon[i]->isBeingCreated())) candidate[j] = i; - } else - { candidate[j] = -1; - } - j += 1; + j++; } for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { if (candidate[i] >= 0) - { popBalloon(i); - } - } JA_PlaySound(mSound[SOUND_BALLOON].sound); } +// Destruye todos los globos +void GameDirector::destroyAllBalloons() +{ + int candidate[MAX_BALLOONS]; + Uint8 j = 0; + for (Uint8 i = 0; i < MAX_BALLOONS; i++) + { + if ((mGame.balloon[i]->isEnabled()) && (!mGame.balloon[i]->isPopping()) && (!mGame.balloon[i]->isBeingCreated())) + candidate[j] = i; + else + candidate[j] = -1; + j++; + } + for (Uint8 i = 0; i < MAX_BALLOONS; i++) + if (candidate[i] >= 0) + destroyBalloon(i); + JA_PlaySound(mSound[SOUND_BALLOON].sound); + mGame.effect.flash = true; + mGame.effect.shake = true; +} + // Detiene todos los globos void GameDirector::stopAllBalloons(Uint16 time) { for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - if (mGame.balloon[i]->isActive()) + if (mGame.balloon[i]->isEnabled()) { mGame.balloon[i]->setStop(true); mGame.balloon[i]->setStoppedTimer(time); } - } } // Pone en marcha todos los globos void GameDirector::startAllBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - if ((mGame.balloon[i]->isActive()) && (mGame.balloon[i]->isBeingCreated() == false)) + if ((mGame.balloon[i]->isEnabled()) && (!mGame.balloon[i]->isBeingCreated())) { mGame.balloon[i]->setStop(false); mGame.balloon[i]->setStoppedTimer(0); } - } } // Obtiene el numero de globos activos @@ -1849,12 +2803,9 @@ Uint8 GameDirector::countBalloons() { Uint8 num = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - if (mGame.balloon[i]->isActive()) - { - ++num; - } - } + if (mGame.balloon[i]->isEnabled()) + if (!mGame.balloon[i]->isPopping()) + num++; return num; } @@ -1865,15 +2816,12 @@ bool GameDirector::checkPlayerBalloonCollision() bool result = false; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - //if ((mGame.balloon[i]->isActive()) && (mGame.balloon[i]->isStopped() == false)) - if ((mGame.balloon[i]->isActive()) && !(mGame.balloon[i]->isStopped()) && !(mGame.balloon[i]->isInvulnerable())) - { + if ((mGame.balloon[i]->isEnabled()) && !(mGame.balloon[i]->isStopped()) && !(mGame.balloon[i]->isInvulnerable())) if (checkCollision(mGame.player->getCollider(), mGame.balloon[i]->getCollider())) { result = true; break; } - } } return result; } @@ -1894,21 +2842,21 @@ void GameDirector::checkPlayerItemCollision() mGame.player->addScore(1000); setScore(mGame.player->getScore()); updateHiScore(); - createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._1000Bitmap->getWidth() / 2), mGame.item[i]->getPosY(), mGame._1000Bitmap); + createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._1000Bitmap->getWidth() / 2), mGame.player->getPosY(), mGame._1000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_2_GAVINA: mGame.player->addScore(2500); setScore(mGame.player->getScore()); updateHiScore(); - createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._2500Bitmap->getWidth() / 2), mGame.item[i]->getPosY(), mGame._2500Bitmap); + createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._2500Bitmap->getWidth() / 2), mGame.player->getPosY(), mGame._2500Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_3_PACMAR: mGame.player->addScore(5000); setScore(mGame.player->getScore()); updateHiScore(); - createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._5000Bitmap->getWidth() / 2), mGame.item[i]->getPosY(), mGame._5000Bitmap); + createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._5000Bitmap->getWidth() / 2), mGame.player->getPosY(), mGame._5000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_CLOCK: @@ -1938,30 +2886,31 @@ void GameDirector::checkPlayerItemCollision() void GameDirector::checkBulletBalloonCollision() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { for (Uint8 j = 0; j < MAX_BULLETS; j++) - { - if (mGame.balloon[i]->isActive() && !(mGame.balloon[i]->isInvulnerable()) && mGame.bullet[j]->isActive()) - { + if (mGame.balloon[i]->isEnabled() && (!mGame.balloon[i]->isInvulnerable()) && mGame.bullet[j]->isActive()) if (checkCollision(mGame.balloon[i]->getCollider(), mGame.bullet[j]->getCollider())) { mGame.player->incScoreMultiplier(); popBalloon(i); - if (mDemo.enabled == false) + if (!mDemo.enabled) JA_PlaySound(mSound[SOUND_BALLOON].sound); mGame.bullet[j]->erase(); - setMenaceLevel(); + setMenace(); Uint8 droppeditem = dropItem(); - if ((droppeditem != NO_KIND) && (mDemo.enabled == false) && (mDemo.recording == false)) + if ((droppeditem != NO_KIND) && !(mDemo.enabled) && !(mDemo.recording)) { - createItem(mGame.balloon[i]->getPosX(), mGame.balloon[i]->getPosY(), droppeditem); - JA_PlaySound(mSound[SOUND_ITEM_DROP].sound); + if (droppeditem == ITEM_POWER_BALL) + { + createPowerBall(); + } + else + { + createItem(mGame.balloon[i]->getPosX(), mGame.balloon[i]->getPosY(), droppeditem); + JA_PlaySound(mSound[SOUND_ITEM_DROP].sound); + } } break; } - } - } - } } // Mueve las balas activas @@ -2070,46 +3019,40 @@ void GameDirector::resetItems() // Devuelve un item en función del azar Uint8 GameDirector::dropItem() { - // return ITEM_COFFEE; + //return ITEM_COFFEE; + Uint8 luckyNumber = rand() % 99; - Uint8 item = rand() % 6; + Uint8 item = rand() % 7; switch (item) { case 0: if (luckyNumber < 10) - { return ITEM_POINTS_1_DISK; - } break; case 1: if (luckyNumber < 6) - { return ITEM_POINTS_2_GAVINA; - } break; case 2: if (luckyNumber < 3) - { return ITEM_POINTS_3_PACMAR; - } break; case 3: if (luckyNumber < 5) - { return ITEM_CLOCK; - } break; case 4: if (luckyNumber < 5) - { - return ITEM_TNT; - } + return NO_KIND; break; case 5: if (luckyNumber < 5) - { return ITEM_COFFEE; - } + break; + case 6: + if (luckyNumber < 5) + if (countBalloons() > 10) + return ITEM_POWER_BALL; break; default: break; @@ -2136,6 +3079,36 @@ void GameDirector::createItemScoreSprite(int x, int y, SmartSprite *sprite) mGame.smartSprite[index]->setEnabledTimer(100); } +// Crea un objeto de bonus en función del azar +void GameDirector::dropBonus() +{ +} + +// Dibuja el efecto de flash +void GameDirector::renderFlashEffect() +{ + if (mGame.effect.flash) + { + // Pantallazo blanco + SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_RenderClear(mRenderer); + + mGame.effect.flash = false; + } +} + +// Actualiza el efecto de agitar la pantalla +void GameDirector::updateShakeEffect() +{ + if (mGame.effect.shake) + { + if (mGame.effect.shakeCounter > 0) + mGame.effect.shakeCounter--; + else + mGame.effect.shake = false; + } +} + // Crea un SmartSprite para arrojar el item café al recibir un impacto void GameDirector::throwCoffee(int x, int y) { @@ -2278,22 +3251,25 @@ void GameDirector::createCoffeDrops(Uint8 num, int x, int y) // Acciones a realizar cuando el jugador muere void GameDirector::killPlayer() { - if (mGame.player->hasExtraHit()) + if (!mGame.player->isInvulnerable()) { - mGame.player->removeExtraHit(); - throwCoffee(mGame.player->getPosX() + (mGame.player->getWidth() / 2), mGame.player->getPosY() + (mGame.player->getHeight() / 2)); - JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); - } - else if (!mGame.player->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(mGame.player->getPosX(), mGame.player->getPosY()); - mGame.player->setAlive(false); + if (mGame.player->hasExtraHit()) + { + mGame.player->removeExtraHit(); + throwCoffee(mGame.player->getPosX() + (mGame.player->getWidth() / 2), mGame.player->getPosY() + (mGame.player->getHeight() / 2)); + JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); + } + else + { + stopAllBalloons(10); // 5000 + JA_StopMusic(); + JA_PlaySound(mSound[SOUND_PLAYER_COLLISION].sound); + shakeScreen(); + SDL_Delay(500); // 1000 + JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); + throwPlayer(mGame.player->getPosX(), mGame.player->getPosY()); + mGame.player->setAlive(false); + } } } @@ -2304,19 +3280,18 @@ Uint8 GameDirector::getSubsection() } // Calcula y establece el valor de amenaza en funcion de los globos activos -void GameDirector::setMenaceLevel() +void GameDirector::setMenace() { - mGame.menaceLevelCurrent = 0; + mGame.menaceCurrent = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - mGame.menaceLevelCurrent += mGame.balloon[i]->getMenace(); - } + if (mGame.balloon[i]->isEnabled()) + mGame.menaceCurrent += mGame.balloon[i]->getMenace(); } // Obtiene el valor de la variable -Uint8 GameDirector::getMenaceLevel() +Uint8 GameDirector::getMenace() { - return mGame.menaceLevelCurrent; + return mGame.menaceCurrent; } // Establece el valor de la variable @@ -2396,62 +3371,60 @@ void GameDirector::updateRemainingExplosionsCounter() } } +// Actualiza la variable EnemyDeployCounter +void GameDirector::updateEnemyDeployCounter() +{ + if (mGame.enemyDeployCounter > 0) + mGame.enemyDeployCounter--; +} + // Actualiza el campo de juego void GameDirector::updatePlayField() { - // Comprueba el teclado/mando - checkGameInput(); + // Comprueba el teclado/mando + checkGameInput(); - // Actualiza el jugador - mGame.player->update(); + // Actualiza las variables del jugador + updatePlayer(); - // Actualiza el fondo - updateBackground(); + // Actualiza el fondo + updateBackground(); - // Mueve los globos - moveBalloons(); + // Mueve los globos + updateBalloons(); - // Mueve las balas - moveBullets(); + // Mueve las balas + moveBullets(); - // Actualiza los items - updateItems(); + // Actualiza los items + updateItems(); - // Actualiza el valor de mGame.stage - updateStage(); + // Actualiza el valor de mGame.currentStage + updateStage(); - // Actualiza el estado de muerte - updateDeath(); + // Actualiza el estado de muerte + updateDeath(); - // Actualiza los SmartSprites - updateSmartSprites(); + // Actualiza los SmartSprites + updateSmartSprites(); - // Actualiza los contadores de estado - updateTimeStoppedCounter(); - updateRemainingExplosionsCounter(); + // Actualiza los contadores de estado y efectos + updateTimeStoppedCounter(); + updateRemainingExplosionsCounter(); + updateEnemyDeployCounter(); + updateShakeEffect(); - // Comprueba las colisiones entre globos y balas - checkBulletBalloonCollision(); + // Comprueba las colisiones entre globos y balas + checkBulletBalloonCollision(); - // Comprueba las colisiones entre elk jugador y los items - checkPlayerItemCollision(); + // Comprueba las colisiones entre elk jugador y los items + checkPlayerItemCollision(); - // Comprueba el nivel de amenaza - updateMenaceLevel(); + // Comprueba el nivel de amenaza + updateMenace(); - // Comprueba la colisión entre el jugador y los globos - if (checkPlayerBalloonCollision()) - { - if (mGame.player->isAlive()) - { - if (mDemo.enabled) - setProgSection(PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS); - else - killPlayer(); - } - } - // Actualiza el mensaje de GetReady - mGame.getReadyBitmap->update(); + // Actualiza la velocidad de los enemigos + updateBalloonSpeed(); } // Actualiza el fondo @@ -2462,48 +3435,44 @@ void GameDirector::updateBackground() mGame.clouds2a->move(); mGame.clouds2b->move(); if (mGame.clouds1a->getPosX() < -mGame.clouds1a->getWidth()) - { mGame.clouds1a->setPosX(mGame.clouds1a->getWidth()); - } if (mGame.clouds1b->getPosX() < -mGame.clouds1b->getWidth()) - { mGame.clouds1b->setPosX(mGame.clouds1b->getWidth()); - } if (mGame.clouds2a->getPosX() < -mGame.clouds2a->getWidth()) - { mGame.clouds2a->setPosX(mGame.clouds2a->getWidth()); - } if (mGame.clouds2b->getPosX() < -mGame.clouds2b->getWidth()) - { mGame.clouds2b->setPosX(mGame.clouds2b->getWidth()); - } mGame.grass->setSpriteClip(256, 85 + (6 * (mGame.counter / 20 % 2)), SCREEN_WIDTH, 6); + if (mGame.effect.shake) + mGame.background->setPosX(((mGame.effect.shakeCounter % 2) * 2) - 1); + else + mGame.background->setPosX(0); } // Dibuja el fondo void GameDirector::renderBackground() { - int alpha = (255 - (mGame.score / 392)); + float gradientNumber = std::min(((float)mGame.balloonsPopped / 900.0f), 3.0f); + float percent = gradientNumber - (int)gradientNumber; + int alpha = std::max((255 - (int)(255 * percent)), 0); - if (alpha < 0) - { - alpha = 0; - } - - mGame.gradient->setSpriteClip(SCREEN_WIDTH, 192, SCREEN_WIDTH, SCREEN_HEIGHT); - mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); + // Dibuja el gradiente 2 + mGame.gradient->setSpriteClip(mGame.gradientRect[((int)gradientNumber + 1) % 4]); mGame.gradient->render(); - mGame.gradient->setSpriteClip(0, 192, SCREEN_WIDTH, SCREEN_HEIGHT); + // Dibuja el gradiente 1 con una opacidad cada vez menor + mGame.gradient->setSpriteClip(mGame.gradientRect[(int)gradientNumber]); mTexture[TEXTURE_GAME_BG].texture->setAlpha(alpha); mGame.gradient->render(); - mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); + mGame.clouds1a->render(); mGame.clouds1b->render(); mGame.clouds2a->render(); mGame.clouds2b->render(); + mGame.background->render(); + mGame.grass->render(); } @@ -2516,30 +3485,20 @@ void GameDirector::renderPlayField() renderItems(); renderSmartSprites(); mGame.player->render(); - renderDeathFade(); - if (!mDemo.enabled) - { - 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(); + renderDeathFade(); + renderScoreBoard(); + renderFlashEffect(); } // Gestiona el nivel de amenaza -void GameDirector::updateMenaceLevel() +void GameDirector::old_updateMenace() { // Aumenta el nivel de amenaza en función de la puntuación - mGame.menaceLevelThreshold = 7 + (4 * (mGame.score / 10000)); + mGame.menaceThreshold = 7 + (4 * (mGame.score / 10000)); // Si el nivel de amenza es inferior al umbral - if (mGame.menaceLevelCurrent < mGame.menaceLevelThreshold) + if (mGame.menaceCurrent < mGame.menaceThreshold) { Uint8 index = 0; @@ -2549,16 +3508,36 @@ void GameDirector::updateMenaceLevel() // Crea un globo sobre el jugador en dirección hacia el centro if (x < (PLAY_AREA_WIDTH / 2)) { - index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_POSITIVE, 400, mTexture[TEXTURE_BALLOON].texture); + index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_POSITIVE, mGame.enemySpeed, 400, mTexture[TEXTURE_BALLOON].texture); } else { - index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_NEGATIVE, 400, mTexture[TEXTURE_BALLOON].texture); + index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_NEGATIVE, mGame.enemySpeed, 400, mTexture[TEXTURE_BALLOON].texture); } mGame.balloon[index]->allignTo(x); // Recalcula el nivel de amenaza con el nuevo globo - setMenaceLevel(); + setMenace(); + } +} + +// Gestiona el nivel de amenaza +void GameDirector::updateMenace() +{ + float percent = mGame.stage[mGame.currentStage].currentPower / mGame.stage[mGame.currentStage].powerToComplete; + Uint8 difference = mGame.stage[mGame.currentStage].maxMenace - mGame.stage[mGame.currentStage].minMenace; + + // Aumenta el nivel de amenaza en función de la puntuación + mGame.menaceThreshold = mGame.stage[mGame.currentStage].minMenace + (difference * percent); + + // Si el nivel de amenza es inferior al umbral + if (mGame.menaceCurrent < mGame.menaceThreshold) + { + // Crea una formación de enemigos + deployEnemyFormation(); + + // Recalcula el nivel de amenaza con el nuevo globo + setMenace(); } } @@ -2992,8 +3971,12 @@ void GameDirector::renderFade(Uint8 index) // Pinta diferentes mensajes en la pantalla void GameDirector::renderMessages() { - // GETREADY! - mGame.getReadyBitmap->render(); + // GetReady + if (mGame.counter < STAGE_COUNTER) + { + mGame.getReadyBitmap->setPosX((int)mGame.getReadyBitmapPath[mGame.counter]); + mGame.getReadyBitmap->render(); + } // Time Stopped if (mGame.timeStopped) @@ -3016,10 +3999,10 @@ void GameDirector::renderMessages() } // STAGE NUMBER - if (mGame.stageCounter < STAGE_COUNTER) + if (mGame.stageBitmapCounter < STAGE_COUNTER) { - mText.blackX2->writeCentered(PLAY_AREA_CENTER_X + 2, mGame.stagePath[mGame.stageCounter] + 2, "STAGE " + std::to_string(mGame.stage)); - mText.whiteX2->writeCentered(PLAY_AREA_CENTER_X, mGame.stagePath[mGame.stageCounter], "STAGE " + std::to_string(mGame.stage)); + mText.blackX2->writeCentered(PLAY_AREA_CENTER_X + 2, mGame.stageBitmapPath[mGame.stageBitmapCounter] + 2, "STAGE " + std::to_string(mGame.stage[mGame.currentStage].number)); + mText.whiteX2->writeCentered(PLAY_AREA_CENTER_X, mGame.stageBitmapPath[mGame.stageBitmapCounter], "STAGE " + std::to_string(mGame.stage[mGame.currentStage].number)); } } @@ -3130,6 +4113,44 @@ void GameDirector::shakeScreen() } } +// Agita la pantalla +void GameDirector::shakeScreen2() +{ + int v[] = {-1, 1, -1, 1, -1, 1, -1, 0}; + for (Uint8 n = 0; n < 8; n++) + { + // Limpia la pantalla + SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); + SDL_RenderClear(mRenderer); + + // Dibuja los objetos + mGame.background->setPosX(0); + mGame.background->setWidth(1); + mGame.background->setSpriteClip(0, 0, 1, 192); + renderBackground(); + + mGame.background->setPosX(255); + mGame.background->setSpriteClip(255, 0, 1, 192); + mGame.background->render(); + + mGame.background->setPosX(v[n]); + mGame.background->setWidth(256); + mGame.background->setSpriteClip(0, 0, 256, 192); + mGame.background->render(); + + mGame.grass->render(); + renderBalloons(); + renderBullets(); + renderItems(); + mGame.player->render(); + renderScoreBoard(); + + // Actualiza la pantalla + SDL_RenderPresent(mRenderer); + SDL_Delay(50); + } +} + // Bucle para el logo del juego void GameDirector::runLogo() { @@ -3689,10 +4710,6 @@ void GameDirector::runTitle(Uint8 subsection) mMenu.active->reset(); toogleTitleNextGS(); renderFade(1); - if (mProg.section == PROG_SECTION_INTRO) - { - JA_StopMusic(); - } enableDemoMode(); } } @@ -3751,16 +4768,48 @@ void GameDirector::runGame() } else if ((mEventHandler->type == SDL_KEYDOWN) && (mEventHandler->key.repeat == 0)) { - switch (mEventHandler->key.keysym.scancode) + if (mDebug.enabled) { - case SDL_SCANCODE_T: - mProg.ticksSpeed *= 2; - break; - case SDL_SCANCODE_G: - mProg.ticksSpeed /= 2; - break; - default: - break; + switch (mEventHandler->key.keysym.scancode) + { + case SDL_SCANCODE_F: + mDebug.enemySet--; + if (mDebug.enemySet == 255) + mDebug.enemySet = 0; + break; + case SDL_SCANCODE_R: + mDebug.enemySet = std::min((int)++mDebug.enemySet, 9); + break; + case SDL_SCANCODE_T: + mProg.ticksSpeed *= 2; + break; + case SDL_SCANCODE_G: + createNewBalloon(100, 0, BALLOON_1, BALLOON_VELX_POSITIVE, 1.0F, 0, mTexture[TEXTURE_BALLOON].texture); + break; + case SDL_SCANCODE_P: + popAllBalloons(); + //mGame.balloonsPopped += 10; + break; + case SDL_SCANCODE_Z: + incBalloonSpeed(); + break; + case SDL_SCANCODE_X: + decBalloonSpeed(); + break; + case SDL_SCANCODE_C: + mDebug.gradB += 10; + break; + case SDL_SCANCODE_I: + mGame.player->setInvulnerable(true); + mGame.player->setInvulnerableCounter(65000); + break; + case SDL_SCANCODE_M: + break; + case SDL_SCANCODE_N: + break; + default: + break; + } } } } @@ -3785,8 +4834,8 @@ void GameDirector::runGame() // Volca el contenido de la textura en el renderizador SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); + renderDebugInfo(); // Actualiza la pantalla - //if (mProg.section == PROG_SECTION_GAME) SDL_RenderPresent(mRenderer); } } @@ -3797,7 +4846,7 @@ void GameDirector::runGame() // Bucle para el menu de pausa del juego void GameDirector::runPausedGame() { - while ((mGame.section == GAME_SECTION_PAUSE) && (mProg.section == PROG_SECTION_GAME)) + while ((mGame.section == GAME_SECTION_PAUSE) && (mProg.section == PROG_SECTION_GAME) && (!exit())) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3884,7 +4933,7 @@ void GameDirector::runInstructions() SDL_Rect rect = {0, 0, 16, 16}; sprite->init(rect1, mTexture[TEXTURE_ITEMS].texture, mRenderer); - while (mTitle.section == TITLE_SECTION_INSTRUCTIONS) + while ((mTitle.section == TITLE_SECTION_INSTRUCTIONS) && (!exit())) { int y = 0; // Comprueba los eventos que hay en la cola @@ -3977,6 +5026,7 @@ void GameDirector::runInstructions() { mTitle.instructionsCounter = INSTRUCTIONS_COUNTER; mTitle.section = TITLE_SECTION_1; + mTitle.nextProgSection = PROG_SECTION_LOGO; } } delete sprite; @@ -3985,7 +5035,7 @@ void GameDirector::runInstructions() // Bucle para la pantalla de game over void GameDirector::runGameOverScreen() { - while ((mGame.section == GAME_SECTION_GAMEOVER) && (mProg.section == PROG_SECTION_GAME)) + while ((mGame.section == GAME_SECTION_GAMEOVER) && (mProg.section == PROG_SECTION_GAME) && (!exit())) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -4016,8 +5066,8 @@ void GameDirector::runGameOverScreen() 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.whiteX2->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), "GAME OVER", 0); + mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 1), "YOUR SCORE: " + std::to_string(mGame.player->getScore()), 0); mText.white->write(PLAY_AREA_CENTER_X - (mText.white->lenght("RETRY?", 0) / 2), PLAY_AREA_CENTER_Y + BLOCK * 2, "RETRY?", 0); mMenu.gameOver->render(); @@ -4063,43 +5113,47 @@ void GameDirector::runGameOverScreen() } } -// Dibuja la informacion de debug en pantalla -void GameDirector::renderDebugInfo() -{ - if (mProg.debug) - { - mText.white->write(0, 0 * BLOCK, "menace: " + std::to_string(mGame.menaceLevelThreshold) + "/" + std::to_string(mGame.menaceLevelCurrent)); - mText.white->write(0, 1 * BLOCK, "item[0].TTL: " + std::to_string(mGame.item[0]->mTimeToLive)); - mText.white->write(0, 2 * BLOCK, "mGame.stage: " + std::to_string(mGame.stage)); - mText.white->write(0, 3 * BLOCK, "mGame.counter : " + std::to_string(mGame.counter)); - mText.white->write(0, 4 * BLOCK, ": "); - mText.white->write(0, 5 * BLOCK, ": "); - } -} - // Activa el modo Demo void GameDirector::enableDemoMode() { mDemo.enabled = true; - //mGame.getReadyBitmap->setEnabled(false); } // Desactiva el modo Demo void GameDirector::disableDemoMode() { mDemo.enabled = false; - //mGame.getReadyBitmap->setEnabled(true); } // Actualiza el proximo estado del juego despues del titulo void GameDirector::toogleTitleNextGS() { if (mTitle.nextProgSection == PROG_SECTION_LOGO) - { mTitle.nextProgSection = PROG_SECTION_GAME; - } else - { mTitle.nextProgSection = PROG_SECTION_LOGO; +} + +// Dibuja la informacion de debug en pantalla +void GameDirector::renderDebugInfo() +{ + if (mDebug.enabled) + { + Uint8 R = 0xFF; + Uint8 G = 0x20; + Uint8 B = 0x20; + + SDL_RenderSetLogicalSize(mRenderer, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2); + + mText.white->writeShadowed(2, 2 + 0 * BLOCK, "menace(umb): " + std::to_string(mGame.menaceCurrent) + "(" + std::to_string(mGame.menaceThreshold) + ")", R, G, B); + mText.white->writeShadowed(2, 2 + 1 * BLOCK, "currentPower: " + std::to_string(mGame.stage[mGame.currentStage].currentPower), R, G, B); + mText.white->writeShadowed(2, 2 + 2 * BLOCK, "currentStage: " + std::to_string(mGame.currentStage), R, G, B); + mText.white->writeShadowed(2, 2 + 3 * BLOCK, "counter: " + std::to_string(mGame.counter), R, G, B); + mText.white->writeShadowed(2, 2 + 4 * BLOCK, "(R)enemyset: " + std::to_string(mDebug.enemySet), R, G, B); + mText.white->writeShadowed(2, 2 + 5 * BLOCK, "RGB: " + std::to_string(mDebug.gradR) + "," + std::to_string(mDebug.gradG) + "," + std::to_string(mDebug.gradB), R, G, B); + mText.white->writeShadowed(2, 2 + 6 * BLOCK, "(I)invuln : " + std::to_string(mGame.player->getInvulnerableCounter()), R, G, B); + mText.white->writeShadowed(2, 2 + 7 * BLOCK, "balloons: " + std::to_string(countBalloons()), R, G, B); + mText.white->writeShadowed(2, 2 + 8 * BLOCK, "balloonsPop: " + std::to_string(mGame.balloonsPopped), R, G, B); + mText.white->writeShadowed(2, 2 + 9 * BLOCK, "(Z-X)ballSped:" + std::to_string(mGame.enemySpeed), R, G, B); } } \ No newline at end of file diff --git a/source/gamedirector.h b/source/gamedirector.h index a096c68..42dae46 100644 --- a/source/gamedirector.h +++ b/source/gamedirector.h @@ -39,7 +39,7 @@ private: CoffeeDrop *mCoffeeDrop[MAX_COFFEE_DROPS]; // Vector con todas ls gotas de café; #endif - struct text + struct text_t { Text *white; // Texto blanco de 8x8 Text *whiteX2; // Texto blanco de 16x16 @@ -47,9 +47,9 @@ private: Text *blackX2; // Texto negro de 16x16 Text *nokia; // Texto de anchura variable y 10px de alto }; - text mText; // Variable con todos los objetos de texto + text_t mText; // Variable con todos los objetos de texto - struct menu + struct menu_t { Menu *title; // Menu de la pantalla de título Menu *pause; // Menú de la pantalla de pausa @@ -58,29 +58,70 @@ private: Menu *active; // Menu activo (de momento para la pantalla del titulo) bool keyPressed; // Variable para evitar la repetición de teclas en los menus }; - menu mMenu; // Variable con todos los objetos menus y sus variables + menu_t mMenu; // Variable con todos los objetos menus y sus variables - struct intro + struct intro_t { SmartSprite *bitmap[INTRO_TOTAL_BITMAPS]; // Vector con los sprites inteligentes para los dibujos de la intro Text2 *text[INTRO_TOTAL_TEXTS]; // Textos de la intro Uint8 events[INTRO_TOTAL_EVENTS]; // Vector para coordinar los eventos de la intro }; - intro mIntro; // Contiene todas las variables de la sección 'Intro' + intro_t mIntro; // Contiene todas las variables de la sección 'Intro' - struct game + struct enemyInits_t + { + int x; // Posición en el eje X donde crear al enemigo + int y; // Posición en el eje Y donde crear al enemigo + float velX; // Velocidad inicial en el eje X + Uint8 kind; // Tipo de enemigo + Uint16 creationCounter; // Temporizador para la creación del enemigo + }; + + struct enemyFormation_t // Contiene la información de una formación enemiga + { + Uint8 numberOfEnemies; // Cantidad de enemigos que forman la formación + enemyInits_t init[50]; // Vector con todas las inicializaciones de los enemigos de la formación + }; + enemyFormation_t mEnemyFormation[100]; // Vector con todas las formaciones enemigas + + struct enemyPool_t + { + enemyFormation_t *set[10]; // Conjunto de formaciones enemigas + }; + enemyPool_t mEnemyPool[10]; // Variable con los diferentes conjuntos de formaciones enemigas + + struct stage_t // Contiene todas las variables relacionadas con una fase + { + enemyPool_t *enemyPool; // El conjunto de formaciones enemigas de la fase + Uint16 currentPower; // Cantidad actual de poder + Uint16 powerToComplete; // Cantidad de poder que se necesita para completar la fase + Uint8 maxMenace; // Umbral máximo de amenaza de la fase + Uint8 minMenace; // Umbral mínimo de amenaza de la fase + Uint8 number; // Numero de fase + }; + + struct effect_t + { + bool flash; // Indica si se ha de pintar la pantalla de blanco + bool shake; // Indica si se ha de agitar la pantalla + Uint8 shakeCounter; // Contador para medir el tiempo que dura el efecto + }; + + struct game_t { Uint32 score; // Puntuación actual Uint32 hiScore; // Puntuación máxima Uint8 section; // Seccion actual dentro del juego bool hiScoreAchieved; // Indica si se ha superado la puntuación máxima - Uint8 stage; // Pantalla actual - Uint8 stageCounter; // Contador para el tiempo visible del texto de Stage - float stagePath[STAGE_COUNTER]; // Vector con los puntos Y por donde pasará la etiqueta + Uint8 currentStage; // Indica la fase actual + stage_t stage[10]; // Variable con los datos de cada pantalla + Uint8 stageBitmapCounter; // Contador para el tiempo visible del texto de Stage + float stageBitmapPath[STAGE_COUNTER]; // Vector con los puntos Y por donde se desplaza el texto + float getReadyBitmapPath[STAGE_COUNTER]; // Vector con los puntos X por donde se desplaza el texto Uint16 deathCounter; // Contador para la animación de muerte del jugador Uint8 deathIndex; // Indice del vector de smartsprites que contiene el sprite del jugador - Uint8 menaceLevelCurrent; // Nivel de amenaza actual - Uint8 menaceLevelThreshold; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el numero de globos + Uint8 menaceCurrent; // Nivel de amenaza actual + Uint8 menaceThreshold; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el numero de globos bool timeStopped; // Indica si el tiempo está detenido Uint16 timeStoppedCounter; // Temporizador para llevar la cuenta del tiempo detenido Uint8 remainingExplosions; // Cantidad de explosiones restantes @@ -88,26 +129,34 @@ private: bool explosionTime; // Indica si las explosiones estan en marcha Uint32 counter; // Contador para el juego Uint32 scoreDataFile[TOTAL_SCORE_DATA]; // Datos del fichero de puntos - SmartSprite *getReadyBitmap; // Sprite para el texto de GetReady del principio de la partida + Sprite *getReadyBitmap; // Sprite para el texto de GetReady del principio de la partida SmartSprite *_1000Bitmap; // Sprite con el texto 1.000 SmartSprite *_2500Bitmap; // Sprite con el texto 2.500 SmartSprite *_5000Bitmap; // Sprite con el texto 5.000 Sprite *background; // Sprite con los graficos frontales del fondo Sprite *gradient; // Sprite con los graficos del degradado de color de fondo + SDL_Rect gradientRect[4]; // Vector con las coordenadas de los 4 gradientes + Uint16 balloonsPopped; // Lleva la cuenta de los globos explotados MovingSprite *clouds1a; // Sprite para las nubes superiores MovingSprite *clouds1b; // Sprite para las nubes superiores MovingSprite *clouds2a; // Sprite para las nubes inferiores MovingSprite *clouds2b; // Sprite para las nubes inferiores Sprite *grass; // Sprite para la hierba + Sprite *scoreBoard; // Sprite para el fondo del marcador + Sprite *powerMeter; // Sprite para el medidor de poder de la fase Player *player; // El jugador Balloon *balloon[MAX_BALLOONS]; // Vector con los objetos globo Bullet *bullet[MAX_BULLETS]; // Vector con los objetos bala Item *item[MAX_ITEMS]; // Vector con los objetos item SmartSprite *smartSprite[MAX_SMART_SPRITES]; // Vector para almacenar y gestionar SmartSprites + Uint8 lastEnemyDeploy; // Guarda cual ha sido la última formación desplegada para no repetir; + Uint8 enemyDeployCounter; // Cuando se lanza una formación, se le da un valor y no sale otra hasta que llegue a cero + float enemySpeed; // Velocidad a la que se mueven los enemigos + effect_t effect; // Variable para gestionar los efectos visuales }; - game mGame; // Variable con todas las variables usadas durante el juego + game_t mGame; // Variable con todas las variables usadas durante el juego - struct title + struct title_t { Uint16 counter; // Temporizador para la pantalla de titulo Uint16 backgroundCounter; // Temporizador para el fondo de tiles de la pantalla de titulo @@ -124,19 +173,19 @@ private: AnimatedSprite *dustBitmapL; // Sprite con la el polvo que aparece al colisionar el texto de la pantalla de titulo AnimatedSprite *dustBitmapR; // Sprite con la el polvo que aparece al colisionar el texto de la pantalla de titulo }; - title mTitle; // Variable con todas las variables de la pantalla de titulo + title_t mTitle; // Variable con todas las variables de la pantalla de titulo - struct demo + struct demo_t { - bool enabled; // Indica si está activo el modo demo - bool recording; // Indica si está activado el modo para grabar la demo - Uint16 counter; // Contador para el modo demo - DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo - DemoKeys dataFile[TOTAL_DEMO_DATA]; // Datos del fichero con los movimientos para la demo + bool enabled; // Indica si está activo el modo demo + bool recording; // Indica si está activado el modo para grabar la demo + Uint16 counter; // Contador para el modo demo + demoKeys_t keys; // Variable con las pulsaciones de teclas del modo demo + demoKeys_t dataFile[TOTAL_DEMO_DATA]; // Datos del fichero con los movimientos para la demo }; - demo mDemo; // Variable con todas las variables relacionadas con el modo demo + demo_t mDemo; // Variable con todas las variables relacionadas con el modo demo - struct options + struct options_t { Uint32 fullScreenMode; // Contiene el valor del modo de pantalla completa Uint32 fullScreenModePrevious; @@ -144,55 +193,63 @@ private: Uint8 windowSizePrevious; bool displayCoffeeDrops; // Indica si se han de mostar las gotas de cafe }; - options mOptions; // Variable con todas las variables de las opciones del programa + options_t mOptions; // Variable con todas las variables de las opciones del programa - struct logo + struct logo_t { Uint16 counter; // Contador Sprite *sprite; // Sprite con la textura del logo }; - logo mLogo; // Variable con las variables para el logo + logo_t mLogo; // Variable con las variables para el logo - struct prog + struct prog_t { - bool debug; // Indica si se va a mostrar la información de debug bool quit; // Indica si hay que salir del programa - Input keyboard; // Almacena los códigos de teclado correspondientes - Input keyboardBuffer; // Buffer para teclas pulsadas + input_t keyboard; // Almacena los códigos de teclado correspondientes + input_t keyboardBuffer; // Buffer para teclas pulsadas std::string executablePath; // Path del ejecutable Uint32 ticks; // Contador de ticks para ajustar la velocidad del programa Uint8 section; // Sección actual del programa; Uint8 subsection; // Subseccion dentro de la sección; Uint8 ticksSpeed; // Velocidad a la que se repiten los bucles del programa }; - prog mProg; // Contiene todas las variables globales del programa + prog_t mProg; // Contiene todas las variables globales del programa - struct resourceBinFile + struct debug_t + { + bool enabled; // Indica si se va a mostrar la información de debug + Uint8 enemySet; // Escoge el set enemigo a generar + Uint8 gradR, gradG, gradB; // Colores RGB para modificar el color del gradiente de fondo + float hudW, hudH; // Multiplica el tamaño del hud de debug; + }; + debug_t mDebug; + + struct resourceBinFile_t { std::string file; // Ruta al fichero }; - resourceBinFile mBinFile[TOTAL_BINFILE]; // Todos los ficheros binarios + resourceBinFile_t mBinFile[TOTAL_BINFILE]; // Todos los ficheros binarios - struct resourceSound + struct resourceSound_t { std::string file; // Ruta al fichero JA_Sound sound; // Variable con el sonido }; - resourceSound mSound[TOTAL_SOUND]; // Todos los sonidos + resourceSound_t mSound[TOTAL_SOUND]; // Todos los sonidos - struct resourceMusic + struct resourceMusic_t { std::string file; // Ruta al fichero JA_Music music; // Variable con la música }; - resourceMusic mMusic[TOTAL_MUSIC]; // Todas las músicas + resourceMusic_t mMusic[TOTAL_MUSIC]; // Todas las músicas - struct resourceTexture + struct resourceTexture_t { std::string file; // Ruta al fichero LTexture *texture; // Variable con la textura }; - resourceTexture mTexture[TOTAL_TEXTURE]; // Todos los gráficos + resourceTexture_t mTexture[TOTAL_TEXTURE]; // Todos los gráficos public: // Constructor @@ -294,6 +351,21 @@ public: // Establece el valor de la variable void setExecutablePath(std::string path); + // Inicializa las formaciones enemigas + void initEnemyFormations(); + + // Inicializa los conjuntos de formaciones + void initEnemyPools(); + + // Inicializa las fases del juego + void initGameStages(); + + // Crea una formación de enemigos + void deployEnemyFormation(); + + // Aumenta el poder de la fase + void increaseStageCurrentPower(); + // Establece el valor de la variable void setScore(Uint32 score); @@ -309,7 +381,10 @@ public: // Pinta el marcador en pantalla usando un objeto texto void renderScoreBoard(); - // Actualiza el valor de la variable mStage + // Actualiza las variables del jugador + void updatePlayer(); + + // Actualiza las variables de la fase void updateStage(); // Actualiza el estado de muerte @@ -318,8 +393,8 @@ public: // Renderiza el fade final cuando se acaba la partida void renderDeathFade(); - // Mueve todos los globos activos - void moveBalloons(); + // Actualiza los globos + void updateBalloons(); // Pinta en pantalla todos los globos activos void renderBalloons(); @@ -328,17 +403,38 @@ public: Uint8 getBalloonFreeIndex(); // Crea un globo nuevo en el vector de globos - Uint8 createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 stoppedcounter, LTexture *texture); + Uint8 createNewBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 stoppedcounter, LTexture *texture); + + // Crea una PowerBall + void createPowerBall(); // Establece a cero todos los valores del vector de objetos globo void resetBalloons(); + // Establece la velocidad de los globos + void setBalloonSpeed(float speed); + + // Incrementa la velocidad de los globos + void incBalloonSpeed(); + + // Decrementa la velocidad de los globos + void decBalloonSpeed(); + + // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase + void updateBalloonSpeed(); + // Explosiona un globo. Lo destruye y crea otros dos si es el caso void popBalloon(Uint8 index); + // Explosiona un globo. Lo destruye + void destroyBalloon(Uint8 index); + // Explosiona todos los globos void popAllBalloons(); + // Destruye todos los globos + void destroyAllBalloons(); + // Detiene todos los globos void stopAllBalloons(Uint16 time); @@ -393,6 +489,15 @@ public: // Crea un objeto SmartSprite void createItemScoreSprite(int x, int y, SmartSprite *sprite); + // Crea un objeto de bonus en función del azar + void dropBonus(); + + // Dibuja el efecto de flash + void renderFlashEffect(); + + // Actualiza el efecto de agitar la pantalla + void updateShakeEffect(); + // Crea un SmartSprite para arrojar el item café al recibir un impacto void throwCoffee(int x, int y); @@ -434,10 +539,10 @@ public: Uint8 getSubsection(); // Calcula y establece el valor de amenaza en funcion de los globos activos - void setMenaceLevel(); + void setMenace(); // Obtiene el valor de la variable - Uint8 getMenaceLevel(); + Uint8 getMenace(); // Establece el valor de la variable void setTimeStopped(bool value); @@ -448,6 +553,9 @@ public: // Establece el valor de la variable void setTimeStoppedCounter(Uint16 value); + // Actualiza la variable EnemyDeployCounter + void updateEnemyDeployCounter(); + // Actualiza y comprueba el valor de la variable void updateTimeStoppedCounter(); @@ -464,7 +572,10 @@ public: void updateRemainingExplosionsCounter(); // Gestiona el nivel de amenaza - void updateMenaceLevel(); + void old_updateMenace(); + + // Gestiona el nivel de amenaza + void updateMenace(); // Actualiza el campo de juego void updatePlayField(); @@ -514,6 +625,9 @@ public: // Agita la pantalla void shakeScreen(); + // Agita la pantalla + void shakeScreen2(); + // Bucle para el logo del juego void runLogo(); @@ -535,9 +649,6 @@ public: // Bucle para la pantalla de game over void runGameOverScreen(); - // Dibuja la informacion de debug en pantalla - void renderDebugInfo(); - // Activa el modo Demo void enableDemoMode(); @@ -546,6 +657,9 @@ public: // Intercambia el proximo estado del juego despues del titulo void toogleTitleNextGS(); + + // Dibuja la informacion de debug en pantalla + void renderDebugInfo(); }; #endif diff --git a/source/item.cpp b/source/item.cpp index 427620f..c6ddf86 100644 --- a/source/item.cpp +++ b/source/item.cpp @@ -251,7 +251,7 @@ void Item::setEnabled(bool value) } // Obtiene el circulo de colisión -Circle &Item::getCollider() +circle_t &Item::getCollider() { return mCollider; } diff --git a/source/item.h b/source/item.h index b554a3f..0c38a48 100644 --- a/source/item.h +++ b/source/item.h @@ -62,7 +62,7 @@ public: void setEnabled(bool value); // Obtiene el circulo de colisión - Circle &getCollider(); + circle_t &getCollider(); // Temporizador con el tiempo que el objeto está presente Uint16 mTimeToLive; @@ -93,10 +93,8 @@ private: // Especifica si está habilitado el objeto bool mEnabled; - - // Circulo de colisión del objeto - Circle mCollider; + circle_t mCollider; // Alinea el circulo de colisión con la posición del objeto void shiftColliders(); diff --git a/source/player.cpp b/source/player.cpp index 3582683..836815d 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -56,7 +56,7 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, mBaseSpeed = 1.5; // Establece la puntuación inicial - mScore = 9500; + mScore = 0; // Establece el multiplicador de puntos inicial mScoreMultiplier = 1.0f; @@ -100,12 +100,6 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT, 4); mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_WALKING_STOP, 4); mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_FIRING_UP, 4); - mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, 4); - mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, 4); - mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, 4); - mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, 4); - mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, 4); - mSpriteBody->setAnimationNumFrames(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, 4); // Establece la velocidad de cada animación mSpriteLegs->setAnimationSpeed(PLAYER_ANIMATION_LEGS_WALKING_STOP, 10); @@ -118,12 +112,6 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_FIRING_RIGHT, 5); mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_WALKING_STOP, 10); mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_FIRING_UP, 5); - mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, 5); - mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, 5); - mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, 5); - mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, 5); - mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, 10); - mSpriteBody->setAnimationSpeed(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, 5); // Establece si la animación se reproduce en bucle mSpriteLegs->setAnimationLoop(PLAYER_ANIMATION_LEGS_WALKING_STOP, true); @@ -136,12 +124,6 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_FIRING_RIGHT, true); mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_WALKING_STOP, true); mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_FIRING_UP, true); - mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, true); - mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, true); - mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, true); - mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, true); - mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, true); - mSpriteBody->setAnimationLoop(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, true); // Establece los frames de cada animación mSpriteLegs->setAnimationFrames(PLAYER_ANIMATION_LEGS_WALKING_LEFT, 0, mWidth * 0, mHeight * 0, mWidth, mHeight); @@ -189,36 +171,6 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP, 2, mWidth * 2, mHeight * 5, mWidth, mHeight); mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP, 3, mWidth * 3, mHeight * 5, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, 0, mWidth * 0, mHeight * 6, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, 1, mWidth * 1, mHeight * 6, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, 2, mWidth * 2, mHeight * 6, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT, 3, mWidth * 3, mHeight * 6, mWidth, mHeight); - - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, 0, mWidth * 0, mHeight * 7, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, 1, mWidth * 1, mHeight * 7, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, 2, mWidth * 2, mHeight * 7, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT, 3, mWidth * 3, mHeight * 7, mWidth, mHeight); - - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, 0, mWidth * 0, mHeight * 8, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, 1, mWidth * 1, mHeight * 8, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, 2, mWidth * 2, mHeight * 8, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT, 3, mWidth * 3, mHeight * 8, mWidth, mHeight); - - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, 0, mWidth * 0, mHeight * 9, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, 1, mWidth * 1, mHeight * 9, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, 2, mWidth * 2, mHeight * 9, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT, 3, mWidth * 3, mHeight * 9, mWidth, mHeight); - - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, 0, mWidth * 0, mHeight * 10, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, 1, mWidth * 1, mHeight * 10, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, 2, mWidth * 2, mHeight * 10, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT, 3, mWidth * 3, mHeight * 10, mWidth, mHeight); - - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, 0, mWidth * 0, mHeight * 11, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, 1, mWidth * 1, mHeight * 11, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, 2, mWidth * 2, mHeight * 11, mWidth, mHeight); - mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT, 3, mWidth * 3, mHeight * 11, mWidth, mHeight); - // Selecciona un frame para pintar mSpriteLegs->setSpriteClip(mSpriteLegs->getAnimationClip(PLAYER_ANIMATION_LEGS_WALKING_STOP, 0)); mSpriteBody->setSpriteClip(mSpriteBody->getAnimationClip(PLAYER_ANIMATION_BODY_WALKING_STOP, 0)); @@ -231,71 +183,29 @@ void Player::setInput(Uint8 input) { case INPUT_LEFT: mVelX = -mBaseSpeed; - if (mExtraHit) - { - setWalkingStatus(PLAYER_STATUS_WALKING_LEFT); - } - else - { - setWalkingStatus(PLAYER_STATUS_WALKING_LEFT); - } + setWalkingStatus(PLAYER_STATUS_WALKING_LEFT); break; case INPUT_RIGHT: mVelX = mBaseSpeed; - if (mExtraHit) - { - setWalkingStatus(PLAYER_STATUS_WALKING_RIGHT); - } - else - { - setWalkingStatus(PLAYER_STATUS_WALKING_RIGHT); - } + setWalkingStatus(PLAYER_STATUS_WALKING_RIGHT); break; case INPUT_FIRE_UP: - if (mExtraHit) - { - setFiringStatus(PLAYER_STATUS_FIRING_UP); - } - else - { - setFiringStatus(PLAYER_STATUS_FIRING_UP); - } + setFiringStatus(PLAYER_STATUS_FIRING_UP); break; case INPUT_FIRE_LEFT: - if (mExtraHit) - { - setFiringStatus(PLAYER_STATUS_FIRING_LEFT); - } - else - { - setFiringStatus(PLAYER_STATUS_FIRING_LEFT); - } + setFiringStatus(PLAYER_STATUS_FIRING_LEFT); break; case INPUT_FIRE_RIGHT: - if (mExtraHit) - { - setFiringStatus(PLAYER_STATUS_FIRING_RIGHT); - } - else - { - setFiringStatus(PLAYER_STATUS_FIRING_RIGHT); - } + setFiringStatus(PLAYER_STATUS_FIRING_RIGHT); break; default: mVelX = 0; - if (mExtraHit) - { - setWalkingStatus(PLAYER_STATUS_WALKING_STOP); - } - else - { - setWalkingStatus(PLAYER_STATUS_WALKING_STOP); - } + setWalkingStatus(PLAYER_STATUS_WALKING_STOP); break; } } @@ -309,7 +219,7 @@ void Player::move() mPosX += mVelX; // Si el jugador abandona el area de juego por los laterales - if ((mPosX < PLAY_AREA_LEFT) || (mPosX + mWidth > PLAY_AREA_RIGHT)) + if ((mPosX < PLAY_AREA_LEFT - 5) || (mPosX + mWidth > PLAY_AREA_RIGHT + 5)) { // Restaura su posición mPosX -= mVelX; @@ -370,6 +280,17 @@ void Player::setFiringStatus(Uint8 status) // Establece la animación correspondiente al estado void Player::setAnimation() { + // Actualiza los frames de la animación en función del número de cafes + for (Uint8 i = 0; i < 4; i++) + { + mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_LEFT, i, mWidth * i, mHeight * (0 + (6 * mCoffees)), mWidth, mHeight); + mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_LEFT, i, mWidth * i, mHeight * (1 + (6 * mCoffees)), mWidth, mHeight); + mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_RIGHT, i, mWidth * i, mHeight * (2 + (6 * mCoffees)), mWidth, mHeight); + mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_RIGHT, i, mWidth * i, mHeight * (3 + (6 * mCoffees)), mWidth, mHeight); + mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_WALKING_STOP, i, mWidth * i, mHeight * (4 + (6 * mCoffees)), mWidth, mHeight); + mSpriteBody->setAnimationFrames(PLAYER_ANIMATION_BODY_FIRING_UP, i, mWidth * i, mHeight * (5 + (6 * mCoffees)), mWidth, mHeight); + } + switch (mStatusWalking) { case PLAYER_STATUS_WALKING_LEFT: @@ -377,51 +298,19 @@ void Player::setAnimation() switch (mStatusFiring) { case PLAYER_STATUS_FIRING_UP: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP); break; case PLAYER_STATUS_FIRING_LEFT: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT); break; case PLAYER_STATUS_FIRING_RIGHT: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT); break; case PLAYER_STATUS_FIRING_NO: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_LEFT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_LEFT); break; default: @@ -435,51 +324,19 @@ void Player::setAnimation() switch (mStatusFiring) { case PLAYER_STATUS_FIRING_UP: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP); break; case PLAYER_STATUS_FIRING_LEFT: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT); break; case PLAYER_STATUS_FIRING_RIGHT: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT); break; case PLAYER_STATUS_FIRING_NO: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_RIGHT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_RIGHT); break; default: @@ -493,51 +350,19 @@ void Player::setAnimation() switch (mStatusFiring) { case PLAYER_STATUS_FIRING_UP: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_UP); break; case PLAYER_STATUS_FIRING_LEFT: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_LEFT); break; case PLAYER_STATUS_FIRING_RIGHT: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_FIRING_RIGHT); break; case PLAYER_STATUS_FIRING_NO: - if (mExtraHit) - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT); - } - else - { - mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_STOP); - } - + mSpriteBody->animate(PLAYER_ANIMATION_BODY_WALKING_STOP); break; default: @@ -616,7 +441,7 @@ void Player::update() setAnimation(); shiftColliders(); updateCooldown(); - updateInvulnerableTimer(); + updateInvulnerableCounter(); } // Obtiene la puntuación del jugador @@ -692,19 +517,19 @@ void Player::setInvulnerable(bool value) } // Obtiene el valor de la variable -bool Player::getInvulnerableTimer() +Uint16 Player::getInvulnerableCounter() { return mInvulnerableCounter; } // Establece el valor de la variable -void Player::setInvulnerableTimer(Uint16 value) +void Player::setInvulnerableCounter(Uint16 value) { mInvulnerableCounter = value; } // Actualiza el valor de la variable -void Player::updateInvulnerableTimer() +void Player::updateInvulnerableCounter() { if (mInvulnerableCounter > 0) { @@ -762,7 +587,7 @@ Uint8 Player::getCoffees() } // Obtiene el circulo de colisión -Circle &Player::getCollider() +circle_t &Player::getCollider() { return mCollider; } diff --git a/source/player.h b/source/player.h index 60ad831..8681590 100644 --- a/source/player.h +++ b/source/player.h @@ -38,7 +38,7 @@ private: AnimatedSprite *mSpriteBody; // Sprite para dibujar el cuerpo AnimatedSprite *mSpriteHead; // Sprite para dibujar la cabeza - Circle mCollider; // Circulo de colisión del jugador + circle_t mCollider; // Circulo de colisión del jugador void shiftColliders(); // Actualiza el circulo de colisión a la posición del jugador public: @@ -125,13 +125,13 @@ public: void setInvulnerable(bool value); // Obtiene el valor de la variable - bool getInvulnerableTimer(); + Uint16 getInvulnerableCounter(); // Establece el valor de la variable - void setInvulnerableTimer(Uint16 value); + void setInvulnerableCounter(Uint16 value); // Actualiza el valor de la variable - void updateInvulnerableTimer(); + void updateInvulnerableCounter(); // Obtiene el valor de la variable bool hasExtraHit(); @@ -152,7 +152,7 @@ public: Uint8 getCoffees(); // Obtiene el circulo de colisión - Circle &getCollider(); + circle_t &getCollider(); }; #endif diff --git a/source/sprite.cpp b/source/sprite.cpp index 23df471..dd4344f 100644 --- a/source/sprite.cpp +++ b/source/sprite.cpp @@ -16,14 +16,14 @@ Sprite::~Sprite() // Inicializador void Sprite::init(int x, int y, int w, int h, LTexture *texture, SDL_Renderer *renderer) { - // Establece el alto y el ancho del sprite - setWidth(w); - setHeight(h); - // Establece la posición X,Y del sprite setPosX(x); setPosY(y); + // Establece el alto y el ancho del sprite + setWidth(w); + setHeight(h); + // Establece el puntero al renderizador de la ventana setRenderer(renderer); diff --git a/source/text.cpp b/source/text.cpp index dbc8ba8..b3545c7 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -191,6 +191,15 @@ void Text::writeColored(int x, int y, std::string text, Uint8 R, Uint8 G, Uint8 mSprite->getTexture()->setColor(255, 255, 255); } +// Escribe el texto con sombra +void Text::writeShadowed(int x, int y, std::string text, Uint8 R, Uint8 G, Uint8 B) +{ + mSprite->getTexture()->setColor(R, G, B); + write(x+1, y+1, text); + mSprite->getTexture()->setColor(255, 255, 255); + write(x, y, text); +} + // Escribe el texto centrado en un punto x y con kerning void Text::writeCentered(int x, int y, std::string text, int kerning) { diff --git a/source/text.h b/source/text.h index 8f6f86b..1a5950b 100644 --- a/source/text.h +++ b/source/text.h @@ -39,6 +39,9 @@ public: // Escribe el texto con colores void writeColored(int x, int y, std::string text, Uint8 R, Uint8 G, Uint8 B); + // Escribe el texto con sombra + void writeShadowed(int x, int y, std::string text, Uint8 R, Uint8 G, Uint8 B); + // Escribe el texto centrado en un punto x y con kerning void writeCentered(int x, int y, std::string text, int kerning = 0); diff --git a/source/utils.cpp b/source/utils.cpp index ca82b32..c6cb741 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -9,7 +9,7 @@ double distanceSquared(int x1, int y1, int x2, int y2) } // Detector de colisiones entre dos circulos -bool checkCollision(Circle &a, Circle &b) +bool checkCollision(circle_t &a, circle_t &b) { // Calcula el radio total al cuadrado int totalRadiusSquared = a.r + b.r; @@ -27,7 +27,7 @@ bool checkCollision(Circle &a, Circle &b) } // Detector de colisiones entre un circulo y un rectangulo -bool checkCollision(Circle &a, SDL_Rect &b) +bool checkCollision(circle_t &a, SDL_Rect &b) { //Closest point on collision box int cX, cY; @@ -60,10 +60,10 @@ bool checkCollision(Circle &a, SDL_Rect &b) cY = a.y; } - //If the closest point is inside the circle + //If the closest point is inside the circle_t if (distanceSquared(a.x, a.y, cX, cY) < a.r * a.r) { - //This box and the circle have collided + //This box and the circle_t have collided return true; } diff --git a/source/utils.h b/source/utils.h index 2efa4e9..8b188eb 100644 --- a/source/utils.h +++ b/source/utils.h @@ -5,7 +5,7 @@ #define UTILS_H // Estructura para definir un circulo -struct Circle +struct circle_t { Uint16 x; Uint16 y; @@ -13,7 +13,7 @@ struct Circle }; // Estructura para definir todas las entradas que aceptará el programa -struct Input +struct input_t { Uint8 up; Uint8 down; @@ -29,7 +29,7 @@ struct Input }; // Estructura para mapear el teclado usado en la demo -struct DemoKeys +struct demoKeys_t { Uint8 left; Uint8 right; @@ -43,15 +43,12 @@ struct DemoKeys double distanceSquared(int x1, int y1, int x2, int y2); // Detector de colisiones entre dos circulos -bool checkCollision(Circle &a, Circle &b); +bool checkCollision(circle_t &a, circle_t &b); // Detector de colisiones entre un circulo y un rectangulo -bool checkCollision(Circle &a, SDL_Rect &b); +bool checkCollision(circle_t &a, SDL_Rect &b); // Detector de colisiones entre un dos rectangulos bool checkCollision(SDL_Rect a, SDL_Rect b); -// Inicializa el vector con los valores del seno -void initSin(); - #endif \ No newline at end of file