#include "game/game.h"

#include <SDL3/SDL.h>

#include <algorithm>  // for max, min
#include <cstdlib>    // for rand
#include <iostream>   // for basic_ostream, char_traits, operator<<
#include <numeric>    // for accumulate

#include "core/audio/audio.hpp"           // for Audio
#include "core/input/global_inputs.hpp"   // for GlobalInputs::handle
#include "core/input/input.h"             // for InputAction, Input, REPEAT_TRUE, REPEAT_FALSE
#include "core/locale/lang.h"             // for Lang
#include "core/rendering/fade.h"          // for Fade, FADE_CENTER
#include "core/rendering/movingsprite.h"  // for MovingSprite
#include "core/rendering/screen.h"        // for Screen
#include "core/rendering/smartsprite.h"   // for SmartSprite
#include "core/rendering/sprite.h"        // for Sprite
#include "core/rendering/text.h"          // for Text, TXT_CENTER
#include "core/rendering/texture.h"       // for Texture
#include "core/resources/asset.h"         // for Asset
#include "core/resources/resource.h"
#include "game/defaults.hpp"        // for PLAY_AREA_CENTER_X, BLOCK, PLAY_AREA_CEN...
#include "game/entities/balloon.h"  // for Balloon, Balloon::VELX_NEGATIVE, BALLOON_...
#include "game/entities/bullet.h"   // for Bullet, Bullet::Kind::LEFT, Bullet::Kind::RIGHT, BULLE...
#include "game/entities/item.h"     // for Item
#include "game/entities/player.h"   // for Player
#include "game/options.hpp"         // for Options
#include "game/ui/menu.h"           // for Menu
namespace Ja {
    struct Sound;
}  // namespace Ja

namespace {
    // Constantes geométricas y temporales compartidas por los helpers de initEnemyFormations
    constexpr int Y4 = (PLAY_AREA_TOP - BLOCK);
    constexpr int X4_0 = PLAY_AREA_LEFT;
    constexpr int X4_100 = PLAY_AREA_RIGHT - Balloon::WIDTH_4;

    constexpr int Y3 = (PLAY_AREA_TOP - BLOCK);
    constexpr int X3_0 = PLAY_AREA_LEFT;
    constexpr int X3_100 = PLAY_AREA_RIGHT - Balloon::WIDTH_3;

    constexpr int Y2 = (PLAY_AREA_TOP - BLOCK);
    constexpr int X2_0 = PLAY_AREA_LEFT;
    constexpr int X2_100 = PLAY_AREA_RIGHT - Balloon::WIDTH_2;

    constexpr int Y1 = (PLAY_AREA_TOP - BLOCK);
    constexpr int X1_0 = PLAY_AREA_LEFT;
    constexpr int X1_50 = PLAY_AREA_CENTER_X - (Balloon::WIDTH_1 / 2);
    constexpr int X1_100 = PLAY_AREA_RIGHT - Balloon::WIDTH_1;

    constexpr Uint16 CREATION_TIME = 300;
}  // namespace

// Constructor
Game::Game(int num_players, int current_stage, SDL_Renderer *renderer, bool demo, Section *section)
    : last_stage_reached_(current_stage) {
    // Copia los punteros
    this->renderer_ = renderer;
    this->section_ = section;

    // Pasa variables
    this->demo_.enabled = demo;
    this->num_players_ = num_players;
#ifdef PAUSE
    this->currentStage = 3;
#else
    this->current_stage_ = current_stage;
#endif
    if (num_players == 1) {  // Si solo juega un jugador, permite jugar tanto con teclado como con mando
        player_one_control_ = Options::inputs[0].device_type;
        Options::inputs[0].device_type = INPUT_USE_ANY;
    }
    difficulty_ = Options::settings.difficulty;

    // Crea los objetos
    fade_ = new Fade(renderer);
    event_handler_ = new SDL_Event();

    // Carga los recursos
    loadMedia();

    // Carga ficheros
    loadDemoFile();

    // Establece la máxima puntuación desde fichero o desde las puntuaciones online
    setHiScore();

    clouds1_a_ = new MovingSprite(0, 0, 256, 52, -0.4F, 0.0F, 0.0F, 0.0F, game_clouds_texture_, renderer);
    clouds1_b_ = new MovingSprite(256, 0, 256, 52, -0.4F, 0.0F, 0.0F, 0.0F, game_clouds_texture_, renderer);
    clouds2_a_ = new MovingSprite(0, 52, 256, 32, -0.2F, 0.0F, 0.0F, 0.0F, game_clouds_texture_, renderer);
    clouds2_b_ = new MovingSprite(256, 52, 256, 32, -0.2F, 0.0F, 0.0F, 0.0F, game_clouds_texture_, renderer);

    n1000_sprite_ = new SmartSprite(game_text_texture_, renderer);
    n2500_sprite_ = new SmartSprite(game_text_texture_, renderer);
    n5000_sprite_ = new SmartSprite(game_text_texture_, renderer);

    buildings_sprite_ = new Sprite(0, 0, 256, 160, game_buildings_texture_, renderer);

    sky_colors_sprite_ = new Sprite(0, 0, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT, game_sky_colors_texture_, renderer);
    grass_sprite_ = new Sprite(0, 0, 256, 6, game_grass_texture_, renderer);
    power_meter_sprite_ = new Sprite(PLAY_AREA_CENTER_X - 20, 170, 40, 7, game_power_meter_texture_, renderer);
    game_over_sprite_ = new Sprite(16, 80, 128, 96, game_over_texture_, renderer);
    game_over_end_sprite_ = new Sprite(PLAY_AREA_CENTER_X - (game_over_end_texture_->getWidth() / 2), 80, 128, 96, game_over_end_texture_, renderer);

    // Inicializa las variables necesarias para la sección 'Game'
    init();
#ifdef PAUSE
    pause = false;
#endif
}

Game::~Game() {
    saveScoreFile();
    saveDemoFile();

    // Restaura el metodo de control
    if (num_players_ == 1) {
        Options::inputs[0].device_type = player_one_control_;
    }

    // Elimina todos los objetos contenidos en vectores (jugadores, balas, etc.)
    deleteAllVectorObjects();

    // Las texturas, animaciones, Text, Menu, sonidos y música son propiedad
    // de Resource — no se liberan aquí. Solo limpiamos nuestras vistas.
    player_animations_.clear();
    balloon_animations_.clear();
    item_animations_.clear();
    player1_textures_.clear();
    player2_textures_.clear();
    item_textures_.clear();
    balloon_textures_.clear();

    delete fade_;
    delete event_handler_;
    delete clouds1_a_;
    delete clouds1_b_;
    delete clouds2_a_;
    delete clouds2_b_;
    delete n1000_sprite_;
    delete n2500_sprite_;
    delete n5000_sprite_;
    delete buildings_sprite_;

    delete sky_colors_sprite_;
    delete grass_sprite_;
    delete power_meter_sprite_;
    delete game_over_sprite_;
    delete game_over_end_sprite_;
}

// Inicializa las variables necesarias para la sección 'Game'
void Game::init() {
    ticks_ = 0;
    ticks_speed_ = 15;

    // Elimina qualquier jugador que hubiese antes de crear los nuevos
    for (auto *player : players_) {
        delete player;
    };
    players_.clear();

    // Crea los jugadores
    if (num_players_ == 1) {
        auto *player = new Player(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, renderer_, player_textures_[Options::settings.player_selected], player_animations_);
        players_.push_back(player);
    }

    else if (num_players_ == 2) {
        auto *player1 = new Player((PLAY_AREA_CENTER_FIRST_QUARTER_X * ((0 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24, renderer_, player_textures_[0], player_animations_);
        auto *player2 = new Player((PLAY_AREA_CENTER_FIRST_QUARTER_X * ((1 * 2) + 1)) - 11, PLAY_AREA_BOTTOM - 24, renderer_, player_textures_[1], player_animations_);
        players_.push_back(player1);
        players_.push_back(player2);
    }

    // Inicializa las variables
    switch (difficulty_) {
        case DIFFICULTY_EASY:
            default_enemy_speed_ = Balloon::SPEED_1;
            difficulty_score_multiplier_ = 0.5F;
            difficulty_color_ = {75, 105, 47};
            pause_menu_->setSelectorColor(difficulty_color_, 255);
            game_over_menu_->setSelectorColor(difficulty_color_, 255);
            break;

        case DIFFICULTY_NORMAL:
            default_enemy_speed_ = Balloon::SPEED_1;
            difficulty_score_multiplier_ = 1.0F;
            difficulty_color_ = {255, 122, 0};
            pause_menu_->setSelectorColor(difficulty_color_, 255);
            game_over_menu_->setSelectorColor(difficulty_color_, 255);
            break;

        case DIFFICULTY_HARD:
            default_enemy_speed_ = Balloon::SPEED_5;
            difficulty_score_multiplier_ = 1.5F;
            difficulty_color_ = {118, 66, 138};
            pause_menu_->setSelectorColor(difficulty_color_, 255);
            game_over_menu_->setSelectorColor(difficulty_color_, 255);
            break;

        default:
            break;
    }

    game_completed_ = false;
    game_completed_counter_ = 0;
    section_->name = SECTION_PROG_GAME;
    section_->subsection = SUBSECTION_GAME_PLAY_1P;
    menace_current_ = 0;
    menace_threshold_ = 0;
    hi_score_achieved_ = false;
    stage_bitmap_counter_ = STAGE_COUNTER;
    death_counter_ = Player::DEATH_COUNTER;
    time_stopped_ = false;
    time_stopped_counter_ = 0;
    counter_ = 0;
    last_enemy_deploy_ = 0;
    enemy_deploy_counter_ = 0;
    enemy_speed_ = default_enemy_speed_;
    effect_.flash = false;
    effect_.shake = false;
    effect_.shake_counter = SHAKE_COUNTER;
    death_shake_.active = false;
    death_shake_.step = 0;
    death_shake_.last_step_ticks = 0;
    death_sequence_.phase = DeathPhase::NONE;
    death_sequence_.phase_start_ticks = 0;
    death_sequence_.player = nullptr;
    helper_.need_coffee = false;
    helper_.need_coffee_machine = false;
    helper_.need_power_ball = false;
    helper_.counter = HELP_COUNTER;
    helper_.item_disk_odds = ITEM_POINTS_1_DISK_ODDS;
    helper_.item_gavina_odds = ITEM_POINTS_2_GAVINA_ODDS;
    helper_.item_paco_odds = ITEM_POINTS_3_PACMAR_ODDS;
    helper_.item_clock_odds = ITEM_CLOCK_ODDS;
    helper_.item_coffee_odds = ITEM_COFFEE_ODDS;
    helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS;
    power_ball_enabled_ = false;
    power_ball_counter_ = 0;
    coffee_machine_enabled_ = false;
    pause_counter_ = 0;
    leaving_pause_menu_ = false;
    pause_initialized_ = false;
    game_over_initialized_ = false;
    game_over_post_fade_ = 0;

    if (demo_.enabled) {
        const int NUM = rand() % 2;
        if (NUM == 0) {
            balloons_popped_ = 1000;
            current_stage_ = 3;
        } else {
            balloons_popped_ = 1800;
            current_stage_ = 6;
        }
    }

    initPaths();
    initEnemyFormations();
    initEnemyPools();
    initGameStages();

    // Mas variables
    balloons_popped_ = 0;
    for (int i = 0; i < current_stage_; ++i) {
        balloons_popped_ += stage_[i].power_to_complete;
    }

    total_power_to_complete_game_ = 0;
    for (auto &i : stage_) {
        total_power_to_complete_game_ += i.power_to_complete;
    }

    // Modo demo
    demo_.recording = false;
    demo_.counter = 0;

    // Inicializa el objeto para el fundido
    fade_->init(0x27, 0x27, 0x36);

    // Sprites
    clouds1_a_->setSpriteClip(0, 0, 256, 52);
    clouds1_b_->setSpriteClip(0, 0, 256, 52);
    clouds2_a_->setSpriteClip(0, 52, 256, 32);
    clouds2_b_->setSpriteClip(0, 52, 256, 32);

    grass_sprite_->setPosY(154);
    power_meter_sprite_->setSpriteClip(0, 0, 40, 7);

    // Con los globos creados, calcula el nivel de amenaza
    evaluateAndSetMenace();

    // Inicializa el bitmap de 1000 puntos
    n1000_sprite_->setPosX(0);
    n1000_sprite_->setPosY(0);
    n1000_sprite_->setWidth(26);
    n1000_sprite_->setHeight(9);
    n1000_sprite_->setVelX(0.0F);
    n1000_sprite_->setVelY(-0.5F);
    n1000_sprite_->setAccelX(0.0F);
    n1000_sprite_->setAccelY(-0.1F);
    n1000_sprite_->setSpriteClip(0, 0, 26, 9);
    n1000_sprite_->setEnabled(false);
    n1000_sprite_->setEnabledCounter(0);
    n1000_sprite_->setDestX(0);
    n1000_sprite_->setDestY(0);

    // Inicializa el bitmap de 2500 puntos
    n2500_sprite_->setPosX(0);
    n2500_sprite_->setPosY(0);
    n2500_sprite_->setWidth(28);
    n2500_sprite_->setHeight(9);
    n2500_sprite_->setVelX(0.0F);
    n2500_sprite_->setVelY(-0.5F);
    n2500_sprite_->setAccelX(0.0F);
    n2500_sprite_->setAccelY(-0.1F);
    n2500_sprite_->setSpriteClip(26, 0, 28, 9);
    n2500_sprite_->setEnabled(false);
    n2500_sprite_->setEnabledCounter(0);
    n2500_sprite_->setDestX(0);
    n2500_sprite_->setDestY(0);

    // Inicializa el bitmap de 5000 puntos
    n5000_sprite_->setPosX(0);
    n5000_sprite_->setPosY(0);
    n5000_sprite_->setWidth(28);
    n5000_sprite_->setHeight(9);
    n5000_sprite_->setVelX(0.0F);
    n5000_sprite_->setVelY(-0.5F);
    n5000_sprite_->setAccelX(0.0F);
    n5000_sprite_->setAccelY(-0.1F);
    n5000_sprite_->setSpriteClip(54, 0, 28, 9);
    n5000_sprite_->setEnabled(false);
    n5000_sprite_->setEnabledCounter(0);
    n5000_sprite_->setDestX(0);
    n5000_sprite_->setDestY(0);

    // Los fondos
    sky_colors_rect_[0] = {.x = 0, .y = 0, .w = GAMECANVAS_WIDTH, .h = GAMECANVAS_HEIGHT};
    sky_colors_rect_[1] = {.x = 256, .y = 0, .w = GAMECANVAS_WIDTH, .h = GAMECANVAS_HEIGHT};
    sky_colors_rect_[2] = {.x = 0, .y = 192, .w = GAMECANVAS_WIDTH, .h = GAMECANVAS_HEIGHT};
    sky_colors_rect_[3] = {.x = 256, .y = 192, .w = GAMECANVAS_WIDTH, .h = GAMECANVAS_HEIGHT};
}

// Carga los recursos necesarios para la sección 'Game'
void Game::loadMedia() {
    if (Options::settings.console) {
        std::cout << '\n'
                  << "** LOADING RESOURCES FOR GAME SECTION" << '\n';
    }

    Resource *resource = Resource::get();

    // Texturas (handles compartidos — no se liberan aquí)
    bullet_texture_ = resource->getTexture("bullet.png");
    game_buildings_texture_ = resource->getTexture("game_buildings.png");
    game_clouds_texture_ = resource->getTexture("game_clouds.png");
    game_grass_texture_ = resource->getTexture("game_grass.png");
    game_power_meter_texture_ = resource->getTexture("game_power_meter.png");
    game_sky_colors_texture_ = resource->getTexture("game_sky_colors.png");
    game_text_texture_ = resource->getTexture("game_text.png");
    game_over_texture_ = resource->getTexture("menu_game_over.png");
    game_over_end_texture_ = resource->getTexture("menu_game_over_end.png");

    // Texturas - Globos
    balloon_textures_.push_back(resource->getTexture("balloon1.png"));
    balloon_textures_.push_back(resource->getTexture("balloon2.png"));
    balloon_textures_.push_back(resource->getTexture("balloon3.png"));
    balloon_textures_.push_back(resource->getTexture("balloon4.png"));

    // Texturas - Items
    item_textures_.push_back(resource->getTexture("item_points1_disk.png"));
    item_textures_.push_back(resource->getTexture("item_points2_gavina.png"));
    item_textures_.push_back(resource->getTexture("item_points3_pacmar.png"));
    item_textures_.push_back(resource->getTexture("item_clock.png"));
    item_textures_.push_back(resource->getTexture("item_coffee.png"));
    item_textures_.push_back(resource->getTexture("item_coffee_machine.png"));

    // Texturas - Player1
    player1_textures_.push_back(resource->getTexture("player_bal1_head.png"));
    player1_textures_.push_back(resource->getTexture("player_bal1_body.png"));
    player1_textures_.push_back(resource->getTexture("player_bal1_legs.png"));
    player1_textures_.push_back(resource->getTexture("player_bal1_death.png"));
    player1_textures_.push_back(resource->getTexture("player_bal1_fire.png"));
    player_textures_.push_back(player1_textures_);

    // Texturas - Player2
    player2_textures_.push_back(resource->getTexture("player_arounder_head.png"));
    player2_textures_.push_back(resource->getTexture("player_arounder_body.png"));
    player2_textures_.push_back(resource->getTexture("player_arounder_legs.png"));
    player2_textures_.push_back(resource->getTexture("player_arounder_death.png"));
    player2_textures_.push_back(resource->getTexture("player_arounder_fire.png"));
    player_textures_.push_back(player2_textures_);

    // Animaciones (handles a los vectores raw de Resource — no se liberan)
    player_animations_.push_back(&resource->getAnimationLines("player_head.ani"));
    player_animations_.push_back(&resource->getAnimationLines("player_body.ani"));
    player_animations_.push_back(&resource->getAnimationLines("player_legs.ani"));
    player_animations_.push_back(&resource->getAnimationLines("player_death.ani"));
    player_animations_.push_back(&resource->getAnimationLines("player_fire.ani"));

    balloon_animations_.push_back(&resource->getAnimationLines("balloon1.ani"));
    balloon_animations_.push_back(&resource->getAnimationLines("balloon2.ani"));
    balloon_animations_.push_back(&resource->getAnimationLines("balloon3.ani"));
    balloon_animations_.push_back(&resource->getAnimationLines("balloon4.ani"));

    item_animations_.push_back(&resource->getAnimationLines("item_points1_disk.ani"));
    item_animations_.push_back(&resource->getAnimationLines("item_points2_gavina.ani"));
    item_animations_.push_back(&resource->getAnimationLines("item_points3_pacmar.ani"));
    item_animations_.push_back(&resource->getAnimationLines("item_clock.ani"));
    item_animations_.push_back(&resource->getAnimationLines("item_coffee.ani"));
    item_animations_.push_back(&resource->getAnimationLines("item_coffee_machine.ani"));

    // Texto
    text_ = resource->getText("smb2");
    text_scoreboard_ = resource->getText("8bithud");
    text_big_ = resource->getText("smb2_big");
    text_nokia2_ = resource->getText("nokia2");
    text_nokia_big2_ = resource->getText("nokia_big2");

    // Menus
    game_over_menu_ = resource->getMenu("gameover");
    game_over_menu_->setItemCaption(0, Lang::get()->getText(48));
    game_over_menu_->setItemCaption(1, Lang::get()->getText(49));
    const int W = text_->getCharacterSize() * Lang::get()->getText(45).length();
    game_over_menu_->setRectSize(W, 0);
    game_over_menu_->centerMenuOnX(199);
    pause_menu_ = resource->getMenu("pause");
    pause_menu_->setItemCaption(0, Lang::get()->getText(41));
    pause_menu_->setItemCaption(1, Lang::get()->getText(46));
    pause_menu_->setItemCaption(2, Lang::get()->getText(47));

    // Sonidos
    balloon_sound_ = resource->getSound("balloon.wav");
    bubble1_sound_ = resource->getSound("bubble1.wav");
    bubble2_sound_ = resource->getSound("bubble2.wav");
    bubble3_sound_ = resource->getSound("bubble3.wav");
    bubble4_sound_ = resource->getSound("bubble4.wav");
    bullet_sound_ = resource->getSound("bullet.wav");
    clock_sound_ = resource->getSound("clock.wav");
    coffee_out_sound_ = resource->getSound("coffeeout.wav");
    hi_score_sound_ = resource->getSound("hiscore.wav");
    item_drop_sound_ = resource->getSound("itemdrop.wav");
    item_pick_up_sound_ = resource->getSound("itempickup.wav");
    player_collision_sound_ = resource->getSound("player_collision.wav");
    power_ball_sound_ = resource->getSound("powerball.wav");
    stage_change_sound_ = resource->getSound("stage_change.wav");
    coffee_machine_sound_ = resource->getSound("title.wav");

    // Musicas
    game_music_ = resource->getMusic("playing.ogg");

    if (Options::settings.console) {
        std::cout << "** RESOURCES FOR GAME SECTION LOADED" << '\n'
                  << '\n';
    }
}

// Carga el fichero de puntos
auto Game::loadScoreFile() -> bool {
    // Indicador de éxito en la carga
    bool success = true;
    const std::string P = Asset::get()->get("score.bin");
    const std::string FILE_NAME = P.substr(P.find_last_of("\\/") + 1);
    SDL_IOStream *file = SDL_IOFromFile(P.c_str(), "r+b");

    // El fichero no existe
    if (file == nullptr) {
        if (Options::settings.console) {
            std::cout << "Warning: Unable to open " << FILE_NAME.c_str() << " file" << '\n';
        }

        // Creamos el fichero para escritura
        file = SDL_IOFromFile(P.c_str(), "w+b");
        if (file != nullptr) {
            if (Options::settings.console) {
                std::cout << "New file (" << FILE_NAME.c_str() << ") created!" << '\n';
            }

            // Inicializamos los datos
            for (unsigned int &i : score_data_file_) {
                i = 0;
                SDL_WriteIO(file, &i, sizeof(Uint32));
            }

            // Cerramos el fichero
            SDL_CloseIO(file);
        } else {
            if (Options::settings.console) {
                std::cout << "Error: Unable to create file " << FILE_NAME.c_str() << '\n';
            }
            success = false;
        }
    }
    // El fichero existe
    else {
        // Cargamos los datos
        if (Options::settings.console) {
            std::cout << "Reading file " << FILE_NAME.c_str() << '\n';
        }
        for (unsigned int &i : score_data_file_) {
            SDL_ReadIO(file, &i, sizeof(Uint32));
        }

        // Cierra el fichero
        SDL_CloseIO(file);
    }

    // Estableix el hi-score si el fitxer és vàlid (≠ 0) i el checksum quadra;
    // en qualsevol altre cas (fitxer absent, corrupte o manipulat) usa el default.
    if (score_data_file_[0] != 0 && score_data_file_[0] % 43 == score_data_file_[1]) {
        hi_score_ = score_data_file_[0];
    } else {
        hi_score_ = 10000;
    }

    return success;
}

// Carga el fichero de datos para la demo
auto Game::loadDemoFile() -> bool {
    // Lee los datos de la demo desde Resource (precargados al arrancar).
    const auto &bytes = Resource::get()->getDemoBytes();
    const size_t EXPECTED = sizeof(DemoKeys) * TOTAL_DEMO_DATA;
    if (bytes.size() >= EXPECTED) {
        for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
            memcpy(&demo_.data_file[i], bytes.data() + (i * sizeof(DemoKeys)), sizeof(DemoKeys));
        }
        if (Options::settings.console) {
            std::cout << "Demo data loaded (" << bytes.size() << " bytes)" << '\n';
        }
    } else {
        // Si no hay datos (bytes vacíos o tamaño inválido), inicializamos a cero.
        if (Options::settings.console) {
            std::cout << "Warning: demo data missing or too small, initializing to zero" << '\n';
        }
        for (auto &i : demo_.data_file) {
            demo_.keys.left = 0;
            demo_.keys.right = 0;
            demo_.keys.no_input = 0;
            demo_.keys.fire = 0;
            demo_.keys.fire_left = 0;
            demo_.keys.fire_right = 0;
            i = demo_.keys;
        }
    }
    return true;
}

// Guarda el fichero de puntos
auto Game::saveScoreFile() -> bool {
    bool success = true;
    const std::string P = Asset::get()->get("score.bin");
    const std::string FILE_NAME = P.substr(P.find_last_of("\\/") + 1);
    SDL_IOStream *file = SDL_IOFromFile(P.c_str(), "w+b");
    if (file != nullptr) {
        // Guardamos los datos
        for (unsigned int &i : score_data_file_) {
            SDL_WriteIO(file, &i, sizeof(Uint32));
        }

        if (Options::settings.console) {
            std::cout << "Writing file " << FILE_NAME.c_str() << '\n';
        }

        // Cerramos el fichero
        SDL_CloseIO(file);
    } else {
        if (Options::settings.console) {
            std::cout << "Error: Unable to save " << FILE_NAME.c_str() << " file! " << SDL_GetError() << '\n';
        }
    }
    return success;
}

// Guarda el fichero de datos para la demo
auto Game::saveDemoFile() -> bool {
    bool success = true;
    const std::string P = Asset::get()->get("demo.bin");
    const std::string FILE_NAME = P.substr(P.find_last_of("\\/") + 1);
    if (demo_.recording) {
        SDL_IOStream *file = SDL_IOFromFile(P.c_str(), "w+b");
        if (file != nullptr) {
            // Guardamos los datos
            for (auto &i : demo_.data_file) {
                SDL_WriteIO(file, &i, sizeof(DemoKeys));
            }

            if (Options::settings.console) {
                std::cout << "Writing file " << FILE_NAME.c_str() << '\n';
            }

            // Cerramos el fichero
            SDL_CloseIO(file);
        } else {
            if (Options::settings.console) {
                std::cout << "Error: Unable to save " << FILE_NAME.c_str() << " file! " << SDL_GetError() << '\n';
            }
        }
    }
    return success;
}

// Inicializa las formaciones enemigas
void Game::initEnemyFormations() {
    initEnemyFormationsZero();
    initEnemyFormationsLinear();
    initEnemyFormationsSymmetric();
    initEnemyFormationsHexagonsAndTest();
}

// Pone a cero todas las formaciones
void Game::initEnemyFormationsZero() {
    for (auto &i : enemy_formation_) {
        i.number_of_enemies = 0;
        for (auto &j : i.init) {
            j.x = 0;
            j.y = 0;
            j.vel_x = 0;
            j.kind = 0;
            j.creation_counter = 0;
        }
    }
}

// Formaciones 0..19: bucles lineales (todos los enemigos van en la misma dirección)
void Game::initEnemyFormationsLinear() {
    int inc_x = 0;
    Uint8 inc_time = 0;
    Uint8 j = 0;

    // #00 - Dos enemigos BALLOON4 uno a cada extremo
    j = 0;
    enemy_formation_[j].number_of_enemies = 2;
    inc_x = X4_100;
    inc_time = 0;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X4_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y4;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE * (((i % 2) * 2) - 1);
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_4;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME + (inc_time * i);
    }

    // #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro
    j = 1;
    enemy_formation_[j].number_of_enemies = 2;
    inc_x = PLAY_AREA_CENTER_X;
    inc_time = 0;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = PLAY_AREA_CENTER_FIRST_QUARTER_X - (Balloon::WIDTH_4 / 2) + (i * inc_x);
        enemy_formation_[j].init[i].y = Y4;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE * (((i % 2) * 2) - 1);
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_4;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME + (inc_time * i);
    }

    // #02 - Cuatro enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro
    j = 2;
    enemy_formation_[j].number_of_enemies = 4;
    inc_x = Balloon::WIDTH_2 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X2_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y2;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_2;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #03 - Cuatro enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro
    j = 3;
    enemy_formation_[j].number_of_enemies = 4;
    inc_x = Balloon::WIDTH_2 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X2_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y2;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_2;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha
    j = 4;
    enemy_formation_[j].number_of_enemies = 3;
    inc_x = Balloon::WIDTH_3 * 2;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda
    j = 5;
    enemy_formation_[j].number_of_enemies = 3;
    inc_x = Balloon::WIDTH_3 * 2;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha
    j = 6;
    enemy_formation_[j].number_of_enemies = 3;
    inc_x = Balloon::WIDTH_3 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda
    j = 7;
    enemy_formation_[j].number_of_enemies = 3;
    inc_x = Balloon::WIDTH_3 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha
    j = 8;
    enemy_formation_[j].number_of_enemies = 6;
    inc_x = Balloon::WIDTH_1 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X1_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = 13;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_1;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda
    j = 9;
    enemy_formation_[j].number_of_enemies = 6;
    inc_x = Balloon::WIDTH_1 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X1_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = 13;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_1;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #10 - Tres enemigos BALLOON4 seguidos desde la izquierda
    j = 10;
    enemy_formation_[j].number_of_enemies = 3;
    inc_x = Balloon::WIDTH_4 + 1;
    inc_time = 15;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X4_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y4;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_4;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #11 - Tres enemigos BALLOON4 seguidos desde la derecha
    j = 11;
    enemy_formation_[j].number_of_enemies = 3;
    inc_x = Balloon::WIDTH_4 + 1;
    inc_time = 15;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X4_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y4;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_4;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #12 - Seis enemigos BALLOON2 uno detras del otro. A la izquierda y hacia el centro
    j = 12;
    enemy_formation_[j].number_of_enemies = 6;
    inc_x = Balloon::WIDTH_2 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X2_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y2;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_2;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #13 - Seis enemigos BALLOON2 uno detras del otro. A la derecha y hacia el centro
    j = 13;
    enemy_formation_[j].number_of_enemies = 6;
    inc_x = Balloon::WIDTH_2 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X2_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y2;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_2;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados
    j = 14;
    enemy_formation_[j].number_of_enemies = 5;
    inc_x = Balloon::WIDTH_3 * 2;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados
    j = 15;
    enemy_formation_[j].number_of_enemies = 5;
    inc_x = Balloon::WIDTH_3 * 2;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos
    j = 16;
    enemy_formation_[j].number_of_enemies = 5;
    inc_x = Balloon::WIDTH_3 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos
    j = 17;
    enemy_formation_[j].number_of_enemies = 5;
    inc_x = Balloon::WIDTH_3 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X3_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos
    j = 18;
    enemy_formation_[j].number_of_enemies = 12;
    inc_x = Balloon::WIDTH_1 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X1_0 + (i * inc_x);
        enemy_formation_[j].init[i].y = Y1;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_1;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }

    // #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos
    j = 19;
    enemy_formation_[j].number_of_enemies = 12;
    inc_x = Balloon::WIDTH_1 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        enemy_formation_[j].init[i].x = X1_100 - (i * inc_x);
        enemy_formation_[j].init[i].y = Y1;
        enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_1;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME - (inc_time * i);
    }
}

// Formaciones 20..25: simétricas (la primera mitad va hacia un lado, la segunda hacia el otro)
void Game::initEnemyFormationsSymmetric() {
    int inc_x = 0;
    Uint8 inc_time = 0;
    Uint8 j = 0;

    // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simetricos
    j = 20;
    enemy_formation_[j].number_of_enemies = 4;
    inc_x = Balloon::WIDTH_4 + 1;
    inc_time = 0;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        Uint8 half = enemy_formation_[j].number_of_enemies / 2;
        if (i < half) {
            enemy_formation_[j].init[i].x = X4_0 + (i * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
        } else {
            enemy_formation_[j].init[i].x = X4_100 - ((i - half) * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
        }
        enemy_formation_[j].init[i].y = Y4;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_4;
        enemy_formation_[j].init[i].creation_counter = CREATION_TIME + (inc_time * i);
    }

    // #21 - Diez enemigos BALLOON2 uno detras del otro. Izquierda/derecha. Simetricos
    j = 21;
    enemy_formation_[j].number_of_enemies = 10;
    inc_x = Balloon::WIDTH_2 + 1;
    inc_time = 3;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        Uint8 half = enemy_formation_[j].number_of_enemies / 2;
        if (i < half) {
            enemy_formation_[j].init[i].x = X2_0 + (i * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * i);
        } else {
            enemy_formation_[j].init[i].x = X2_100 - ((i - half) * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * (i - half));
        }
        enemy_formation_[j].init[i].y = Y2;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_2;
    }

    // #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simetricos
    j = 22;
    enemy_formation_[j].number_of_enemies = 10;
    inc_x = Balloon::WIDTH_3 * 2;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        Uint8 half = enemy_formation_[j].number_of_enemies / 2;
        if (i < half) {
            enemy_formation_[j].init[i].x = X3_0 + (i * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * i);
        } else {
            enemy_formation_[j].init[i].x = X3_100 - ((i - half) * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * (i - half));
        }
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
    }

    // #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simetricos
    j = 23;
    enemy_formation_[j].number_of_enemies = 10;
    inc_x = Balloon::WIDTH_3 + 1;
    inc_time = 10;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        Uint8 half = enemy_formation_[j].number_of_enemies / 2;
        if (i < half) {
            enemy_formation_[j].init[i].x = X3_0 + (i * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * i);
        } else {
            enemy_formation_[j].init[i].x = X3_100 - ((i - half) * inc_x);
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * (i - half));
        }
        enemy_formation_[j].init[i].y = Y3;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_3;
    }

    // #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos
    j = 24;
    enemy_formation_[j].number_of_enemies = 30;
    inc_time = 5;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        Uint8 half = enemy_formation_[j].number_of_enemies / 2;
        if (i < half) {
            enemy_formation_[j].init[i].x = X1_50;
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) + (inc_time * i);
        } else {
            enemy_formation_[j].init[i].x = X1_50;
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) + (inc_time * (i - half));
        }
        enemy_formation_[j].init[i].y = Y1;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_1;
    }

    // #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos
    j = 25;
    enemy_formation_[j].number_of_enemies = 30;
    inc_time = 5;
    for (int i = 0; i < enemy_formation_[j].number_of_enemies; i++) {
        Uint8 half = enemy_formation_[j].number_of_enemies / 2;
        if (i < half) {
            enemy_formation_[j].init[i].x = X1_50 + 20;
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_NEGATIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * i);
        } else {
            enemy_formation_[j].init[i].x = X1_50 - 20;
            enemy_formation_[j].init[i].vel_x = Balloon::VELX_POSITIVE;
            enemy_formation_[j].init[i].creation_counter = (CREATION_TIME) - (inc_time * (i - half));
        }
        enemy_formation_[j].init[i].y = Y1;
        enemy_formation_[j].init[i].kind = Balloon::BALLOON_1;
    }
}

// Duplica las formaciones 0..25 como hexágonos en el rango 50..75, y configura la formación 99 (TEST)
void Game::initEnemyFormationsHexagonsAndTest() {
    constexpr Uint8 LAST_SYMMETRIC = 25;

    // Crea las mismas formaciones pero con hexagonos a partir de la posición 50 del vector
    for (int k = 0; k < LAST_SYMMETRIC + 1; k++) {
        enemy_formation_[k + 50].number_of_enemies = enemy_formation_[k].number_of_enemies;
        for (int i = 0; i < enemy_formation_[k + 50].number_of_enemies; i++) {
            enemy_formation_[k + 50].init[i].x = enemy_formation_[k].init[i].x;
            enemy_formation_[k + 50].init[i].y = enemy_formation_[k].init[i].y;
            enemy_formation_[k + 50].init[i].vel_x = enemy_formation_[k].init[i].vel_x;
            enemy_formation_[k + 50].init[i].creation_counter = enemy_formation_[k].init[i].creation_counter;
            enemy_formation_[k + 50].init[i].kind = enemy_formation_[k].init[i].kind + 4;
        }
    }

    // TEST
    enemy_formation_[99].number_of_enemies = 4;

    enemy_formation_[99].init[0].x = 10;
    enemy_formation_[99].init[0].y = Y1;
    enemy_formation_[99].init[0].vel_x = 0;
    enemy_formation_[99].init[0].kind = Balloon::BALLOON_1;
    enemy_formation_[99].init[0].creation_counter = 200;

    enemy_formation_[99].init[1].x = 50;
    enemy_formation_[99].init[1].y = Y1;
    enemy_formation_[99].init[1].vel_x = 0;
    enemy_formation_[99].init[1].kind = Balloon::BALLOON_2;
    enemy_formation_[99].init[1].creation_counter = 200;

    enemy_formation_[99].init[2].x = 90;
    enemy_formation_[99].init[2].y = Y1;
    enemy_formation_[99].init[2].vel_x = 0;
    enemy_formation_[99].init[2].kind = Balloon::BALLOON_3;
    enemy_formation_[99].init[2].creation_counter = 200;

    enemy_formation_[99].init[3].x = 140;
    enemy_formation_[99].init[3].y = Y1;
    enemy_formation_[99].init[3].vel_x = 0;
    enemy_formation_[99].init[3].kind = Balloon::BALLOON_4;
    enemy_formation_[99].init[3].creation_counter = 200;
}

// Inicializa los conjuntos de formaciones
void Game::initEnemyPools() {
    // EnemyPool #0
    enemy_pool_[0].set[0] = &enemy_formation_[0];
    enemy_pool_[0].set[1] = &enemy_formation_[1];
    enemy_pool_[0].set[2] = &enemy_formation_[2];
    enemy_pool_[0].set[3] = &enemy_formation_[3];
    enemy_pool_[0].set[4] = &enemy_formation_[4];
    enemy_pool_[0].set[5] = &enemy_formation_[5];
    enemy_pool_[0].set[6] = &enemy_formation_[6];
    enemy_pool_[0].set[7] = &enemy_formation_[7];
    enemy_pool_[0].set[8] = &enemy_formation_[8];
    enemy_pool_[0].set[9] = &enemy_formation_[9];

    // EnemyPool #1
    enemy_pool_[1].set[0] = &enemy_formation_[10];
    enemy_pool_[1].set[1] = &enemy_formation_[11];
    enemy_pool_[1].set[2] = &enemy_formation_[12];
    enemy_pool_[1].set[3] = &enemy_formation_[13];
    enemy_pool_[1].set[4] = &enemy_formation_[14];
    enemy_pool_[1].set[5] = &enemy_formation_[15];
    enemy_pool_[1].set[6] = &enemy_formation_[16];
    enemy_pool_[1].set[7] = &enemy_formation_[17];
    enemy_pool_[1].set[8] = &enemy_formation_[18];
    enemy_pool_[1].set[9] = &enemy_formation_[19];

    // EnemyPool #2
    enemy_pool_[2].set[0] = &enemy_formation_[0];
    enemy_pool_[2].set[1] = &enemy_formation_[1];
    enemy_pool_[2].set[2] = &enemy_formation_[2];
    enemy_pool_[2].set[3] = &enemy_formation_[3];
    enemy_pool_[2].set[4] = &enemy_formation_[4];
    enemy_pool_[2].set[5] = &enemy_formation_[55];
    enemy_pool_[2].set[6] = &enemy_formation_[56];
    enemy_pool_[2].set[7] = &enemy_formation_[57];
    enemy_pool_[2].set[8] = &enemy_formation_[58];
    enemy_pool_[2].set[9] = &enemy_formation_[59];

    // EnemyPool #3
    enemy_pool_[3].set[0] = &enemy_formation_[50];
    enemy_pool_[3].set[1] = &enemy_formation_[51];
    enemy_pool_[3].set[2] = &enemy_formation_[52];
    enemy_pool_[3].set[3] = &enemy_formation_[53];
    enemy_pool_[3].set[4] = &enemy_formation_[54];
    enemy_pool_[3].set[5] = &enemy_formation_[5];
    enemy_pool_[3].set[6] = &enemy_formation_[6];
    enemy_pool_[3].set[7] = &enemy_formation_[7];
    enemy_pool_[3].set[8] = &enemy_formation_[8];
    enemy_pool_[3].set[9] = &enemy_formation_[9];

    // EnemyPool #4
    enemy_pool_[4].set[0] = &enemy_formation_[60];
    enemy_pool_[4].set[1] = &enemy_formation_[61];
    enemy_pool_[4].set[2] = &enemy_formation_[62];
    enemy_pool_[4].set[3] = &enemy_formation_[63];
    enemy_pool_[4].set[4] = &enemy_formation_[64];
    enemy_pool_[4].set[5] = &enemy_formation_[65];
    enemy_pool_[4].set[6] = &enemy_formation_[66];
    enemy_pool_[4].set[7] = &enemy_formation_[67];
    enemy_pool_[4].set[8] = &enemy_formation_[68];
    enemy_pool_[4].set[9] = &enemy_formation_[69];

    // EnemyPool #5
    enemy_pool_[5].set[0] = &enemy_formation_[10];
    enemy_pool_[5].set[1] = &enemy_formation_[61];
    enemy_pool_[5].set[2] = &enemy_formation_[12];
    enemy_pool_[5].set[3] = &enemy_formation_[63];
    enemy_pool_[5].set[4] = &enemy_formation_[14];
    enemy_pool_[5].set[5] = &enemy_formation_[65];
    enemy_pool_[5].set[6] = &enemy_formation_[16];
    enemy_pool_[5].set[7] = &enemy_formation_[67];
    enemy_pool_[5].set[8] = &enemy_formation_[18];
    enemy_pool_[5].set[9] = &enemy_formation_[69];

    // EnemyPool #6
    enemy_pool_[6].set[0] = &enemy_formation_[60];
    enemy_pool_[6].set[1] = &enemy_formation_[11];
    enemy_pool_[6].set[2] = &enemy_formation_[62];
    enemy_pool_[6].set[3] = &enemy_formation_[13];
    enemy_pool_[6].set[4] = &enemy_formation_[64];
    enemy_pool_[6].set[5] = &enemy_formation_[15];
    enemy_pool_[6].set[6] = &enemy_formation_[66];
    enemy_pool_[6].set[7] = &enemy_formation_[17];
    enemy_pool_[6].set[8] = &enemy_formation_[68];
    enemy_pool_[6].set[9] = &enemy_formation_[19];

    // EnemyPool #7
    enemy_pool_[7].set[0] = &enemy_formation_[20];
    enemy_pool_[7].set[1] = &enemy_formation_[21];
    enemy_pool_[7].set[2] = &enemy_formation_[22];
    enemy_pool_[7].set[3] = &enemy_formation_[23];
    enemy_pool_[7].set[4] = &enemy_formation_[24];
    enemy_pool_[7].set[5] = &enemy_formation_[65];
    enemy_pool_[7].set[6] = &enemy_formation_[66];
    enemy_pool_[7].set[7] = &enemy_formation_[67];
    enemy_pool_[7].set[8] = &enemy_formation_[68];
    enemy_pool_[7].set[9] = &enemy_formation_[69];

    // EnemyPool #8
    enemy_pool_[8].set[0] = &enemy_formation_[70];
    enemy_pool_[8].set[1] = &enemy_formation_[71];
    enemy_pool_[8].set[2] = &enemy_formation_[72];
    enemy_pool_[8].set[3] = &enemy_formation_[73];
    enemy_pool_[8].set[4] = &enemy_formation_[74];
    enemy_pool_[8].set[5] = &enemy_formation_[15];
    enemy_pool_[8].set[6] = &enemy_formation_[16];
    enemy_pool_[8].set[7] = &enemy_formation_[17];
    enemy_pool_[8].set[8] = &enemy_formation_[18];
    enemy_pool_[8].set[9] = &enemy_formation_[19];

    // EnemyPool #9
    enemy_pool_[9].set[0] = &enemy_formation_[20];
    enemy_pool_[9].set[1] = &enemy_formation_[21];
    enemy_pool_[9].set[2] = &enemy_formation_[22];
    enemy_pool_[9].set[3] = &enemy_formation_[23];
    enemy_pool_[9].set[4] = &enemy_formation_[24];
    enemy_pool_[9].set[5] = &enemy_formation_[70];
    enemy_pool_[9].set[6] = &enemy_formation_[71];
    enemy_pool_[9].set[7] = &enemy_formation_[72];
    enemy_pool_[9].set[8] = &enemy_formation_[73];
    enemy_pool_[9].set[9] = &enemy_formation_[74];
}

// Inicializa las fases del juego
void Game::initGameStages() {
    // STAGE 1
    stage_[0].number = 1;
    stage_[0].current_power = 0;
    stage_[0].power_to_complete = 200;
    stage_[0].min_menace = 7 + (4 * 1);
    stage_[0].max_menace = 7 + (4 * 3);
    stage_[0].enemy_pool = &enemy_pool_[0];

    // STAGE 2
    stage_[1].number = 2;
    stage_[1].current_power = 0;
    stage_[1].power_to_complete = 300;
    stage_[1].min_menace = 7 + (4 * 2);
    stage_[1].max_menace = 7 + (4 * 4);
    stage_[1].enemy_pool = &enemy_pool_[1];

    // STAGE 3
    stage_[2].number = 3;
    stage_[2].current_power = 0;
    stage_[2].power_to_complete = 600;
    stage_[2].min_menace = 7 + (4 * 3);
    stage_[2].max_menace = 7 + (4 * 5);
    stage_[2].enemy_pool = &enemy_pool_[2];

    // STAGE 4
    stage_[3].number = 4;
    stage_[3].current_power = 0;
    stage_[3].power_to_complete = 600;
    stage_[3].min_menace = 7 + (4 * 3);
    stage_[3].max_menace = 7 + (4 * 5);
    stage_[3].enemy_pool = &enemy_pool_[3];

    // STAGE 5
    stage_[4].number = 5;
    stage_[4].current_power = 0;
    stage_[4].power_to_complete = 600;
    stage_[4].min_menace = 7 + (4 * 4);
    stage_[4].max_menace = 7 + (4 * 6);
    stage_[4].enemy_pool = &enemy_pool_[4];

    // STAGE 6
    stage_[5].number = 6;
    stage_[5].current_power = 0;
    stage_[5].power_to_complete = 600;
    stage_[5].min_menace = 7 + (4 * 4);
    stage_[5].max_menace = 7 + (4 * 6);
    stage_[5].enemy_pool = &enemy_pool_[5];

    // STAGE 7
    stage_[6].number = 7;
    stage_[6].current_power = 0;
    stage_[6].power_to_complete = 650;
    stage_[6].min_menace = 7 + (4 * 5);
    stage_[6].max_menace = 7 + (4 * 7);
    stage_[6].enemy_pool = &enemy_pool_[6];

    // STAGE 8
    stage_[7].number = 8;
    stage_[7].current_power = 0;
    stage_[7].power_to_complete = 750;
    stage_[7].min_menace = 7 + (4 * 5);
    stage_[7].max_menace = 7 + (4 * 7);
    stage_[7].enemy_pool = &enemy_pool_[7];

    // STAGE 9
    stage_[8].number = 9;
    stage_[8].current_power = 0;
    stage_[8].power_to_complete = 850;
    stage_[8].min_menace = 7 + (4 * 6);
    stage_[8].max_menace = 7 + (4 * 8);
    stage_[8].enemy_pool = &enemy_pool_[8];

    // STAGE 10
    stage_[9].number = 10;
    stage_[9].current_power = 0;
    stage_[9].power_to_complete = 950;
    stage_[9].min_menace = 7 + (4 * 7);
    stage_[9].max_menace = 7 + (4 * 10);
    stage_[9].enemy_pool = &enemy_pool_[9];
}

// Crea una formación de enemigos
void Game::deployEnemyFormation() {
    // Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última
    if (enemy_deploy_counter_ == 0) {
        // En este punto se decide entre crear una powerball o una formación enemiga
        if ((rand() % 100 < 15) && (canPowerBallBeCreated())) {
            // Crea una powerball
            createPowerBall();

            // Da un poco de margen para que se creen mas enemigos
            enemy_deploy_counter_ = 50;
        } else {
            // Decrementa el contador de despliegues enemigos de la PowerBall
            power_ball_counter_ > 0 ? power_ball_counter_-- : power_ball_counter_ = 0;

            // Elige una formación enemiga la azar
            Uint8 set = (rand() % 10);

            // Evita repetir la ultima formación enemiga desplegada
            if (set == last_enemy_deploy_) {
                ++set %= 10;
            }

            last_enemy_deploy_ = set;

            const Uint8 NUM_ENEMIES = stage_[current_stage_].enemy_pool->set[set]->number_of_enemies;
            for (int i = 0; i < NUM_ENEMIES; ++i) {
                createBalloon(stage_[current_stage_].enemy_pool->set[set]->init[i].x,
                    stage_[current_stage_].enemy_pool->set[set]->init[i].y,
                    stage_[current_stage_].enemy_pool->set[set]->init[i].kind,
                    stage_[current_stage_].enemy_pool->set[set]->init[i].vel_x,
                    enemy_speed_,
                    stage_[current_stage_].enemy_pool->set[set]->init[i].creation_counter);
            }

            enemy_deploy_counter_ = 300;
        }
    }
}

// Aumenta el poder de la fase
void Game::increaseStageCurrentPower(Uint8 power) {
    stage_[current_stage_].current_power += power;
}

// Establece el valor de la variable
void Game::setHiScore(Uint32 score) {
    hi_score_ = score;
}

// Actualiza el valor de hiScore en caso necesario
void Game::updateHiScore() {
    // Si la puntuación actual es mayor que la máxima puntuación
    for (auto *player : players_) {
        if (player->getScore() > hi_score_) {
            // Actualiza la máxima puntuación
            hi_score_ = player->getScore();

            // Almacena la máxima puntuación en el fichero junto con un checksum
            score_data_file_[0] = hi_score_;
            score_data_file_[1] = hi_score_ % 43;

            // Si se supera la máxima puntuación emite sonido
            if (!hi_score_achieved_) {
                hi_score_achieved_ = true;
                Audio::get()->playSound(hi_score_sound_);
            }
        }
    }
}

// Transforma un valor numérico en una cadena de 6 cifras
auto Game::updateScoreText(Uint32 num) -> std::string {
    if ((num >= 0) && (num <= 9)) {
        return ("000000" + std::to_string(num));
    }

    if ((num >= 10) && (num <= 99)) {
        return ("00000" + std::to_string(num));
    }

    if ((num >= 100) && (num <= 999)) {
        return ("0000" + std::to_string(num));
    }

    if ((num >= 1000) && (num <= 9999)) {
        return ("000" + std::to_string(num));
    }

    if ((num >= 010000) && (num <= 99999)) {
        return ("00" + std::to_string(num));
    }

    if ((num >= 100000) && (num <= 999999)) {
        return ("0" + std::to_string(num));
    }

    if ((num >= 1000000) && (num <= 9999999)) {
        return (std::to_string(num));
    }

    return (std::to_string(num));
}

// Pinta el marcador en pantalla usando un objeto texto
void Game::renderScoreBoard() {
    // Dibuja el fondo del marcador
    if (difficulty_ == DIFFICULTY_NORMAL) {  // Pone el color gris de siempre
        SDL_SetRenderDrawColor(renderer_, 46, 63, 71, 255);
    } else {  // Pinta el fondo del marcador del color de la dificultad
        SDL_SetRenderDrawColor(renderer_, difficulty_color_.r, difficulty_color_.g, difficulty_color_.b, 255);
    }
    SDL_FRect f_rect = {0, 160, 256, 32};
    SDL_RenderFillRect(renderer_, &f_rect);

    // Dibuja la linea que separa el marcador de la zona de juego
    SDL_SetRenderDrawColor(renderer_, 13, 26, 43, 255);
    SDL_RenderLine(renderer_, 0, 160, 255, 160);

    // Anclas para los elementos
    const int OFFSET1 = 162;
    const int OFFSET2 = OFFSET1 + 7;
    const int OFFSET3 = OFFSET2 + 7;
    const int OFFSET4 = OFFSET3 + 7;

    const int OFFSET_LEFT = PLAY_AREA_LEFT + 45;
    const int OFFSET_RIGHT = PLAY_AREA_RIGHT - 45;

    // PLAYER1 - SCORE
    text_scoreboard_->writeCentered(OFFSET_LEFT, OFFSET1, Lang::get()->getText(53));
    text_scoreboard_->writeCentered(OFFSET_LEFT, OFFSET2, updateScoreText(players_[0]->getScore()));

    // PLAYER1 - MULT
    text_scoreboard_->writeCentered(OFFSET_LEFT, OFFSET3, Lang::get()->getText(55));
    text_scoreboard_->writeCentered(OFFSET_LEFT, OFFSET4, std::to_string(players_[0]->getScoreMultiplier()).substr(0, 3));

    if (num_players_ == 2) {
        // PLAYER2 - SCORE
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET1, Lang::get()->getText(54));
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET2, updateScoreText(players_[1]->getScore()));

        // PLAYER2 - MULT
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET3, Lang::get()->getText(55));
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET4, std::to_string(players_[1]->getScoreMultiplier()).substr(0, 3));
    } else {
        // PLAYER2 - SCORE
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET1, Lang::get()->getText(54));
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET2, "0000000");

        // PLAYER2 - MULT
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET3, Lang::get()->getText(55));
        text_scoreboard_->writeCentered(OFFSET_RIGHT, OFFSET4, "1.0");
    }

    // STAGE
    text_scoreboard_->writeCentered(PLAY_AREA_CENTER_X, OFFSET1, Lang::get()->getText(57) + std::to_string(stage_[current_stage_].number));

    // POWERMETER
    power_meter_sprite_->setPosY(OFFSET2);
    power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
    power_meter_sprite_->render();
    const float PERCENT = (stage_[current_stage_].current_power * 40.0F) / stage_[current_stage_].power_to_complete;
    power_meter_sprite_->setSpriteClip(40, 0, (int)PERCENT, 7);
    power_meter_sprite_->render();

    // HI-SCORE
    text_scoreboard_->writeCentered(PLAY_AREA_CENTER_X, OFFSET3, Lang::get()->getText(56));
    text_scoreboard_->writeCentered(PLAY_AREA_CENTER_X, OFFSET4, hi_score_name_ + updateScoreText(hi_score_));
}

// Actualiza las variables del jugador
void Game::updatePlayers() {
    for (auto *player : players_) {
        player->update();

        // Comprueba la colisión entre el jugador y los globos
        if (checkPlayerBalloonCollision(player)) {
            if (player->isAlive()) {
                if (demo_.enabled) {
                    section_->name = SECTION_PROG_TITLE;
                    section_->subsection = SUBSECTION_TITLE_INSTRUCTIONS;
                } else {
                    killPlayer(player);
                }
            }
        }

        // Comprueba las colisiones entre el jugador y los items
        checkPlayerItemCollision(player);
    }
}

// Dibuja a los jugadores
void Game::renderPlayers() {
    for (auto *player : players_) {
        player->render();
    }
}

// Actualiza las variables de la fase
void Game::updateStage() {
    if (stage_[current_stage_].current_power >= stage_[current_stage_].power_to_complete) {
        // Cambio de fase
        current_stage_++;
        last_stage_reached_ = current_stage_;
        if (current_stage_ == 10) {                    // Ha llegado al final el juego
            game_completed_ = true;                    // Marca el juego como completado
            current_stage_ = 9;                        // Deja el valor dentro de los limites
            stage_[current_stage_].current_power = 0;  // Deja el poder a cero para que no vuelva a entrar en esta condición
            destroyAllBalloons();                      // Destruye a todos los enemigos
            stage_[current_stage_].current_power = 0;  // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos lo globos
            menace_current_ = 255;                     // Sube el nivel de amenaza para que no cree mas globos
            for (auto *player : players_) {            // Añade un millon de puntos a los jugadores que queden vivos
                if (player->isAlive()) {
                    player->addScore(1000000);
                }
            }
            updateHiScore();
            Audio::get()->stopMusic();
        }
        Audio::get()->playSound(stage_change_sound_);
        stage_bitmap_counter_ = 0;
        enemy_speed_ = default_enemy_speed_;
        setBalloonSpeed(enemy_speed_);
        effect_.flash = true;
        effect_.shake = true;
    }

    // Incrementa el contador del bitmap que aparece mostrando el cambio de fase
    if (stage_bitmap_counter_ < STAGE_COUNTER) {
        stage_bitmap_counter_++;
    }

    // Si el juego se ha completado, el bitmap se detiene en el centro de la pantalla
    if (game_completed_) {
        stage_bitmap_counter_ = std::min<int>(stage_bitmap_counter_, 100);
    }
}

// Actualiza el estado de muerte
void Game::updateDeath() {
    // Comprueba si todos los jugadores estan muertos
    bool all_dead = true;
    for (auto *player : players_) {
        all_dead &= (!player->isAlive());
    }

    if (all_dead) {
        if (death_counter_ > 0) {
            death_counter_--;

            if ((death_counter_ == 250) || (death_counter_ == 200) || (death_counter_ == 180) || (death_counter_ == 120) || (death_counter_ == 60)) {
                // Hace sonar aleatoriamente uno de los 4 sonidos de burbujas
                if (!demo_.enabled) {
                    const Uint8 INDEX = rand() % 4;
                    Ja::Sound *sound[4] = {bubble1_sound_, bubble2_sound_, bubble3_sound_, bubble4_sound_};
                    Audio::get()->playSound(sound[INDEX]);
                }
            }
        } else {
            section_->subsection = SUBSECTION_GAME_GAMEOVER;
        }
    }
}

// Renderiza el fade final cuando se acaba la partida
void Game::renderDeathFade(int counter) {  // Counter debe ir de 0 a 150
    SDL_SetRenderDrawColor(renderer_, 0x27, 0x27, 0x36, 255);

    if (counter < 150) {
        // 192 / 6 = 32, 6 cuadrados de 32 pixeles
        SDL_FRect rect[12];
        auto h = (float)(counter / 3);
        for (int i = 0; i < 12; ++i) {
            rect[i].x = 0;
            rect[i].y = (float)(i * 16);
            rect[i].w = (float)GAMECANVAS_WIDTH;
            if (i == 0) {
                rect[i].h = h;
            } else {
                rect[i].h = std::max(rect[i - 1].h - 3.0F, 0.0F);
            }
            SDL_RenderFillRect(renderer_, &rect[i]);
        }
    } else {
        SDL_FRect rect = {0, 0, (float)GAMECANVAS_WIDTH, (float)GAMECANVAS_HEIGHT};
        SDL_RenderFillRect(renderer_, &rect);
    }
}

// Actualiza los globos
void Game::updateBalloons() {
    for (auto *balloon : balloons_) {
        balloon->update();
    }
}

// Pinta en pantalla todos los globos activos
void Game::renderBalloons() {
    for (auto *balloon : balloons_) {
        balloon->render();
    }
}

// Crea un globo nuevo en el vector de globos
auto Game::createBalloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer) -> Uint8 {
    const int INDEX = (kind - 1) % 4;
    auto *b = new Balloon(x, y, kind, velx, speed, creationtimer, balloon_textures_[INDEX], balloon_animations_[INDEX], renderer_);
    balloons_.push_back(b);
    return (Uint8)(balloons_.size() - 1);
}

// Crea una PowerBall
void Game::createPowerBall() {
    const int POS_Y = PLAY_AREA_TOP;

    const int LEFT = PLAY_AREA_LEFT;
    const int CENTER = PLAY_AREA_CENTER_X - (Balloon::WIDTH_4 / 2);
    const int RIGHT = PLAY_AREA_RIGHT - Balloon::WIDTH_4;

    const int LUCK = rand() % 3;
    const int X[3] = {LEFT, CENTER, RIGHT};
    const float VX[3] = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE};

    auto *b = new Balloon(X[LUCK], POS_Y, Balloon::POWER_BALL, VX[LUCK], enemy_speed_, 100, balloon_textures_[3], balloon_animations_[3], renderer_);
    balloons_.push_back(b);

    power_ball_enabled_ = true;
    power_ball_counter_ = Balloon::POWERBALL_COUNTER;
}

// Establece la velocidad de los globos
void Game::setBalloonSpeed(float speed) {
    for (auto *balloon : balloons_) {
        if (balloon->isEnabled()) {
            balloon->setSpeed(speed);
        }
    }
}

// Incrementa la velocidad de los globos
void Game::incBalloonSpeed() {
    // La velocidad solo se incrementa en el modo normal
    if (difficulty_ == DIFFICULTY_NORMAL) {
        if (enemy_speed_ == Balloon::SPEED_1) {
            enemy_speed_ = Balloon::SPEED_2;
        }

        else if (enemy_speed_ == Balloon::SPEED_2) {
            enemy_speed_ = Balloon::SPEED_3;
        }

        else if (enemy_speed_ == Balloon::SPEED_3) {
            enemy_speed_ = Balloon::SPEED_4;
        }

        else if (enemy_speed_ == Balloon::SPEED_4) {
            enemy_speed_ = Balloon::SPEED_5;
        }

        setBalloonSpeed(enemy_speed_);
    }
}

// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
void Game::updateBalloonSpeed() {
    const float PERCENT = (float)stage_[current_stage_].current_power / (float)stage_[current_stage_].power_to_complete;
    if (enemy_speed_ == Balloon::SPEED_1) {
        if (PERCENT > 0.2F) { incBalloonSpeed(); }
    } else if (enemy_speed_ == Balloon::SPEED_2) {
        if (PERCENT > 0.4F) { incBalloonSpeed(); }
    } else if (enemy_speed_ == Balloon::SPEED_3) {
        if (PERCENT > 0.6F) { incBalloonSpeed(); }
    } else if (enemy_speed_ == Balloon::SPEED_4) {
        if (PERCENT > 0.8F) { incBalloonSpeed(); }
    }
}

// Explosiona un globo. Lo destruye y crea otros dos si es el caso
void Game::popBalloon(Balloon *balloon) {
    // Aumenta el poder de la fase
    increaseStageCurrentPower(1);
    balloons_popped_++;

    const Uint8 KIND = balloon->getKind();
    switch (KIND) {
        // Tipus més petits: simplement elimina el globó
        case Balloon::BALLOON_1:
        case Balloon::HEXAGON_1:
            balloon->pop();
            break;

        // Si es del tipo PowerBall, destruye todos los globos
        case Balloon::POWER_BALL:
            destroyAllBalloons();
            power_ball_enabled_ = false;
            enemy_deploy_counter_ = 20;
            break;

        // En cualquier otro caso, crea dos globos de un tipo inferior
        default:
            const int INDEX = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, Balloon::VELX_NEGATIVE, enemy_speed_, 0);
            balloons_[INDEX]->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
            if (balloons_[INDEX]->getClass() == Balloon::BALLOON_CLASS) {
                balloons_[INDEX]->setVelY(-2.50F);
            } else {
                balloons_[INDEX]->setVelY(Balloon::VELX_NEGATIVE);
            }

            const int INDEX2 = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, Balloon::VELX_POSITIVE, enemy_speed_, 0);
            balloons_[INDEX2]->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
            if (balloons_[INDEX2]->getClass() == Balloon::BALLOON_CLASS) {
                balloons_[INDEX2]->setVelY(-2.50F);
            } else {
                balloons_[INDEX2]->setVelY(Balloon::VELX_NEGATIVE);
            }

            // Elimina el globo
            balloon->pop();
            break;
    }

    // Recalcula el nivel de amenaza
    evaluateAndSetMenace();
}

// Explosiona un globo. Lo destruye
void Game::destroyBalloon(Balloon *balloon) {
    int score = 0;
    Uint8 power = 0;

    // Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos
    switch (balloon->getSize()) {
        case Balloon::SIZE_4:
            score = Balloon::SCORE_4 + (2 * Balloon::SCORE_3) + (4 * Balloon::SCORE_2) + (8 * Balloon::SCORE_1);
            power = 15;
            break;

        case Balloon::SIZE_3:
            score = Balloon::SCORE_3 + (2 * Balloon::SCORE_2) + (4 * Balloon::SCORE_1);
            power = 7;
            break;

        case Balloon::SIZE_2:
            score = Balloon::SCORE_2 + (2 * Balloon::SCORE_1);
            power = 3;
            break;

        case Balloon::SIZE_1:
            score = Balloon::SCORE_1;
            power = 1;
            break;

        default:
            score = 0;
            power = 0;
            break;
    }

    // Otorga los puntos correspondientes al globo
    for (auto *player : players_) {
        player->addScore(Uint32(score * player->getScoreMultiplier() * difficulty_score_multiplier_));
    }
    updateHiScore();

    // Aumenta el poder de la fase
    increaseStageCurrentPower(power);
    balloons_popped_ += power;

    // Destruye el globo
    balloon->pop();

    // Recalcula el nivel de amenaza
    evaluateAndSetMenace();
}

// Destruye todos los globos
void Game::destroyAllBalloons() {
    for (auto *balloon : balloons_) {
        if ((balloon->isEnabled()) && (!balloon->isPopping())) {
            destroyBalloon(balloon);
        }
    }

    enemy_deploy_counter_ = 255;
    if (!demo_.enabled) {
        Audio::get()->playSound(power_ball_sound_);
    }
    effect_.flash = true;
    effect_.shake = true;
}

// Detiene todos los globos
void Game::stopAllBalloons(Uint16 time) {
    for (auto *balloon : balloons_) {
        if (balloon->isEnabled()) {
            balloon->setStop(true);
            balloon->setStoppedTimer(time);
        }
    }
}

// Pone en marcha todos los globos
void Game::startAllBalloons() {
    for (auto *balloon : balloons_) {
        if ((balloon->isEnabled()) && (!balloon->isBeingCreated())) {
            balloon->setStop(false);
            balloon->setStoppedTimer(0);
        }
    }
}

// Vacia el vector de globos
void Game::freeBalloons() {
    if (!balloons_.empty()) {
        for (int i = balloons_.size() - 1; i >= 0; --i) {
            if (!balloons_[i]->isEnabled()) {
                delete balloons_[i];
                balloons_.erase(balloons_.begin() + i);
            }
        }
    }
}

// Comprueba la colisión entre el jugador y los globos activos
auto Game::checkPlayerBalloonCollision(Player *player) -> bool {
    for (auto *balloon : balloons_) {
        if ((balloon->isEnabled()) && !(balloon->isStopped()) && !(balloon->isInvulnerable())) {
            if (checkCollision(player->getCollider(), balloon->getCollider())) {
                return true;
            }
        }
    }

    return false;
}

// Comprueba la colisión entre el jugador y los items
void Game::checkPlayerItemCollision(Player *player) {
    if (!player->isAlive()) {
        return;
    }

    for (auto *item : items_) {
        if (item->isEnabled()) {
            if (checkCollision(player->getCollider(), item->getCollider())) {
                switch (item->getId()) {
                    case Item::Id::DISK:
                        player->addScore(1000);
                        updateHiScore();
                        createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n1000_sprite_->getWidth() / 2), player->getPosY(), n1000_sprite_);
                        Audio::get()->playSound(item_pick_up_sound_);
                        break;

                    case Item::Id::GAVINA:
                        player->addScore(2500);
                        updateHiScore();
                        createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n2500_sprite_->getWidth() / 2), player->getPosY(), n2500_sprite_);
                        Audio::get()->playSound(item_pick_up_sound_);
                        break;

                    case Item::Id::PACMAR:
                        player->addScore(5000);
                        updateHiScore();
                        createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n5000_sprite_->getWidth() / 2), player->getPosY(), n5000_sprite_);
                        Audio::get()->playSound(item_pick_up_sound_);
                        break;

                    case Item::Id::CLOCK:
                        enableTimeStopItem();
                        Audio::get()->playSound(item_pick_up_sound_);
                        break;

                    case Item::Id::COFFEE:
                        if (player->getCoffees() == 2) {
                            player->addScore(5000);
                            updateHiScore();
                            createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (n5000_sprite_->getWidth() / 2), player->getPosY(), n5000_sprite_);
                        }
                        player->giveExtraHit();
                        Audio::get()->playSound(item_pick_up_sound_);
                        break;

                    case Item::Id::COFFEE_MACHINE:
                        player->setPowerUp(true);
                        Audio::get()->playSound(item_pick_up_sound_);
                        coffee_machine_enabled_ = false;
                        break;

                    default:
                        break;
                }

                item->disable();
            }
        }
    }
}

// Comprueba y procesa la colisión entre las balas y los globos
void Game::checkBulletBalloonCollision() {
    for (auto *bullet : bullets_) {
        for (auto *balloon : balloons_) {
            if (!balloon->isEnabled() || balloon->isInvulnerable() || !bullet->isEnabled()) {
                continue;
            }
            if (checkCollision(balloon->getCollider(), bullet->getCollider())) {
                resolveBulletBalloonHit(bullet, balloon);
                break;
            }
        }
    }
}

// Resuelve un impacto bala-globo: puntos, sonido, explosión, drop de item
void Game::resolveBulletBalloonHit(Bullet *bullet, Balloon *balloon) {
    // Otorga los puntos al jugador que disparó la bala
    const int INDEX = bullet->getOwner();
    players_[INDEX]->incScoreMultiplier();
    players_[INDEX]->addScore(Uint32(balloon->getScore() * players_[INDEX]->getScoreMultiplier() * difficulty_score_multiplier_));
    updateHiScore();

    popBalloon(balloon);

    if (!demo_.enabled) {
        Audio::get()->playSound(balloon_sound_);
    }

    bullet->disable();

    // Suelta el item en caso de que salga uno
    const Item::Id DROPPED_ITEM = dropItem();
    if ((DROPPED_ITEM == Item::Id::NONE) || demo_.enabled || demo_.recording) {
        return;
    }

    if (DROPPED_ITEM != Item::Id::COFFEE_MACHINE) {
        createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY());
        Audio::get()->playSound(item_drop_sound_);
    } else {
        createItem(DROPPED_ITEM, players_[INDEX]->getPosX(), 0);
        coffee_machine_enabled_ = true;
    }
}

// Mueve las balas activas
void Game::moveBullets() {
    for (auto *bullet : bullets_) {
        if (bullet->isEnabled()) {
            if (bullet->move() == Bullet::MoveResult::OUT) {
                players_[bullet->getOwner()]->decScoreMultiplier();
            }
        }
    }
}

// Pinta las balas activas
void Game::renderBullets() {
    for (auto *bullet : bullets_) {
        if (bullet->isEnabled()) {
            bullet->render();
        }
    }
}

// Crea un objeto bala
void Game::createBullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner) {
    auto *b = new Bullet(x, y, kind, powered_up, owner, bullet_texture_, renderer_);
    bullets_.push_back(b);
}

// Vacia el vector de balas
void Game::freeBullets() {
    if (!bullets_.empty()) {
        for (int i = bullets_.size() - 1; i >= 0; --i) {
            if (!bullets_[i]->isEnabled()) {
                delete bullets_[i];
                bullets_.erase(bullets_.begin() + i);
            }
        }
    }
}

// Actualiza los items
void Game::updateItems() {
    for (auto *item : items_) {
        if (item->isEnabled()) {
            item->update();
            if (item->isOnFloor()) {
                Audio::get()->playSound(coffee_machine_sound_);
                effect_.shake = true;
            }
        }
    }
}

// Pinta los items activos
void Game::renderItems() {
    for (auto *item : items_) {
        item->render();
    }
}

// Devuelve un item en función del azar
auto Game::dropItem() -> Item::Id {
    const Uint8 LUCKY_NUMBER = rand() % 100;
    const Uint8 ITEM = rand() % 6;

    switch (ITEM) {
        case 0:
            if (LUCKY_NUMBER < helper_.item_disk_odds) { return Item::Id::DISK; }
            break;

        case 1:
            if (LUCKY_NUMBER < helper_.item_gavina_odds) { return Item::Id::GAVINA; }
            break;

        case 2:
            if (LUCKY_NUMBER < helper_.item_paco_odds) { return Item::Id::PACMAR; }
            break;

        case 3:
            if (LUCKY_NUMBER < helper_.item_clock_odds) { return Item::Id::CLOCK; }
            break;

        case 4:
            if (LUCKY_NUMBER < helper_.item_coffee_odds) {
                helper_.item_coffee_odds = ITEM_COFFEE_ODDS;
                return Item::Id::COFFEE;
            } else {
                if (helper_.need_coffee) {
                    helper_.item_coffee_odds++;
                }
            }
            break;

        case 5:
            if (LUCKY_NUMBER < helper_.item_coffee_machine_odds) {
                helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS;
                if ((!coffee_machine_enabled_) && (helper_.need_coffee_machine)) { return Item::Id::COFFEE_MACHINE; }
            } else {
                if (helper_.need_coffee_machine) { helper_.item_coffee_machine_odds++; }
            }
            break;

        default:
            break;
    }

    return Item::Id::NONE;
}

// Crea un objeto item
void Game::createItem(Item::Id kind, float x, float y) {
    const auto INDEX = static_cast<std::size_t>(kind) - 1;
    Item *item = new Item(kind, x, y, item_textures_[INDEX], item_animations_[INDEX], renderer_);
    items_.push_back(item);
}

// Vacia el vector de items
void Game::freeItems() {
    if (!items_.empty()) {
        for (int i = items_.size() - 1; i >= 0; --i) {
            if (!items_[i]->isEnabled()) {
                delete items_[i];
                items_.erase(items_.begin() + i);
            }
        }
    }
}

// Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto
void Game::createItemScoreSprite(int x, int y, const SmartSprite *sprite) {
    auto *ss = new SmartSprite(nullptr, renderer_);
    smart_sprites_.push_back(ss);

    // Crea una copia del objeto
    *ss = *sprite;
    ss->setPosX(x);
    ss->setPosY(y);
    ss->setDestX(x);
    ss->setDestY(y - 15);
    ss->setEnabled(true);
    ss->setEnabledCounter(100);
}

// Vacia el vector de smartsprites
void Game::freeSmartSprites() {
    if (!smart_sprites_.empty()) {
        for (int i = smart_sprites_.size() - 1; i >= 0; --i) {
            if (smart_sprites_[i]->hasFinished()) {
                delete smart_sprites_[i];
                smart_sprites_.erase(smart_sprites_.begin() + i);
            }
        }
    }
}

// Dibuja el efecto de flash
void Game::renderFlashEffect() {
    if (effect_.flash) {
        // Pantallazo blanco
        SDL_SetRenderDrawColor(renderer_, 0xFF, 0xFF, 0xFF, 0xFF);
        SDL_RenderClear(renderer_);

        effect_.flash = false;
    }
}

// Actualiza el efecto de agitar la pantalla
void Game::updateShakeEffect() {
    if (effect_.shake) {
        if (effect_.shake_counter > 0) {
            effect_.shake_counter--;
        } else {
            effect_.shake = false;
            effect_.shake_counter = SHAKE_COUNTER;
        }
    }
}

// Crea un SmartSprite para arrojar el item café al recibir un impacto
void Game::throwCoffee(int x, int y) {
    auto *ss = new SmartSprite(item_textures_[4], renderer_);
    smart_sprites_.push_back(ss);

    ss->setPosX(x - 8);
    ss->setPosY(y - 8);
    ss->setWidth(16);
    ss->setHeight(16);
    ss->setVelX(-1.0F + ((rand() % 5) * 0.5F));
    ss->setVelY(-4.0F);
    ss->setAccelX(0.0F);
    ss->setAccelY(0.2F);
    ss->setDestX(x + (ss->getVelX() * 50));
    ss->setDestY(GAMECANVAS_HEIGHT + 1);
    ss->setEnabled(true);
    ss->setEnabledCounter(1);
    ss->setSpriteClip(0, 0, 16, 16);
    ss->setRotate(true);
    ss->setRotateSpeed(10);
    ss->setRotateAmount(90.0);
}

// Actualiza los SmartSprites
void Game::updateSmartSprites() {
    for (auto *ss : smart_sprites_) {
        ss->update();
    }
}

// Pinta los SmartSprites activos
void Game::renderSmartSprites() {
    for (auto *ss : smart_sprites_) {
        ss->render();
    }
}

// Acciones a realizar cuando el jugador muere
void Game::killPlayer(Player *player) {
    if (!player->isInvulnerable()) {
        if (player->hasExtraHit()) {
            player->removeExtraHit();
            throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2));
            if (!demo_.enabled) {
                Audio::get()->playSound(coffee_out_sound_);
            }
        } else if (death_sequence_.phase == DeathPhase::NONE) {
            if (!demo_.enabled) {
                Audio::get()->pauseMusic();
                Audio::get()->playSound(player_collision_sound_);
            }
            stopAllBalloons(10);
            shakeScreen();
            death_sequence_.phase = DeathPhase::SHAKING;
            death_sequence_.phase_start_ticks = SDL_GetTicks();
            death_sequence_.player = player;
        }
    }
}

// Actualiza la secuencia de muerte del jugador
void Game::updateDeathSequence() {
    switch (death_sequence_.phase) {
        case DeathPhase::NONE:
        case DeathPhase::DONE:
            break;

        case DeathPhase::SHAKING:
            // Espera a que termine el efecto de agitación
            if (!isDeathShaking()) {
                death_sequence_.phase = DeathPhase::WAITING;
                death_sequence_.phase_start_ticks = SDL_GetTicks();
            }
            break;

        case DeathPhase::WAITING:
            // Espera 500ms antes de completar la muerte
            if (SDL_GetTicks() - death_sequence_.phase_start_ticks >= 500) {
                if (!demo_.enabled) {
                    Audio::get()->playSound(coffee_out_sound_);
                    if (allPlayersAreDead()) {
                        Audio::get()->stopMusic();
                    } else {
                        Audio::get()->resumeMusic();
                    }
                }
                death_sequence_.player->setAlive(false);
                death_sequence_.phase = DeathPhase::DONE;
                death_sequence_.player = nullptr;
            }
            break;
    }
}

// Calcula y establece el valor de amenaza en funcion de los globos activos
void Game::evaluateAndSetMenace() {
    menace_current_ = std::accumulate(balloons_.begin(), balloons_.end(), Uint8(0), [](Uint8 acc, Balloon *b) { return b->isEnabled() ? acc + b->getMenace() : acc; });
}

// Obtiene el valor de la variable
auto Game::getMenace() const -> Uint8 {
    return menace_current_;
}

// Establece el valor de la variable
void Game::setTimeStopped(bool value) {
    time_stopped_ = value;
}

// Obtiene el valor de la variable
auto Game::isTimeStopped() const -> bool {
    return time_stopped_;
}

// Establece el valor de la variable
void Game::setTimeStoppedCounter(Uint16 value) {
    time_stopped_counter_ = value;
}

// Incrementa el valor de la variable
void Game::incTimeStoppedCounter(Uint16 value) {
    time_stopped_counter_ += value;
}

// Actualiza y comprueba el valor de la variable
void Game::updateTimeStoppedCounter() {
    if (isTimeStopped()) {
        if (time_stopped_counter_ > 0) {
            time_stopped_counter_--;
            stopAllBalloons(TIME_STOPPED_COUNTER);
        } else {
            disableTimeStopItem();
        }
    }
}

// Actualiza la variable enemyDeployCounter
void Game::updateEnemyDeployCounter() {
    if (enemy_deploy_counter_ > 0) {
        enemy_deploy_counter_--;
    }
}

// Actualiza el juego
void Game::update() {
    // Actualiza el audio
    Audio::update();

    // Actualiza los efectos basados en tiempo real (no en el throttle del juego)
    updateDeathShake();
    updateDeathSequence();

    // Durante la secuencia de muerte, congela el resto del juego
    if (death_sequence_.phase == DeathPhase::SHAKING || death_sequence_.phase == DeathPhase::WAITING) {
        return;
    }

    // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
    if (SDL_GetTicks() - ticks_ > ticks_speed_) {
        // Actualiza el contador de ticks
        ticks_ = SDL_GetTicks();

        // Actualiza el contador de juego
        counter_++;

        // Comprueba el teclado/mando
        checkGameInput();

        // Actualiza las variables del jugador
        updatePlayers();

        // Actualiza el fondo
        updateBackground();

        // Mueve los globos
        updateBalloons();

        // Mueve las balas
        moveBullets();

        // Actualiza los items
        updateItems();

        // Actualiza el valor de currentStage
        updateStage();

        // Actualiza el estado de muerte
        updateDeath();

        // Actualiza los SmartSprites
        updateSmartSprites();

        // Actualiza los contadores de estado y efectos
        updateTimeStoppedCounter();
        updateEnemyDeployCounter();
        updateShakeEffect();

        // Actualiza el ayudante
        updateHelper();

        // Comprueba las colisiones entre globos y balas
        checkBulletBalloonCollision();

        // Comprueba el nivel de amenaza para ver si se han de crear nuevos enemigos
        updateMenace();

        // Actualiza la velocidad de los enemigos
        updateBalloonSpeed();

        // Actualiza el tramo final de juego, una vez completado
        updateGameCompleted();

        // Vacia los vectores
        freeBullets();
        freeBalloons();
        freeItems();
        freeSmartSprites();
    }
}

// Actualiza el fondo
void Game::updateBackground() {
    if (!game_completed_) {  // Si el juego no esta completo, la velocidad de las nubes es igual a los globos explotados
        clouds_speed_ = balloons_popped_;
    } else {  // Si el juego está completado, se reduce la velocidad de las nubes
        if (clouds_speed_ > 400) {
            clouds_speed_ -= 25;
        } else {
            clouds_speed_ = 200;
        }
    }

    // Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego
    const float SPEED = (-0.2F) + (-3.00F * ((float)clouds_speed_ / (float)total_power_to_complete_game_));

    // Aplica la velocidad calculada a las nubes
    clouds1_a_->setVelX(SPEED);
    clouds1_b_->setVelX(SPEED);
    clouds2_a_->setVelX(SPEED / 2);
    clouds2_b_->setVelX(SPEED / 2);

    // Mueve las nubes
    clouds1_a_->move();
    clouds1_b_->move();
    clouds2_a_->move();
    clouds2_b_->move();

    // Calcula el offset de las nubes
    if (clouds1_a_->getPosX() < -clouds1_a_->getWidth()) {
        clouds1_a_->setPosX(clouds1_a_->getWidth());
    }

    if (clouds1_b_->getPosX() < -clouds1_b_->getWidth()) {
        clouds1_b_->setPosX(clouds1_b_->getWidth());
    }

    if (clouds2_a_->getPosX() < -clouds2_a_->getWidth()) {
        clouds2_a_->setPosX(clouds2_a_->getWidth());
    }

    if (clouds2_b_->getPosX() < -clouds2_b_->getWidth()) {
        clouds2_b_->setPosX(clouds2_b_->getWidth());
    }

    // Calcula el frame de la hierba
    grass_sprite_->setSpriteClip(0, (6 * (counter_ / 20 % 2)), 256, 6);

    // Mueve los edificios en funcion de si está activo el efecto de agitarlos
    if (death_shake_.active) {
        const int V[] = {-1, 1, -1, 1, -1, 1, -1, 0};
        buildings_sprite_->setPosX(V[death_shake_.step]);
    } else if (effect_.shake) {
        buildings_sprite_->setPosX(((effect_.shake_counter % 2) * 2) - 1);
    } else {
        buildings_sprite_->setPosX(0);
    }
}

// Dibuja el fondo
void Game::renderBackground() {
    const float GRADIENT_NUMBER = std::min(((float)balloons_popped_ / 1250.0F), 3.0F);
    const float PERCENT = GRADIENT_NUMBER - (int)GRADIENT_NUMBER;
    const int ALPHA = std::max((255 - (int)(255 * PERCENT)), 0);

    // Dibuja el gradiente 2
    sky_colors_sprite_->setSpriteClip(sky_colors_rect_[((int)GRADIENT_NUMBER + 1) % 4]);
    game_sky_colors_texture_->setAlpha(255);
    sky_colors_sprite_->render();

    // Dibuja el gradiente 1 con una opacidad cada vez menor
    sky_colors_sprite_->setSpriteClip(sky_colors_rect_[(int)GRADIENT_NUMBER]);
    game_sky_colors_texture_->setAlpha(ALPHA);
    sky_colors_sprite_->render();

    // Dibuja las nubes
    clouds1_a_->render();
    clouds1_b_->render();
    clouds2_a_->render();
    clouds2_b_->render();

    // Dinuja los edificios
    buildings_sprite_->render();

    // Dibuja la hierba
    grass_sprite_->render();
}

// Dibuja el juego
void Game::render() {
    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();

    // Limpia la pantalla
    Screen::get()->clean(bgColor);

    // Dibuja los objetos
    renderBackground();
    renderBalloons();
    renderBullets();
    renderMessages();
    renderItems();
    renderSmartSprites();
    renderScoreBoard();
    renderPlayers();

    if ((death_counter_ <= 150) && !players_[0]->isAlive()) {
        renderDeathFade(150 - death_counter_);
    }

    if ((game_completed_) && (game_completed_counter_ >= GAME_COMPLETED_START_FADE)) {
        renderDeathFade(game_completed_counter_ - GAME_COMPLETED_START_FADE);
    }

    renderFlashEffect();

    // Vuelca el contenido del renderizador en pantalla
    Screen::get()->blit();
}

// Gestiona el nivel de amenaza
void Game::updateMenace() {
    if (game_completed_) {
        return;
    }

    const float PERCENT = stage_[current_stage_].current_power / stage_[current_stage_].power_to_complete;
    const Uint8 DIFFERENCE = stage_[current_stage_].max_menace - stage_[current_stage_].min_menace;

    // Aumenta el nivel de amenaza en función de la puntuación
    menace_threshold_ = stage_[current_stage_].min_menace + (DIFFERENCE * PERCENT);

    // Si el nivel de amenza es inferior al umbral
    if (menace_current_ < menace_threshold_) {
        // Crea una formación de enemigos
        deployEnemyFormation();

        // Recalcula el nivel de amenaza con el nuevo globo
        evaluateAndSetMenace();
    }
}

// Gestiona la entrada durante el juego
void Game::checkGameInput() {
    demo_.keys.left = 0;
    demo_.keys.right = 0;
    demo_.keys.no_input = 0;
    demo_.keys.fire = 0;
    demo_.keys.fire_left = 0;
    demo_.keys.fire_right = 0;

    // Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
    GlobalInputs::handle();

    if (demo_.enabled) {
        processDemoInput();
    } else {
        processLiveInput();
    }
}

// Rama de checkGameInput: reproduce el input grabado en data_file
void Game::processDemoInput() {
    const int INDEX = 0;
    const DemoKeys &keys = demo_.data_file[demo_.counter];

    if (keys.left == 1) {
        players_[INDEX]->setInput(LEFT);
    }
    if (keys.right == 1) {
        players_[INDEX]->setInput(RIGHT);
    }
    if (keys.no_input == 1) {
        players_[INDEX]->setInput(INVALID);
    }

    if (keys.fire == 1 && players_[INDEX]->canFire()) {
        players_[INDEX]->setInput(FIRE_CENTER);
        createBullet(players_[INDEX]->getPosX() + (players_[INDEX]->getWidth() / 2) - 4, players_[INDEX]->getPosY() + (players_[INDEX]->getHeight() / 2), Bullet::Kind::UP, players_[INDEX]->isPowerUp(), INDEX);
        players_[INDEX]->setFireCooldown(10);
    }

    if (keys.fire_left == 1 && players_[INDEX]->canFire()) {
        players_[INDEX]->setInput(FIRE_LEFT);
        createBullet(players_[INDEX]->getPosX() + (players_[INDEX]->getWidth() / 2) - 4, players_[INDEX]->getPosY() + (players_[INDEX]->getHeight() / 2), Bullet::Kind::LEFT, players_[INDEX]->isPowerUp(), INDEX);
        players_[INDEX]->setFireCooldown(10);
    }

    if (keys.fire_right == 1 && players_[INDEX]->canFire()) {
        players_[INDEX]->setInput(FIRE_RIGHT);
        createBullet(players_[INDEX]->getPosX() + (players_[INDEX]->getWidth() / 2) - 4, players_[INDEX]->getPosY() + (players_[INDEX]->getHeight() / 2), Bullet::Kind::RIGHT, players_[INDEX]->isPowerUp(), INDEX);
        players_[INDEX]->setFireCooldown(10);
    }

    // Si se pulsa cualquier tecla, se sale del modo demo
    if (Input::get()->checkAnyInput()) {
        section_->name = SECTION_PROG_TITLE;
    }

    // Incrementa el contador de la demo
    if (demo_.counter < TOTAL_DEMO_DATA) {
        demo_.counter++;
    } else {
        section_->name = SECTION_PROG_TITLE;
        section_->subsection = SUBSECTION_TITLE_INSTRUCTIONS;
    }
}

// Rama de checkGameInput: lee inputs reales del teclado/gamepad por jugador
void Game::processLiveInput() {
    int i = 0;
    for (auto *player : players_) {
        if (player->isAlive()) {
            processPlayerLiveInput(player, i);
            i++;
        }
    }
}

// Cuerpo per-player de processLiveInput
void Game::processPlayerLiveInput(Player *player, int i) {
    auto *input = Input::get();
    const auto &device = Options::inputs[i];

    // Movimiento izquierda / derecha / nada
    if (input->checkInput(LEFT, REPEAT_TRUE, device.device_type, device.id)) {
        player->setInput(LEFT);
        demo_.keys.left = 1;
    } else if (input->checkInput(RIGHT, REPEAT_TRUE, device.device_type, device.id)) {
        player->setInput(RIGHT);
        demo_.keys.right = 1;
    } else {
        player->setInput(INVALID);
        demo_.keys.no_input = 1;
    }

    // Disparo al centro
    if (input->checkInput(FIRE_CENTER, REPEAT_TRUE, device.device_type, device.id) && player->canFire()) {
        player->setInput(FIRE_CENTER);
        createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), Bullet::Kind::UP, player->isPowerUp(), i);
        player->setFireCooldown(10);
        Audio::get()->playSound(bullet_sound_);
        demo_.keys.fire = 1;
    }

    // Disparo a la izquierda
    if (input->checkInput(FIRE_LEFT, REPEAT_TRUE, device.device_type, device.id) && player->canFire()) {
        player->setInput(FIRE_LEFT);
        createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), Bullet::Kind::LEFT, player->isPowerUp(), i);
        player->setFireCooldown(10);
        Audio::get()->playSound(bullet_sound_);
        demo_.keys.fire_left = 1;
    }

    // Disparo a la derecha
    if (input->checkInput(FIRE_RIGHT, REPEAT_TRUE, device.device_type, device.id) && player->canFire()) {
        player->setInput(FIRE_RIGHT);
        createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), Bullet::Kind::RIGHT, player->isPowerUp(), i);
        player->setFireCooldown(10);
        Audio::get()->playSound(bullet_sound_);
        demo_.keys.fire_right = 1;
    }

    // Pausa
    if (input->checkInput(PAUSE, REPEAT_FALSE, device.device_type, device.id)) {
        section_->subsection = SUBSECTION_GAME_PAUSE;
    }

    // Grabación de demo
    if (demo_.counter < TOTAL_DEMO_DATA) {
        if (demo_.recording) {
            demo_.data_file[demo_.counter] = demo_.keys;
        }
        demo_.counter++;
    } else if (demo_.recording) {
        section_->name = SECTION_PROG_QUIT;
    }
}

// Pinta diferentes mensajes en la pantalla
void Game::renderMessages() {
    // GetReady
    if ((counter_ < STAGE_COUNTER) && (!demo_.enabled)) {
        text_nokia_big2_->write((int)get_ready_bitmap_path_[counter_], PLAY_AREA_CENTER_Y - 8, Lang::get()->getText(75), -2);
    }

    // Time Stopped
    if (time_stopped_) {
        if ((time_stopped_counter_ > 100) || (time_stopped_counter_ % 10 > 4)) {
            text_nokia2_->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, Lang::get()->getText(36) + std::to_string(time_stopped_counter_ / 10), -1, noColor, 1, shdwTxtColor);
        }

        if (time_stopped_counter_ > 100) {
            if (time_stopped_counter_ % 30 == 0) {
                Audio::get()->playSound(clock_sound_);
            }
        } else {
            if (time_stopped_counter_ % 15 == 0) {
                Audio::get()->playSound(clock_sound_);
            }
        }
    }

    // D E M O
    if (demo_.enabled) {
        if (demo_.counter % 30 > 14) {
            text_nokia_big2_->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, Lang::get()->getText(37), 0, noColor, 2, shdwTxtColor);
        }
    }

    // STAGE NUMBER
    if (stage_bitmap_counter_ < STAGE_COUNTER) {
        const int STAGE_NUM = stage_[current_stage_].number;
        std::string stage_text;

        if (STAGE_NUM == 10) {  // Ultima fase
            stage_text = Lang::get()->getText(79);
        } else {  // X fases restantes
            stage_text = std::to_string(11 - stage_[current_stage_].number) + Lang::get()->getText(38);
        }

        if (!game_completed_) {  // Escribe el numero de fases restantes
            text_nokia_big2_->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stage_bitmap_path_[stage_bitmap_counter_], stage_text, -2, noColor, 2, shdwTxtColor);
        } else {  // Escribe el texto de juego completado
            stage_text = Lang::get()->getText(50);
            text_nokia_big2_->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stage_bitmap_path_[stage_bitmap_counter_], stage_text, -2, noColor, 1, shdwTxtColor);
            text_nokia2_->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stage_bitmap_path_[stage_bitmap_counter_] + text_nokia_big2_->getCharacterSize() + 2, Lang::get()->getText(76), -1, noColor, 1, shdwTxtColor);
        }
    }
}

// Habilita el efecto del item de detener el tiempo
void Game::enableTimeStopItem() {
    stopAllBalloons(TIME_STOPPED_COUNTER);
    setTimeStopped(true);
    incTimeStoppedCounter(TIME_STOPPED_COUNTER);
    if (Audio::getRealMusicState() == Audio::MusicState::PLAYING) {
        Audio::get()->pauseMusic();
    }
}

// Deshabilita el efecto del item de detener el tiempo
void Game::disableTimeStopItem() {
    time_stopped_ = false;
    setTimeStoppedCounter(0);
    startAllBalloons();
    if (Audio::getRealMusicState() == Audio::MusicState::PAUSED) {
        Audio::get()->resumeMusic();
    }
}

// Inicia el efecto de agitación intensa de la pantalla
void Game::shakeScreen() {
    death_shake_.active = true;
    death_shake_.step = 0;
    death_shake_.last_step_ticks = SDL_GetTicks();
}

// Actualiza el efecto de agitación intensa
void Game::updateDeathShake() {
    if (!death_shake_.active) {
        return;
    }

    Uint32 now = SDL_GetTicks();
    if (now - death_shake_.last_step_ticks >= 50) {
        death_shake_.last_step_ticks = now;
        death_shake_.step++;
        if (death_shake_.step >= 8) {
            death_shake_.active = false;
        }
    }
}

// Indica si el efecto de agitación intensa está activo
auto Game::isDeathShaking() const -> bool {
    return death_shake_.active;
}

// Ejecuta un frame del juego
void Game::iterate() {
    // En modo demo, no hay pausa ni game over
    if (demo_.enabled) {
        if (section_->subsection == SUBSECTION_GAME_PAUSE || section_->subsection == SUBSECTION_GAME_GAMEOVER) {
            section_->name = SECTION_PROG_TITLE;
            section_->subsection = SUBSECTION_TITLE_INSTRUCTIONS;
            return;
        }
    }

    // Sección juego en pausa
    if (section_->subsection == SUBSECTION_GAME_PAUSE) {
        if (!pause_initialized_) {
            enterPausedGame();
        }
        updatePausedGame();
        renderPausedGame();
    }

    // Sección Game Over
    else if (section_->subsection == SUBSECTION_GAME_GAMEOVER) {
        if (!game_over_initialized_) {
            enterGameOverScreen();
        }
        updateGameOverScreen();
        renderGameOverScreen();
    }

    // Sección juego jugando
    else if ((section_->subsection == SUBSECTION_GAME_PLAY_1P) || (section_->subsection == SUBSECTION_GAME_PLAY_2P)) {
        // Resetea los flags de inicialización de sub-estados
        pause_initialized_ = false;
        game_over_initialized_ = false;

        // Si la música no está sonando
        if ((Audio::getRealMusicState() == Audio::MusicState::STOPPED) || (Audio::getRealMusicState() == Audio::MusicState::STOPPED)) {
            // Reproduce la música (nunca en modo demo: deja sonar la del título)
            if (!game_completed_ && !demo_.enabled) {
                if (players_[0]->isAlive()) {
                    Audio::get()->playMusic(game_music_);
                }
            }
        }

#ifdef PAUSE
        if (!pause)
            update();
#else
        // Actualiza la lógica del juego
        update();
#endif

        // Dibuja los objetos
        render();
    }
}

// Indica si el juego ha terminado
auto Game::hasFinished() const -> bool {
    return section_->name != SECTION_PROG_GAME;
}

// Procesa un evento individual
void Game::handleEvent(const SDL_Event *event) {
    // SDL_EVENT_QUIT ya lo maneja Director

    if (event->type == SDL_EVENT_WINDOW_FOCUS_LOST) {
        // Solo pausar durante el juego activo (no en demo, game over, ni ya en pausa)
        if (!demo_.enabled && (section_->subsection == SUBSECTION_GAME_PLAY_1P || section_->subsection == SUBSECTION_GAME_PLAY_2P)) {
            section_->subsection = SUBSECTION_GAME_PAUSE;
        }
    }

#ifdef PAUSE
    if (event->type == SDL_EVENT_KEY_DOWN) {
        if (event->key.scancode == SDL_SCANCODE_P) {
            pause = !pause;
        }
    }
#endif

    // Eventos específicos de la pantalla de game over
    if (section_->subsection == SUBSECTION_GAME_GAMEOVER) {
        if (event->type == SDL_EVENT_KEY_DOWN && static_cast<int>(event->key.repeat) == 0) {
            if (game_completed_) {
                game_over_post_fade_ = 1;
                fade_->activateFade();
                Audio::get()->playSound(item_pick_up_sound_);
            }
        }
    }
}

// Bucle para el juego
void Game::run() {
    while (!hasFinished()) {
        iterate();
    }
}

// Actualiza las variables del menu de pausa del juego
void Game::updatePausedGame() {
    if (SDL_GetTicks() - ticks_ <= ticks_speed_) {
        return;
    }
    ticks_ = SDL_GetTicks();

    // Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
    GlobalInputs::handle();

    if (leaving_pause_menu_) {
        updateLeavingPauseMenu();
    } else {
        updatePauseMenuUI();
    }
}

// Rama de updatePausedGame: cuenta atrás de salida y vuelta al juego
void Game::updateLeavingPauseMenu() {
    if (pause_counter_ > 0) {  // El contador está descendiendo
        const bool A = pause_counter_ == 90;
        const bool B = pause_counter_ == 60;
        const bool C = pause_counter_ == 30;
        if (A || B || C) {
            Audio::get()->playSound(clock_sound_);
        }
        pause_counter_--;
        return;
    }

    // Ha finalizado el contador
    section_->name = SECTION_PROG_GAME;
    section_->subsection = num_players_ == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P;

    if (Audio::getRealMusicState() == Audio::MusicState::PAUSED) {
        Audio::get()->resumeMusic();
    }
}

// Rama de updatePausedGame: lógica del menú de pausa
void Game::updatePauseMenuUI() {
    pause_menu_->update();
    pause_menu_->checkInput();

    switch (pause_menu_->getItemSelected()) {
        case 1:
            leaving_pause_menu_ = true;
            break;

        case 2:
            fade_->setFadeType(FADE_CENTER);
            fade_->activateFade();
            break;

        default:
            break;
    }

    fade_->update();
    if (fade_->hasEnded()) {
        section_->name = SECTION_PROG_TITLE;
        section_->subsection = SUBSECTION_TITLE_1;
        Audio::get()->stopMusic();
    }
}

// Dibuja el menu de pausa del juego
void Game::renderPausedGame() {
    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();

    // Limpia la pantalla
    Screen::get()->clean(bgColor);

    // Pinta el escenario
    {
        renderBackground();
        renderBalloons();
        renderBullets();
        renderMessages();
        renderItems();
        renderSmartSprites();
        renderScoreBoard();
        renderPlayers();

        if ((death_counter_ <= 150) && !players_[0]->isAlive()) {
            renderDeathFade(150 - death_counter_);
        }

        if ((game_completed_) && (game_completed_counter_ >= GAME_COMPLETED_START_FADE)) {
            renderDeathFade(game_completed_counter_ - GAME_COMPLETED_START_FADE);
        }

        renderFlashEffect();
    }

    if (leaving_pause_menu_) {
        text_nokia_big2_->writeCentered(GAMECANVAS_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, std::to_string((pause_counter_ / 30) + 1));
    } else {
        pause_menu_->render();
    }
    fade_->render();

    // Vuelca el contenido del renderizador en pantalla
    Screen::get()->blit();
}

// Inicializa el estado de pausa del juego
void Game::enterPausedGame() {
    // Pone en pausa la música
    if (Audio::getRealMusicState() == Audio::MusicState::PLAYING) {
        Audio::get()->pauseMusic();
    }

    // Reinicia el menu
    pause_menu_->reset();
    leaving_pause_menu_ = false;

    // Inicializa variables
    pause_counter_ = 90;
    pause_initialized_ = true;
}

// Actualiza los elementos de la pantalla de game over
void Game::updateGameOverScreen() {
    // Calcula la lógica de los objetos
    if (SDL_GetTicks() - ticks_ > ticks_speed_) {
        // Actualiza el contador de ticks
        ticks_ = SDL_GetTicks();

        // Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
        GlobalInputs::handle();

        // Actualiza la lógica del menu
        game_over_menu_->update();

        // Actualiza el fade
        fade_->update();

        // Si ha terminado el fade, actua segun se haya operado
        if (fade_->hasEnded()) {
            switch (game_over_post_fade_) {
                case 0:  // YES
                    section_->name = SECTION_PROG_GAME;
                    deleteAllVectorObjects();
                    init();
                    section_->subsection = num_players_ == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P;
                    break;

                case 1:  // NO
                    section_->name = SECTION_PROG_TITLE;
                    section_->subsection = SUBSECTION_TITLE_1;
                    break;

                default:
                    break;
            }
        }

        // Comprueba las entradas para el menu solo si no esta el juego completo
        if (!game_completed_) {
            game_over_menu_->checkInput();

            // Comprueba si se ha seleccionado algún item del menú
            switch (game_over_menu_->getItemSelected()) {
                case 0:  // YES
                    game_over_post_fade_ = 0;
                    fade_->activateFade();
                    break;

                case 1:  // NO
                    game_over_post_fade_ = 1;
                    fade_->activateFade();
                    break;

                default:
                    break;
            }
        }
    }
}

// Dibuja los elementos de la pantalla de game over
void Game::renderGameOverScreen() {
    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();

    // Limpia la pantalla
    Screen::get()->clean(bgColor);

    // Dibujo
    if (!game_completed_) {  // Dibujo de haber perdido la partida
        game_over_sprite_->render();
    } else {  // Dinujo de haber completado la partida
        game_over_end_sprite_->render();
    }

    // Dibuja los objetos
    if (num_players_ == 1) {
        // Congratulations!!
        if (game_completed_) {
            text_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 8), Lang::get()->getText(50));
        }

        // Game Over
        text_big_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 6), Lang::get()->getText(43));

        // Your Score
        text_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 3), Lang::get()->getText(44) + std::to_string(players_[0]->getScore()));
    } else {
        // Congratulations!!
        if (game_completed_) {
            text_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 9), Lang::get()->getText(50));
        }

        // Game Over
        text_big_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 7), Lang::get()->getText(43));

        // Player1 Score
        text_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), Lang::get()->getText(77) + std::to_string(players_[0]->getScore()));

        // Player2 Score
        text_->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 2), Lang::get()->getText(78) + std::to_string(players_[1]->getScore()));
    }

    // Continue?
    if (!game_completed_) {  // Solo dibuja el menu de continuar en el caso de no haber completado la partida
        text_->writeCentered(199, PLAY_AREA_CENTER_Y + (BLOCK * 3), Lang::get()->getText(45));
        game_over_menu_->render();
    }

    // Pinta el fade
    fade_->render();

    // Vuelca el contenido del renderizador en pantalla
    Screen::get()->blit();
}

// Inicializa el estado de game over
void Game::enterGameOverScreen() {
    // Guarda los puntos
    saveScoreFile();

    // Reinicia el menu
    game_over_menu_->reset();
    game_over_post_fade_ = 0;
    game_over_initialized_ = true;
}

// Indica si se puede crear una powerball
auto Game::canPowerBallBeCreated() -> bool {
    return (!power_ball_enabled_) && (calculateScreenPower() > Balloon::POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0);
}

// Calcula el poder actual de los globos en pantalla
auto Game::calculateScreenPower() -> int {
    return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int acc, Balloon *b) { return b->isEnabled() ? acc + b->getPower() : acc; });
}

// Inicializa las variables que contienen puntos de ruta para mover objetos
void Game::initPaths() {
    // Vector con los valores del seno para 360 grados
    float sin[360];
    for (int i = 0; i < 360; ++i) {
        sin[i] = SDL_sinf((float)i * 3.14F / 180.0F);
    }

    // Letrero de STAGE #
    const int FIRST_PART = STAGE_COUNTER / 4;  // 50
    const int SECOND_PART = FIRST_PART * 3;    // 150
    const int CENTER_POINT = PLAY_AREA_CENTER_Y - (BLOCK * 2);
    const int DISTANCE = (PLAY_AREA_BOTTOM) - (PLAY_AREA_CENTER_Y - 16);

    for (int i = 0; i < STAGE_COUNTER; ++i) {
        if (i < FIRST_PART) {
            stage_bitmap_path_[i] = (sin[(int)((i * 1.8F) + 90)] * (DISTANCE) + CENTER_POINT);
        }

        else if (i < SECOND_PART) {
            stage_bitmap_path_[i] = (int)CENTER_POINT;
        }

        else {
            stage_bitmap_path_[i] = (sin[(int)(((i - 149) * 1.8F) + 90)] * (CENTER_POINT + 17) - 17);
        }
    }

    // Letrero de GetReady
    const int SIZE = text_nokia_big2_->lenght(Lang::get()->getText(75), -2);

    const float START1 = PLAY_AREA_LEFT - SIZE;
    const float FINISH1 = PLAY_AREA_CENTER_X - (SIZE / 2);

    const float START2 = FINISH1;
    const float FINISH2 = PLAY_AREA_RIGHT;

    const float DISTANCE1 = FINISH1 - START1;
    const float DISTANCE2 = FINISH2 - START2;

    for (int i = 0; i < STAGE_COUNTER; ++i) {
        if (i < FIRST_PART) {
            get_ready_bitmap_path_[i] = sin[(int)(i * 1.8F)];
            get_ready_bitmap_path_[i] *= DISTANCE1;
            get_ready_bitmap_path_[i] -= SIZE;
        }

        else if (i < SECOND_PART) {
            get_ready_bitmap_path_[i] = (int)FINISH1;
        }

        else {
            get_ready_bitmap_path_[i] = sin[(int)((i - 150) * 1.8F)];
            get_ready_bitmap_path_[i] *= DISTANCE2;
            get_ready_bitmap_path_[i] += FINISH1;
        }
    }
}

// Actualiza el tramo final de juego, una vez completado
void Game::updateGameCompleted() {
    if (game_completed_) {
        game_completed_counter_++;
    }

    if (game_completed_counter_ == GAME_COMPLETED_END) {
        section_->subsection = SUBSECTION_GAME_GAMEOVER;
    }
}

// Actualiza las variables de ayuda
void Game::updateHelper() {
    // Solo ofrece ayuda cuando la amenaza es elevada
    if (menace_current_ > 15) {
        for (auto *player : players_) {
            helper_.need_coffee = player->getCoffees() == 0;

            helper_.need_coffee_machine = !player->isPowerUp();
        }
    } else {
        helper_.need_coffee = false;
        helper_.need_coffee_machine = false;
    }
}

// Comprueba si todos los jugadores han muerto
auto Game::allPlayersAreDead() -> bool {
    bool success = true;
    for (auto *player : players_) {
        success &= (!player->isAlive());
    }

    return success;
}

// Elimina todos los objetos contenidos en vectores
void Game::deleteAllVectorObjects() {
    for (auto *player : players_) {
        delete player;
    };
    players_.clear();

    for (auto *ballon : balloons_) {
        delete ballon;
    };
    balloons_.clear();

    for (auto *bullet : bullets_) {
        delete bullet;
    };
    bullets_.clear();

    for (auto *item : items_) {
        delete item;
    };
    items_.clear();

    for (auto *smart_sprite : smart_sprites_) {
        delete smart_sprite;
    };
    smart_sprites_.clear();
}

// Establece la máxima puntuación desde fichero o desde las puntuaciones online
void Game::setHiScore() {
    // Carga el fichero de puntos
    loadScoreFile();
    hi_score_name_ = "";
}