new: ja es pot començar el joc els dos jugador a l'hora

new: feedback visual per a saber qui ha pulsat start en la pantalla de titol
Player: afegit estat RESPAWNING per no tindre que estar fent cabrioles amb la invulnerabilitat al crear als jugadors
This commit is contained in:
2025-07-14 11:41:12 +02:00
parent e14336bad9
commit 31910b8a74
9 changed files with 309 additions and 112 deletions

View File

@@ -102,10 +102,7 @@ void Credits::update()
balloon_manager_->update();
updateTextureDstRects();
throwBalloons();
for (auto &player : players_)
{
player->update();
}
updatePlayers();
updateAllFades();
++counter_;
}
@@ -254,10 +251,7 @@ void Credits::fillCanvas()
// Dibuja el fondo, los globos y los jugadores
tiled_bg_->render();
balloon_manager_->render();
for (auto const &player : players_)
{
player->render();
}
renderPlayers();
// Dibuja los titulos de credito
SDL_RenderTexture(Screen::get()->getRenderer(), text_texture_, &credits_rect_src_, &credits_rect_dst_);
@@ -381,19 +375,17 @@ void Credits::initPlayers()
}
// Crea los dos jugadores
constexpr int player_width = 30;
const int y = play_area_.y + play_area_.h - player_width;
constexpr bool demo = false;
constexpr int away_distance = 700;
players_.emplace_back(std::make_unique<Player>(1, play_area_.x - away_distance - player_width, y, demo, play_area_, player_textures.at(0), player_animations));
constexpr int PLAYER_WIDTH = 32;
const int Y = play_area_.y + play_area_.h - PLAYER_WIDTH;
constexpr bool DEMO = false;
constexpr int AWAY_DISTANCE = 700;
players_.emplace_back(std::make_unique<Player>(1, play_area_.x - AWAY_DISTANCE - PLAYER_WIDTH, Y, DEMO, play_area_, player_textures.at(0), player_animations));
players_.back()->setWalkingState(PlayerState::WALKING_RIGHT);
players_.back()->setPlayingState(PlayerState::CREDITS);
players_.back()->setInvulnerable(false);
players_.emplace_back(std::make_unique<Player>(2, play_area_.x + play_area_.w + away_distance, y, demo, play_area_, player_textures.at(1), player_animations));
players_.emplace_back(std::make_unique<Player>(2, play_area_.x + play_area_.w + AWAY_DISTANCE, Y, DEMO, play_area_, player_textures.at(1), player_animations));
players_.back()->setWalkingState(PlayerState::WALKING_LEFT);
players_.back()->setPlayingState(PlayerState::CREDITS);
players_.back()->setInvulnerable(false);
}
// Actualiza los rectangulos negros
@@ -532,3 +524,21 @@ void Credits::cycleColors()
// Aplicar el color, redondeando a enteros antes de usar
tiled_bg_->setColor(Color(static_cast<int>(r), static_cast<int>(g), static_cast<int>(b)));
}
// Actualza los jugadores
void Credits::updatePlayers()
{
for (auto &player : players_)
{
player->update();
}
}
// Renderiza los jugadores
void Credits::renderPlayers()
{
for (auto const &player : players_)
{
player->render();
}
}

View File

@@ -58,7 +58,7 @@ private:
// --- Control de audio ---
int initial_volume_ = Options::audio.music.volume; // Volumen inicial
int steps_ = 0; // Pasos para reducir audio
int steps_ = 0; // Pasos para reducir audio
// --- Rectángulos de renderizado ---
// Texto de créditos
@@ -111,12 +111,14 @@ private:
void fillTextTexture(); // Crear textura de texto de créditos
void fillCanvas(); // Renderizar todos los sprites y fondos
void updateTextureDstRects(); // Actualizar destinos de texturas
void renderPlayers(); // Renderiza los jugadores
// --- Métodos de lógica del juego ---
void throwBalloons(); // Lanzar globos al escenario
void initPlayers(); // Inicializar jugadores
void updateAllFades(); // Actualizar estados de fade
void cycleColors(); // Cambiar colores de fondo
void updatePlayers(); // Actualza los jugadores
// --- Métodos de interfaz ---
void updateBlackRects(); // Actualizar rectángulos negros (letterbox)

View File

@@ -927,7 +927,7 @@ void Game::handlePlayerCollision(std::shared_ptr<Player> &player)
playSound("player_collision.wav");
screen_->shake();
playSound("voice_no.wav");
player->setPlayingState(PlayerState::DYING);
player->setPlayingState(PlayerState::ROLLING);
players_to_reorder.push_back(player);
if (allPlayersAreNotPlaying())
{
@@ -1586,9 +1586,7 @@ void Game::handlePlayerContinue(const std::shared_ptr<Player> &player)
const auto controllerIndex = player->getController();
if (input_->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index))
{
player->setPlayingState(PlayerState::PLAYING);
player->addCredit();
playSound("voice_thankyou.wav");
player->setPlayingState(PlayerState::RESPAWNING);
}
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
@@ -1799,10 +1797,17 @@ void Game::initPlayers(int player_id)
players_.back()->setName(Lang::getText("[SCOREBOARD] 2"));
players_.back()->setController(getController(players_.back()->getId()));
// Activa el jugador que coincide con el "player_id"
auto player = getPlayer(player_id);
player->setPlayingState((demo_.enabled) ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN);
player->setInvulnerable(false);
// Activa el jugador que coincide con el "player_id" o ambos si es "0"
if (player_id == 0)
{
// Activa ambos jugadores
getPlayer(1)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN);
getPlayer(2)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN);
}
else
{
getPlayer(player_id)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN);
}
}
// Hace sonar la música
@@ -2053,30 +2058,29 @@ void Game::movePlayersToFront()
// Comprueba si está activo el menu de servicio para poner el juego en pausa
void Game::checkServiceMenu()
{
if (demo_.enabled)
return;
if (demo_.enabled)
return;
static bool was_paused_before_service_menu = false;
static bool service_menu_was_active = false;
static bool was_paused_before_service_menu = false;
static bool service_menu_was_active = false;
bool service_menu_is_active = ServiceMenu::get()->isEnabled();
bool service_menu_is_active = ServiceMenu::get()->isEnabled();
if (service_menu_is_active && !service_menu_was_active)
{
// El menú acaba de abrirse
was_paused_before_service_menu = paused_;
pause(true);
}
else if (!service_menu_is_active && service_menu_was_active)
{
// El menú acaba de cerrarse
pause(was_paused_before_service_menu);
}
if (service_menu_is_active && !service_menu_was_active)
{
// El menú acaba de abrirse
was_paused_before_service_menu = paused_;
pause(true);
}
else if (!service_menu_is_active && service_menu_was_active)
{
// El menú acaba de cerrarse
pause(was_paused_before_service_menu);
}
service_menu_was_active = service_menu_is_active;
service_menu_was_active = service_menu_is_active;
}
#ifdef DEBUG
// Comprueba los eventos en el modo DEBUG
void Game::checkDebugEvents(const SDL_Event &event)

View File

@@ -16,6 +16,7 @@
#include "notifier.h" // Para Notifier
#include "options.h" // Para OptionsController, Options, options
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "player.h" // Para Player, PlayerState
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Options, Name, name, AttractMode, options
@@ -43,6 +44,7 @@ Title::Title()
fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE);
fade_->setPostDuration(param.fade.post_duration);
initPlayers();
// Asigna valores a otras variables
Section::options = Section::Options::TITLE_1;
@@ -75,6 +77,7 @@ void Title::update()
updateFade();
updateState();
updateStartPrompt();
updatePlayers();
Screen::get()->update();
}
}
@@ -89,6 +92,7 @@ void Title::render()
game_logo_->render();
renderStartPrompt();
renderCopyright();
renderPlayers();
define_buttons_->render();
fade_->render();
@@ -144,38 +148,42 @@ void Title::checkEvents()
// Comprueba las entradas
void Title::checkInput()
{
// Comprueba las entradas solo si no se estan definiendo los botones
if (define_buttons_->isEnabled())
return;
Input::get()->update();
if (!ServiceMenu::get()->isEnabled())
{
// Comprueba las entradas solo si no se estan definiendo los botones
if (!define_buttons_->isEnabled())
// Comprueba todos los métodos de control
for (const auto &CONTROLLER : Options::controllers)
{
// Comprueba todos los métodos de control
for (const auto &CONTROLLER : Options::controllers)
// Boton START
if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index))
{
// START
if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index))
if ((state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP))
{
if ((state_ == TitleState::LOGO_FINISHED || ALLOW_TITLE_ANIMATION_SKIP) && !fade_->isEnabled())
if (CONTROLLER.player_id == 1)
{
Audio::get()->playSound("game_start.wav");
Audio::get()->fadeOutMusic(1500);
switch (CONTROLLER.player_id)
if (!player1_start_pressed_)
{
case 1:
selection_ = Section::Options::GAME_PLAY_1P;
break;
case 2:
selection_ = Section::Options::GAME_PLAY_2P;
break;
default:
selection_ = Section::Options::TITLE_TIME_OUT;
break;
player1_start_pressed_ = true;
getPlayer(1)->setPlayingState(PlayerState::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
}
}
if (CONTROLLER.player_id == 2)
{
if (!player2_start_pressed_)
{
player2_start_pressed_ = true;
getPlayer(2)->setPlayingState(PlayerState::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
}
state_ = TitleState::START_HAS_BEEN_PRESSED;
counter_ = 0;
return;
}
}
}
@@ -183,10 +191,7 @@ void Title::checkInput()
}
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
if (!define_buttons_->isEnabled())
{
GlobalInputs::check();
}
GlobalInputs::check();
}
// Bucle para el titulo del juego
@@ -208,9 +213,7 @@ void Title::resetCounter() { counter_ = 0; }
void Title::swapControllers()
{
if (Input::get()->getNumControllers() == 0)
{
return;
}
Options::swapControllers();
showControllers();
@@ -259,17 +262,31 @@ void Title::updateFade()
fade_->update();
if (fade_->hasEnded())
{
if (selection_ == Section::Options::TITLE_TIME_OUT)
const int COMBO = (player1_start_pressed_ ? 1 : 0) | (player2_start_pressed_ ? 2 : 0);
switch (COMBO)
{
// El menu ha hecho time out
case 0: // Ningún jugador ha pulsado Start
Section::name = next_section_;
}
else
{
// Se ha pulsado para jugar
break;
case 1: // Solo el jugador 1 ha pulsado Start
Section::name = Section::Name::GAME;
Section::options = selection_;
Section::options = Section::Options::GAME_PLAY_1P;
Audio::get()->stopMusic();
break;
case 2: // Solo el jugador 2 ha pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_2P;
Audio::get()->stopMusic();
break;
case 3: // Ambos jugadores han pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_BOTH;
Audio::get()->stopMusic();
break;
}
}
}
@@ -285,8 +302,7 @@ void Title::updateState()
game_logo_->update();
if (game_logo_->hasFinished())
{
state_ = TitleState::LOGO_FINISHED;
Audio::get()->playMusic("title.ogg");
setState(TitleState::LOGO_FINISHED);
}
break;
}
@@ -395,4 +411,94 @@ void Title::renderCopyright()
1,
TITLE_SHADOW_TEXT_COLOR);
}
}
// Cambia el estado
void Title::setState(TitleState state)
{
if (state_ == state)
return;
state_ = state;
switch (state_)
{
case TitleState::LOGO_ANIMATING:
break;
case TitleState::LOGO_FINISHED:
Audio::get()->playMusic("title.ogg");
break;
case TitleState::START_HAS_BEEN_PRESSED:
Audio::get()->fadeOutMusic(1500);
break;
}
}
// Inicializa los jugadores
void Title::initPlayers()
{
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores;
std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador
// Texturas - Player1
{
std::vector<std::shared_ptr<Texture>> player_texture;
player_texture.emplace_back(Resource::get()->getTexture("player1.gif"));
player_texture.emplace_back(Resource::get()->getTexture("player1_power.png"));
player_textures.push_back(player_texture);
}
// Texturas - Player2
{
std::vector<std::shared_ptr<Texture>> player_texture;
player_texture.emplace_back(Resource::get()->getTexture("player2.gif"));
player_texture.emplace_back(Resource::get()->getTexture("player2_power.png"));
player_textures.push_back(player_texture);
}
// Animaciones -- Jugador
{
player_animations.emplace_back(Resource::get()->getAnimation("player.ani"));
player_animations.emplace_back(Resource::get()->getAnimation("player_power.ani"));
}
// Crea los dos jugadores
constexpr int PLAYER_WIDTH = 32;
const int Y = param.title.press_start_position;
constexpr bool DEMO = false;
players_.emplace_back(std::make_unique<Player>(1, param.game.game_area.first_quarter_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(0), player_animations));
players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN);
players_.emplace_back(std::make_unique<Player>(2, param.game.game_area.third_quarter_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(1), player_animations));
players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN);
}
// Actualza los jugadores
void Title::updatePlayers()
{
for (auto &player : players_)
{
player->update();
}
}
// Renderiza los jugadores
void Title::renderPlayers()
{
for (auto const &player : players_)
{
player->render();
}
}
// Obtiene un jugador a partir de su "id"
std::shared_ptr<Player> Title::getPlayer(int id)
{
auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player)
{ return player->getId() == id; });
if (it != players_.end())
{
return *it;
}
return nullptr;
}

View File

@@ -2,11 +2,13 @@
#include <SDL3/SDL_stdinc.h> // Para Uint32
#include <memory> // Para unique_ptr, shared_ptr
#include "section.h" // Para Options
#include <vector>
#include "section.h" // Para Options
class DefineButtons;
class Fade;
class GameLogo;
class Player;
class Sprite;
class Text;
class TiledBG;
@@ -57,6 +59,7 @@ private:
std::unique_ptr<GameLogo> game_logo_; // Logo del juego
std::unique_ptr<Sprite> mini_logo_sprite_; // Logo JailGames mini
std::unique_ptr<DefineButtons> define_buttons_; // Definición de botones del joystick
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
// --- Variables de estado ---
int counter_ = 0; // Temporizador para la pantalla de título
@@ -66,22 +69,29 @@ private:
int num_controllers_; // Número de mandos conectados
TitleState state_; // Estado actual de la sección
bool should_render_start_prompt = false; // Indica si se muestra o no el texto de PRESS START BUTTON TO PLAY
bool player1_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 1
bool player2_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 2
// -- Variables de diseño ---
Anchor anchor_; // Anclas para definir la posición de los elementos del titulo
// --- Métodos internos ---
void update(); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void resetCounter(); // Reinicia el contador interno
void swapControllers(); // Intercambia la asignación de mandos a los jugadores
void swapKeyboard(); // Intercambia el teclado de jugador
void showControllers(); // Muestra información sobre los controles y los jugadores
void updateFade(); // Actualiza el fade
void updateState(); // Actualiza el estado
void updateStartPrompt();
void renderStartPrompt();
void renderCopyright();
void update(); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void resetCounter(); // Reinicia el contador interno
void swapControllers(); // Intercambia la asignación de mandos a los jugadores
void swapKeyboard(); // Intercambia el teclado de jugador
void showControllers(); // Muestra información sobre los controles y los jugadores
void updateFade(); // Actualiza el efecto de fundido (fade in/out)
void updateState(); // Actualiza el estado actual del título
void updateStartPrompt(); // Actualiza el mensaje de "Pulsa Start"
void renderStartPrompt(); // Dibuja el mensaje de "Pulsa Start" en pantalla
void renderCopyright(); // Dibuja el aviso de copyright
void setState(TitleState state); // Cambia el estado del título
void initPlayers(); // Inicializa los jugadores
void renderPlayers(); // Renderiza los jugadores
void updatePlayers(); // Actualza los jugadores
std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id"
};