fix: nou metode per ordenar i dibuixar els jugadors ordenats en l'eix Z

codi: eliminat tot el codi mort de Hit
This commit is contained in:
2025-10-21 09:30:31 +02:00
parent d7836eedd7
commit bf12c1664a
10 changed files with 95 additions and 130 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

View File

@@ -1,50 +0,0 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint
#include <memory> // 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>& texture) // Constructor con textura obligatoria
: sprite_(std::make_unique<Sprite>(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_; // Sprite asociado al Hit
bool enabled_{false}; // Indica si el Hit está activo
};

View File

@@ -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<std::string, std::function<void(const std::string&)>> 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); }},

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
tabe_(std::make_unique<Tabe>()),
hit_(Hit(Resource::get()->getTexture("hit.png"))) {
tabe_(std::make_unique<Tabe>()) {
// 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>& 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>& 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>& 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<Player>& 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<Player>& 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>& 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>& 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<Player>& a, const std::shared_ptr<Player>& 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<Player>& a, const std::shared_ptr<Player>& 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<Player>& who) -> size_t {
for (size_t i = 0; i < elems.size(); ++i)
if (elems[i] == who) return static_cast<int>(i); // compara shared_ptr directamente
return -1;
}
void Game::sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& 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<Player>& 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<int>(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) {

View File

@@ -2,14 +2,13 @@
#include <SDL3/SDL.h> // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64
#include <list> // Para list
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include <list> // 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<std::shared_ptr<Player>>;
// --- 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<std::shared_ptr<Player>> 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<std::unique_ptr<Item>> items_; // Vector con los items
std::list<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites
std::list<std::unique_ptr<PathSprite>> 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<std::shared_ptr<Player>> players_to_put_at_back_;
std::vector<std::shared_ptr<Player>> 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<Player>; // 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>& 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>& bullet) -> bool; // Detecta colisión bala-Tabe
auto checkBulletBalloonCollision(const std::shared_ptr<Bullet>& 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>& player); // Mueve el jugador para pintarlo al fondo de la lista de jugadores
void sendPlayerToTheFront(const std::shared_ptr<Player>& 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<Player>& who) -> size_t;
void sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& who, Players& drawList); // Envia al jugador al fondo de la pantalla
void bringPlayerToFront(Players& elements, const std::shared_ptr<Player>& who, Players& drawList); // Envia al jugador al frente de la pantalla
// --- Varios ---
void onPauseStateChanged(bool is_paused);
// SISTEMA DE GRABACIÓN (CONDICIONAL)