afegit efecte de "colp" quan el jugador es "alcanssat" per un globo

This commit is contained in:
2025-07-25 13:45:55 +02:00
parent f001e433e0
commit b165484e03
11 changed files with 145 additions and 12 deletions

View File

@@ -9,7 +9,7 @@ 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 true # Indica si debe haber un paro cuando el jugador es golpeado por un globo
game.hit_stop_ms 300 # Cantidad de milisegundos que dura el hit_stop
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

@@ -9,7 +9,7 @@ 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 true # Indica si debe haber un paro cuando el jugador es golpeado por un globo
game.hit_stop_ms 300 # Cantidad de milisegundos que dura el hit_stop
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
## --- FADE ---
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido

BIN
data/gfx/player/hit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

View File

@@ -60,6 +60,16 @@ void Audio::pauseMusic() {
}
}
// Continua la música pausada
void Audio::resumeMusic() {
if (music_enabled_ && music_.state == MusicState::PAUSED) {
#ifndef NO_AUDIO
JA_ResumeMusic();
#endif
music_.state = MusicState::PLAYING;
}
}
// Detiene la música
void Audio::stopMusic() {
if (music_enabled_) {

View File

@@ -25,11 +25,12 @@ class Audio {
auto operator=(const Audio &) -> Audio & = delete; // Evitar asignación
// --- Método principal ---
void update();
void update();
// --- Control de Música ---
void playMusic(const std::string &name, int loop = -1); // Reproducir música en bucle
void pauseMusic(); // Pausar reproducción de música
void resumeMusic(); // Continua la música pausada
void stopMusic(); // Detener completamente la música
void fadeOutMusic(int milliseconds) const; // Fundido de salida de la música

View File

@@ -402,10 +402,13 @@ void Director::setFileList() {
Asset::get()->add(PREFIX + "/data/gfx/player/player2_coffee2.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player2_invencible.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player2_power.png", AssetType::BITMAP);
// Animaciones del jugador
Asset::get()->add(PREFIX + "/data/gfx/player/player.ani", AssetType::ANIMATION);
Asset::get()->add(PREFIX + "/data/gfx/player/player_power.ani", AssetType::ANIMATION);
// Texturas - Golpe del jugador
Asset::get()->add(PREFIX + "/data/gfx/player/hit.png", AssetType::BITMAP);
// Fuentes de texto
Asset::get()->add(PREFIX + "/data/font/8bithud.png", AssetType::BITMAP);

61
source/hit.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint
#include <memory> // Para std::unique_ptr y std::shared_ptr
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
// Estructura que representa una colisión o impacto visual
struct Hit {
private:
// Indica si el Hit está activo o no
bool enabled{false};
// Sprite asociado al Hit, gestionado con un puntero único
std::unique_ptr<Sprite> sprite;
public:
// Elimina el constructor por defecto para obligar a pasar una textura
Hit() = delete;
// Constructor que obliga a pasar una textura compartida para crear el Sprite
// Esto evita que se pueda crear un Hit sin recursos gráficos válidos
explicit Hit(std::shared_ptr<Texture> texture)
: sprite(std::make_unique<Sprite>(texture)) {}
// Establece la posición del Sprite en el espacio
void setPos(SDL_FPoint position) {
SDL_FPoint centered_position = {position.x - (sprite->getWidth() / 2), position.y - (sprite->getHeight() / 2)};
sprite->setPosition(centered_position);
}
// Activa o desactiva el Hit
void enable(bool value) {
enabled = value;
}
// Consulta si el Hit está activo
bool isEnabled() const {
return enabled;
}
// Crea un "Hit" en la posición especificada
void create(SDL_FPoint position) {
setPos(position);
enable(true);
}
// Dibuja el hit
void render() {
if (enabled) {
sprite->render();
}
}
// Deshabilita el hit
void disable() {
enabled = false;
}
};

View File

@@ -8,6 +8,7 @@
#include <functional> // Para function
#include <iostream> // Para std::cout, std::endl
#include <iterator> // Para distance, size
#include <memory> // Para std::make_unique
#include "asset.h" // Para Asset
#include "audio.h" // Para Audio
@@ -20,6 +21,7 @@
#include "fade.h" // Para Fade, FadeType, FadeMode
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "hit.h" // Para Hit
#include "input.h" // Para InputAction, Input, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_ALLOW_REPEAT, InputDevice
#include "item.h" // Para Item, ItemType
#include "lang.h" // Para getText
@@ -49,7 +51,8 @@ Game::Game(int player_id, int current_stage, bool demo)
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
balloon_manager_(std::make_unique<BalloonManager>()),
tabe_(std::make_unique<Tabe>()) {
tabe_(std::make_unique<Tabe>()),
hit_(Hit(Resource::get()->getTexture("hit.png"))) {
// Pasa variables
demo_.enabled = demo;
@@ -213,7 +216,7 @@ void Game::updatePlayers() {
}
// En caso contrario, el jugador ha sido golpeado por un globo activo
else {
handlePlayerCollision(player);
handlePlayerCollision(player, balloon);
if (demo_.enabled && allPlayersAreNotPlaying()) {
fade_out_->setType(FadeType::RANDOM_SQUARE);
@@ -830,7 +833,7 @@ void Game::renderPathSprites() {
}
// Acciones a realizar cuando el jugador colisiona con un globo
void Game::handlePlayerCollision(std::shared_ptr<Player> &player) {
void Game::handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_ptr<Balloon> &balloon) {
if (!player->isPlaying() || player->isInvulnerable()) {
// Si no está jugando o tiene inmunidad, no hace nada
return;
@@ -847,15 +850,19 @@ void Game::handlePlayerCollision(std::shared_ptr<Player> &player) {
// Si no tiene cafes, muere
playSound("player_collision.wav");
if (param.game.hit_stop) {
pauseMusic();
auto position = getCollisionPoint(player->getCollider(), balloon->getCollider());
putHitOnScreen(position);
SDL_Delay(param.game.hit_stop_ms);
hit_.disable();
resumeMusic();
}
screen_->shake();
playSound("voice_no.wav");
player->setPlayingState(PlayerState::ROLLING);
players_to_reorder_.push_back(player);
if (allPlayersAreNotPlaying()) {
// No se puede subir poder de fase si no hay nadie jugando
Stage::power_can_be_added = false;
Stage::power_can_be_added = false; // No se puede subir poder de fase si no hay nadie jugando
}
}
}
@@ -979,6 +986,7 @@ void Game::fillCanvas() {
// Dibuja los objetos
background_->render();
renderPlayers();
renderHit();
renderSmartSprites();
renderItems();
balloon_manager_->render();
@@ -1598,6 +1606,16 @@ void Game::playMusic() {
Audio::get()->playMusic("playing.ogg");
}
// Pausa la música
void Game::pauseMusic() {
Audio::get()->pauseMusic();
}
// Retoma la música que eestaba pausada
void Game::resumeMusic() {
Audio::get()->resumeMusic();
}
// Detiene la música
void Game::stopMusic() const {
if (!demo_.enabled) {
@@ -1823,6 +1841,19 @@ void Game::checkServiceMenu() {
service_menu_was_active_ = service_menu_is_active;
}
// Dibuja el golpe que recibe el jugador al impactarle un globo
void Game::renderHit() {
hit_.render();
}
// Coloca el Hit en pantalla obligando a hacer un renderizado
void Game::putHitOnScreen(SDL_FPoint position) {
hit_.create(position);
fillCanvas();
render();
hit_.disable();
}
#ifdef _DEBUG
// Comprueba los eventos en el modo DEBUG
void Game::checkDebugEvents(const SDL_Event &event) {

View File

@@ -6,6 +6,7 @@
#include <string> // Para string
#include <vector> // Para vector
#include "hit.h" // Para Hit
#include "item.h" // Para Item, ItemType
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
@@ -147,6 +148,7 @@ class Game {
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
GameState state_ = GameState::FADE_IN; // Estado
std::vector<std::shared_ptr<Player>> players_to_reorder_;
Hit hit_; // Para representar colisiones en pantalla
#ifdef _DEBUG
bool auto_pop_balloons_ = false; // Si es true, incrementa automaticamente los globos explotados
@@ -186,9 +188,9 @@ class Game {
auto allPlayersAreNotPlaying() -> bool; // Verifica si ningún jugador está activo
// --- Colisiones de jugadores ---
void handlePlayerCollision(std::shared_ptr<Player> &player); // Procesa colisión de jugador con globo
auto checkPlayerBalloonCollision(std::shared_ptr<Player> &player) -> std::shared_ptr<Balloon>; // Detecta colisión jugador-globo
void checkPlayerItemCollision(std::shared_ptr<Player> &player); // Detecta colisión jugador-ítem
void handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_ptr<Balloon> &balloon); // Procesa colisión de jugador con globo
auto checkPlayerBalloonCollision(std::shared_ptr<Player> &player) -> std::shared_ptr<Balloon>; // Detecta colisión jugador-globo
void checkPlayerItemCollision(std::shared_ptr<Player> &player); // Detecta colisión jugador-ítem
// --- Sistema de entrada (input) ---
void checkInput(); // Gestiona toda la entrada durante el juego
@@ -284,11 +286,16 @@ class Game {
// --- Sistema de audio ---
static void playMusic(); // Reproduce la música de fondo
void stopMusic() const; // Detiene la reproducción de música
void pauseMusic(); // Pausa la música
void resumeMusic(); // Retoma la música que eestaba pausada
void playSound(const std::string &name) const; // Reproduce un efecto de sonido específico
// --- Utilidades y servicios ---
void checkServiceMenu(); // Verifica si el menú de servicio está activo
void renderHit(); // Dibuja el golpe que recibe el jugador al impactarle un globo
void putHitOnScreen(SDL_FPoint position); // Coloca el Hit en pantalla obligando a hacer un renderizado
// SISTEMA DE GRABACIÓN (CONDICIONAL)
#ifdef RECORDING
void updateRecording(); // Actualiza variables durante modo de grabación

View File

@@ -24,6 +24,25 @@ auto distanceSquared(int x1, int y1, int x2, int y2) -> double {
return DELTA_X * DELTA_X + DELTA_Y * DELTA_Y;
}
// Obtiene el punto de colisión entre dos circulos
auto getCollisionPoint(const Circle &a, const Circle &b) -> SDL_FPoint {
float dx = b.x - a.x;
float dy = b.y - a.y;
float dist = std::sqrt(dx * dx + dy * dy);
// Normaliza el vector
float nx = dx / dist;
float ny = dy / dist;
// Punto en el borde del círculo A hacia B
SDL_FPoint contact;
contact.x = a.x + nx * a.r;
contact.y = a.y + ny * a.r;
return contact;
}
// Detector de colisiones entre dos circulos
auto checkCollision(const Circle &a, const Circle &b) -> bool {
// Calcula el radio total al cuadrado

View File

@@ -79,6 +79,7 @@ struct Zone {
// 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;