From bf12c1664a9ead923b7519e518a6618d9b24bc2b Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 21 Oct 2025 09:30:31 +0200 Subject: [PATCH] fix: nou metode per ordenar i dibuixar els jugadors ordenats en l'eix Z codi: eliminat tot el codi mort de Hit --- config/assets.txt | 3 - config/param_320x240.txt | 2 - config/param_320x256.txt | 2 - data/gfx/player/hit.png | Bin 312 -> 0 bytes source/hit.hpp | 50 --------------- source/param.cpp | 2 - source/param.hpp | 3 - source/player.hpp | 4 ++ source/sections/game.cpp | 133 ++++++++++++++++++++++----------------- source/sections/game.hpp | 26 ++++---- 10 files changed, 95 insertions(+), 130 deletions(-) delete mode 100644 data/gfx/player/hit.png delete mode 100644 source/hit.hpp diff --git a/config/assets.txt b/config/assets.txt index 2325ca7..804d95a 100644 --- a/config/assets.txt +++ b/config/assets.txt @@ -180,9 +180,6 @@ PALETTE|${PREFIX}/data/gfx/player/player2_invencible.pal ANIMATION|${PREFIX}/data/gfx/player/player_power.ani ANIMATION|${PREFIX}/data/gfx/player/player.ani -# Texturas - Golpe del jugador -BITMAP|${PREFIX}/data/gfx/player/hit.png - # Fuentes de texto BITMAP|${PREFIX}/data/font/04b_25_2x.png BITMAP|${PREFIX}/data/font/04b_25_2x_white.png diff --git a/config/param_320x240.txt b/config/param_320x240.txt index 4599e72..3334701 100644 --- a/config/param_320x240.txt +++ b/config/param_320x240.txt @@ -11,8 +11,6 @@ game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.h 200 # Alto de la zona jugable game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida -game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo -game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop # --- FADE --- fade.color 1F2B30 # Color hexadecimal para el efecto de fundido diff --git a/config/param_320x256.txt b/config/param_320x256.txt index 8cbac9d..8ec032f 100644 --- a/config/param_320x256.txt +++ b/config/param_320x256.txt @@ -11,8 +11,6 @@ game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.h 216 # Alto de la zona jugable game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida -game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo -game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop # --- FADE --- fade.color 1F2B30 # Color hexadecimal para el efecto de fundido diff --git a/data/gfx/player/hit.png b/data/gfx/player/hit.png deleted file mode 100644 index 1239dd8ecc80dfc555e9d995c47f03fb07c61277..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmV-80muG{P)Px#@<~KNR5*=|lg$mnFbsviRFPN!4yapnfvU@dHM&N-4C0Qu1?mA7;6gYwj^ZRm zyh~-re$Rh3{DA9v4=^t`v-cJa*hE%@cKmZ8fom!b$uCdY_{*~quinnV2goB(jPjYSobB*=!S4oM;GU)c%L7UGNA zMp9<9EB!;bgBoIBIBy}Bt5icw)kQV`1M^K4L)=RgM1S(b0ek_`yP()hzC6GH0000< KMNUMnLSTZ_U4?c4 diff --git a/source/hit.hpp b/source/hit.hpp deleted file mode 100644 index 08901b4..0000000 --- a/source/hit.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include // Para SDL_FPoint - -#include // Para std::unique_ptr y std::shared_ptr - -#include "sprite.hpp" // Para Sprite -#include "texture.hpp" // Para Texture - -// --- Estructura Hit: representa una colisión o impacto visual --- -struct Hit { - public: - // --- Constructor --- - Hit() = delete; // Elimina el constructor por defecto para obligar a pasar una textura - explicit Hit(const std::shared_ptr& texture) // Constructor con textura obligatoria - : sprite_(std::make_unique(texture)) {} - - // --- Métodos principales --- - void create(SDL_FPoint position) { // Crea un "Hit" en la posición especificada - setPos(position); - enable(true); - } - void render() { // Dibuja el hit - if (enabled_) { - sprite_->render(); - } - } - void disable() { // Deshabilita el hit - enabled_ = false; - } - - // --- Configuración --- - void setPos(SDL_FPoint position) { // Establece la posición del Sprite en el espacio - SDL_FPoint centered_position = {position.x - (sprite_->getWidth() / 2), position.y - (sprite_->getHeight() / 2)}; - sprite_->setPosition(centered_position); - } - void enable(bool value) { // Activa o desactiva el Hit - enabled_ = value; - } - - // --- Getters --- - [[nodiscard]] auto isEnabled() const -> bool { // Consulta si el Hit está activo - return enabled_; - } - - private: - // --- Variables de estado --- - std::unique_ptr sprite_; // Sprite asociado al Hit - bool enabled_{false}; // Indica si el Hit está activo -}; diff --git a/source/param.cpp b/source/param.cpp index 88b3675..9f3fef8 100644 --- a/source/param.cpp +++ b/source/param.cpp @@ -96,7 +96,6 @@ auto setParams(const std::string& var, const std::string& value) -> bool { {"game.play_area.rect.h", [](const std::string& v) { param.game.play_area.rect.h = std::stoi(v); }}, {"game.name_entry_idle_time", [](const std::string& v) { param.game.name_entry_idle_time = std::stoi(v); }}, {"game.name_entry_total_time", [](const std::string& v) { param.game.name_entry_total_time = std::stoi(v); }}, - {"game.hit_stop_ms", [](const std::string& v) { param.game.hit_stop_ms = std::stoi(v); }}, {"fade.num_squares_width", [](const std::string& v) { param.fade.num_squares_width = std::stoi(v); }}, {"fade.num_squares_height", [](const std::string& v) { param.fade.num_squares_height = std::stoi(v); }}, {"fade.random_squares_duration_ms", [](const std::string& v) { param.fade.random_squares_duration_ms = std::stoi(v); }}, @@ -165,7 +164,6 @@ auto setParams(const std::string& var, const std::string& value) -> bool { {"player.outline_color[1]", [](const std::string& v) { param.player.outline_color[1] = Color::fromHex(v); }}}; static const std::unordered_map> BOOL_PARAMS = { - {"game.hit_stop", [](const std::string& v) { param.game.hit_stop = stringToBool(v); }}, {"scoreboard.separator_autocolor", [](const std::string& v) { param.scoreboard.separator_autocolor = stringToBool(v); }}, {"scoreboard.text_autocolor", [](const std::string& v) { param.scoreboard.text_autocolor = stringToBool(v); }}, {"balloon.bouncing_sound", [](const std::string& v) { param.balloon.bouncing_sound = stringToBool(v); }}, diff --git a/source/param.hpp b/source/param.hpp index 5ab69a2..e5444a8 100644 --- a/source/param.hpp +++ b/source/param.hpp @@ -18,9 +18,6 @@ struct ParamGame { Zone game_area{}; // Se inicializa en el constructor de Param int name_entry_idle_time = GameDefaults::Game::NAME_ENTRY_IDLE_TIME; int name_entry_total_time = GameDefaults::Game::NAME_ENTRY_TOTAL_TIME; - Uint32 speed = 15; // Este valor no estaba en el archivo de configuración - bool hit_stop = GameDefaults::Game::HIT_STOP; - Uint32 hit_stop_ms = GameDefaults::Game::HIT_STOP_MS; Color item_text_outline_color; }; diff --git a/source/player.hpp b/source/player.hpp index a684013..742e1c4 100644 --- a/source/player.hpp +++ b/source/player.hpp @@ -187,6 +187,9 @@ class Player { [[nodiscard]] auto getName() const -> const std::string& { return name_; } [[nodiscard]] auto getPlayingState() const -> State { return playing_state_; } auto getCollider() -> Circle& { return collider_; } + [[nodiscard]] auto getZOrder() const -> size_t { return z_order_; } + void setZOrder(size_t z_order) { z_order_ = z_order; } + // Puntuación y juego [[nodiscard]] auto getScore() const -> int { return score_; } @@ -309,6 +312,7 @@ class Player { 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 + size_t z_order_ = 0; // Orden de dibujado en la pantalla 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 diff --git a/source/sections/game.cpp b/source/sections/game.cpp index 05b2db9..a50799e 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -25,7 +25,6 @@ #include "fade.hpp" // Para Fade #include "global_events.hpp" // Para handle #include "global_inputs.hpp" // Para check -#include "hit.hpp" // Para Hit #include "input.hpp" // Para Input #include "input_types.hpp" // Para InputAction #include "item.hpp" // Para Item, ItemType @@ -65,8 +64,7 @@ Game::Game(Player::Id player_id, int current_stage, bool demo_enabled) background_(std::make_unique(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))), fade_in_(std::make_unique()), fade_out_(std::make_unique()), - tabe_(std::make_unique()), - hit_(Hit(Resource::get()->getTexture("hit.png"))) { + tabe_(std::make_unique()) { // Pasa variables demo_.enabled = demo_enabled; @@ -275,14 +273,12 @@ void Game::updatePlayers(float delta_time) { } // Organiza la lista de jugadores - //sortPlayersByZOrder(); + // sortPlayersByZOrder(); } // Dibuja a los jugadores void Game::renderPlayers() { - for (auto& player : players_) { - player->render(); - } + renderPlayerDrawList(players_draw_list_); } // Comprueba si hay cambio de fase y actualiza las variables @@ -812,15 +808,10 @@ void Game::handlePlayerCollision(std::shared_ptr& player, std::shared_pt } else { // Si no tiene cafes, muere playSound("player_collision.wav"); - if (param.game.hit_stop) { - pauseMusic(); - SDL_Delay(param.game.hit_stop_ms); - resumeMusic(); - } screen_->shake(); playSound("voice_no.wav"); player->setPlayingState(Player::State::ROLLING); - sendPlayerToTheBack(player); + sendPlayerToBack(players_, player, players_draw_list_); if (allPlayersAreNotPlaying()) { stage_manager_->disablePowerCollection(); } @@ -1137,11 +1128,11 @@ void Game::handleEvents() { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_WINDOW_FOCUS_LOST: { - //pause_manager_->setFocusLossPause(!demo_.enabled); + pause_manager_->setFocusLossPause(!demo_.enabled); break; } case SDL_EVENT_WINDOW_FOCUS_GAINED: { - //pause_manager_->setFocusLossPause(false); + pause_manager_->setFocusLossPause(false); break; } default: @@ -1416,7 +1407,7 @@ 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(); - sendPlayerToTheFront(player); + bringPlayerToFront(players_, player, players_draw_list_); return; } @@ -1435,7 +1426,7 @@ 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(); - sendPlayerToTheFront(player); + bringPlayerToFront(players_, player, players_draw_list_); } } @@ -1634,6 +1625,7 @@ 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); + player1->setZOrder(0); #ifdef RECORDING player1->setPlayingState(Player::State::PLAYING); #else @@ -1661,6 +1653,7 @@ void Game::initPlayers(Player::Id player_id) { player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance); player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id); player2->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER2) ? STATE : Player::State::WAITING); + player2->setZOrder(1); // Añade los jugadores al vector de forma que el jugador 1 se pinte por delante del jugador 2 players_.push_back(std::move(player2)); @@ -1671,6 +1664,9 @@ void Game::initPlayers(Player::Id player_id) { Options::keyboard.addPlayer(player); Options::gamepad_manager.addPlayer(player); } + + // Construye la lista de dibujado de los jugadores + buildPlayerDrawList(players_, players_draw_list_); } // Hace sonar la música @@ -1908,45 +1904,6 @@ void Game::playSound(const std::string& name) const { audio_->playSound(name); } -// Organiza los jugadores para que los vivos se pinten sobre los muertos -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_) { - auto it = std::ranges::find(players_, player); - if (it != players_.end() && it != players_.begin()) { - const std::shared_ptr& dying_player = *it; - players_.erase(it); - players_.insert(players_.begin(), dying_player); - } - } - players_to_put_at_back_.clear(); - } - - // Procesar jugadores que van al frente (se dibujan últimos) - if (!players_to_put_at_front_.empty()) { - for (auto& player : players_to_put_at_front_) { - auto it = std::ranges::find(players_, player); - if (it != players_.end() && it != players_.end() - 1) { - const std::shared_ptr& front_player = *it; - players_.erase(it); - players_.push_back(front_player); - } - } - players_to_put_at_front_.clear(); - } -} - -// Mueve el jugador para pintarlo al fondo de la lista de jugadores -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) { - players_to_put_at_front_.push_back(player); -} - void Game::onPauseStateChanged(bool is_paused) { screen_->attenuate(is_paused); tabe_->pauseTimer(is_paused); @@ -2013,6 +1970,70 @@ void Game::handleGameOverEvents() { } } +// Construye (una vez) el drawList a partir del vector principal +// drawList almacena punteros a los elementos y queda reservado +void Game::buildPlayerDrawList(const Players& elements, Players& drawList) { + drawList.clear(); + drawList.reserve(elements.size()); + for (const auto& e : elements) drawList.push_back(e); // copia el shared_ptr + std::stable_sort(drawList.begin(), drawList.end(), [](const std::shared_ptr& a, const std::shared_ptr& b) { + return a->getZOrder() < b->getZOrder(); + }); +} + +// Actualiza drawList tras cambios en los z_order. Implementación simple: +// reordena drawList según los z_order actuales. Llamar cuando cambian z_order +void Game::updatePlayerDrawList(const Players& elements, Players& drawList) { + // Si drawList está vacío o su tamaño no coincide, reconstruirlo. + if (drawList.size() != elements.size()) { + buildPlayerDrawList(elements, drawList); + return; + } + // Dado que apuntan a los mismos elementos, basta ordenar por los z_order actuales. + std::stable_sort(drawList.begin(), drawList.end(), [](const std::shared_ptr& a, const std::shared_ptr& b) { + return a->getZOrder() < b->getZOrder(); + }); +} + +// Dibuja en el orden definido por drawList +void Game::renderPlayerDrawList(const Players& drawList) { + for (const auto& e : drawList) e->render(); +} + +// Operaciones sobre z_order que mantienen la invariante y actualizan drawList. +auto Game::findPlayerIndex(const Players& elems, const std::shared_ptr& who) -> size_t { + for (size_t i = 0; i < elems.size(); ++i) + if (elems[i] == who) return static_cast(i); // compara shared_ptr directamente + return -1; +} + +void Game::sendPlayerToBack(Players& elements, const std::shared_ptr& who, Players& drawList) { + int idx = findPlayerIndex(elements, who); + if (idx < 0) return; // no encontrado + const int oldZ = elements[idx]->getZOrder(); + if (oldZ <= 0) return; + for (auto& p : elements) { + int z = p->getZOrder(); + if (z < oldZ) p->setZOrder(z + 1); + } + elements[idx]->setZOrder(0); + updatePlayerDrawList(elements, drawList); +} + +void Game::bringPlayerToFront(Players& elements, const std::shared_ptr& who, Players& drawList) { + int idx = findPlayerIndex(elements, who); + if (idx < 0) return; // no encontrado + const int oldZ = elements[idx]->getZOrder(); + const int N = static_cast(elements.size()); + if (oldZ >= N - 1) return; + for (auto& p : elements) { + int z = p->getZOrder(); + if (z > oldZ) p->setZOrder(z - 1); + } + elements[idx]->setZOrder(N - 1); + updatePlayerDrawList(elements, drawList); +} + #ifdef _DEBUG // Comprueba los eventos en el modo DEBUG void Game::handleDebugEvents(const SDL_Event& event) { diff --git a/source/sections/game.hpp b/source/sections/game.hpp index ef40682..7a82485 100644 --- a/source/sections/game.hpp +++ b/source/sections/game.hpp @@ -2,14 +2,13 @@ #include // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64 +#include // Para list #include // Para shared_ptr, unique_ptr #include // Para string #include // Para vector -#include // Para list #include "bullet.hpp" // for Bullet #include "demo.hpp" // for Demo -#include "hit.hpp" // for Hit #include "item.hpp" // for Item (ptr only), ItemType #include "manage_hiscore_table.hpp" // for HiScoreEntry #include "options.hpp" // for Settings, settings @@ -65,6 +64,8 @@ class Game { void run(); // Ejecuta el bucle principal del juego private: + using Players = std::vector>; + // --- Enums --- enum class State { FADE_IN, // Transición de entrada @@ -121,7 +122,8 @@ class Game { SDL_Texture* canvas_; // Textura para dibujar la zona de juego - std::vector> players_; // Vector con los jugadores + Players players_; // Vector con los jugadores + Players players_draw_list_; // Vector con los jugadores ordenados para ser renderizados std::list> items_; // Vector con los items std::list> smart_sprites_; // Vector con los smartsprites std::list> path_sprites_; // Vector con los pathsprites @@ -163,9 +165,6 @@ class Game { int menace_ = 0; // Nivel de amenaza actual int menace_threshold_ = 0; // 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 número de globos State state_ = State::FADE_IN; // Estado - std::vector> players_to_put_at_back_; - std::vector> players_to_put_at_front_; - Hit hit_; // Para representar colisiones en pantalla // Estructuras para gestionar flags de eventos basados en tiempo struct GameOverFlags { @@ -226,7 +225,6 @@ class Game { void initPlayers(Player::Id player_id); // Inicializa los datos de los jugadores void updatePlayers(float delta_time); // Actualiza las variables y estados de los jugadores void renderPlayers(); // Renderiza todos los jugadores en pantalla - void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores auto getPlayer(Player::Id id) -> std::shared_ptr; // Obtiene un jugador por su identificador static auto getController(Player::Id player_id) -> int; // Obtiene el controlador asignado a un jugador @@ -260,9 +258,6 @@ class Game { void demoHandlePassInput(); // Permite saltar la demostración void demoHandlePlayerInput(const std::shared_ptr& player, int index); // Procesa entrada de jugador en demo - // --- Sistema de balas y proyectiles --- - void checkBulletCollision(); // Verifica colisiones de todas las balas (delegado a BulletManager) - // --- Colisiones específicas de balas --- auto checkBulletTabeCollision(const std::shared_ptr& bullet) -> bool; // Detecta colisión bala-Tabe auto checkBulletBalloonCollision(const std::shared_ptr& bullet) -> bool; // Detecta colisión bala-globo @@ -338,8 +333,15 @@ class Game { static void resumeMusic(); // Retoma la música que eestaba pausada void playSound(const std::string& name) const; // Reproduce un efecto de sonido específico - void sendPlayerToTheBack(const std::shared_ptr& player); // Mueve el jugador para pintarlo al fondo de la lista de jugadores - void sendPlayerToTheFront(const std::shared_ptr& player); // Mueve el jugador para pintarlo el primero de la lista de jugadores + // --- Gestion y dibujado de jugadores en z-order --- + void buildPlayerDrawList(const Players& elements, Players& drawList); // Construye el drawList a partir del vector principal + void updatePlayerDrawList(const Players& elements, Players& drawList); // Actualiza drawList tras cambios en los z_order + void renderPlayerDrawList(const Players& drawList); // Dibuja en el orden definido + static auto findPlayerIndex(const Players& elems, const std::shared_ptr& who) -> size_t; + void sendPlayerToBack(Players& elements, const std::shared_ptr& who, Players& drawList); // Envia al jugador al fondo de la pantalla + void bringPlayerToFront(Players& elements, const std::shared_ptr& who, Players& drawList); // Envia al jugador al frente de la pantalla + + // --- Varios --- void onPauseStateChanged(bool is_paused); // SISTEMA DE GRABACIÓN (CONDICIONAL)