diff --git a/config/assets.txt b/config/assets.txt index 67c9780..5d8d0d3 100644 --- a/config/assets.txt +++ b/config/assets.txt @@ -20,6 +20,7 @@ DATA|${PREFIX}/config/stages.txt # Archivos con los datos de la demo DEMODATA|${PREFIX}/data/demo/demo1.bin DEMODATA|${PREFIX}/data/demo/demo2.bin +DEMODATA|${PREFIX}/data/demo/demo3.bin # Música MUSIC|${PREFIX}/data/music/congratulations.ogg diff --git a/source/asset.cpp b/source/asset.cpp index 34069b1..4f18be2 100644 --- a/source/asset.cpp +++ b/source/asset.cpp @@ -2,6 +2,7 @@ #include // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError, SDL_LogWarn +#include // Para std::sort #include // Para size_t #include // Para exception #include // Para std::filesystem @@ -299,6 +300,9 @@ auto Asset::getListByType(Type type) const -> std::vector { } } + // Ordenar alfabéticamente para garantizar orden consistente + std::sort(list.begin(), list.end()); + return list; } diff --git a/source/player.h b/source/player.h index 1f8376d..fcd4417 100644 --- a/source/player.h +++ b/source/player.h @@ -137,15 +137,15 @@ class Player { void decScoreMultiplier(); // Decrementa el multiplicador // --- Estados de juego --- - void setPlayingState(State state); // Cambia el estado de juego - void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad - void setPowerUp(); // Activa el modo PowerUp - void updatePowerUp(float deltaTime); // Actualiza el valor de PowerUp - void giveExtraHit(); // Concede un toque extra al jugador - void removeExtraHit(); // Quita el toque extra al jugador - void decContinueCounter(); // Decrementa el contador de continuar - void setWalkingState(State state) { walking_state_ = state; } // Establece el estado de caminar - void startFiringSystem(int cooldown_frames); // Inicia el sistema de disparo + void setPlayingState(State state); // Cambia el estado de juego + void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad + void setPowerUp(); // Activa el modo PowerUp + void updatePowerUp(float deltaTime); // Actualiza el valor de PowerUp + void giveExtraHit(); // Concede un toque extra al jugador + void removeExtraHit(); // Quita el toque extra al jugador + void decContinueCounter(); // Decrementa el contador de continuar + void setWalkingState(State state) { walking_state_ = state; } // Establece el estado de caminar + void startFiringSystem(int cooldown_frames); // Inicia el sistema de disparo void setScoreBoardPanel(Scoreboard::Id panel) { scoreboard_panel_ = panel; } // Establece el panel del marcador void addCredit(); void passShowingName(); @@ -196,11 +196,11 @@ class Player { [[nodiscard]] auto getCoffees() const -> int { return coffees_; } [[nodiscard]] auto getPowerUpCounter() const -> int { return power_up_counter_; } [[nodiscard]] auto getInvulnerableCounter() const -> int { return invulnerable_counter_; } - [[nodiscard]] auto getBulletColor() const -> Bullet::Color; // Devuelve el color actual de bala según el estado - auto getNextBulletColor() -> Bullet::Color; // Devuelve el color para la próxima bala (alterna si está en modo toggle) - void setBulletColors(Bullet::Color normal, Bullet::Color powered); // Establece los colores de bala para este jugador + [[nodiscard]] auto getBulletColor() const -> Bullet::Color; // Devuelve el color actual de bala según el estado + auto getNextBulletColor() -> Bullet::Color; // Devuelve el color para la próxima bala (alterna si está en modo toggle) + void setBulletColors(Bullet::Color normal, Bullet::Color powered); // Establece los colores de bala para este jugador [[nodiscard]] auto getBulletSoundFile() const -> std::string { return bullet_sound_file_; } // Devuelve el archivo de sonido de bala - void setBulletSoundFile(const std::string& filename); // Establece el archivo de sonido de bala para este jugador + void setBulletSoundFile(const std::string& filename); // Establece el archivo de sonido de bala para este jugador // Contadores y timers [[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; } @@ -217,6 +217,10 @@ class Player { [[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; } [[nodiscard]] auto getController() const -> int { return controller_index_; } + // Demo file management + [[nodiscard]] auto getDemoFile() const -> size_t { return demo_file_; } + void setDemoFile(size_t demo_file) { demo_file_ = demo_file; } + private: // --- Constantes de física y movimiento --- static constexpr float BASE_SPEED = 90.0f; // Velocidad base del jugador (pixels/segundo) @@ -249,17 +253,17 @@ class Player { IStageInfo* stage_info_; // Informacion de la pantalla actual // --- Variables de estado --- - SDL_FRect play_area_; // Rectángulo con la zona de juego - Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador - std::string name_; // Nombre del jugador - std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones - Scoreboard::Id scoreboard_panel_ = Scoreboard::Id::LEFT; // Panel del marcador asociado al jugador - Id id_; // Identificador para el jugador - State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse - State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar - State playing_state_ = State::WAITING; // Estado del jugador en el juego + SDL_FRect play_area_; // Rectángulo con la zona de juego + Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador + std::string name_; // Nombre del jugador + std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones + Scoreboard::Id scoreboard_panel_ = Scoreboard::Id::LEFT; // Panel del marcador asociado al jugador + Id id_; // Identificador para el jugador + State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse + State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar + State playing_state_ = State::WAITING; // Estado del jugador en el juego BulletColorPair bullet_colors_ = {Bullet::Color::YELLOW, Bullet::Color::GREEN}; // Par de colores de balas para este jugador - std::string bullet_sound_file_ = "bullet1p.wav"; // Archivo de sonido de bala para este jugador + std::string bullet_sound_file_ = "bullet1p.wav"; // Archivo de sonido de bala para este jugador float pos_x_ = 0.0F; // Posición en el eje X float default_pos_x_; // Posición inicial para el jugador @@ -304,6 +308,7 @@ class Player { int power_up_x_offset_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador int continue_counter_ = 10; // Contador para poder continuar int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse + size_t demo_file_ = 0; // Indice del fichero de datos para el modo demo float name_entry_idle_time_accumulator_ = 0.0f; // Tiempo idle acumulado para poner nombre (milisegundos) float name_entry_total_time_accumulator_ = 0.0f; // Tiempo total acumulado poniendo nombre (milisegundos) int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente @@ -313,16 +318,16 @@ class Player { bool invulnerable_ = true; // Indica si el jugador es invulnerable bool extra_hit_ = false; // Indica si el jugador tiene un toque extra bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp - bool power_sprite_visible_ = false; // Indica si el sprite de power-up debe ser visible - bool in_power_up_ending_phase_ = false; // Indica si está en la fase final del power-up (alternando colores) - bool bullet_color_toggle_ = false; // Para alternar entre verde y amarillo en fase final + bool power_sprite_visible_ = false; // Indica si el sprite de power-up debe ser visible + bool in_power_up_ending_phase_ = false; // Indica si está en la fase final del power-up (alternando colores) + bool bullet_color_toggle_ = false; // Para alternar entre verde y amarillo en fase final bool demo_ = false; // Para que el jugador sepa si está en el modo demostración bool game_completed_ = false; // Indica si ha completado el juego bool uses_keyboard_ = false; // Indica si usa el teclado como dispositivo de control // --- Métodos internos --- - void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador - void shiftSprite(); // Recoloca el sprite + void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador + void shiftSprite(); // Recoloca el sprite // --- Setters internos --- void setController(int index) { controller_index_ = index; } @@ -333,11 +338,11 @@ class Player { void setScoreMultiplier(float value) { score_multiplier_ = value; } // --- Actualizadores de estado (time-based) --- - void updateInvulnerable(float deltaTime); // Monitoriza el estado de invulnerabilidad - void updateContinueCounter(float deltaTime); // Actualiza el contador de continue - void updateEnterNameCounter(float deltaTime); // Actualiza el contador de entrar nombre - void updateShowingName(float deltaTime); // Actualiza el estado SHOWING_NAME - void decNameEntryCounter(); // Decrementa el contador de entrar nombre + void updateInvulnerable(float deltaTime); // Monitoriza el estado de invulnerabilidad + void updateContinueCounter(float deltaTime); // Actualiza el contador de continue + void updateEnterNameCounter(float deltaTime); // Actualiza el contador de entrar nombre + void updateShowingName(float deltaTime); // Actualiza el estado SHOWING_NAME + void decNameEntryCounter(); // Decrementa el contador de entrar nombre // --- Utilidades generales --- void updateScoreboard(); // Actualiza el panel del marcador @@ -347,41 +352,41 @@ class Player { void addScoreToScoreBoard() const; // Añade una puntuación a la tabla de records // --- Sistema de disparo (nuevo - dos líneas) --- - void updateFireSystem(float deltaTime); // Método principal del nuevo sistema de disparo - void updateFunctionalLine(float deltaTime); // Actualiza la línea funcional (CanFire) - void updateVisualLine(float deltaTime); // Actualiza la línea visual (Animaciones) - void updateFiringStateFromVisual(); // Sincroniza firing_state_ con visual_fire_state_ - void transitionToRecoilingNew(); // Transición AIMING → RECOILING - void transitionToThreatPose(); // Transición RECOILING → THREAT_POSE - void transitionToNormalNew(); // Transición THREAT_POSE → NORMAL + void updateFireSystem(float deltaTime); // Método principal del nuevo sistema de disparo + void updateFunctionalLine(float deltaTime); // Actualiza la línea funcional (CanFire) + void updateVisualLine(float deltaTime); // Actualiza la línea visual (Animaciones) + void updateFiringStateFromVisual(); // Sincroniza firing_state_ con visual_fire_state_ + void transitionToRecoilingNew(); // Transición AIMING → RECOILING + void transitionToThreatPose(); // Transición RECOILING → THREAT_POSE + void transitionToNormalNew(); // Transición THREAT_POSE → NORMAL // --- Manejadores de movimiento --- - void handlePlayingMovement(float deltaTime); // Gestiona el movimiento durante el juego - void handleRecoverMovement(); // Comprueba si ha acabado la animación de recuperación - void updateStepCounter(float deltaTime); // Incrementa o ajusta el contador de pasos - void setInputBasedOnPlayerId(); // Asocia las entradas de control según el jugador + void handlePlayingMovement(float deltaTime); // Gestiona el movimiento durante el juego + void handleRecoverMovement(); // Comprueba si ha acabado la animación de recuperación + void updateStepCounter(float deltaTime); // Incrementa o ajusta el contador de pasos + void setInputBasedOnPlayerId(); // Asocia las entradas de control según el jugador // --- Manejadores de estados especiales --- - void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" - void handleRollingBoundaryCollision(); // Detecta colisiones con límites durante rodamiento - void handleRollingGroundCollision(); // Gestiona interacción con el suelo durante rodamiento - void handleRollingStop(); // Detiene el movimiento del objeto rodante - void handleRollingBounce(); // Aplica lógica de rebote durante rodamiento - void handleContinueTimeOut(); // Gestiona tiempo de espera en pantalla "Continuar" + void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" + void handleRollingBoundaryCollision(); // Detecta colisiones con límites durante rodamiento + void handleRollingGroundCollision(); // Gestiona interacción con el suelo durante rodamiento + void handleRollingStop(); // Detiene el movimiento del objeto rodante + void handleRollingBounce(); // Aplica lógica de rebote durante rodamiento + void handleContinueTimeOut(); // Gestiona tiempo de espera en pantalla "Continuar" // --- Manejadores de transiciones de pantalla --- - void handleTitleAnimation(float deltaTime); // Ejecuta animación del título - void handleLeavingScreen(float deltaTime); // Lógica para salir de pantalla - void handleEnteringScreen(float deltaTime); // Lógica para entrar en pantalla - void handlePlayer1Entering(float deltaTime); // Entrada del Jugador 1 - void handlePlayer2Entering(float deltaTime); // Entrada del Jugador 2 + void handleTitleAnimation(float deltaTime); // Ejecuta animación del título + void handleLeavingScreen(float deltaTime); // Lógica para salir de pantalla + void handleEnteringScreen(float deltaTime); // Lógica para entrar en pantalla + void handlePlayer1Entering(float deltaTime); // Entrada del Jugador 1 + void handlePlayer2Entering(float deltaTime); // Entrada del Jugador 2 // --- Manejadores de pantallas especiales --- - void handleCreditsMovement(float deltaTime); // Movimiento en pantalla de créditos - void handleCreditsRightMovement(); // Movimiento hacia la derecha en créditos - void handleCreditsLeftMovement(); // Movimiento hacia la izquierda en créditos - void handleWaitingMovement(float deltaTime); // Animación del jugador saludando - void updateWalkingStateForCredits(); // Actualiza estado de caminata en créditos + void handleCreditsMovement(float deltaTime); // Movimiento en pantalla de créditos + void handleCreditsRightMovement(); // Movimiento hacia la derecha en créditos + void handleCreditsLeftMovement(); // Movimiento hacia la izquierda en créditos + void handleWaitingMovement(float deltaTime); // Animación del jugador saludando + void updateWalkingStateForCredits(); // Actualiza estado de caminata en créditos // --- Utilidades de animación --- [[nodiscard]] auto computeAnimation() const -> std::pair; // Calcula animación de movimiento y disparo diff --git a/source/resource.cpp b/source/resource.cpp index 4326216..d760bf0 100644 --- a/source/resource.cpp +++ b/source/resource.cpp @@ -316,6 +316,11 @@ auto Resource::getAnimation(const std::string& name) -> AnimationsFileBuffer& { // Obtiene el fichero con los datos para el modo demostración a partir de un índice auto Resource::getDemoData(int index) -> DemoData& { + if (index < 0 || index >= static_cast(demos_.size())) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Index %d out of range for demo data (size: %d)", index, static_cast(demos_.size())); + static DemoData empty_demo; + return empty_demo; + } return demos_.at(index); } @@ -538,12 +543,13 @@ void Resource::loadAnimations() { // Carga los datos para el modo demostración void Resource::loadDemoData() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> DEMO FILES"); + auto list = Asset::get()->getListByType(Asset::Type::DEMODATA); + demos_.clear(); - constexpr std::array DEMO_FILES = {"demo1.bin", "demo2.bin"}; - - for (const auto& file : DEMO_FILES) { - updateLoadingProgress(file); - demos_.emplace_back(loadDemoDataFromFile(Asset::get()->get(file))); + for (const auto& l : list) { + auto name = getFileName(l); + updateLoadingProgress(name); + demos_.emplace_back(loadDemoDataFromFile(l)); } } @@ -836,12 +842,11 @@ void Resource::checkEvents() { // Carga los datos para el modo demostración (sin mostrar progreso) void Resource::loadDemoDataQuiet() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> DEMO FILES (quiet load)"); + auto list = Asset::get()->getListByType(Asset::Type::DEMODATA); + demos_.clear(); - constexpr std::array DEMO_FILES = {"demo1.bin", "demo2.bin"}; - - for (const auto& file : DEMO_FILES) { - demos_.emplace_back(loadDemoDataFromFile(Asset::get()->get(file))); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Demo file loaded: %s", file); + for (const auto& l : list) { + demos_.emplace_back(loadDemoDataFromFile(l)); } } diff --git a/source/sections/game.cpp b/source/sections/game.cpp index aa51618..8b54513 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -2,12 +2,13 @@ #include // Para SDL_GetTicks, SDL_SetRenderTarget, SDL_CreateTexture, SDL_Delay, SDL_DestroyTexture, SDL_EventType, SDL_GetRenderTarget, SDL_PollEvent, SDL_RenderTexture, SDL_SetTextureBlendMode, SDL_BLENDMODE_BLEND, SDL_Event, SDL_PixelFormat, SDL_Point, SDL_TextureAccess -#include // Para find, clamp, find_if, min +#include // Para find, clamp, find_if, min, std::shuffle, std::iota #include // Para array #include // Para rand, size_t #include // Para function #include // Para size #include // Para shared_ptr, unique_ptr, __shared_ptr_access, allocator, make_unique, operator==, make_shared +#include // std::random_device, std::default_random_engine #include // Para move #include "asset.h" // Para Asset @@ -1292,13 +1293,10 @@ 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_) { if (player->isPlaying()) { - // Maneja el input específico del jugador en modo demo. - demoHandlePlayerInput(player, index); + demoHandlePlayerInput(player, player->getDemoFile()); // Maneja el input específico del jugador en modo demo. } - ++index; } } @@ -1530,18 +1528,31 @@ void Game::initDemo(Player::Id player_id) { setState(State::PLAYING); #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)); + // En modo juego: cargar todas las demos y asignar una diferente a cada jugador + auto const NUM_DEMOS = Asset::get()->getListByType(Asset::Type::DEMODATA).size(); + for (size_t num_demo = 0; num_demo < NUM_DEMOS; ++num_demo) { + demo_.data.emplace_back(Resource::get()->getDemoData(num_demo)); + } + + // Crear índices mezclados para asignación aleatoria + std::vector demo_indices(NUM_DEMOS); + std::iota(demo_indices.begin(), demo_indices.end(), 0); + std::random_device rd; + std::default_random_engine rng(rd()); + std::shuffle(demo_indices.begin(), demo_indices.end(), rng); + + // Asignar demos a jugadores (round-robin si hay más jugadores que demos) + for (size_t i = 0; i < players_.size(); ++i) { + size_t demo_index = demo_indices[i % NUM_DEMOS]; + players_.at(i)->setDemoFile(demo_index); + } #endif // Selecciona una pantalla al azar - constexpr auto NUM_DEMOS = 3; - const auto DEMO = rand() % NUM_DEMOS; - constexpr std::array STAGES = {0, 3, 5}; - stage_manager_->jumpToStage(STAGES.at(DEMO)); + constexpr auto NUM_STAGES = 3; + const auto STAGE = rand() % NUM_STAGES; + constexpr std::array STAGES = {0, 3, 5}; + stage_manager_->jumpToStage(STAGES.at(STAGE)); // Activa o no al otro jugador if (rand() % 3 != 0) {