#pragma once #include #include // for uint8_t #include // for string, basic_string #include // for vector #include "game/entities/bullet.h" // for Bullet::Kind (signatura de createBullet) #include "game/entities/item.h" // for Item::Id (signatura de dropItem/createItem) #include "utils/utils.h" // for DemoKeys, Color class Balloon; class Fade; class Menu; class MovingSprite; class Player; class SmartSprite; class Sprite; class Text; class Texture; namespace Ja { struct Music; struct Sound; } // namespace Ja // Clase Game class Game { public: Game(int num_players, int current_stage, SDL_Renderer *renderer, bool demo, Section *section); // Constructor ~Game(); // Destructor Game(const Game &) = delete; auto operator=(const Game &) -> Game & = delete; void run(); // Bucle para el juego void iterate(); // Ejecuta un frame del juego [[nodiscard]] auto hasFinished() const -> bool; // Indica si el juego ha terminado void handleEvent(const SDL_Event *event); // Procesa un evento private: // Branques de iterate() — separades per a reduir la complexitat cognitiva void iteratePaused(float dt_s); void iterateGameOver(float dt_s); void iteratePlaying(float dt_s); // Cantidad de elementos a escribir en los ficheros de datos static constexpr int TOTAL_SCORE_DATA = 3; static constexpr int TOTAL_DEMO_DATA = 2000; // Contadores static constexpr int STAGE_COUNTER = 200; static constexpr int SHAKE_COUNTER = 10; static constexpr int HELP_COUNTER = 1000; static constexpr int GAME_COMPLETED_START_FADE = 500; static constexpr int GAME_COMPLETED_END = 700; // Formaciones enemigas static constexpr int NUMBER_OF_ENEMY_FORMATIONS = 100; static constexpr int MAX_NUMBER_OF_ENEMIES_IN_A_FORMATION = 50; // Porcentaje de aparición de los objetos static constexpr int ITEM_POINTS_1_DISK_ODDS = 10; static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6; static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3; static constexpr int ITEM_CLOCK_ODDS = 5; static constexpr int ITEM_COFFEE_ODDS = 5; static constexpr int ITEM_COFFEE_MACHINE_ODDS = 4; // Valores para las variables asociadas a los objetos static constexpr int TIME_STOPPED_COUNTER = 300; struct EnemyInit { int x; // Posición en el eje X donde crear al enemigo int y; // Posición en el eje Y donde crear al enemigo float vel_x; // Velocidad inicial en el eje X Uint8 kind; // Tipo de enemigo Uint16 creation_counter; // Temporizador para la creación del enemigo }; struct EnemyFormation // Contiene la información de una formación enemiga { Uint8 number_of_enemies; // Cantidad de enemigos que forman la formación EnemyInit init[MAX_NUMBER_OF_ENEMIES_IN_A_FORMATION]; // Vector con todas las inicializaciones de los enemigos de la formación }; struct EnemyPool { EnemyFormation *set[10]; // Conjunto de formaciones enemigas }; struct Stage // Contiene todas las variables relacionadas con una fase { EnemyPool *enemy_pool; // El conjunto de formaciones enemigas de la fase Uint16 current_power; // Cantidad actual de poder Uint16 power_to_complete; // Cantidad de poder que se necesita para completar la fase Uint8 max_menace; // Umbral máximo de amenaza de la fase Uint8 min_menace; // Umbral mínimo de amenaza de la fase Uint8 number; // Numero de fase }; struct Effect { bool flash; // Indica si se ha de pintar la pantalla de blanco bool shake; // Indica si se ha de agitar la pantalla Uint8 shake_counter; // Contador para medir el tiempo que dura el efecto }; // Estado para el efecto de agitación intensa (muerte del jugador) struct DeathShake { bool active; // Indica si el efecto está activo Uint8 step; // Paso actual del efecto (0-7) Uint32 last_step_ticks; // Ticks del último paso }; // Fases de la secuencia de muerte del jugador enum class DeathPhase : std::uint8_t { NONE, SHAKING, WAITING, DONE }; // Estado de la secuencia de muerte del jugador struct DeathSequence { DeathPhase phase; // Fase actual Uint32 phase_start_ticks; // Ticks del inicio de la fase actual Player *player; // Jugador que está muriendo }; struct Helper { bool need_coffee; // Indica si se necesitan cafes bool need_coffee_machine; // Indica si se necesita PowerUp bool need_power_ball; // Indica si se necesita una PowerBall int counter; // Contador para no dar ayudas consecutivas int item_disk_odds; // Probabilidad de aparición del objeto int item_gavina_odds; // Probabilidad de aparición del objeto int item_paco_odds; // Probabilidad de aparición del objeto int item_clock_odds; // Probabilidad de aparición del objeto int item_coffee_odds; // Probabilidad de aparición del objeto int item_coffee_machine_odds; // Probabilidad de aparición del objeto }; struct Demo { bool enabled; // Indica si está activo el modo demo bool recording; // Indica si está activado el modo para grabar la demo Uint16 counter; // Contador para el modo demo DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo DemoKeys data_file[TOTAL_DEMO_DATA]; // Datos del fichero con los movimientos para la demo }; void update(float dt_s); // Actualiza el juego void render(); // Dibuja el juego void init(); // Inicializa las variables necesarias para la sección 'Game' void loadMedia(); // Carga los recursos necesarios para la sección 'Game' auto loadScoreFile() -> bool; // Carga el fichero de puntos auto loadDemoFile() -> bool; // Carga el fichero de datos para la demo auto saveScoreFile() -> bool; // Guarda el fichero de puntos auto saveDemoFile() -> bool; // Guarda el fichero de datos para la demo void initEnemyFormations(); // Inicializa las formaciones enemigas void initEnemyFormationsZero(); // Helper de initEnemyFormations void initEnemyFormationsLinear(); // Helper de initEnemyFormations void initEnemyFormationsSymmetric(); // Helper de initEnemyFormations void initEnemyFormationsHexagonsAndTest(); // Helper de initEnemyFormations void initEnemyPools(); // Inicializa los conjuntos de formaciones void initGameStages(); // Inicializa las fases del juego void deployEnemyFormation(); // Crea una formación de enemigos void increaseStageCurrentPower(Uint8 power); // Aumenta el poder de la fase void setHiScore(Uint32 score); // Establece el valor de la variable void updateHiScore(); // Actualiza el valor de HiScore en caso necesario static auto updateScoreText(Uint32 num) -> std::string; // Transforma un valor numérico en una cadena de 6 cifras void renderScoreBoard(); // Pinta el marcador en pantalla usando un objeto texto void updatePlayers(float dt_s); // Actualiza las variables del jugador void renderPlayers(); // Dibuja a los jugadores void updateStage(float dt_s); // Actualiza las variables de la fase void updateDeath(float dt_s); // Actualiza el estado de muerte void renderDeathFade(int counter); // Renderiza el fade final cuando se acaba la partida void updateBalloons(float dt_s); // Actualiza los globos void renderBalloons(); // Pinta en pantalla todos los globos activos auto createBalloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer) -> Uint8; // Crea un globo nuevo en el vector de globos void createPowerBall(); // Crea una PowerBall void setBalloonSpeed(float speed); // Establece la velocidad de los globos void incBalloonSpeed(); // Incrementa la velocidad de los globos void updateBalloonSpeed(); // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase void popBalloon(Balloon *balloon); // Explosiona un globo. Lo destruye y crea otros dos si es el caso void destroyBalloon(Balloon *balloon); // Explosiona un globo. Lo destruye void destroyAllBalloons(); // Destruye todos los globos void stopAllBalloons(Uint16 time); // Detiene todos los globos void startAllBalloons(); // Pone en marcha todos los globos void freeBalloons(); // Vacia el vector de globos auto checkPlayerBalloonCollision(Player *player) -> bool; // Comprueba la colisión entre el jugador y los globos activos void checkPlayerItemCollision(Player *player); // Comprueba la colisión entre el jugador y los items void checkBulletBalloonCollision(); // Comprueba la colisión entre las balas y los globos void resolveBulletBalloonHit(Bullet *bullet, Balloon *balloon); // Resuelve un impacto bala-globo (helper de checkBulletBalloonCollision) void moveBullets(float dt_s); // Mueve las balas activas void renderBullets(); // Pinta las balas activas void createBullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner); // Crea un objeto bala void freeBullets(); // Vacia el vector de balas void updateItems(float dt_s); // Actualiza los items void renderItems(); // Pinta los items activos auto dropItem() -> Item::Id; // Devuelve un item en función del azar void createItem(Item::Id kind, float x, float y); // Crea un objeto item void freeItems(); // Vacia el vector de items void createItemScoreSprite(int x, int y, const SmartSprite *sprite); // Crea un objeto SmartSprite void freeSmartSprites(); // Vacia el vector de smartsprites void renderFlashEffect(); // Dibuja el efecto de flash void updateShakeEffect(float dt_s); // Actualiza el efecto de agitar la pantalla void throwCoffee(int x, int y); // Crea un SmartSprite para arrojar el item café al recibir un impacto void updateSmartSprites(float dt_s); // Actualiza los SmartSprites void renderSmartSprites(); // Pinta los SmartSprites activos void killPlayer(Player *player); // Acciones a realizar cuando el jugador muere void evaluateAndSetMenace(); // Calcula y establece el valor de amenaza en funcion de los globos activos [[nodiscard]] auto getMenace() const -> Uint8; // Obtiene el valor de la variable void setTimeStopped(bool value); // Establece el valor de la variable [[nodiscard]] auto isTimeStopped() const -> bool; // Obtiene el valor de la variable void setTimeStoppedCounter(Uint16 value); // Establece el valor de la variable void incTimeStoppedCounter(Uint16 value); // Incrementa el valor de la variable void updateEnemyDeployCounter(float dt_s); // Actualiza la variable EnemyDeployCounter void updateTimeStoppedCounter(float dt_s); // Actualiza y comprueba el valor de la variable void updateMenace(); // Gestiona el nivel de amenaza void updateBackground(float dt_s); // Actualiza el fondo void renderBackground(); // Dibuja el fondo void checkGameInput(); // Gestiona la entrada durante el juego void processDemoInput(); // Helper de checkGameInput void processLiveInput(); // Helper de checkGameInput void processPlayerLiveInput(Player *player, int i); // Helper de checkGameInput void renderMessages(); // Pinta diferentes mensajes en la pantalla void enableTimeStopItem(); // Habilita el efecto del item de detener el tiempo void disableTimeStopItem(); // Deshabilita el efecto del item de detener el tiempo void shakeScreen(); // Inicia el efecto de agitación intensa de la pantalla void updateDeathShake(); // Actualiza el efecto de agitación intensa [[nodiscard]] auto isDeathShaking() const -> bool; // Indica si el efecto de agitación intensa está activo void updateDeathSequence(); // Actualiza la secuencia de muerte del jugador void updatePausedGame(float dt_s); // Actualiza el menu de pausa void updateLeavingPauseMenu(float dt_s); // Helper void updatePauseMenuUI(float dt_s); // Helper void renderPausedGame(); // Dibuja el menu de pausa del juego void enterPausedGame(); // Inicializa el estado de pausa del juego void updateGameOverScreen(float dt_s); // Actualiza game over void renderGameOverScreen(); // Dibuja los elementos de la pantalla de game over void enterGameOverScreen(); // Inicializa el estado de game over auto canPowerBallBeCreated() -> bool; // Indica si se puede crear una powerball auto calculateScreenPower() -> int; // Calcula el poder actual de los globos en pantalla void initPaths(); // Inicializa las variables que contienen puntos de ruta para mover objetos void updateGameCompleted(float dt_s); // Actualiza el tramo final de juego void updateHelper(float dt_s); // Actualiza las variables de ayuda auto allPlayersAreDead() -> bool; // Comprueba si todos los jugadores han muerto void deleteAllVectorObjects(); // Elimina todos los objetos contenidos en vectores void setHiScore(); // Establece la máxima puntuación desde fichero o desde las puntuaciones online // Objetos y punteros SDL_Renderer *renderer_; // El renderizador de la ventana Section *section_; // Seccion actual dentro del juego std::vector players_; // Vector con los jugadores std::vector balloons_; // Vector con los globos std::vector bullets_; // Vector con las balas std::vector items_; // Vector con los items std::vector smart_sprites_; // Vector con los smartsprites Texture *bullet_texture_; // Textura para las balas std::vector item_textures_; // Vector con las texturas de los items std::vector balloon_textures_; // Vector con las texturas de los globos std::vector player1_textures_; // Vector con las texturas del jugador std::vector player2_textures_; // Vector con las texturas del jugador std::vector> player_textures_; // Vector con todas las texturas de los jugadores; Texture *game_buildings_texture_; // Textura con los edificios de fondo Texture *game_clouds_texture_; // Textura con las nubes de fondo Texture *game_grass_texture_; // Textura con la hierba del suelo Texture *game_power_meter_texture_; // Textura con el marcador de poder de la fase Texture *game_sky_colors_texture_; // Textura con los diferentes colores de fondo del juego Texture *game_text_texture_; // Textura para los sprites con textos Texture *game_over_texture_; // Textura para la pantalla de game over Texture *game_over_end_texture_; // Textura para la pantalla de game over de acabar el juego std::vector *> item_animations_; // Vector con las animaciones de los items std::vector *> player_animations_; // Vector con las animaciones del jugador std::vector *> balloon_animations_; // Vector con las animaciones de los globos Text *text_; // Fuente para los textos del juego Text *text_big_; // Fuente de texto grande Text *text_scoreboard_; // Fuente para el marcador del juego Text *text_nokia2_; // Otra fuente de texto para mensajes Text *text_nokia_big2_; // Y la versión en grande Menu *game_over_menu_; // Menú de la pantalla de game over Menu *pause_menu_; // Menú de la pantalla de pausa Fade *fade_; // Objeto para renderizar fades SDL_Event *event_handler_; // Manejador de eventos MovingSprite *clouds1_a_; // Sprite para las nubes superiores MovingSprite *clouds1_b_; // Sprite para las nubes superiores MovingSprite *clouds2_a_; // Sprite para las nubes inferiores MovingSprite *clouds2_b_; // Sprite para las nubes inferiores SmartSprite *n1000_sprite_; // Sprite con el texto 1.000 SmartSprite *n2500_sprite_; // Sprite con el texto 2.500 SmartSprite *n5000_sprite_; // Sprite con el texto 5.000 Sprite *buildings_sprite_; // Sprite con los edificios de fondo Sprite *sky_colors_sprite_; // Sprite con los graficos del degradado de color de fondo Sprite *grass_sprite_; // Sprite para la hierba Sprite *power_meter_sprite_; // Sprite para el medidor de poder de la fase Sprite *game_over_sprite_; // Sprite para dibujar los graficos del game over Sprite *game_over_end_sprite_; // Sprite para dibujar los graficos del game over de acabar el juego Ja::Sound *balloon_sound_; // Sonido para la explosión del globo Ja::Sound *bullet_sound_; // Sonido para los disparos Ja::Sound *player_collision_sound_; // Sonido para la colisión del jugador con un enemigo Ja::Sound *hi_score_sound_; // Sonido para cuando se alcanza la máxima puntuación Ja::Sound *item_drop_sound_; // Sonido para cuando se genera un item Ja::Sound *item_pick_up_sound_; // Sonido para cuando se recoge un item Ja::Sound *coffee_out_sound_; // Sonido para cuando el jugador pierde el café al recibir un impacto Ja::Sound *stage_change_sound_; // Sonido para cuando se cambia de fase Ja::Sound *bubble1_sound_; // Sonido para cuando el jugador muere Ja::Sound *bubble2_sound_; // Sonido para cuando el jugador muere Ja::Sound *bubble3_sound_; // Sonido para cuando el jugador muere Ja::Sound *bubble4_sound_; // Sonido para cuando el jugador muere Ja::Sound *clock_sound_; // Sonido para cuando se detiene el tiempo con el item reloj Ja::Sound *power_ball_sound_; // Sonido para cuando se explota una Power Ball Ja::Sound *coffee_machine_sound_; // Sonido para cuando la máquina de café toca el suelo Ja::Music *game_music_; // Musica de fondo // Variables int num_players_; // Numero de jugadores float elapsed_s_{0.0F}; // Acumulador global de temps de joc Uint32 hi_score_; // Puntuación máxima bool hi_score_achieved_; // Indica si se ha superado la puntuación máxima std::string hi_score_name_; // Nombre del jugador que ostenta la máxima puntuación Stage stage_[10]; // Variable con los datos de cada pantalla Uint8 current_stage_; // Indica la fase actual Uint8 stage_bitmap_counter_; // Contador para el tiempo visible del texto de Stage (frame-based) float stage_bitmap_counter_s_{0.0F}; // Contador (time-based) float stage_bitmap_path_[STAGE_COUNTER]; // Vector con los puntos Y por donde se desplaza el texto float get_ready_bitmap_path_[STAGE_COUNTER]; // Vector con los puntos X por donde se desplaza el texto Uint16 death_counter_; // Contador para la animación de muerte del jugador (frame-based) float death_counter_s_{0.0F}; // Contador (time-based) Uint8 menace_current_; // Nivel de amenaza actual Uint8 menace_threshold_; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el numero de globos bool time_stopped_; // Indica si el tiempo está detenido Uint16 time_stopped_counter_; // Temporizador (frame-based) float time_stopped_counter_s_{0.0F}; // Temporizador (time-based) Uint32 counter_; // Contador para el juego (frame-based, derivat de elapsed_s_*60 en time-based) Uint32 score_data_file_[TOTAL_SCORE_DATA]; // Datos del fichero de puntos SDL_Rect sky_colors_rect_[4]; // Vector con las coordenadas de los 4 colores de cielo Uint16 balloons_popped_; // Lleva la cuenta de los globos explotados Uint8 last_enemy_deploy_; // Guarda cual ha sido la última formación desplegada para no repetir; int enemy_deploy_counter_; // Cuando se lanza una formación, se le da un valor y no sale otra hasta que llegue a cero (frame-based) float enemy_deploy_counter_s_{0.0F}; // Comptador (time-based) float enemy_speed_; // Velocidad a la que se mueven los enemigos float default_enemy_speed_; // Velocidad base de los enemigos, sin incrementar Effect effect_; // Variable para gestionar los efectos visuales float shake_phase_s_{0.0F}; // Acumulador per decrementar shake_counter a 60Hz (time-based) float helper_counter_s_{0.0F}; // Acumulador per al comptador helper_.counter (time-based) float enemy_deploy_phase_s_{0.0F}; // Acumulador per al decrement de enemy_deploy_counter_ (time-based) DeathShake death_shake_; // Variable para gestionar el efecto de agitación intensa DeathSequence death_sequence_; // Variable para gestionar la secuencia de muerte Helper helper_; // Variable para gestionar las ayudas bool power_ball_enabled_; // Indica si hay una powerball ya activa Uint8 power_ball_counter_; // Contador de formaciones enemigas entre la aparicion de una PowerBall y otra bool coffee_machine_enabled_; // Indica si hay una máquina de café en el terreno de juego bool game_completed_; // Indica si se ha completado la partida, llegando al final de la ultima pantalla int game_completed_counter_; // Contador per al tram final (frame-based) float game_completed_counter_s_{0.0F}; // Comptador (time-based) Uint8 difficulty_; // Dificultad del juego float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad Color difficulty_color_; // Color asociado a la dificultad Input::Device player_one_control_; // Variable para almacenar el valor de las opciones EnemyFormation enemy_formation_[NUMBER_OF_ENEMY_FORMATIONS]; // Vector con todas las formaciones enemigas EnemyPool enemy_pool_[10]; // Variable con los diferentes conjuntos de formaciones enemigas Uint8 last_stage_reached_; // Contiene el numero de la última pantalla que se ha alcanzado Demo demo_; // Variable con todas las variables relacionadas con el modo demo int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases int clouds_speed_{0}; // Velocidad a la que se desplazan las nubes int pause_counter_; // Contador per a sortir del menu de pausa (frame-based, frames) float pause_counter_phase_s_{0.0F}; // Acumulador de fase per decrementar pause_counter_ a 60Hz (time-based) bool leaving_pause_menu_; // Indica si esta saliendo del menu de pausa para volver al juego bool pause_initialized_; // Indica si la pausa ha sido inicializada bool game_over_initialized_; // Indica si el game over ha sido inicializado int game_over_post_fade_; // Opción a realizar cuando termina el fundido del game over };