treballant en context per a jugador 1, jugador 2 o els dos

This commit is contained in:
2025-12-12 10:43:17 +01:00
parent 0ceaa75862
commit 0c75f56cb5
6 changed files with 217 additions and 53 deletions

View File

@@ -3,6 +3,8 @@
#pragma once
#include "core/system/game_config.hpp"
namespace GestorEscenes {
// Context de transició entre escenes
@@ -58,9 +60,20 @@ public:
opcio_ = Opcio::NONE;
}
// Configurar partida abans de transicionar a JOC
void set_config_partida(const GameConfig::ConfigPartida& config) {
config_partida_ = config;
}
// Obtenir configuració de partida (consumit per EscenaJoc)
[[nodiscard]] const GameConfig::ConfigPartida& get_config_partida() const {
return config_partida_;
}
private:
Escena escena_desti_; // Escena a la qual transicionar
Opcio opcio_; // Opció específica per l'escena
Escena escena_desti_; // Escena a la qual transicionar
Opcio opcio_; // Opció específica per l'escena
GameConfig::ConfigPartida config_partida_; // Configuració de partida (jugadors actius, mode)
};
// Variable global inline per gestionar l'escena actual (backward compatibility)

View File

@@ -0,0 +1,51 @@
#pragma once
#include <cstdint>
namespace GameConfig {
// Mode de joc
enum class Mode {
NORMAL, // Partida normal
DEMO // Mode demostració (futur)
};
// Configuració d'una partida
struct ConfigPartida {
bool jugador1_actiu{false}; // És actiu el jugador 1?
bool jugador2_actiu{false}; // És actiu el jugador 2?
Mode mode{Mode::NORMAL}; // Mode de joc
// Mètodes auxiliars
// Retorna true si només hi ha un jugador actiu
[[nodiscard]] bool es_un_jugador() const {
return (jugador1_actiu && !jugador2_actiu) ||
(!jugador1_actiu && jugador2_actiu);
}
// Retorna true si hi ha dos jugadors actius
[[nodiscard]] bool son_dos_jugadors() const {
return jugador1_actiu && jugador2_actiu;
}
// Retorna true si no hi ha cap jugador actiu
[[nodiscard]] bool cap_jugador() const {
return !jugador1_actiu && !jugador2_actiu;
}
// Compte de jugadors actius (0, 1 o 2)
[[nodiscard]] uint8_t compte_jugadors() const {
return (jugador1_actiu ? 1 : 0) + (jugador2_actiu ? 1 : 0);
}
// Retorna l'ID de l'únic jugador actiu (0 o 1)
// Només vàlid si es_un_jugador() retorna true
[[nodiscard]] uint8_t id_unic_jugador() const {
if (jugador1_actiu && !jugador2_actiu) return 0;
if (!jugador1_actiu && jugador2_actiu) return 1;
return 0; // Fallback (cal comprovar es_un_jugador() primer)
}
};
} // namespace GameConfig

View File

@@ -30,6 +30,16 @@ EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context)
debris_manager_(sdl.obte_renderer()),
gestor_puntuacio_(sdl.obte_renderer()),
text_(sdl.obte_renderer()) {
// Recuperar configuració de partida des del context
config_partida_ = context_.get_config_partida();
// Debug output de la configuració
std::cout << "[EscenaJoc] Configuració de partida - P1: "
<< (config_partida_.jugador1_actiu ? "ACTIU" : "INACTIU")
<< ", P2: "
<< (config_partida_.jugador2_actiu ? "ACTIU" : "INACTIU")
<< std::endl;
// Consumir opcions (preparació per MODE_DEMO futur)
auto opcio = context_.consumir_opcio();
(void)opcio; // Suprimir warning de variable no usada
@@ -155,10 +165,22 @@ void EscenaJoc::inicialitzar() {
punt_spawn_.x = zona.x + zona.w * 0.5f;
punt_spawn_.y = zona.y + zona.h * Defaults::Game::INIT_HUD_SHIP_START_Y_RATIO;
// Inicialitzar AMBAS naus amb posicions específiques
// Inicialitzar naus segons configuració (només jugadors actius)
for (uint8_t i = 0; i < 2; i++) {
Punt spawn_pos = obtenir_punt_spawn(i);
naus_[i].inicialitzar(&spawn_pos, false); // No invulnerability at start
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu) {
// Jugador actiu: inicialitzar normalment
Punt spawn_pos = obtenir_punt_spawn(i);
naus_[i].inicialitzar(&spawn_pos, false); // No invulnerability at start
std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inicialitzat\n";
} else {
// Jugador inactiu: marcar com a mort permanent
naus_[i].marcar_tocada();
itocado_per_jugador_[i] = 999.0f; // Valor sentinella (permanent inactiu)
vides_per_jugador_[i] = 0; // Sense vides
std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inactiu\n";
}
}
// [MODIFIED] Initialize enemies as inactive (stage system will spawn them)
@@ -183,14 +205,18 @@ void EscenaJoc::actualitzar(float delta_time) {
if (!game_over_) {
auto* input = Input::get();
// Jugador 1 dispara
if (input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
disparar_bala(0);
// Jugador 1 dispara (només si està actiu)
if (config_partida_.jugador1_actiu) {
if (input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
disparar_bala(0);
}
}
// Jugador 2 dispara
if (input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
disparar_bala(1);
// Jugador 2 dispara (només si està actiu)
if (config_partida_.jugador2_actiu) {
if (input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
disparar_bala(1);
}
}
}
@@ -247,8 +273,11 @@ void EscenaJoc::actualitzar(float delta_time) {
// Set sentinel value to prevent re-entering this block
itocado_per_jugador_[i] = 999.0f;
// Check if BOTH players are dead (game over)
if (vides_per_jugador_[0] <= 0 && vides_per_jugador_[1] <= 0) {
// Check if ALL ACTIVE players are dead (game over)
bool p1_dead = !config_partida_.jugador1_actiu || vides_per_jugador_[0] <= 0;
bool p2_dead = !config_partida_.jugador2_actiu || vides_per_jugador_[1] <= 0;
if (p1_dead && p2_dead) {
game_over_ = true;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
}
@@ -298,10 +327,13 @@ void EscenaJoc::actualitzar(float delta_time) {
float ship_anim_progress = ship_progress / Defaults::Game::INIT_HUD_SHIP_RATIO;
ship_anim_progress = std::min(1.0f, ship_anim_progress);
// Actualitzar posició de la nau P1 segons animació (P2 apareix directamente)
// [NOU] Determinar quina nau ha d'animar-se (jugador actiu)
uint8_t ship_to_animate = config_partida_.jugador1_actiu ? 0 : 1;
// Actualitzar posició de la nau activa segons animació
if (ship_anim_progress < 1.0f) {
Punt pos_animada = calcular_posicio_nau_init_hud(ship_anim_progress);
naus_[0].set_centre(pos_animada); // Solo P1 animación
naus_[ship_to_animate].set_centre(pos_animada); // Nau del jugador actiu
}
// Una vegada l'animació acaba, permetre control normal
@@ -323,7 +355,8 @@ void EscenaJoc::actualitzar(float delta_time) {
// [NEW] Allow both ships movement and shooting during intro
for (uint8_t i = 0; i < 2; i++) {
if (itocado_per_jugador_[i] == 0.0f) { // Only alive players
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) { // Only active, alive players
naus_[i].processar_input(delta_time, i);
naus_[i].actualitzar(delta_time);
}
@@ -355,9 +388,10 @@ void EscenaJoc::actualitzar(float delta_time) {
}
}
// [EXISTING] Normal gameplay - update BOTH players
// [EXISTING] Normal gameplay - update active players
for (uint8_t i = 0; i < 2; i++) {
if (itocado_per_jugador_[i] == 0.0f) { // Only alive players
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) { // Only active, alive players
naus_[i].processar_input(delta_time, i);
naus_[i].actualitzar(delta_time);
}
@@ -384,7 +418,8 @@ void EscenaJoc::actualitzar(float delta_time) {
// [NEW] Allow both ships movement and shooting during outro
for (uint8_t i = 0; i < 2; i++) {
if (itocado_per_jugador_[i] == 0.0f) { // Only alive players
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) { // Only active, alive players
naus_[i].processar_input(delta_time, i);
naus_[i].actualitzar(delta_time);
}
@@ -468,10 +503,10 @@ void EscenaJoc::dibuixar() {
dibuixar_marcador_animat(score_progress);
}
// Dibuixar nau P1 (usant el sistema normal, la posició ja està actualitzada)
// Durante INIT_HUD solo se anima P1
if (ship_progress > 0.0f && !naus_[0].esta_tocada()) {
naus_[0].dibuixar();
// Dibuixar nau del jugador actiu (posició ja actualitzada en actualitzar())
uint8_t ship_to_draw = config_partida_.jugador1_actiu ? 0 : 1;
if (ship_progress > 0.0f && !naus_[ship_to_draw].esta_tocada()) {
naus_[ship_to_draw].dibuixar();
}
break;
@@ -479,9 +514,10 @@ void EscenaJoc::dibuixar() {
case StageSystem::EstatStage::LEVEL_START:
dibuixar_marges();
// [NEW] Draw both ships if alive
// [NEW] Draw both ships if active and alive
for (uint8_t i = 0; i < 2; i++) {
if (itocado_per_jugador_[i] == 0.0f) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {
naus_[i].dibuixar();
}
}
@@ -503,9 +539,10 @@ void EscenaJoc::dibuixar() {
case StageSystem::EstatStage::PLAYING:
dibuixar_marges();
// [EXISTING] Normal rendering - both ships
// [EXISTING] Normal rendering - active ships
for (uint8_t i = 0; i < 2; i++) {
if (itocado_per_jugador_[i] == 0.0f) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {
naus_[i].dibuixar();
}
}
@@ -525,9 +562,10 @@ void EscenaJoc::dibuixar() {
case StageSystem::EstatStage::LEVEL_COMPLETED:
dibuixar_marges();
// [NEW] Draw both ships if alive
// [NEW] Draw both ships if active and alive
for (uint8_t i = 0; i < 2; i++) {
if (itocado_per_jugador_[i] == 0.0f) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {
naus_[i].dibuixar();
}
}
@@ -746,31 +784,41 @@ Punt EscenaJoc::calcular_posicio_nau_init_hud(float progress) const {
}
std::string EscenaJoc::construir_marcador() const {
// Puntuació P1 (6 dígits)
std::string score_p1 = std::to_string(puntuacio_per_jugador_[0]);
score_p1 = std::string(6 - std::min(6, static_cast<int>(score_p1.length())), '0') + score_p1;
// Vides P1 (2 dígits)
std::string vides_p1 = (vides_per_jugador_[0] < 10)
? "0" + std::to_string(vides_per_jugador_[0])
: std::to_string(vides_per_jugador_[0]);
// Puntuació P1 (6 dígits) - mostrar zeros si inactiu
std::string score_p1;
std::string vides_p1;
if (config_partida_.jugador1_actiu) {
score_p1 = std::to_string(puntuacio_per_jugador_[0]);
score_p1 = std::string(6 - std::min(6, static_cast<int>(score_p1.length())), '0') + score_p1;
vides_p1 = (vides_per_jugador_[0] < 10)
? "0" + std::to_string(vides_per_jugador_[0])
: std::to_string(vides_per_jugador_[0]);
} else {
score_p1 = "000000";
vides_p1 = "00";
}
// Nivell (2 dígits)
uint8_t stage_num = stage_manager_->get_stage_actual();
std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num)
: std::to_string(stage_num);
// Puntuació P2 (6 dígits)
std::string score_p2 = std::to_string(puntuacio_per_jugador_[1]);
score_p2 = std::string(6 - std::min(6, static_cast<int>(score_p2.length())), '0') + score_p2;
// Vides P2 (2 dígits)
std::string vides_p2 = (vides_per_jugador_[1] < 10)
? "0" + std::to_string(vides_per_jugador_[1])
: std::to_string(vides_per_jugador_[1]);
// Puntuació P2 (6 dígits) - mostrar zeros si inactiu
std::string score_p2;
std::string vides_p2;
if (config_partida_.jugador2_actiu) {
score_p2 = std::to_string(puntuacio_per_jugador_[1]);
score_p2 = std::string(6 - std::min(6, static_cast<int>(score_p2.length())), '0') + score_p2;
vides_p2 = (vides_per_jugador_[1] < 10)
? "0" + std::to_string(vides_per_jugador_[1])
: std::to_string(vides_per_jugador_[1]);
} else {
score_p2 = "000000";
vides_p2 = "00";
}
// Format: "123456 03 LEVEL 01 654321 02"
// Nota: dos espais entre seccions
// Nota: dos espais entre seccions, mantenir ambdós slots sempre visibles
return score_p1 + " " + vides_p1 + " LEVEL " + stage_str + " " + score_p2 + " " + vides_p2;
}
@@ -975,9 +1023,17 @@ void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
Punt EscenaJoc::obtenir_punt_spawn(uint8_t player_id) const {
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
float x_ratio = (player_id == 0)
? Defaults::Game::P1_SPAWN_X_RATIO
: Defaults::Game::P2_SPAWN_X_RATIO;
float x_ratio;
if (config_partida_.es_un_jugador()) {
// Un sol jugador: spawn al centre (50%)
x_ratio = 0.5f;
} else {
// Dos jugadors: spawn a posicions separades
x_ratio = (player_id == 0)
? Defaults::Game::P1_SPAWN_X_RATIO // 33%
: Defaults::Game::P2_SPAWN_X_RATIO; // 67%
}
return {
zona.x + zona.w * x_ratio,

View File

@@ -20,6 +20,7 @@
#include "core/graphics/vector_text.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/game_config.hpp"
#include "core/types.hpp"
#include <memory>
@@ -38,6 +39,7 @@ class EscenaJoc {
private:
SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_;
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
// Efectes visuals
Effects::DebrisManager debris_manager_;

View File

@@ -34,6 +34,11 @@ EscenaTitol::EscenaTitol(SDLManager& sdl, ContextEscenes& context)
factor_lerp_(0.0f) {
std::cout << "Escena Titol: Inicialitzant...\n";
// Inicialitzar configuració de partida (cap jugador actiu per defecte)
config_partida_.jugador1_actiu = false;
config_partida_.jugador2_actiu = false;
config_partida_.mode = GameConfig::Mode::NORMAL;
// Processar opció del context
auto opcio = context_.consumir_opcio();
@@ -366,6 +371,25 @@ void EscenaTitol::actualitzar(float delta_time) {
// Continuar animació orbital durant la transició
actualitzar_animacio_logo(delta_time);
// [NOU] Continuar comprovant si l'altre jugador vol unir-se durant la transició
auto* input = Input::get();
for (auto action : SKIP_BUTTONS_TITOL) {
if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT)) {
if (!config_partida_.jugador1_actiu) {
config_partida_.jugador1_actiu = true;
context_.set_config_partida(config_partida_);
std::cout << "[EscenaTitol] P1 s'ha unit durant la transició!\n";
}
}
if (input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) {
if (!config_partida_.jugador2_actiu) {
config_partida_.jugador2_actiu = true;
context_.set_config_partida(config_partida_);
std::cout << "[EscenaTitol] P2 s'ha unit durant la transició!\n";
}
}
}
if (temps_acumulat_ >= DURACIO_TRANSITION) {
// Transició a JOC (la música ja s'ha parat en el fade)
GestorEscenes::actual = Escena::JOC;
@@ -391,6 +415,14 @@ void EscenaTitol::actualitzar(float delta_time) {
case EstatTitol::MAIN:
// Iniciar partida (transición a JOC)
// Configurar partida abans de canviar d'escena
context_.set_config_partida(config_partida_);
std::cout << "[EscenaTitol] Configuració de partida - P1: "
<< (config_partida_.jugador1_actiu ? "ACTIU" : "INACTIU")
<< ", P2: "
<< (config_partida_.jugador2_actiu ? "ACTIU" : "INACTIU")
<< std::endl;
context_.canviar_escena(Escena::JOC);
estat_actual_ = EstatTitol::TRANSITION_TO_GAME;
temps_acumulat_ = 0.0f;
@@ -577,13 +609,21 @@ void EscenaTitol::dibuixar() {
auto EscenaTitol::checkSkipButtonPressed() -> bool {
auto* input = Input::get();
bool p1_pressed = false;
bool p2_pressed = false;
for (auto action : SKIP_BUTTONS_TITOL) {
if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT) ||
input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) {
return true;
if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT)) {
p1_pressed = true;
config_partida_.jugador1_actiu = true; // Marcar P1 com a actiu
}
if (input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) {
p2_pressed = true;
config_partida_.jugador2_actiu = true; // Marcar P2 com a actiu
}
}
return false;
return p1_pressed || p2_pressed;
}
void EscenaTitol::processar_events(const SDL_Event& event) {

View File

@@ -17,6 +17,7 @@
#include "core/input/input_types.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/game_config.hpp"
#include "core/types.hpp"
// Botones que permiten saltar/avanzar la escena (extensible)
@@ -50,6 +51,7 @@ class EscenaTitol {
SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_;
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
Graphics::VectorText text_; // Sistema de text vectorial
std::unique_ptr<Graphics::Starfield> starfield_; // Camp d'estrelles de fons
EstatTitol estat_actual_; // Estat actual de la màquina