diff --git a/CMakeLists.txt b/CMakeLists.txt index ebf795a..b000469 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,9 @@ target_compile_options(${PROJECT_NAME} PRIVATE $<$:-Os -ffunctio # Definir _DEBUG en modo Debug target_compile_definitions(${PROJECT_NAME} PRIVATE $<$:_DEBUG>) +# Descomentar la siguiente línea para activar el modo grabación de demos +# target_compile_definitions(${PROJECT_NAME} PRIVATE RECORDING) + # Configuración específica para cada plataforma if(WIN32) diff --git a/config/param_320x256.txt b/config/param_320x256.txt index 2023d6f..e14472d 100644 --- a/config/param_320x256.txt +++ b/config/param_320x256.txt @@ -40,7 +40,7 @@ scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás ( # --- TITLE --- title.press_start_position 180 # Posición Y del texto "Press Start" -title.title_duration 14 # Duración de la pantalla de título (segundos) +title.title_duration 1 # Duración de la pantalla de título (segundos) title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" title.title_c_c_position 80 # Posición Y del título principal title.bg_color 41526F # Color de fondo en la sección titulo diff --git a/data/demo/demo1.bin b/data/demo/demo1.bin index d17b069..b95466e 100644 Binary files a/data/demo/demo1.bin and b/data/demo/demo1.bin differ diff --git a/data/demo/demo2.bin b/data/demo/demo2.bin index 4b63448..9844aaf 100644 Binary files a/data/demo/demo2.bin and b/data/demo/demo2.bin differ diff --git a/data/demo/demo3.bin b/data/demo/demo3.bin new file mode 100644 index 0000000..233aa92 Binary files /dev/null and b/data/demo/demo3.bin differ diff --git a/source/director.cpp b/source/director.cpp index 9fdca43..29e9e3f 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -42,7 +42,7 @@ Director::Director(int argc, std::span argv) { Section::name = Section::Name::GAME; Section::options = Section::Options::GAME_PLAY_1P; #elif _DEBUG - Section::name = Section::Name::GAME; + Section::name = Section::Name::TITLE; Section::options = Section::Options::GAME_PLAY_1P; #else // NORMAL GAME Section::name = Section::Name::LOGO; diff --git a/source/screen.cpp b/source/screen.cpp index fd525a1..1644e96 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -217,6 +217,10 @@ void Screen::renderInfo() { // FPS const std::string FPS_TEXT = std::to_string(fps_.last_value) + " FPS"; debug_info_.text->writeDX(Text::COLOR | Text::STROKE, param.game.width - debug_info_.text->length(FPS_TEXT) - 2, 1 + debug_info_.text->getCharacterSize(), FPS_TEXT, 1, param.debug.color, 1, param.debug.color.DARKEN(150)); +#ifdef RECORDING + // RECORDING + debug_info_.text->writeDX(Text::COLOR | Text::STROKE, param.game.width - debug_info_.text->length("RECORDING"), 2*(1 + debug_info_.text->getCharacterSize()), "RECORDING", 1, param.debug.color, 1, param.debug.color.DARKEN(150)); +#endif } } #endif diff --git a/source/sections/game.cpp b/source/sections/game.cpp index b182021..aa51618 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -48,7 +48,7 @@ #endif // Constructor -Game::Game(Player::Id player_id, int current_stage, bool demo) +Game::Game(Player::Id player_id, int current_stage, bool demo_enabled) : renderer_(Screen::get()->getRenderer()), screen_(Screen::get()), input_(Input::get()), @@ -62,7 +62,7 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) tabe_(std::make_unique()), hit_(Hit(Resource::get()->getTexture("hit.png"))) { // Pasa variables - demo_.enabled = demo; + demo_.enabled = demo_enabled; // Otras variables Section::name = Section::Name::GAME; @@ -83,7 +83,9 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) fade_in_->setPostDuration(0); fade_in_->setType(Fade::Type::RANDOM_SQUARE2); fade_in_->setMode(Fade::Mode::IN); +#ifndef RECORDING fade_in_->activate(); +#endif fade_out_->setColor(param.fade.color); fade_out_->setPostDuration(param.fade.post_duration_ms); @@ -109,6 +111,9 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) pause_manager_->setServiceMenuPause(is_active); } }); +#ifdef RECORDING + setState(State::PLAYING); +#endif } Game::~Game() { @@ -202,7 +207,7 @@ void Game::updateHiScore() { hi_score_.name = Options::settings.hi_score_table.front().name; // Si la puntuación actual es mayor que la máxima puntuación - for (const auto &player : players_) { + for (const auto& player : players_) { if (player->getScore() > hi_score_.score) { // Actualiza la máxima puntuación hi_score_.score = player->getScore(); @@ -220,7 +225,7 @@ void Game::updateHiScore() { // Actualiza las variables del jugador void Game::updatePlayers(float deltaTime) { - for (auto &player : players_) { + for (auto& player : players_) { player->update(deltaTime); if (player->isPlaying()) { @@ -256,7 +261,7 @@ void Game::updatePlayers(float deltaTime) { // Dibuja a los jugadores void Game::renderPlayers() { - for (auto &player : players_) { + for (auto& player : players_) { player->render(); } } @@ -395,31 +400,33 @@ void Game::checkState() { // Destruye todos los items void Game::destroyAllItems() { - for (auto &item : items_) { + for (auto& item : items_) { item->disable(); } } // Comprueba la colisión entre el jugador y los globos activos -auto Game::checkPlayerBalloonCollision(std::shared_ptr &player) -> std::shared_ptr { - for (auto &balloon : balloon_manager_->getBalloons()) { +auto Game::checkPlayerBalloonCollision(std::shared_ptr& player) -> std::shared_ptr { +#ifndef RECORDING + for (auto& balloon : balloon_manager_->getBalloons()) { if (!balloon->isInvulnerable() && !balloon->isPowerBall()) { if (checkCollision(player->getCollider(), balloon->getCollider())) { return balloon; // Devuelve el globo con el que se ha producido la colisión } } } +#endif return nullptr; // No se ha producido ninguna colisión } // Comprueba la colisión entre el jugador y los items -void Game::checkPlayerItemCollision(std::shared_ptr &player) { +void Game::checkPlayerItemCollision(std::shared_ptr& player) { if (!player->isPlaying()) { return; } - for (auto &item : items_) { + for (auto& item : items_) { if (item->isEnabled()) { if (checkCollision(player->getCollider(), item->getCollider())) { switch (item->getType()) { @@ -492,7 +499,7 @@ void Game::checkPlayerItemCollision(std::shared_ptr &player) { // Comprueba y procesa la colisión de las balas void Game::checkBulletCollision() { - for (auto &bullet : bullets_) { + for (auto& bullet : bullets_) { if (!bullet->isEnabled()) { continue; } @@ -508,7 +515,7 @@ void Game::checkBulletCollision() { } // Maneja la colisión entre bala y Tabe -auto Game::checkBulletTabeCollision(const std::shared_ptr &bullet) -> bool { +auto Game::checkBulletTabeCollision(const std::shared_ptr& bullet) -> bool { if (!tabe_->isEnabled()) { return false; } @@ -540,8 +547,8 @@ void Game::handleTabeHitEffects() { } // Maneja la colisión entre bala y globos -auto Game::checkBulletBalloonCollision(const std::shared_ptr &bullet) -> bool { - for (auto &balloon : balloon_manager_->getBalloons()) { +auto Game::checkBulletBalloonCollision(const std::shared_ptr& bullet) -> bool { + for (auto& balloon : balloon_manager_->getBalloons()) { if (!balloon->isEnabled() || balloon->isInvulnerable()) { continue; } @@ -557,7 +564,7 @@ auto Game::checkBulletBalloonCollision(const std::shared_ptr &bullet) -> } // Procesa el impacto en un globo -void Game::processBalloonHit(const std::shared_ptr &bullet, const std::shared_ptr &balloon) { +void Game::processBalloonHit(const std::shared_ptr& bullet, const std::shared_ptr& balloon) { auto player = getPlayer(static_cast(bullet->getOwner())); handleItemDrop(balloon, player); @@ -567,7 +574,7 @@ void Game::processBalloonHit(const std::shared_ptr &bullet, const std::s } // Maneja la caída de items cuando se destruye un globo -void Game::handleItemDrop(const std::shared_ptr &balloon, const std::shared_ptr &player) { +void Game::handleItemDrop(const std::shared_ptr& balloon, const std::shared_ptr& player) { const auto DROPPED_ITEM = dropItem(); if (DROPPED_ITEM == ItemType::NONE || demo_.recording) { return; @@ -583,7 +590,7 @@ void Game::handleItemDrop(const std::shared_ptr &balloon, const std::sh } // Maneja la destrucción del globo y puntuación -void Game::handleBalloonDestruction(std::shared_ptr balloon, const std::shared_ptr &player) { +void Game::handleBalloonDestruction(std::shared_ptr balloon, const std::shared_ptr& player) { if (player->isPlaying()) { auto const SCORE = balloon_manager_->popBalloon(std::move(balloon)) * player->getScoreMultiplier() * difficulty_score_multiplier_; player->addScore(SCORE, Options::settings.hi_score_table.back().score); @@ -595,7 +602,7 @@ void Game::handleBalloonDestruction(std::shared_ptr balloon, const std: // Mueve las balas activas void Game::updateBullets(float deltaTime) { - for (auto &bullet : bullets_) { + for (auto& bullet : bullets_) { if (bullet->update(deltaTime) == Bullet::MoveStatus::OUT) { getPlayer(static_cast(bullet->getOwner()))->decScoreMultiplier(); } @@ -604,7 +611,7 @@ void Game::updateBullets(float deltaTime) { // Pinta las balas activas void Game::renderBullets() { - for (auto &bullet : bullets_) { + for (auto& bullet : bullets_) { bullet->render(); } } @@ -627,7 +634,7 @@ void Game::freeBullets() { // Actualiza los items void Game::updateItems(float deltaTime) { - for (auto &item : items_) { + for (auto& item : items_) { if (item->isEnabled()) { item->update(deltaTime); if (item->isOnFloor()) { @@ -640,7 +647,7 @@ void Game::updateItems(float deltaTime) { // Pinta los items activos void Game::renderItems() { - for (auto &item : items_) { + for (auto& item : items_) { item->render(); } } @@ -717,7 +724,7 @@ void Game::freeItems() { } // Crea un objeto PathSprite -void Game::createItemText(int x, const std::shared_ptr &texture) { +void Game::createItemText(int x, const std::shared_ptr& texture) { path_sprites_.emplace_back(std::make_unique(texture)); const auto W = texture->getWidth(); @@ -740,11 +747,11 @@ void Game::createItemText(int x, const std::shared_ptr &texture) { } // Crea un objeto PathSprite -void Game::createMessage(const std::vector &paths, const std::shared_ptr &texture) { +void Game::createMessage(const std::vector& paths, const std::shared_ptr& texture) { path_sprites_.emplace_back(std::make_unique(texture)); // Inicializa - for (const auto &path : paths) { + for (const auto& path : paths) { path_sprites_.back()->addPath(path, true); } path_sprites_.back()->enable(); @@ -797,34 +804,34 @@ void Game::throwCoffee(int x, int y) { // Actualiza los SmartSprites void Game::updateSmartSprites(float deltaTime) { - for (auto &sprite : smart_sprites_) { + for (auto& sprite : smart_sprites_) { sprite->update(deltaTime); } } // Pinta los SmartSprites activos void Game::renderSmartSprites() { - for (auto &sprite : smart_sprites_) { + for (auto& sprite : smart_sprites_) { sprite->render(); } } // Actualiza los PathSprites void Game::updatePathSprites(float deltaTime) { - for (auto &sprite : path_sprites_) { + for (auto& sprite : path_sprites_) { sprite->update(deltaTime); } } // Pinta los PathSprites activos void Game::renderPathSprites() { - for (auto &sprite : path_sprites_) { + for (auto& sprite : path_sprites_) { sprite->render(); } } // Acciones a realizar cuando el jugador colisiona con un globo -void Game::handlePlayerCollision(std::shared_ptr &player, std::shared_ptr &balloon) { +void Game::handlePlayerCollision(std::shared_ptr& player, std::shared_ptr& balloon) { if (!player->isPlaying() || player->isInvulnerable()) { return; // Si no está jugando o tiene inmunidad, no hace nada } @@ -909,9 +916,9 @@ void Game::update(float deltaTime) { screen_->update(deltaTime); // Actualiza el objeto screen Audio::update(); // Actualiza el objeto audio - updateDemo(); + updateDemo(deltaTime); #ifdef RECORDING - updateRecording(); + updateRecording(deltaTime); #endif updateGameStates(deltaTime); fillCanvas(); @@ -965,7 +972,7 @@ void Game::updateBackground(float deltaTime) { // Dibuja los elementos de la zona de juego en su textura void Game::fillCanvas() { // Dibuja el contenido de la zona de juego en su textura - auto *temp = SDL_GetRenderTarget(renderer_); + auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, canvas_); // Dibuja los objetos @@ -1012,10 +1019,8 @@ void Game::run() { last_time_ = SDL_GetTicks(); while (Section::name == Section::Name::GAME) { -#ifndef RECORDING - checkInput(); -#endif const float delta_time = calculateDeltaTime(); + checkInput(); update(delta_time); handleEvents(); // Tiene que ir antes del render render(); @@ -1026,7 +1031,7 @@ void Game::run() { void Game::initPaths() { // Recorrido para el texto de "Get Ready!" (0,1) { - const auto &texture = Resource::get()->getTexture("game_text_get_ready"); + const auto& texture = Resource::get()->getTexture("game_text_get_ready"); const auto W = texture->getWidth(); const int X0 = -W; const int X1 = param.game.play_area.center_x - (W / 2); @@ -1038,7 +1043,7 @@ void Game::initPaths() { // Recorrido para el texto de "Last Stage!" o de "X stages left" (2,3) { - const auto &texture = Resource::get()->getTexture("game_text_last_stage"); + const auto& texture = Resource::get()->getTexture("game_text_last_stage"); const auto H = texture->getHeight(); const int Y0 = param.game.play_area.rect.h - H; const int Y1 = param.game.play_area.center_y - (H / 2); @@ -1050,7 +1055,7 @@ void Game::initPaths() { // Recorrido para el texto de "Congratulations!!" (4,5) { - const auto &texture = Resource::get()->getTexture("game_text_congratulations"); + const auto& texture = Resource::get()->getTexture("game_text_congratulations"); const auto W = texture->getWidth(); const auto H = texture->getHeight(); const int X0 = -W; @@ -1063,7 +1068,7 @@ void Game::initPaths() { // Recorrido para el texto de "1.000.000 points!" (6,7) { - const auto &texture = Resource::get()->getTexture("game_text_1000000_points"); + const auto& texture = Resource::get()->getTexture("game_text_1000000_points"); const auto W = texture->getWidth(); const auto H = texture->getHeight(); const int X0 = param.game.play_area.rect.w; @@ -1076,7 +1081,7 @@ void Game::initPaths() { // Recorrido para el texto de "New Record!" (8,9) { - const auto &texture = Resource::get()->getTexture("game_text_new_record"); + const auto& texture = Resource::get()->getTexture("game_text_new_record"); const auto W = texture->getWidth(); const auto H = texture->getHeight(); const int X0 = -W; @@ -1089,7 +1094,7 @@ void Game::initPaths() { // Recorrido para el texto de "Game Over" (10,11) { - const auto &texture = Resource::get()->getTexture("game_text_game_over"); + const auto& texture = Resource::get()->getTexture("game_text_game_over"); const auto H = texture->getHeight(); const int Y0 = param.game.play_area.rect.h - H; const int Y1 = param.game.play_area.center_y - (H / 2); @@ -1106,7 +1111,7 @@ void Game::updateHelper() { if (menace_ > 15) { helper_.need_coffee = true; helper_.need_coffee_machine = true; - for (const auto &player : players_) { + for (const auto& player : players_) { if (player->isPlaying()) { helper_.need_coffee &= (player->getCoffees() == 0); helper_.need_coffee_machine &= (!player->isPowerUp()); @@ -1120,7 +1125,7 @@ void Game::updateHelper() { // Comprueba si todos los jugadores han terminado de jugar auto Game::allPlayersAreWaitingOrGameOver() -> bool { auto success = true; - for (const auto &player : players_) { + for (const auto& player : players_) { success &= player->isWaiting() || player->isGameOver(); } @@ -1130,7 +1135,7 @@ auto Game::allPlayersAreWaitingOrGameOver() -> bool { // Comprueba si todos los jugadores han terminado de jugar auto Game::allPlayersAreGameOver() -> bool { auto success = true; - for (const auto &player : players_) { + for (const auto& player : players_) { success &= player->isGameOver(); } @@ -1140,7 +1145,7 @@ auto Game::allPlayersAreGameOver() -> bool { // Comprueba si todos los jugadores han terminado de jugar auto Game::allPlayersAreNotPlaying() -> bool { auto success = true; - for (const auto &player : players_) { + for (const auto& player : players_) { success &= !player->isPlaying(); } @@ -1173,7 +1178,7 @@ void Game::handleEvents() { // Actualiza el marcador void Game::updateScoreboard() { - for (const auto &player : players_) { + for (const auto& player : players_) { scoreboard_->setScore(player->getScoreBoardPanel(), player->getScore()); scoreboard_->setMult(player->getScoreBoardPanel(), player->getScoreMultiplier()); } @@ -1208,7 +1213,7 @@ void Game::checkPlayersStatusPlaying() { // Comprueba si todos los jugadores estan esperando if (allPlayersAreWaitingOrGameOver()) { // Entonces los pone en estado de Game Over - for (auto &player : players_) { + for (auto& player : players_) { player->setPlayingState(Player::State::GAME_OVER); } } @@ -1220,7 +1225,7 @@ void Game::checkPlayersStatusPlaying() { // Obtiene un jugador a partir de su "id" auto Game::getPlayer(Player::Id id) -> std::shared_ptr { - auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) { return player->getId() == id; }); + auto it = std::find_if(players_.begin(), players_.end(), [id](const auto& player) { return player->getId() == id; }); if (it != players_.end()) { return *it; @@ -1262,7 +1267,7 @@ void Game::checkInput() { // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego. void Game::checkPauseInput() { // Comprueba los mandos - for (const auto &gamepad : input_->getGamepads()) { + for (const auto& gamepad : input_->getGamepads()) { if (input_->checkAction(Input::Action::PAUSE, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) { pause_manager_->togglePlayerPause(); return; @@ -1288,7 +1293,7 @@ void Game::demoHandlePassInput() { // Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos. void Game::demoHandleInput() { int index = 0; - for (const auto &player : players_) { + for (const auto& player : players_) { if (player->isPlaying()) { // Maneja el input específico del jugador en modo demo. demoHandlePlayerInput(player, index); @@ -1298,8 +1303,8 @@ void Game::demoHandleInput() { } // Procesa las entradas para un jugador específico durante el modo demo. -void Game::demoHandlePlayerInput(const std::shared_ptr &player, int index) { - const auto &demo_data = demo_.data.at(index).at(demo_.counter); +void Game::demoHandlePlayerInput(const std::shared_ptr& player, int index) { + const auto& demo_data = demo_.data.at(index).at(demo_.index); if (demo_data.left == 1) { player->setInput(Input::Action::LEFT); @@ -1319,7 +1324,7 @@ void Game::demoHandlePlayerInput(const std::shared_ptr &player, int inde } // Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos. -void Game::handleFireInput(const std::shared_ptr &player, Bullet::Type type) { +void Game::handleFireInput(const std::shared_ptr& player, Bullet::Type type) { if (player->canFire()) { SDL_Point bullet = {0, 0}; switch (type) { @@ -1364,7 +1369,7 @@ void Game::handleFireInput(const std::shared_ptr &player, Bullet::Type t // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo) void Game::handlePlayersInput() { - for (const auto &player : players_) { + for (const auto& player : players_) { if (player->isPlaying()) { handleNormalPlayerInput(player); // Maneja el input de los jugadores en modo normal } else if (player->isContinue()) { @@ -1378,7 +1383,7 @@ void Game::handlePlayersInput() { } // Maneja las entradas de movimiento y disparo para un jugador en modo normal. -void Game::handleNormalPlayerInput(const std::shared_ptr &player) { +void Game::handleNormalPlayerInput(const std::shared_ptr& player) { if (input_->checkAction(Input::Action::LEFT, Input::ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { player->setInput(Input::Action::LEFT); #ifdef RECORDING @@ -1401,7 +1406,7 @@ void Game::handleNormalPlayerInput(const std::shared_ptr &player) { } // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado. -void Game::handleFireInputs(const std::shared_ptr &player, bool autofire) { +void Game::handleFireInputs(const std::shared_ptr& player, bool autofire) { if (!player) { return; } @@ -1431,7 +1436,7 @@ void Game::handleFireInputs(const std::shared_ptr &player, bool autofire } // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio. -void Game::handlePlayerContinueInput(const std::shared_ptr &player) { +void Game::handlePlayerContinueInput(const std::shared_ptr& player) { if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { player->setPlayingState(Player::State::RECOVER); player->addCredit(); @@ -1450,7 +1455,7 @@ void Game::handlePlayerContinueInput(const std::shared_ptr &player) { } // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio. -void Game::handlePlayerWaitingInput(const std::shared_ptr &player) { +void Game::handlePlayerWaitingInput(const std::shared_ptr& player) { if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { player->setPlayingState(Player::State::ENTERING_SCREEN); player->addCredit(); @@ -1459,7 +1464,7 @@ void Game::handlePlayerWaitingInput(const std::shared_ptr &player) { } // Procesa las entradas para la introducción del nombre del jugador. -void Game::handleNameInput(const std::shared_ptr &player) { +void Game::handleNameInput(const std::shared_ptr& player) { if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { if (player->isShowingName()) { player->passShowingName(); @@ -1514,15 +1519,23 @@ void Game::handleNameInput(const std::shared_ptr &player) { // Inicializa las variables para el modo DEMO void Game::initDemo(Player::Id player_id) { +#ifdef RECORDING + // En modo grabación, inicializar vector vacío para almacenar teclas + demo_.data.emplace_back(); // Vector vacío para grabación + demo_.data.at(0).reserve(TOTAL_DEMO_DATA); // Reservar espacio para 2000 elementos +#endif + if (demo_.enabled) { // Cambia el estado del juego setState(State::PLAYING); - // Aleatoriza la asignación del fichero con los datos del modo demostracion +#ifndef RECORDING + // Solo en modo reproducción: aleatoriza la asignación del fichero con los datos del modo demostracion const auto DEMO1 = rand() % 2; const auto DEMO2 = (DEMO1 == 0) ? 1 : 0; demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1)); demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2)); +#endif // Selecciona una pantalla al azar constexpr auto NUM_DEMOS = 3; @@ -1538,7 +1551,7 @@ void Game::initDemo(Player::Id player_id) { } // Asigna cafes a los jugadores - for (auto &player : players_) { + for (auto& player : players_) { if (player->isPlaying()) { for (int i = 0; i < rand() % 3; ++i) { player->giveExtraHit(); @@ -1562,14 +1575,14 @@ void Game::initDemo(Player::Id player_id) { #else demo_.recording = false; #endif - demo_.counter = 0; + demo_.index = 0; } // Inicializa el marcador void Game::initScoreboard() { scoreboard_->setPos(param.scoreboard.rect); scoreboard_->setMode(Scoreboard::Id::CENTER, Scoreboard::Mode::STAGE_INFO); - for (const auto &player : players_) { + for (const auto& player : players_) { scoreboard_->setName(player->getScoreBoardPanel(), player->getName()); if (player->isWaiting()) { scoreboard_->setMode(player->getScoreBoardPanel(), Scoreboard::Mode::WAITING); @@ -1617,7 +1630,11 @@ void Game::initPlayers(Player::Id player_id) { // Crea al jugador uno y lo pone en modo espera Player::Config config_player1{ .id = Player::Id::PLAYER1, +#ifdef RECORDING + .x = param.game.play_area.center_x - (Player::WIDTH / 2), +#else .x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2), +#endif .y = Y, .demo = demo_.enabled, .play_area = ¶m.game.play_area.rect, @@ -1634,7 +1651,11 @@ void Game::initPlayers(Player::Id player_id) { player1->setName(Lang::getText("[SCOREBOARD] 1")); player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance); player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id); +#ifdef RECORDING + player1->setPlayingState(Player::State::PLAYING); +#else player1->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER1) ? STATE : Player::State::WAITING); +#endif // Crea al jugador dos y lo pone en modo espera Player::Config config_player2{ @@ -1663,14 +1684,14 @@ void Game::initPlayers(Player::Id player_id) { players_.push_back(std::move(player1)); // Registra los jugadores en Options - for (const auto &player : players_) { + for (const auto& player : players_) { Options::keyboard.addPlayer(player); Options::gamepad_manager.addPlayer(player); } } // Hace sonar la música -void Game::playMusic(const std::string &music_file, int loop) { +void Game::playMusic(const std::string& music_file, int loop) { Audio::get()->playMusic(music_file, loop); } @@ -1692,7 +1713,7 @@ void Game::stopMusic() const { } // Actualiza las variables durante el modo demo -void Game::updateDemo() { +void Game::updateDemo(float deltaTime) { if (demo_.enabled) { balloon_manager_->setCreationTimeEnabled(balloon_manager_->getNumBalloons() != 0); @@ -1700,16 +1721,12 @@ void Game::updateDemo() { fade_in_->update(); fade_out_->update(); - // Incrementa el contador de la demo cada 1/60 segundos (16.67ms) - static float demo_frame_timer = 0.0f; - demo_frame_timer += calculateDeltaTime(); - if (demo_frame_timer >= 0.01667f && demo_.counter < TOTAL_DEMO_DATA) { - demo_.counter++; - demo_frame_timer -= 0.01667f; // Mantener precisión acumulada - } + // Actualiza el contador de tiempo y el índice + demo_.elapsed_s += deltaTime; + demo_.index = static_cast(demo_.elapsed_s * 60.0F); // Activa el fundido antes de acabar con los datos de la demo - if (demo_.counter == TOTAL_DEMO_DATA - 200) { + if (demo_.index == TOTAL_DEMO_DATA - 200) { fade_out_->setType(Fade::Type::RANDOM_SQUARE2); fade_out_->setPostDuration(param.fade.post_duration_ms); fade_out_->activate(); @@ -1725,22 +1742,28 @@ void Game::updateDemo() { #ifdef RECORDING // Actualiza las variables durante el modo de grabación -void Game::updateRecording() { - // Solo mira y guarda el input en cada update - checkInput(); +void Game::updateRecording(float deltaTime) { + // Actualiza el contador de tiempo y el índice + demo_.elapsed_s += deltaTime; + demo_.index = static_cast(demo_.elapsed_s * 60.0F); - // Incrementa el contador de la demo cada 1/60 segundos (16.67ms) - static float recording_frame_timer = 0.0f; - recording_frame_timer += calculateDeltaTime(); - if (recording_frame_timer >= 0.01667f && demo_.counter < TOTAL_DEMO_DATA) { - demo_.counter++; - recording_frame_timer -= 0.01667f; // Mantener precisión acumulada + if (demo_.index >= TOTAL_DEMO_DATA) { + Section::name = Section::Name::QUIT; + return; } - // Si se ha llenado el vector con datos, sale del programa - else { - section::name = section::Name::QUIT; - return; + // Almacenar las teclas del frame actual en el vector de grabación + if (demo_.index < TOTAL_DEMO_DATA && demo_.data.size() > 0) { + // Asegurar que el vector tenga el tamaño suficiente + if (demo_.data.at(0).size() <= static_cast(demo_.index)) { + demo_.data.at(0).resize(demo_.index + 1); + } + + // Almacenar las teclas del frame actual + demo_.data.at(0).at(demo_.index) = demo_.keys; + + // Resetear las teclas para el siguiente frame + demo_.keys = DemoKeys(); } } #endif @@ -1763,7 +1786,7 @@ void Game::updateGameStateEnteringPlayer(float deltaTime) { updatePlayers(deltaTime); updateScoreboard(); updateBackground(deltaTime); - for (const auto &player : players_) { + for (const auto& player : players_) { if (player->isPlaying()) { setState(State::SHOWING_GET_READY_MESSAGE); createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready")); @@ -1833,7 +1856,7 @@ void Game::updateMenace() { return; } - const auto &stage = current_stage.value(); + const auto& stage = current_stage.value(); const double FRACTION = stage_manager_->getCurrentStageProgressFraction(); const int DIFFERENCE = stage.getMaxMenace() - stage.getMinMenace(); @@ -1886,19 +1909,19 @@ void Game::setState(State state) { game_completed_flags_.reset(); // Resetea flags de juego completado break; case State::GAME_OVER: - game_over_flags_.reset(); // Resetea flags de game over + game_over_flags_.reset(); // Resetea flags de game over break; default: break; } } -void Game::playSound(const std::string &name) const { +void Game::playSound(const std::string& name) const { if (demo_.enabled) { return; } - static auto *audio_ = Audio::get(); + static auto* audio_ = Audio::get(); audio_->playSound(name); } @@ -1906,7 +1929,7 @@ void Game::playSound(const std::string &name) const { void Game::sortPlayersByZOrder() { // Procesar jugadores que van al fondo (se dibujan primero) if (!players_to_put_at_back_.empty()) { - for (auto &player : players_to_put_at_back_) { + for (auto& player : players_to_put_at_back_) { auto it = std::find(players_.begin(), players_.end(), player); if (it != players_.end() && it != players_.begin()) { std::shared_ptr dying_player = *it; @@ -1919,7 +1942,7 @@ void Game::sortPlayersByZOrder() { // Procesar jugadores que van al frente (se dibujan últimos) if (!players_to_put_at_front_.empty()) { - for (auto &player : players_to_put_at_front_) { + for (auto& player : players_to_put_at_front_) { auto it = std::find(players_.begin(), players_.end(), player); if (it != players_.end() && it != players_.end() - 1) { std::shared_ptr front_player = *it; @@ -1932,12 +1955,12 @@ void Game::sortPlayersByZOrder() { } // Mueve el jugador para pintarlo al fondo de la lista de jugadores -void Game::sendPlayerToTheBack(const std::shared_ptr &player) { +void Game::sendPlayerToTheBack(const std::shared_ptr& player) { players_to_put_at_back_.push_back(player); } // Mueve el jugador para pintarlo el primero de la lista de jugadores -void Game::sendPlayerToTheFront(const std::shared_ptr &player) { +void Game::sendPlayerToTheFront(const std::shared_ptr& player) { players_to_put_at_front_.push_back(player); } @@ -1956,7 +1979,7 @@ void Game::handleGameCompletedEvents() { createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations")); createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points")); - for (auto &player : players_) { + for (auto& player : players_) { if (player->isPlaying()) { player->addScore(1000000, Options::settings.hi_score_table.back().score); player->setPlayingState(Player::State::CELEBRATING); @@ -1972,7 +1995,7 @@ void Game::handleGameCompletedEvents() { // Fin de celebraciones if (!game_completed_flags_.end_celebrations_triggered && game_completed_timer_ >= END_CELEBRATIONS_S) { - for (auto &player : players_) { + for (auto& player : players_) { if (player->isCelebrating()) { player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN); } @@ -2009,7 +2032,7 @@ void Game::handleGameOverEvents() { #ifdef _DEBUG // Comprueba los eventos en el modo DEBUG -void Game::handleDebugEvents(const SDL_Event &event) { +void Game::handleDebugEvents(const SDL_Event& event) { static int formation_id_ = 0; if (event.type == SDL_EVENT_KEY_DOWN && static_cast(event.key.repeat) == 0) { switch (event.key.key) { @@ -2052,7 +2075,7 @@ void Game::handleDebugEvents(const SDL_Event &event) { break; } case SDLK_8: { - for (const auto &player : players_) { + for (const auto& player : players_) { if (player->isPlaying()) { createItem(ItemType::COFFEE_MACHINE, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT); break; diff --git a/source/sections/game.h b/source/sections/game.h index 587d015..10d1c3f 100644 --- a/source/sections/game.h +++ b/source/sections/game.h @@ -6,7 +6,7 @@ #include // Para string #include // Para vector -#include "bullet.h" // Para Bullet +#include "bullet.h" // Para Bullet #include "hit.h" // Para Hit #include "item.h" // Para Item, ItemType #include "manage_hiscore_table.h" // Para HiScoreEntry @@ -56,8 +56,8 @@ class Game { static constexpr bool DEMO_ON = true; // Modo demo activado // --- Constructor y destructor --- - Game(Player::Id player_id, int current_stage, bool demo); // Constructor principal - ~Game(); // Destructor + Game(Player::Id player_id, int current_stage, bool demo_enabled); // Constructor principal + ~Game(); // Destructor // --- Bucle principal --- void run(); // Ejecuta el bucle principal del juego @@ -77,9 +77,9 @@ class Game { static constexpr float HELP_COUNTER_S = 16.667f; // Contador de ayuda (1000 frames a 60fps → segundos) static constexpr float GAME_COMPLETED_START_FADE_S = 8.333f; // Inicio del fade al completar (500 frames → segundos) static constexpr float GAME_COMPLETED_END_S = 11.667f; // Fin del juego completado (700 frames → segundos) - static constexpr float GAME_OVER_DURATION_S = 8.5f; - static constexpr float TIME_STOPPED_DURATION_S = 6.0f; - static constexpr float DEMO_FADE_PRE_DURATION_S = 0.5f; + static constexpr float GAME_OVER_DURATION_S = 8.5f; + static constexpr float TIME_STOPPED_DURATION_S = 6.0f; + static constexpr float DEMO_FADE_PRE_DURATION_S = 0.5f; 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; @@ -167,35 +167,35 @@ class Game { // Estructuras para gestionar flags de eventos basados en tiempo struct GameOverFlags { - bool music_fade_triggered = false; - bool message_triggered = false; - bool fade_out_triggered = false; + bool music_fade_triggered = false; + bool message_triggered = false; + bool fade_out_triggered = false; - void reset() { - music_fade_triggered = false; - message_triggered = false; - fade_out_triggered = false; - } + void reset() { + music_fade_triggered = false; + message_triggered = false; + fade_out_triggered = false; + } } game_over_flags_; struct GameCompletedFlags { - bool start_celebrations_triggered = false; - bool end_celebrations_triggered = false; + bool start_celebrations_triggered = false; + bool end_celebrations_triggered = false; - void reset() { - start_celebrations_triggered = false; - end_celebrations_triggered = false; - } + void reset() { + start_celebrations_triggered = false; + end_celebrations_triggered = false; + } } game_completed_flags_; struct TimeStoppedFlags { - bool color_flash_sound_played = false; - bool warning_phase_started = false; + bool color_flash_sound_played = false; + bool warning_phase_started = false; - void reset() { - color_flash_sound_played = false; - warning_phase_started = false; - } + void reset() { + color_flash_sound_played = false; + warning_phase_started = false; + } } time_stopped_flags_; #ifdef _DEBUG @@ -259,11 +259,11 @@ class Game { void demoHandlePlayerInput(const std::shared_ptr& player, int index); // Procesa entrada de jugador en demo // --- Sistema de balas y proyectiles --- - void updateBullets(float deltaTime); // Actualiza posición y estado de todas las balas (time-based) - void renderBullets(); // Renderiza todas las balas activas + void updateBullets(float deltaTime); // Actualiza posición y estado de todas las balas (time-based) + void renderBullets(); // Renderiza todas las balas activas void createBullet(int x, int y, Bullet::Type kind, Bullet::Color color, int owner); // Crea una nueva bala - void checkBulletCollision(); // Verifica colisiones de todas las balas - void freeBullets(); // Libera memoria del vector de balas + void checkBulletCollision(); // Verifica colisiones de todas las balas + void freeBullets(); // Libera memoria del vector de balas // --- Colisiones específicas de balas --- auto checkBulletTabeCollision(const std::shared_ptr& bullet) -> bool; // Detecta colisión bala-Tabe @@ -325,7 +325,7 @@ class Game { // --- Modo demostración --- void initDemo(Player::Id player_id); // Inicializa variables para el modo demostración - void updateDemo(); // Actualiza lógica específica del modo demo + void updateDemo(float deltaTime); // Actualiza lógica específica del modo demo // --- Recursos y renderizado --- void setResources(); // Asigna texturas y animaciones a los objetos @@ -346,7 +346,7 @@ class Game { // SISTEMA DE GRABACIÓN (CONDICIONAL) #ifdef RECORDING - void updateRecording(); // Actualiza variables durante modo de grabación + void updateRecording(float deltaTime); // Actualiza variables durante modo de grabación #endif // --- Depuración (solo en modo DEBUG) --- diff --git a/source/utils.cpp b/source/utils.cpp index 7398ab6..60c2e0f 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -363,7 +363,7 @@ bool saveDemoFile(const std::string &file_path, const DemoData &dd) { if (file) { // Guarda los datos for (const auto &data : dd) { - if (SDL_RWwrite(file, &data, sizeof(DemoKeys), 1) != 1) { + if (SDL_WriteIO(file, &data, sizeof(DemoKeys)) != sizeof(DemoKeys)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al escribir el fichero %s", getFileName(file_path).c_str()); success = false; break; diff --git a/source/utils.h b/source/utils.h index 639a1c6..73230d5 100644 --- a/source/utils.h +++ b/source/utils.h @@ -53,20 +53,19 @@ struct DemoKeys { using DemoData = std::vector; struct Demo { - bool enabled; // Indica si está activo el modo demo - bool recording; // Indica si está activado el modo para grabar la demo - int counter; // Contador para el modo demo + bool enabled = false; // Indica si está activo el modo demo + bool recording = false; // Indica si está activado el modo para grabar la demo + float elapsed_s = 0.0F; // Segundos transcurridos de demo + int index = 0; // Contador para el modo demo DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo std::vector data; // Vector con diferentes sets de datos con los movimientos para la demo - Demo() - : enabled(false), - recording(false), - counter(0) {} - Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector &d) + Demo() = default; + + Demo(bool e, bool r, int c, const DemoKeys& k, const std::vector& d) : enabled(e), recording(r), - counter(c), + index(c), keys(k), data(d) {} }; @@ -88,21 +87,21 @@ extern Overrides overrides; // Configuración global de overrides // Colisiones y geometría auto distanceSquared(int x1, int y1, int x2, int y2) -> double; -auto getCollisionPoint(const Circle &a, const Circle &b) -> SDL_FPoint; -auto checkCollision(const Circle &a, const Circle &b) -> bool; -auto checkCollision(const Circle &a, const SDL_FRect &b) -> bool; -auto checkCollision(const SDL_FRect &a, const SDL_FRect &b) -> bool; -auto checkCollision(const SDL_FPoint &p, const SDL_FRect &r) -> bool; +auto getCollisionPoint(const Circle& a, const Circle& b) -> SDL_FPoint; +auto checkCollision(const Circle& a, const Circle& b) -> bool; +auto checkCollision(const Circle& a, const SDL_FRect& b) -> bool; +auto checkCollision(const SDL_FRect& a, const SDL_FRect& b) -> bool; +auto checkCollision(const SDL_FPoint& p, const SDL_FRect& r) -> bool; // Conversión y manipulación de cadenas -auto stringToBool(const std::string &str) -> bool; +auto stringToBool(const std::string& str) -> bool; auto boolToString(bool value) -> std::string; auto boolToOnOff(bool value) -> std::string; -auto toLower(const std::string &str) -> std::string; -auto trim(const std::string &str) -> std::string; +auto toLower(const std::string& str) -> std::string; +auto trim(const std::string& str) -> std::string; // Dibujo -void drawCircle(SDL_Renderer *renderer, int32_t center_x, int32_t center_y, int32_t radius); +void drawCircle(SDL_Renderer* renderer, int32_t center_x, int32_t center_y, int32_t radius); // Funciones de suavizado (easing) auto easeOutQuint(double time) -> double; @@ -123,17 +122,17 @@ auto easeOutCubic(double time) -> double; auto easeInCubic(double time) -> double; // Utilidades varias -auto stringInVector(const std::vector &vec, const std::string &str) -> bool; // Comprueba si un vector contiene una cadena -void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3); // Imprime una línea con puntos -auto truncateWithEllipsis(const std::string &input, size_t length) -> std::string; // Trunca un string y le añade puntos suspensivos +auto stringInVector(const std::vector& vec, const std::string& str) -> bool; // Comprueba si un vector contiene una cadena +void printWithDots(const std::string& text1, const std::string& text2, const std::string& text3); // Imprime una línea con puntos +auto truncateWithEllipsis(const std::string& input, size_t length) -> std::string; // Trunca un string y le añade puntos suspensivos // Demo -auto loadDemoDataFromFile(const std::string &file_path) -> DemoData; +auto loadDemoDataFromFile(const std::string& file_path) -> DemoData; #ifdef RECORDING -bool saveDemoFile(const std::string &file_path, const DemoData &dd); +bool saveDemoFile(const std::string& file_path, const DemoData& dd); #endif // Ficheros y rutas -auto getFileName(const std::string &path) -> std::string; // Obtiene el nombre de un fichero a partir de una ruta -auto getPath(const std::string &full_path) -> std::string; // Obtiene la ruta eliminando el nombre del fichero \ No newline at end of file +auto getFileName(const std::string& path) -> std::string; // Obtiene el nombre de un fichero a partir de una ruta +auto getPath(const std::string& full_path) -> std::string; // Obtiene la ruta eliminando el nombre del fichero \ No newline at end of file