Fase 1c: rename d'escenes i sistema d'escenes

Tots els tipus, fitxers, namespace, enums i metodes relacionats amb
les escenes passen del catala a l'angles seguint el .clang-tidy:

Fitxers (renames git):
- source/game/escenes/escena_joc.{hpp,cpp} -> game/scenes/game_scene.{hpp,cpp}
- source/game/escenes/escena_titol.{hpp,cpp} -> game/scenes/title_scene.{hpp,cpp}
- source/game/escenes/escena_logo.{hpp,cpp} -> game/scenes/logo_scene.{hpp,cpp}
- source/core/system/context_escenes.hpp -> core/system/scene_context.hpp
- Carpeta game/escenes/ -> game/scenes/

Tipus (CamelCase):
- EscenaJoc -> GameScene
- EscenaTitol -> TitleScene
- EscenaLogo -> LogoScene
- ContextEscenes -> SceneContext
- Escena (enum class) -> SceneType
- Opcio -> Option
- EstatGameOver -> GameOverState
- EstatTitol -> TitleState
- EstatAnimacio -> AnimationState
- ConfigPartida -> MatchConfig

Namespace:
- GestorEscenes -> SceneManager

Valors d'enum SceneType:
- TITOL -> TITLE
- JOC -> GAME
- EIXIR -> EXIT
(LOGO mantingut)

Metodes (camelBack):
- executar -> run
- canviar_escena -> setNextScene
- escena_desti -> nextScene
- opcio (getter) -> option
- consumir_opcio -> consumeOption
- reset_opcio -> resetOption
- set_config_partida -> setMatchConfig
- get_config_partida -> getMatchConfig

Camps privats (lower_case_):
- escena_desti_ -> next_scene_
- opcio_ -> option_
- config_partida_ -> match_config_

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 11:41:11 +02:00
parent ae5cc1cfb4
commit 5871d29d48
14 changed files with 368 additions and 368 deletions
-81
View File
@@ -1,81 +0,0 @@
// context_escenes.hpp - Sistema de gestió d'escenes i context de transicions
// © 2025 Port a C++20
#pragma once
#include "core/system/game_config.hpp"
namespace GestorEscenes {
// Context de transició entre escenes
// Conté l'escena destinació i opcions específiques per aquella escena
class ContextEscenes {
public:
// Tipus d'escena del joc
enum class Escena {
LOGO, // Pantalla d'inici (logo JAILGAMES)
TITOL, // Pantalla de títol amb menú
JOC, // Joc principal (Asteroids)
EIXIR // Sortir del programa
};
// Opcions específiques per a cada escena
enum class Opcio {
NONE, // Sense opcions especials (comportament per defecte)
JUMP_TO_TITLE_MAIN, // TITOL: Saltar directament a MAIN (starfield instantani)
// MODE_DEMO, // JOC: Mode demostració amb IA (futur)
};
// Constructor inicial amb escena LOGO i sense opcions
ContextEscenes() = default;
// Canviar escena amb opció específica
void canviar_escena(Escena nova_escena, Opcio opcio = Opcio::NONE) {
escena_desti_ = nova_escena;
opcio_ = opcio;
}
// Consultar escena destinació
[[nodiscard]] auto escena_desti() const -> Escena {
return escena_desti_;
}
// Consultar opció actual
[[nodiscard]] auto opcio() const -> Opcio {
return opcio_;
}
// Consumir opció (retorna valor i reseteja a NONE)
// Utilitzar quan l'escena processa l'opció
[[nodiscard]] auto consumir_opcio() -> Opcio {
Opcio valor = opcio_;
opcio_ = Opcio::NONE;
return valor;
}
// Reset opció a NONE (sense retornar valor)
void reset_opcio() {
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::LOGO}; // Escena a la qual transicionar
Opcio opcio_{Opcio::NONE}; // 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)
// Sincronitzada amb context.escena_desti() pel Director
inline ContextEscenes::Escena actual = ContextEscenes::Escena::LOGO;
} // namespace GestorEscenes
+25 -25
View File
@@ -7,7 +7,7 @@
#include <cstdlib>
#include <iostream>
#include "context_escenes.hpp"
#include "scene_context.hpp"
#include "core/audio/audio.hpp"
#include "core/audio/audio_cache.hpp"
#include "core/defaults.hpp"
@@ -17,9 +17,9 @@
#include "core/resources/resource_helper.hpp"
#include "core/resources/resource_loader.hpp"
#include "core/utils/path_utils.hpp"
#include "game/escenes/escena_joc.hpp"
#include "game/escenes/escena_logo.hpp"
#include "game/escenes/escena_titol.hpp"
#include "game/scenes/game_scene.hpp"
#include "game/scenes/logo_scene.hpp"
#include "game/scenes/title_scene.hpp"
#include "game/options.hpp"
#include "project.h"
@@ -29,8 +29,8 @@
#endif
// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;
// Constructor
Director::Director(std::vector<std::string> const& args) {
@@ -238,35 +238,35 @@ auto Director::run() -> int {
}
// Crear context d'escenes
ContextEscenes context;
SceneContext context;
#ifdef _DEBUG
context.canviar_escena(Escena::TITOL);
context.setNextScene(SceneType::TITLE);
#else
context.canviar_escena(Escena::LOGO);
context.setNextScene(SceneType::LOGO);
#endif
// Bucle principal de gestió d'escenes
while (context.escena_desti() != Escena::EIXIR) {
// Sincronitzar GestorEscenes::actual amb context
// (altres sistemes encara poden llegir GestorEscenes::actual)
GestorEscenes::actual = context.escena_desti();
while (context.nextScene() != SceneType::EXIT) {
// Sincronitzar SceneManager::actual amb context
// (altres sistemes encara poden llegir SceneManager::actual)
SceneManager::actual = context.nextScene();
switch (context.escena_desti()) {
case Escena::LOGO: {
EscenaLogo logo(sdl, context);
logo.executar();
switch (context.nextScene()) {
case SceneType::LOGO: {
LogoScene logo(sdl, context);
logo.run();
break;
}
case Escena::TITOL: {
EscenaTitol titol(sdl, context);
titol.executar();
case SceneType::TITLE: {
TitleScene titol(sdl, context);
titol.run();
break;
}
case Escena::JOC: {
EscenaJoc joc(sdl, context);
joc.executar();
case SceneType::GAME: {
GameScene joc(sdl, context);
joc.run();
break;
}
@@ -275,8 +275,8 @@ auto Director::run() -> int {
}
}
// Sincronitzar final amb GestorEscenes::actual
GestorEscenes::actual = Escena::EIXIR;
// Sincronitzar final amb SceneManager::actual
SceneManager::actual = SceneType::EXIT;
return 0;
}
+1 -1
View File
@@ -11,7 +11,7 @@ enum class Mode {
};
// Configuració d'una partida
struct ConfigPartida {
struct MatchConfig {
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
+8 -8
View File
@@ -5,18 +5,18 @@
#include <iostream>
#include "context_escenes.hpp"
#include "scene_context.hpp"
#include "core/input/input.hpp"
#include "core/input/mouse.hpp"
#include "core/rendering/sdl_manager.hpp"
// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;
namespace GlobalEvents {
bool handle(const SDL_Event& event, SDLManager& sdl, ContextEscenes& context) {
bool handle(const SDL_Event& event, SDLManager& sdl, SceneContext& context) {
// 1. Permitir que Input procese el evento (para hotplug de gamepads)
auto event_msg = Input::get()->handleEvent(event);
if (!event_msg.empty()) {
@@ -25,8 +25,8 @@ bool handle(const SDL_Event& event, SDLManager& sdl, ContextEscenes& context) {
// 2. Procesar SDL_EVENT_QUIT directamente (no es input de juego)
if (event.type == SDL_EVENT_QUIT) {
context.canviar_escena(Escena::EIXIR);
GestorEscenes::actual = Escena::EIXIR;
context.setNextScene(SceneType::EXIT);
SceneManager::actual = SceneType::EXIT;
return true;
}
@@ -54,8 +54,8 @@ bool handle(const SDL_Event& event, SDLManager& sdl, ContextEscenes& context) {
return true;
case SDL_SCANCODE_ESCAPE:
context.canviar_escena(Escena::EIXIR);
GestorEscenes::actual = Escena::EIXIR;
context.setNextScene(SceneType::EXIT);
SceneManager::actual = SceneType::EXIT;
return true;
default:
+3 -3
View File
@@ -8,12 +8,12 @@
// Forward declarations
class SDLManager;
namespace GestorEscenes {
class ContextEscenes;
namespace SceneManager {
class SceneContext;
}
namespace GlobalEvents {
// Processa events globals (F1/F2/F3/ESC/QUIT)
// Retorna true si l'event ha estat processat i no cal seguir processant-lo
bool handle(const SDL_Event& event, SDLManager& sdl, GestorEscenes::ContextEscenes& context);
bool handle(const SDL_Event& event, SDLManager& sdl, SceneManager::SceneContext& context);
} // namespace GlobalEvents
+81
View File
@@ -0,0 +1,81 @@
// context_escenes.hpp - Sistema de gestió d'escenes i context de transicions
// © 2025 Port a C++20
#pragma once
#include "core/system/game_config.hpp"
namespace SceneManager {
// Context de transició entre escenes
// Conté l'escena destinació i opcions específiques per aquella escena
class SceneContext {
public:
// Tipus d'escena del joc
enum class SceneType {
LOGO, // Pantalla d'inici (logo JAILGAMES)
TITLE, // Pantalla de títol amb menú
GAME, // Joc principal (Asteroids)
EXIT // Sortir del programa
};
// Opcions específiques per a cada escena
enum class Option {
NONE, // Sense opcions especials (comportament per defecte)
JUMP_TO_TITLE_MAIN, // TITLE: Saltar directament a MAIN (starfield instantani)
// MODE_DEMO, // GAME: Mode demostració amb IA (futur)
};
// Constructor inicial amb escena LOGO i sense opcions
SceneContext() = default;
// Canviar escena amb opció específica
void setNextScene(SceneType next_scene, Option option = Option::NONE) {
next_scene_ = next_scene;
option_ = option;
}
// Consultar escena destinació
[[nodiscard]] auto nextScene() const -> SceneType {
return next_scene_;
}
// Consultar opció actual
[[nodiscard]] auto option() const -> Option {
return option_;
}
// Consumir opció (retorna valor i reseteja a NONE)
// Utilitzar quan l'escena processa l'opció
[[nodiscard]] auto consumeOption() -> Option {
Option valor = option_;
option_ = Option::NONE;
return valor;
}
// Reset opció a NONE (sense retornar valor)
void resetOption() {
option_ = Option::NONE;
}
// Configurar partida abans de transicionar a GAME
void setMatchConfig(const GameConfig::MatchConfig& config) {
match_config_ = config;
}
// Obtenir configuració de partida (consumit per GameScene)
[[nodiscard]] const GameConfig::MatchConfig& getMatchConfig() const {
return match_config_;
}
private:
SceneType next_scene_{SceneType::LOGO}; // SceneType a la qual transicionar
Option option_{Option::NONE}; // Opció específica per l'escena
GameConfig::MatchConfig match_config_; // Configuració de partida (jugadors actius, mode)
};
// Variable global inline per gestionar l'escena actual (backward compatibility)
// Sincronitzada amb context.nextScene() pel Director
inline SceneContext::SceneType actual = SceneContext::SceneType::LOGO;
} // namespace SceneManager
@@ -2,7 +2,7 @@
// © 1999 Visente i Sergi (versió Pascal)
// © 2025 Port a C++20 amb SDL3
#include "escena_joc.hpp"
#include "game_scene.hpp"
#include <algorithm>
#include <cmath>
@@ -18,16 +18,16 @@
#include "core/math/easing.hpp"
#include "core/physics/collision.hpp"
#include "core/rendering/line_renderer.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/scene_context.hpp"
#include "core/system/global_events.hpp"
#include "game/stage_system/stage_loader.hpp"
// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;
using Opcio = ContextEscenes::Opcio;
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;
using Option = SceneContext::Option;
EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context)
GameScene::GameScene(SDLManager& sdl, SceneContext& context)
: sdl_(sdl),
context_(context),
debris_manager_(sdl.obte_renderer()),
@@ -35,18 +35,18 @@ EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context)
text_(sdl.obte_renderer()),
init_hud_rect_sound_played_(false) {
// Recuperar configuració de partida des del context
config_partida_ = context_.get_config_partida();
match_config_ = context_.getMatchConfig();
// Debug output de la configuració
std::cout << "[EscenaJoc] Configuració de partida - P1: "
<< (config_partida_.jugador1_actiu ? "ACTIU" : "INACTIU")
std::cout << "[GameScene] Configuració de partida - P1: "
<< (match_config_.jugador1_actiu ? "ACTIU" : "INACTIU")
<< ", P2: "
<< (config_partida_.jugador2_actiu ? "ACTIU" : "INACTIU")
<< (match_config_.jugador2_actiu ? "ACTIU" : "INACTIU")
<< '\n';
// Consumir opcions (preparació per MODE_DEMO futur)
auto opcio = context_.consumir_opcio();
(void)opcio; // Suprimir warning de variable no usada
auto option = context_.consumeOption();
(void)option; // Suprimir warning de variable no usada
// Inicialitzar naus amb renderer (P1=ship.shp, P2=ship2.shp)
naus_[0] = Ship(sdl.obte_renderer(), "ship.shp"); // Jugador 1: nave estàndar
@@ -63,8 +63,8 @@ EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context)
}
}
void EscenaJoc::executar() {
std::cout << "Escena Joc: Inicialitzant...\n";
void GameScene::run() {
std::cout << "SceneType Joc: Inicialitzant...\n";
// Inicialitzar estat del joc
init();
@@ -72,7 +72,7 @@ void EscenaJoc::executar() {
SDL_Event event;
Uint64 last_time = SDL_GetTicks();
while (GestorEscenes::actual == Escena::JOC) {
while (SceneManager::actual == SceneType::GAME) {
// Calcular delta_time real
Uint64 current_time = SDL_GetTicks();
float delta_time = (current_time - last_time) / 1000.0F;
@@ -123,10 +123,10 @@ void EscenaJoc::executar() {
sdl_.presenta();
}
std::cout << "Escena Joc: Finalitzant...\n";
std::cout << "SceneType Joc: Finalitzant...\n";
}
void EscenaJoc::init() {
void GameScene::init() {
// Inicialitzar generador de números aleatoris
// Basat en el codi Pascal original: line 376
std::srand(static_cast<unsigned>(std::time(nullptr)));
@@ -135,7 +135,7 @@ void EscenaJoc::init() {
if (!stage_config_) {
stage_config_ = StageSystem::StageLoader::carregar("data/stages/stages.yaml");
if (!stage_config_) {
std::cerr << "[EscenaJoc] Error: no s'ha pogut carregar stages.yaml" << '\n';
std::cerr << "[GameScene] Error: no s'ha pogut carregar stages.yaml" << '\n';
// Continue without stage system (will crash, but helps debugging)
}
}
@@ -154,7 +154,7 @@ void EscenaJoc::init() {
// Initialize lives and game over state (independent lives per player)
vides_per_jugador_[0] = Defaults::Game::STARTING_LIVES;
vides_per_jugador_[1] = Defaults::Game::STARTING_LIVES;
estat_game_over_ = EstatGameOver::NONE;
estat_game_over_ = GameOverState::NONE;
continue_counter_ = 0;
continue_tick_timer_ = 0.0F;
continues_usados_ = 0;
@@ -172,19 +172,19 @@ void EscenaJoc::init() {
// Inicialitzar naus segons configuració (només jugadors actius)
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu) {
// Jugador actiu: init normalment
Vec2 spawn_pos = obtenir_punt_spawn(i);
naus_[i].init(&spawn_pos, false); // No invulnerability at start
std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inicialitzat\n";
std::cout << "[GameScene] Jugador " << (i + 1) << " inicialitzat\n";
} else {
// Jugador inactiu: marcar com a mort permanent
naus_[i].markHit();
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";
std::cout << "[GameScene] Jugador " << (i + 1) << " inactiu\n";
}
}
@@ -208,20 +208,20 @@ void EscenaJoc::init() {
init_hud_rect_sound_played_ = false;
}
void EscenaJoc::update(float delta_time) {
void GameScene::update(float delta_time) {
// Processar disparos (state-based, no event-based)
if (estat_game_over_ == EstatGameOver::NONE) {
if (estat_game_over_ == GameOverState::NONE) {
auto* input = Input::get();
// Jugador 1 dispara (només si està actiu)
if (config_partida_.jugador1_actiu) {
if (match_config_.jugador1_actiu) {
if (input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
disparar_bala(0);
}
}
// Jugador 2 dispara (només si està actiu)
if (config_partida_.jugador2_actiu) {
if (match_config_.jugador2_actiu) {
if (input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
disparar_bala(1);
}
@@ -232,17 +232,17 @@ void EscenaJoc::update(float delta_time) {
if (stage_manager_->get_estat() == StageSystem::EstatStage::PLAYING) {
// Check if at least one player is alive and playing (game in progress)
bool algun_jugador_viu = false;
if (config_partida_.jugador1_actiu && itocado_per_jugador_[0] != 999.0F) {
if (match_config_.jugador1_actiu && itocado_per_jugador_[0] != 999.0F) {
algun_jugador_viu = true;
}
if (config_partida_.jugador2_actiu && itocado_per_jugador_[1] != 999.0F) {
if (match_config_.jugador2_actiu && itocado_per_jugador_[1] != 999.0F) {
algun_jugador_viu = true;
}
// Only allow join if there's an active game
if (algun_jugador_viu) {
// P2 can join if not currently playing (never joined OR dead without lives)
bool p2_no_juga = !config_partida_.jugador2_actiu || // Never joined
bool p2_no_juga = !match_config_.jugador2_actiu || // Never joined
itocado_per_jugador_[1] == 999.0F; // Dead without lives
if (p2_no_juga) {
@@ -252,7 +252,7 @@ void EscenaJoc::update(float delta_time) {
}
// P1 can join if not currently playing (never joined OR dead without lives)
bool p1_no_juga = !config_partida_.jugador1_actiu || // Never joined
bool p1_no_juga = !match_config_.jugador1_actiu || // Never joined
itocado_per_jugador_[0] == 999.0F; // Dead without lives
if (p1_no_juga) {
@@ -265,7 +265,7 @@ void EscenaJoc::update(float delta_time) {
}
// Handle CONTINUE screen
if (estat_game_over_ == EstatGameOver::CONTINUE) {
if (estat_game_over_ == GameOverState::CONTINUE) {
actualitzar_continue(delta_time);
processar_input_continue();
@@ -282,7 +282,7 @@ void EscenaJoc::update(float delta_time) {
}
// Handle final GAME OVER state
if (estat_game_over_ == EstatGameOver::GAME_OVER) {
if (estat_game_over_ == GameOverState::GAME_OVER) {
// Game over: only update timer, enemies, bullets, and debris
game_over_timer_ -= delta_time;
@@ -290,8 +290,8 @@ void EscenaJoc::update(float delta_time) {
// Aturar música de joc abans de tornar al títol
Audio::get()->stopMusic();
// Transició a pantalla de títol
context_.canviar_escena(Escena::TITOL);
GestorEscenes::actual = Escena::TITOL;
context_.setNextScene(SceneType::TITLE);
SceneManager::actual = SceneType::TITLE;
return;
}
@@ -335,11 +335,11 @@ void EscenaJoc::update(float delta_time) {
itocado_per_jugador_[i] = 999.0F;
// Check if ALL ACTIVE players are dead (trigger continue screen)
bool p1_dead = !config_partida_.jugador1_actiu || vides_per_jugador_[0] <= 0;
bool p2_dead = !config_partida_.jugador2_actiu || vides_per_jugador_[1] <= 0;
bool p1_dead = !match_config_.jugador1_actiu || vides_per_jugador_[0] <= 0;
bool p2_dead = !match_config_.jugador2_actiu || vides_per_jugador_[1] <= 0;
if (p1_dead && p2_dead) {
estat_game_over_ = EstatGameOver::CONTINUE;
estat_game_over_ = GameOverState::CONTINUE;
continue_counter_ = Defaults::Game::CONTINUE_COUNT_START;
continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION;
}
@@ -396,12 +396,12 @@ void EscenaJoc::update(float delta_time) {
Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
// [MODIFICAT] Animar AMBAS naus con sus progress respectivos
if (config_partida_.jugador1_actiu && ship1_progress < 1.0F) {
if (match_config_.jugador1_actiu && ship1_progress < 1.0F) {
Vec2 pos_p1 = calcular_posicio_nau_init_hud(ship1_progress, 0);
naus_[0].setCenter(pos_p1);
}
if (config_partida_.jugador2_actiu && ship2_progress < 1.0F) {
if (match_config_.jugador2_actiu && ship2_progress < 1.0F) {
Vec2 pos_p2 = calcular_posicio_nau_init_hud(ship2_progress, 1);
naus_[1].setCenter(pos_p2);
}
@@ -425,7 +425,7 @@ void EscenaJoc::update(float delta_time) {
// [NEW] Allow both ships movement and shooting during intro
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players
naus_[i].processInput(delta_time, i);
naus_[i].update(delta_time);
@@ -460,7 +460,7 @@ void EscenaJoc::update(float delta_time) {
// [EXISTING] Normal gameplay - update active players
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players
naus_[i].processInput(delta_time, i);
naus_[i].update(delta_time);
@@ -489,7 +489,7 @@ void EscenaJoc::update(float delta_time) {
// [NEW] Allow both ships movement and shooting during outro
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players
naus_[i].processInput(delta_time, i);
naus_[i].update(delta_time);
@@ -508,9 +508,9 @@ void EscenaJoc::update(float delta_time) {
}
}
void EscenaJoc::draw() {
void GameScene::draw() {
// Handle CONTINUE screen
if (estat_game_over_ == EstatGameOver::CONTINUE) {
if (estat_game_over_ == GameOverState::CONTINUE) {
// Draw game background elements first
dibuixar_marges();
@@ -532,7 +532,7 @@ void EscenaJoc::draw() {
}
// Handle final GAME OVER state
if (estat_game_over_ == EstatGameOver::GAME_OVER) {
if (estat_game_over_ == GameOverState::GAME_OVER) {
// Game over: draw enemies, bullets, debris, and "GAME OVER" text
dibuixar_marges();
@@ -610,11 +610,11 @@ void EscenaJoc::draw() {
}
// [MODIFICAT] Dibuixar naus amb progress independent
if (ship1_progress > 0.0F && config_partida_.jugador1_actiu && !naus_[0].isHit()) {
if (ship1_progress > 0.0F && match_config_.jugador1_actiu && !naus_[0].isHit()) {
naus_[0].draw();
}
if (ship2_progress > 0.0F && config_partida_.jugador2_actiu && !naus_[1].isHit()) {
if (ship2_progress > 0.0F && match_config_.jugador2_actiu && !naus_[1].isHit()) {
naus_[1].draw();
}
@@ -625,7 +625,7 @@ void EscenaJoc::draw() {
dibuixar_marges();
// [NEW] Draw both ships if active and alive
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) {
naus_[i].draw();
}
@@ -650,7 +650,7 @@ void EscenaJoc::draw() {
// [EXISTING] Normal rendering - active ships
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) {
naus_[i].draw();
}
@@ -673,7 +673,7 @@ void EscenaJoc::draw() {
dibuixar_marges();
// [NEW] Draw both ships if active and alive
for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) {
naus_[i].draw();
}
@@ -695,7 +695,7 @@ void EscenaJoc::draw() {
}
}
void EscenaJoc::tocado(uint8_t player_id) {
void GameScene::tocado(uint8_t player_id) {
// Death sequence: 3 phases
// Phase 1: First call (itocado_per_jugador_[player_id] == 0) - trigger explosion
// Phase 2: Animation (0 < itocado_ < 3.0s) - debris animation
@@ -734,7 +734,7 @@ void EscenaJoc::tocado(uint8_t player_id) {
// Phase 3 is handled in update() when itocado_per_jugador_ >= DEATH_DURATION
}
void EscenaJoc::dibuixar_marges() const {
void GameScene::dibuixar_marges() const {
// Dibuixar rectangle de la zona de joc
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
@@ -751,7 +751,7 @@ void EscenaJoc::dibuixar_marges() const {
Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2); // Right
}
void EscenaJoc::dibuixar_marcador() {
void GameScene::dibuixar_marcador() {
// Construir text del marcador
std::string text = construir_marcador();
@@ -768,7 +768,7 @@ void EscenaJoc::dibuixar_marcador() {
text_.render_centered(text, {.x = centre_x, .y = centre_y}, escala, spacing);
}
void EscenaJoc::dibuixar_marges_animat(float progress) const {
void GameScene::dibuixar_marges_animat(float progress) const {
// Animació seqüencial del rectangle amb efecte de "pinzell"
// Dos pinzells comencen al centre superior i baixen pels laterals
@@ -827,7 +827,7 @@ void EscenaJoc::dibuixar_marges_animat(float progress) const {
}
}
void EscenaJoc::dibuixar_marcador_animat(float progress) {
void GameScene::dibuixar_marcador_animat(float progress) {
// Animació del marcador pujant des de baix amb easing
// Calcular progrés amb easing
@@ -855,7 +855,7 @@ void EscenaJoc::dibuixar_marcador_animat(float progress) {
text_.render_centered(text, {.x = centre_x, .y = centre_y_animada}, escala, spacing);
}
Vec2 EscenaJoc::calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const {
Vec2 GameScene::calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const {
// Animació de la nau pujant des de baix amb easing
// [MODIFICAT] Ambas naves usan ease_out_quad (desfase temporal via INIT/END)
@@ -879,7 +879,7 @@ Vec2 EscenaJoc::calcular_posicio_nau_init_hud(float progress, uint8_t player_id)
return {.x = x_final, .y = y_animada};
}
float EscenaJoc::calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const {
float GameScene::calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const {
// Convierte global_progress (0.0→1.0) a element_progress usando ventana [INIT, END]
//
// Casos:
@@ -904,11 +904,11 @@ float EscenaJoc::calcular_progress_rango(float global_progress, float ratio_init
return (global_progress - ratio_init) / (ratio_end - ratio_init);
}
std::string EscenaJoc::construir_marcador() const {
std::string GameScene::construir_marcador() const {
// Puntuació P1 (6 dígits) - mostrar zeros si inactiu
std::string score_p1;
std::string vides_p1;
if (config_partida_.jugador1_actiu) {
if (match_config_.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)
@@ -927,7 +927,7 @@ std::string EscenaJoc::construir_marcador() const {
// Puntuació P2 (6 dígits) - mostrar zeros si inactiu
std::string score_p2;
std::string vides_p2;
if (config_partida_.jugador2_actiu) {
if (match_config_.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)
@@ -943,7 +943,7 @@ std::string EscenaJoc::construir_marcador() const {
return score_p1 + " " + vides_p1 + " LEVEL " + stage_str + " " + score_p2 + " " + vides_p2;
}
void EscenaJoc::detectar_col·lisions_bales_enemics() {
void GameScene::detectar_col·lisions_bales_enemics() {
// Amplificador per hitbox més generós (115%)
constexpr float AMPLIFIER = Defaults::Game::COLLISION_BULLET_ENEMY_AMPLIFIER;
@@ -1007,7 +1007,7 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() {
}
}
void EscenaJoc::detectar_col·lisio_naus_enemics() {
void GameScene::detectar_col·lisio_naus_enemics() {
// Amplificador per hitbox generós (80%)
constexpr float AMPLIFIER = Defaults::Game::COLLISION_SHIP_ENEMY_AMPLIFIER;
@@ -1040,7 +1040,7 @@ void EscenaJoc::detectar_col·lisio_naus_enemics() {
}
}
void EscenaJoc::detectar_col·lisions_bales_jugadors() {
void GameScene::detectar_col·lisions_bales_jugadors() {
// Skip if friendly fire disabled
if (!Defaults::Game::FRIENDLY_FIRE_ENABLED) {
return;
@@ -1076,8 +1076,8 @@ void EscenaJoc::detectar_col·lisions_bales_jugadors() {
}
// Skip inactive players
bool jugador_actiu = (player_id == 0) ? config_partida_.jugador1_actiu
: config_partida_.jugador2_actiu;
bool jugador_actiu = (player_id == 0) ? match_config_.jugador1_actiu
: match_config_.jugador2_actiu;
if (!jugador_actiu) {
continue;
}
@@ -1113,7 +1113,7 @@ void EscenaJoc::detectar_col·lisions_bales_jugadors() {
// [NEW] Stage system helper methods
void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
void GameScene::dibuixar_missatge_stage(const std::string& missatge) {
constexpr float escala_base = 1.0F;
constexpr float spacing = 2.0F;
@@ -1182,11 +1182,11 @@ void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
// Helper methods for 2-player support
// ========================================
Vec2 EscenaJoc::obtenir_punt_spawn(uint8_t player_id) const {
Vec2 GameScene::obtenir_punt_spawn(uint8_t player_id) const {
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
float x_ratio;
if (config_partida_.es_un_jugador()) {
if (match_config_.es_un_jugador()) {
// Un sol jugador: spawn al centre (50%)
x_ratio = 0.5F;
} else {
@@ -1201,7 +1201,7 @@ Vec2 EscenaJoc::obtenir_punt_spawn(uint8_t player_id) const {
.y = zona.y + (zona.h * Defaults::Game::SPAWN_Y_RATIO)};
}
void EscenaJoc::disparar_bala(uint8_t player_id) {
void GameScene::disparar_bala(uint8_t player_id) {
// Verificar que el jugador está vivo
if (itocado_per_jugador_[player_id] > 0.0F) {
return;
@@ -1234,14 +1234,14 @@ void EscenaJoc::disparar_bala(uint8_t player_id) {
// ==================== CONTINUE & JOIN SYSTEM ====================
void EscenaJoc::check_and_apply_continue_timeout() {
void GameScene::check_and_apply_continue_timeout() {
if (continue_counter_ < 0) {
estat_game_over_ = EstatGameOver::GAME_OVER;
estat_game_over_ = GameOverState::GAME_OVER;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
}
}
void EscenaJoc::actualitzar_continue(float delta_time) {
void GameScene::actualitzar_continue(float delta_time) {
continue_tick_timer_ -= delta_time;
if (continue_tick_timer_ <= 0.0F) {
@@ -1252,13 +1252,13 @@ void EscenaJoc::actualitzar_continue(float delta_time) {
check_and_apply_continue_timeout();
// Play sound only if still in CONTINUE state (not transitioned to GAME_OVER)
if (estat_game_over_ == EstatGameOver::CONTINUE) {
if (estat_game_over_ == GameOverState::CONTINUE) {
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
}
}
}
void EscenaJoc::processar_input_continue() {
void GameScene::processar_input_continue() {
auto* input = Input::get();
// Check START for both players
@@ -1269,7 +1269,7 @@ void EscenaJoc::processar_input_continue() {
// Check continue limit (skip if infinite continues)
if (!Defaults::Game::INFINITE_CONTINUES && continues_usados_ >= Defaults::Game::MAX_CONTINUES) {
// Max continues reached → final game over
estat_game_over_ = EstatGameOver::GAME_OVER;
estat_game_over_ = GameOverState::GAME_OVER;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
return;
}
@@ -1289,9 +1289,9 @@ void EscenaJoc::processar_input_continue() {
// Activate player if not already
if (player_to_revive == 0) {
config_partida_.jugador1_actiu = true;
match_config_.jugador1_actiu = true;
} else {
config_partida_.jugador2_actiu = true;
match_config_.jugador2_actiu = true;
}
// Spawn with invulnerability
@@ -1304,13 +1304,13 @@ void EscenaJoc::processar_input_continue() {
puntuacio_per_jugador_[other_player] = 0;
vides_per_jugador_[other_player] = Defaults::Game::STARTING_LIVES;
itocado_per_jugador_[other_player] = 0.0F;
config_partida_.jugador2_actiu = true;
match_config_.jugador2_actiu = true;
Vec2 spawn_pos2 = obtenir_punt_spawn(other_player);
naus_[other_player].init(&spawn_pos2, true);
}
// Resume game
estat_game_over_ = EstatGameOver::NONE;
estat_game_over_ = GameOverState::NONE;
continue_counter_ = 0;
continue_tick_timer_ = 0.0F;
@@ -1333,7 +1333,7 @@ void EscenaJoc::processar_input_continue() {
check_and_apply_continue_timeout();
// Play sound only if still in CONTINUE state
if (estat_game_over_ == EstatGameOver::CONTINUE) {
if (estat_game_over_ == GameOverState::CONTINUE) {
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
}
@@ -1342,7 +1342,7 @@ void EscenaJoc::processar_input_continue() {
}
}
void EscenaJoc::dibuixar_continue() {
void GameScene::dibuixar_continue() {
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
constexpr float spacing = 4.0F;
@@ -1377,12 +1377,12 @@ void EscenaJoc::dibuixar_continue() {
}
}
void EscenaJoc::unir_jugador(uint8_t player_id) {
void GameScene::unir_jugador(uint8_t player_id) {
// Activate player
if (player_id == 0) {
config_partida_.jugador1_actiu = true;
match_config_.jugador1_actiu = true;
} else {
config_partida_.jugador2_actiu = true;
match_config_.jugador2_actiu = true;
}
// Reset stats
@@ -1396,5 +1396,5 @@ void EscenaJoc::unir_jugador(uint8_t player_id) {
// No visual message, just spawn (per user requirement)
std::cout << "[EscenaJoc] Jugador " << (int)(player_id + 1) << " s'ha unit a la partida!" << '\n';
std::cout << "[GameScene] Jugador " << (int)(player_id + 1) << " s'ha unit a la partida!" << '\n';
}
@@ -12,7 +12,7 @@
#include "core/graphics/vector_text.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/scene_context.hpp"
#include "core/system/game_config.hpp"
#include "core/types.hpp"
#include "game/constants.hpp"
@@ -25,27 +25,27 @@
#include "game/stage_system/stage_manager.hpp"
// Game over state machine
enum class EstatGameOver {
enum class GameOverState {
NONE, // Normal gameplay
CONTINUE, // Continue countdown screen (9→0)
GAME_OVER // Final game over (returning to title)
};
// Classe principal del joc (escena)
class EscenaJoc {
class GameScene {
public:
explicit EscenaJoc(SDLManager& sdl, GestorEscenes::ContextEscenes& context);
~EscenaJoc() = default;
explicit GameScene(SDLManager& sdl, SceneManager::SceneContext& context);
~GameScene() = default;
void executar(); // Bucle principal de l'escena
void run(); // Bucle principal de l'escena
void init();
void update(float delta_time);
void draw();
private:
SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_;
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
SceneManager::SceneContext& context_;
GameConfig::MatchConfig match_config_; // Configuració de jugadors actius
// Efectes visuals
Effects::DebrisManager debris_manager_;
@@ -59,7 +59,7 @@ class EscenaJoc {
// Lives and game over system
std::array<int, 2> vides_per_jugador_; // [0]=P1, [1]=P2
EstatGameOver estat_game_over_; // Game over state machine (NONE, CONTINUE, GAME_OVER)
GameOverState estat_game_over_; // Game over state machine (NONE, CONTINUE, GAME_OVER)
int continue_counter_; // Continue countdown (9→0)
float continue_tick_timer_; // Timer for countdown tick (1.0s)
int continues_usados_; // Continues used this game (0-3 max)
@@ -1,7 +1,7 @@
// escena_logo.cpp - Implementació de l'escena logo
// © 2025 Port a C++20
#include "escena_logo.hpp"
#include "logo_scene.hpp"
#include <algorithm>
#include <cfloat>
@@ -14,13 +14,13 @@
#include "core/input/input.hpp"
#include "core/input/mouse.hpp"
#include "core/rendering/shape_renderer.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/scene_context.hpp"
#include "core/system/global_events.hpp"
// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;
using Opcio = ContextEscenes::Opcio;
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;
using Option = SceneContext::Option;
// Helper: calcular el progrés individual d'una lletra
// en funció del progrés global (efecte seqüencial)
@@ -45,35 +45,35 @@ static float calcular_progress_letra(size_t letra_index, size_t num_letras, floa
return (global_progress - start) / (end - start);
}
EscenaLogo::EscenaLogo(SDLManager& sdl, ContextEscenes& context)
LogoScene::LogoScene(SDLManager& sdl, SceneContext& context)
: sdl_(sdl),
context_(context),
estat_actual_(EstatAnimacio::PRE_ANIMATION),
estat_actual_(AnimationState::PRE_ANIMATION),
temps_estat_actual_(0.0F),
debris_manager_(std::make_unique<Effects::DebrisManager>(sdl.obte_renderer())),
lletra_explosio_index_(0),
temps_des_ultima_explosio_(0.0F) {
std::cout << "Escena Logo: Inicialitzant...\n";
std::cout << "SceneType Logo: Inicialitzant...\n";
// Consumir opcions (LOGO no processa opcions actualment)
auto opcio = context_.consumir_opcio();
(void)opcio; // Suprimir warning
auto option = context_.consumeOption();
(void)option; // Suprimir warning
so_reproduit_.fill(false); // Inicialitzar seguiment de sons
inicialitzar_lletres();
}
EscenaLogo::~EscenaLogo() {
LogoScene::~LogoScene() {
// Aturar tots els sons i la música
Audio::get()->stopAllSounds();
std::cout << "Escena Logo: Sons aturats\n";
std::cout << "SceneType Logo: Sons aturats\n";
}
void EscenaLogo::executar() {
void LogoScene::run() {
SDL_Event event;
Uint64 last_time = SDL_GetTicks();
while (GestorEscenes::actual == Escena::LOGO) {
while (SceneManager::actual == SceneType::LOGO) {
// Calcular delta_time real
Uint64 current_time = SDL_GetTicks();
float delta_time = (current_time - last_time) / 1000.0F;
@@ -120,10 +120,10 @@ void EscenaLogo::executar() {
draw();
}
std::cout << "Escena Logo: Finalitzant...\n";
std::cout << "SceneType Logo: Finalitzant...\n";
}
void EscenaLogo::inicialitzar_lletres() {
void LogoScene::inicialitzar_lletres() {
using namespace Graphics;
// Llista de fitxers .shp (A repetida per a les dues A's)
@@ -144,7 +144,7 @@ void EscenaLogo::inicialitzar_lletres() {
for (const auto& fitxer : fitxers) {
auto forma = ShapeLoader::load(fitxer);
if (!forma || !forma->es_valida()) {
std::cerr << "[EscenaLogo] Error carregant " << fitxer << '\n';
std::cerr << "[LogoScene] Error carregant " << fitxer << '\n';
continue;
}
@@ -198,16 +198,16 @@ void EscenaLogo::inicialitzar_lletres() {
x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
}
std::cout << "[EscenaLogo] " << lletres_.size()
std::cout << "[LogoScene] " << lletres_.size()
<< " lletres carregades, ancho total: " << ancho_total << " px\n";
}
void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) {
void LogoScene::canviar_estat(AnimationState nou_estat) {
estat_actual_ = nou_estat;
temps_estat_actual_ = 0.0F; // Reset temps
// Inicialitzar estat d'explosió
if (nou_estat == EstatAnimacio::EXPLOSION) {
if (nou_estat == AnimationState::EXPLOSION) {
lletra_explosio_index_ = 0;
temps_des_ultima_explosio_ = 0.0F;
@@ -219,20 +219,20 @@ void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) {
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(ordre_explosio_.begin(), ordre_explosio_.end(), g);
} else if (nou_estat == EstatAnimacio::POST_EXPLOSION) {
} else if (nou_estat == AnimationState::POST_EXPLOSION) {
Audio::get()->playMusic("title.ogg");
}
std::cout << "[EscenaLogo] Canvi a estat: " << static_cast<int>(nou_estat)
std::cout << "[LogoScene] Canvi a estat: " << static_cast<int>(nou_estat)
<< "\n";
}
bool EscenaLogo::totes_lletres_completes() const {
bool LogoScene::totes_lletres_completes() const {
// Quan global_progress = 1.0, totes les lletres tenen letra_progress = 1.0
return temps_estat_actual_ >= DURACIO_ZOOM;
}
void EscenaLogo::actualitzar_explosions(float delta_time) {
void LogoScene::actualitzar_explosions(float delta_time) {
temps_des_ultima_explosio_ += delta_time;
// Comprovar si és el moment d'explotar la següent lletra
@@ -252,29 +252,29 @@ void EscenaLogo::actualitzar_explosions(float delta_time) {
{.x = 0.0F, .y = 0.0F} // Sense velocitat (per defecte)
);
std::cout << "[EscenaLogo] Explota lletra " << lletra_explosio_index_ << "\n";
std::cout << "[LogoScene] Explota lletra " << lletra_explosio_index_ << "\n";
// Passar a la següent lletra
lletra_explosio_index_++;
temps_des_ultima_explosio_ = 0.0F;
} else {
// Totes les lletres han explotat, transició a POST_EXPLOSION
canviar_estat(EstatAnimacio::POST_EXPLOSION);
canviar_estat(AnimationState::POST_EXPLOSION);
}
}
}
void EscenaLogo::update(float delta_time) {
void LogoScene::update(float delta_time) {
temps_estat_actual_ += delta_time;
switch (estat_actual_) {
case EstatAnimacio::PRE_ANIMATION:
case AnimationState::PRE_ANIMATION:
if (temps_estat_actual_ >= DURACIO_PRE) {
canviar_estat(EstatAnimacio::ANIMATION);
canviar_estat(AnimationState::ANIMATION);
}
break;
case EstatAnimacio::ANIMATION: {
case AnimationState::ANIMATION: {
// Reproduir so per cada lletra quan comença a aparèixer
float global_progress = std::min(temps_estat_actual_ / DURACIO_ZOOM, 1.0F);
@@ -295,55 +295,55 @@ void EscenaLogo::update(float delta_time) {
}
if (totes_lletres_completes()) {
canviar_estat(EstatAnimacio::POST_ANIMATION);
canviar_estat(AnimationState::POST_ANIMATION);
}
break;
}
case EstatAnimacio::POST_ANIMATION:
case AnimationState::POST_ANIMATION:
if (temps_estat_actual_ >= DURACIO_POST_ANIMATION) {
canviar_estat(EstatAnimacio::EXPLOSION);
canviar_estat(AnimationState::EXPLOSION);
}
break;
case EstatAnimacio::EXPLOSION:
case AnimationState::EXPLOSION:
actualitzar_explosions(delta_time);
break;
case EstatAnimacio::POST_EXPLOSION:
case AnimationState::POST_EXPLOSION:
if (temps_estat_actual_ >= DURACIO_POST_EXPLOSION) {
// Transició a pantalla de títol
context_.canviar_escena(Escena::TITOL);
GestorEscenes::actual = Escena::TITOL;
context_.setNextScene(SceneType::TITLE);
SceneManager::actual = SceneType::TITLE;
}
break;
}
// Verificar botones de skip (SHOOT P1/P2)
if (checkSkipButtonPressed()) {
context_.canviar_escena(Escena::TITOL, Opcio::JUMP_TO_TITLE_MAIN);
GestorEscenes::actual = Escena::TITOL;
context_.setNextScene(SceneType::TITLE, Option::JUMP_TO_TITLE_MAIN);
SceneManager::actual = SceneType::TITLE;
}
// Actualitzar animacions de debris
debris_manager_->update(delta_time);
}
void EscenaLogo::draw() {
void LogoScene::draw() {
// Fons negre
sdl_.neteja(0, 0, 0);
// PRE_ANIMATION: Només pantalla negra
if (estat_actual_ == EstatAnimacio::PRE_ANIMATION) {
if (estat_actual_ == AnimationState::PRE_ANIMATION) {
sdl_.presenta();
return; // No renderitzar lletres
}
// ANIMATION o POST_ANIMATION: Dibuixar lletres amb animació
if (estat_actual_ == EstatAnimacio::ANIMATION ||
estat_actual_ == EstatAnimacio::POST_ANIMATION) {
if (estat_actual_ == AnimationState::ANIMATION ||
estat_actual_ == AnimationState::POST_ANIMATION) {
float global_progress =
(estat_actual_ == EstatAnimacio::ANIMATION)
(estat_actual_ == AnimationState::ANIMATION)
? std::min(temps_estat_actual_ / DURACIO_ZOOM, 1.0F)
: 1.0F; // POST: mantenir al 100%
@@ -384,7 +384,7 @@ void EscenaLogo::draw() {
}
// EXPLOSION: Dibuixar només lletres que encara no han explotat
if (estat_actual_ == EstatAnimacio::EXPLOSION) {
if (estat_actual_ == AnimationState::EXPLOSION) {
// Crear conjunt de lletres ja explotades
std::set<size_t> explotades;
for (size_t i = 0; i < lletra_explosio_index_; i++) {
@@ -415,10 +415,10 @@ void EscenaLogo::draw() {
sdl_.presenta();
}
auto EscenaLogo::checkSkipButtonPressed() -> bool {
auto LogoScene::checkSkipButtonPressed() -> bool {
return Input::get()->checkAnyPlayerAction(ARCADE_BUTTONS);
}
void EscenaLogo::processar_events(const SDL_Event& event) {
void LogoScene::processar_events(const SDL_Event& event) {
// No procesar eventos genéricos aquí - la lógica se movió a update()
}
@@ -14,19 +14,19 @@
#include "core/graphics/shape.hpp"
#include "core/input/input_types.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/scene_context.hpp"
#include "core/types.hpp"
#include "game/effects/debris_manager.hpp"
class EscenaLogo {
class LogoScene {
public:
explicit EscenaLogo(SDLManager& sdl, GestorEscenes::ContextEscenes& context);
~EscenaLogo(); // Destructor per aturar sons
void executar(); // Bucle principal de l'escena
explicit LogoScene(SDLManager& sdl, SceneManager::SceneContext& context);
~LogoScene(); // Destructor per aturar sons
void run(); // Bucle principal de l'escena
private:
// Màquina d'estats per l'animació
enum class EstatAnimacio {
enum class AnimationState {
PRE_ANIMATION, // Pantalla negra inicial
ANIMATION, // Animació de zoom de lletres
POST_ANIMATION, // Logo complet visible
@@ -35,8 +35,8 @@ class EscenaLogo {
};
SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_;
EstatAnimacio estat_actual_; // Estat actual de la màquina
SceneManager::SceneContext& context_;
AnimationState estat_actual_; // Estat actual de la màquina
float
temps_estat_actual_; // Temps en l'estat actual (reset en cada transició)
@@ -86,6 +86,6 @@ class EscenaLogo {
auto checkSkipButtonPressed() -> bool;
// Mètodes de gestió d'estats
void canviar_estat(EstatAnimacio nou_estat);
void canviar_estat(AnimationState nou_estat);
[[nodiscard]] bool totes_lletres_completes() const;
};
@@ -1,7 +1,7 @@
// escena_titol.cpp - Implementació de l'escena de títol
// © 2025 Port a C++20
#include "escena_titol.hpp"
#include "title_scene.hpp"
#include <algorithm>
#include <cfloat>
@@ -15,38 +15,38 @@
#include "core/input/input.hpp"
#include "core/input/mouse.hpp"
#include "core/rendering/shape_renderer.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/scene_context.hpp"
#include "core/system/global_events.hpp"
#include "project.h"
// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;
using Opcio = ContextEscenes::Opcio;
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;
using Option = SceneContext::Option;
EscenaTitol::EscenaTitol(SDLManager& sdl, ContextEscenes& context)
TitleScene::TitleScene(SDLManager& sdl, SceneContext& context)
: sdl_(sdl),
context_(context),
text_(sdl.obte_renderer()),
estat_actual_(EstatTitol::STARFIELD_FADE_IN),
estat_actual_(TitleState::STARFIELD_FADE_IN),
temps_acumulat_(0.0F),
temps_animacio_(0.0F),
temps_estat_main_(0.0F),
animacio_activa_(false),
factor_lerp_(0.0F) {
std::cout << "Escena Titol: Inicialitzant...\n";
std::cout << "SceneType 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;
match_config_.jugador1_actiu = false;
match_config_.jugador2_actiu = false;
match_config_.mode = GameConfig::Mode::NORMAL;
// Processar opció del context
auto opcio = context_.consumir_opcio();
auto option = context_.consumeOption();
if (opcio == Opcio::JUMP_TO_TITLE_MAIN) {
std::cout << "Escena Titol: Opció JUMP_TO_TITLE_MAIN activada\n";
estat_actual_ = EstatTitol::MAIN;
if (option == Option::JUMP_TO_TITLE_MAIN) {
std::cout << "SceneType Titol: Opció JUMP_TO_TITLE_MAIN activada\n";
estat_actual_ = TitleState::MAIN;
temps_estat_main_ = 0.0F;
}
@@ -69,7 +69,7 @@ EscenaTitol::EscenaTitol(SDLManager& sdl, ContextEscenes& context)
);
// Brightness depèn de l'opció
if (estat_actual_ == EstatTitol::MAIN) {
if (estat_actual_ == TitleState::MAIN) {
// Si saltem a MAIN, starfield instantàniament brillant
starfield_->set_brightness(BRIGHTNESS_STARFIELD);
} else {
@@ -81,7 +81,7 @@ EscenaTitol::EscenaTitol(SDLManager& sdl, ContextEscenes& context)
ship_animator_ = std::make_unique<Title::ShipAnimator>(sdl_.obte_renderer());
ship_animator_->init();
if (estat_actual_ == EstatTitol::MAIN) {
if (estat_actual_ == TitleState::MAIN) {
// Jump to MAIN: empezar entrada inmediatamente
ship_animator_->set_visible(true);
ship_animator_->start_entry_animation();
@@ -99,12 +99,12 @@ EscenaTitol::EscenaTitol(SDLManager& sdl, ContextEscenes& context)
}
}
EscenaTitol::~EscenaTitol() {
TitleScene::~TitleScene() {
// Aturar música de títol quan es destrueix l'escena
Audio::get()->stopMusic();
}
void EscenaTitol::inicialitzar_titol() {
void TitleScene::inicialitzar_titol() {
using namespace Graphics;
// === LÍNIA 1: "ORNI" ===
@@ -120,7 +120,7 @@ void EscenaTitol::inicialitzar_titol() {
for (const auto& fitxer : fitxers_orni) {
auto forma = ShapeLoader::load(fitxer);
if (!forma || !forma->es_valida()) {
std::cerr << "[EscenaTitol] Error carregant " << fitxer << '\n';
std::cerr << "[TitleScene] Error carregant " << fitxer << '\n';
continue;
}
@@ -165,7 +165,7 @@ void EscenaTitol::inicialitzar_titol() {
x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
}
std::cout << "[EscenaTitol] Línia 1 (ORNI): " << lletres_orni_.size()
std::cout << "[TitleScene] Línia 1 (ORNI): " << lletres_orni_.size()
<< " lletres, ancho total: " << ancho_total_orni << " px\n";
// === Calcular posició Y dinàmica per "ATTACK!" ===
@@ -175,7 +175,7 @@ void EscenaTitol::inicialitzar_titol() {
float separacion_lineas = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_LINE_SPACING;
y_attack_dinamica_ = y_orni + altura_orni + separacion_lineas;
std::cout << "[EscenaTitol] Altura ORNI: " << altura_orni
std::cout << "[TitleScene] Altura ORNI: " << altura_orni
<< " px, Y_ATTACK dinàmica: " << y_attack_dinamica_ << " px\n";
// === LÍNIA 2: "ATTACK!" ===
@@ -194,7 +194,7 @@ void EscenaTitol::inicialitzar_titol() {
for (const auto& fitxer : fitxers_attack) {
auto forma = ShapeLoader::load(fitxer);
if (!forma || !forma->es_valida()) {
std::cerr << "[EscenaTitol] Error carregant " << fitxer << '\n';
std::cerr << "[TitleScene] Error carregant " << fitxer << '\n';
continue;
}
@@ -239,7 +239,7 @@ void EscenaTitol::inicialitzar_titol() {
x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
}
std::cout << "[EscenaTitol] Línia 2 (ATTACK!): " << lletres_attack_.size()
std::cout << "[TitleScene] Línia 2 (ATTACK!): " << lletres_attack_.size()
<< " lletres, ancho total: " << ancho_total_attack << " px\n";
// Guardar posicions originals per l'animació orbital
@@ -253,14 +253,14 @@ void EscenaTitol::inicialitzar_titol() {
posicions_originals_attack_.push_back(lletra.posicio);
}
std::cout << "[EscenaTitol] Animació: Posicions originals guardades\n";
std::cout << "[TitleScene] Animació: Posicions originals guardades\n";
}
void EscenaTitol::executar() {
void TitleScene::run() {
SDL_Event event;
Uint64 last_time = SDL_GetTicks();
while (GestorEscenes::actual == Escena::TITOL) {
while (SceneManager::actual == SceneType::TITLE) {
// Calcular delta_time real
Uint64 current_time = SDL_GetTicks();
float delta_time = (current_time - last_time) / 1000.0F;
@@ -316,10 +316,10 @@ void EscenaTitol::executar() {
sdl_.presenta();
}
std::cout << "Escena Titol: Finalitzant...\n";
std::cout << "SceneType Titol: Finalitzant...\n";
}
void EscenaTitol::update(float delta_time) {
void TitleScene::update(float delta_time) {
// Actualitzar starfield (sempre actiu)
if (starfield_) {
starfield_->update(delta_time);
@@ -327,15 +327,15 @@ void EscenaTitol::update(float delta_time) {
// Actualitzar naus (quan visibles)
if (ship_animator_ &&
(estat_actual_ == EstatTitol::STARFIELD_FADE_IN ||
estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
(estat_actual_ == TitleState::STARFIELD_FADE_IN ||
estat_actual_ == TitleState::STARFIELD ||
estat_actual_ == TitleState::MAIN ||
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
ship_animator_->update(delta_time);
}
switch (estat_actual_) {
case EstatTitol::STARFIELD_FADE_IN: {
case TitleState::STARFIELD_FADE_IN: {
temps_acumulat_ += delta_time;
// Calcular progrés del fade (0.0 → 1.0)
@@ -347,17 +347,17 @@ void EscenaTitol::update(float delta_time) {
// Transició a STARFIELD quan el fade es completa
if (temps_acumulat_ >= DURACIO_FADE_IN) {
estat_actual_ = EstatTitol::STARFIELD;
estat_actual_ = TitleState::STARFIELD;
temps_acumulat_ = 0.0F; // Reset timer per al següent estat
starfield_->set_brightness(BRIGHTNESS_STARFIELD); // Assegurar valor final
}
break;
}
case EstatTitol::STARFIELD:
case TitleState::STARFIELD:
temps_acumulat_ += delta_time;
if (temps_acumulat_ >= DURACIO_INIT) {
estat_actual_ = EstatTitol::MAIN;
estat_actual_ = TitleState::MAIN;
temps_estat_main_ = 0.0F; // Reset timer al entrar a MAIN
animacio_activa_ = false; // Comença estàtic
factor_lerp_ = 0.0F; // Sense animació encara
@@ -366,7 +366,7 @@ void EscenaTitol::update(float delta_time) {
}
break;
case EstatTitol::MAIN: {
case TitleState::MAIN: {
temps_estat_main_ += delta_time;
// Iniciar animació d'entrada de naus després del delay
@@ -399,7 +399,7 @@ void EscenaTitol::update(float delta_time) {
break;
}
case EstatTitol::PLAYER_JOIN_PHASE:
case TitleState::PLAYER_JOIN_PHASE:
temps_acumulat_ += delta_time;
// Continuar animació orbital durant la transició
@@ -407,22 +407,22 @@ void EscenaTitol::update(float delta_time) {
// [NOU] Continuar comprovant si l'altre jugador vol unir-se durant la transició ("late join")
{
bool p1_actiu_abans = config_partida_.jugador1_actiu;
bool p2_actiu_abans = config_partida_.jugador2_actiu;
bool p1_actiu_abans = match_config_.jugador1_actiu;
bool p2_actiu_abans = match_config_.jugador2_actiu;
if (checkStartGameButtonPressed()) {
// Updates config_partida_ if pressed, logs are in the method
context_.set_config_partida(config_partida_);
// Updates match_config_ if pressed, logs are in the method
context_.setMatchConfig(match_config_);
// Trigger animació de sortida per la nau que acaba d'unir-se
if (ship_animator_) {
if (config_partida_.jugador1_actiu && !p1_actiu_abans) {
if (match_config_.jugador1_actiu && !p1_actiu_abans) {
ship_animator_->trigger_exit_animation_for_player(1);
std::cout << "[EscenaTitol] P1 late join - ship exiting\n";
std::cout << "[TitleScene] P1 late join - ship exiting\n";
}
if (config_partida_.jugador2_actiu && !p2_actiu_abans) {
if (match_config_.jugador2_actiu && !p2_actiu_abans) {
ship_animator_->trigger_exit_animation_for_player(2);
std::cout << "[EscenaTitol] P2 late join - ship exiting\n";
std::cout << "[TitleScene] P2 late join - ship exiting\n";
}
}
@@ -432,35 +432,35 @@ void EscenaTitol::update(float delta_time) {
// Reiniciar el timer per allargar el temps de transició
temps_acumulat_ = 0.0F;
std::cout << "[EscenaTitol] Segon jugador s'ha unit - so i timer reiniciats\n";
std::cout << "[TitleScene] Segon jugador s'ha unit - so i timer reiniciats\n";
}
}
if (temps_acumulat_ >= DURACIO_TRANSITION) {
// Transició a pantalla negra
estat_actual_ = EstatTitol::BLACK_SCREEN;
estat_actual_ = TitleState::BLACK_SCREEN;
temps_acumulat_ = 0.0F;
std::cout << "[EscenaTitol] Passant a BLACK_SCREEN\n";
std::cout << "[TitleScene] Passant a BLACK_SCREEN\n";
}
break;
case EstatTitol::BLACK_SCREEN:
case TitleState::BLACK_SCREEN:
temps_acumulat_ += delta_time;
// No animation, no input checking - just wait
if (temps_acumulat_ >= DURACIO_BLACK_SCREEN) {
// Transició a escena JOC
GestorEscenes::actual = Escena::JOC;
std::cout << "[EscenaTitol] Canviant a escena JOC\n";
// Transició a escena GAME
SceneManager::actual = SceneType::GAME;
std::cout << "[TitleScene] Canviant a escena GAME\n";
}
break;
}
// Verificar botones de skip (FIRE/THRUST/START) para saltar escenas ANTES de MAIN
if (estat_actual_ == EstatTitol::STARFIELD_FADE_IN || estat_actual_ == EstatTitol::STARFIELD) {
if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) {
if (checkSkipButtonPressed()) {
// Saltar a MAIN
estat_actual_ = EstatTitol::MAIN;
estat_actual_ = TitleState::MAIN;
starfield_->set_brightness(BRIGHTNESS_STARFIELD);
temps_estat_main_ = 0.0F;
@@ -469,10 +469,10 @@ void EscenaTitol::update(float delta_time) {
}
// Verificar boton START para iniciar partida desde MAIN
if (estat_actual_ == EstatTitol::MAIN) {
if (estat_actual_ == TitleState::MAIN) {
// Guardar estat anterior per detectar qui ha premut START AQUEST frame
bool p1_actiu_abans = config_partida_.jugador1_actiu;
bool p2_actiu_abans = config_partida_.jugador2_actiu;
bool p1_actiu_abans = match_config_.jugador1_actiu;
bool p2_actiu_abans = match_config_.jugador2_actiu;
if (checkStartGameButtonPressed()) {
// Si START es prem durant el delay (naus encara invisibles), saltar-les a FLOATING
@@ -482,26 +482,26 @@ void EscenaTitol::update(float delta_time) {
}
// 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")
context_.setMatchConfig(match_config_);
std::cout << "[TitleScene] Configuració de partida - P1: "
<< (match_config_.jugador1_actiu ? "ACTIU" : "INACTIU")
<< ", P2: "
<< (config_partida_.jugador2_actiu ? "ACTIU" : "INACTIU")
<< (match_config_.jugador2_actiu ? "ACTIU" : "INACTIU")
<< '\n';
context_.canviar_escena(Escena::JOC);
estat_actual_ = EstatTitol::PLAYER_JOIN_PHASE;
context_.setNextScene(SceneType::GAME);
estat_actual_ = TitleState::PLAYER_JOIN_PHASE;
temps_acumulat_ = 0.0F;
// Trigger animació de sortida NOMÉS per les naus que han premut START
if (ship_animator_) {
if (config_partida_.jugador1_actiu && !p1_actiu_abans) {
if (match_config_.jugador1_actiu && !p1_actiu_abans) {
ship_animator_->trigger_exit_animation_for_player(1);
std::cout << "[EscenaTitol] P1 ship exiting\n";
std::cout << "[TitleScene] P1 ship exiting\n";
}
if (config_partida_.jugador2_actiu && !p2_actiu_abans) {
if (match_config_.jugador2_actiu && !p2_actiu_abans) {
ship_animator_->trigger_exit_animation_for_player(2);
std::cout << "[EscenaTitol] P2 ship exiting\n";
std::cout << "[TitleScene] P2 ship exiting\n";
}
}
@@ -511,7 +511,7 @@ void EscenaTitol::update(float delta_time) {
}
}
void EscenaTitol::actualitzar_animacio_logo(float delta_time) {
void TitleScene::actualitzar_animacio_logo(float delta_time) {
// Només calcular i aplicar offsets si l'animació està activa
if (animacio_activa_) {
// Acumular temps escalat
@@ -541,29 +541,29 @@ void EscenaTitol::actualitzar_animacio_logo(float delta_time) {
}
}
void EscenaTitol::draw() {
void TitleScene::draw() {
// Dibuixar starfield de fons (en tots els estats excepte BLACK_SCREEN)
if (starfield_ && estat_actual_ != EstatTitol::BLACK_SCREEN) {
if (starfield_ && estat_actual_ != TitleState::BLACK_SCREEN) {
starfield_->draw();
}
// Dibuixar naus (després starfield, abans logo)
if (ship_animator_ &&
(estat_actual_ == EstatTitol::STARFIELD_FADE_IN ||
estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
(estat_actual_ == TitleState::STARFIELD_FADE_IN ||
estat_actual_ == TitleState::STARFIELD ||
estat_actual_ == TitleState::MAIN ||
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
ship_animator_->draw();
}
// En els estats STARFIELD_FADE_IN i STARFIELD, només mostrar starfield (sense text)
if (estat_actual_ == EstatTitol::STARFIELD_FADE_IN || estat_actual_ == EstatTitol::STARFIELD) {
if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) {
return;
}
// Estat MAIN i PLAYER_JOIN_PHASE: Dibuixar títol i text (sobre el starfield)
// BLACK_SCREEN: no draw res (fons negre ja està netejat)
if (estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) {
if (estat_actual_ == TitleState::MAIN || estat_actual_ == TitleState::PLAYER_JOIN_PHASE) {
// === Calcular i renderitzar ombra (només si animació activa) ===
if (animacio_activa_) {
float temps_shadow = temps_animacio_ - SHADOW_DELAY;
@@ -648,7 +648,7 @@ void EscenaTitol::draw() {
const float spacing = Defaults::Title::Layout::TEXT_SPACING;
bool mostrar_text = true;
if (estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) {
if (estat_actual_ == TitleState::PLAYER_JOIN_PHASE) {
// Parpelleig: sin oscil·la entre -1 i 1, volem ON quan > 0
float fase = temps_acumulat_ * BLINK_FREQUENCY * 2.0F * std::numbers::pi_v<float>; // 2π × freq × temps
mostrar_text = (std::sin(fase) > 0.0F);
@@ -697,27 +697,27 @@ void EscenaTitol::draw() {
}
}
auto EscenaTitol::checkSkipButtonPressed() -> bool {
auto TitleScene::checkSkipButtonPressed() -> bool {
return Input::get()->checkAnyPlayerAction(ARCADE_BUTTONS);
}
auto EscenaTitol::checkStartGameButtonPressed() -> bool {
auto TitleScene::checkStartGameButtonPressed() -> bool {
auto* input = Input::get();
bool any_pressed = false;
for (auto action : START_GAME_BUTTONS) {
if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT)) {
if (!config_partida_.jugador1_actiu) {
config_partida_.jugador1_actiu = true;
if (!match_config_.jugador1_actiu) {
match_config_.jugador1_actiu = true;
any_pressed = true;
std::cout << "[EscenaTitol] P1 pressed START\n";
std::cout << "[TitleScene] P1 pressed START\n";
}
}
if (input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) {
if (!config_partida_.jugador2_actiu) {
config_partida_.jugador2_actiu = true;
if (!match_config_.jugador2_actiu) {
match_config_.jugador2_actiu = true;
any_pressed = true;
std::cout << "[EscenaTitol] P2 pressed START\n";
std::cout << "[TitleScene] P2 pressed START\n";
}
}
}
@@ -725,6 +725,6 @@ auto EscenaTitol::checkStartGameButtonPressed() -> bool {
return any_pressed;
}
void EscenaTitol::processar_events(const SDL_Event& event) {
void TitleScene::processar_events(const SDL_Event& event) {
// No procesar eventos genéricos aquí - la lógica se movió a update()
}
@@ -16,7 +16,7 @@
#include "core/graphics/vector_text.hpp"
#include "core/input/input_types.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/scene_context.hpp"
#include "core/system/game_config.hpp"
#include "core/types.hpp"
#include "game/title/ship_animator.hpp"
@@ -25,15 +25,15 @@
static constexpr std::array<InputAction, 1> START_GAME_BUTTONS = {
InputAction::START};
class EscenaTitol {
class TitleScene {
public:
explicit EscenaTitol(SDLManager& sdl, GestorEscenes::ContextEscenes& context);
~EscenaTitol(); // Destructor per aturar música
void executar(); // Bucle principal de l'escena
explicit TitleScene(SDLManager& sdl, SceneManager::SceneContext& context);
~TitleScene(); // Destructor per aturar música
void run(); // Bucle principal de l'escena
private:
// Màquina d'estats per la pantalla de títol
enum class EstatTitol {
enum class TitleState {
STARFIELD_FADE_IN, // Fade-in del starfield (3.0s)
STARFIELD, // Pantalla amb camp d'estrelles (4.0s)
MAIN, // Pantalla de títol amb text (indefinit, fins START)
@@ -51,12 +51,12 @@ class EscenaTitol {
};
SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_;
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
SceneManager::SceneContext& context_;
GameConfig::MatchConfig match_config_; // Configuració de jugadors actius
Graphics::VectorText text_; // Sistema de text vectorial
std::unique_ptr<Graphics::Starfield> starfield_; // Camp d'estrelles de fons
std::unique_ptr<Title::ShipAnimator> ship_animator_; // Naus 3D flotants
EstatTitol estat_actual_; // Estat actual de la màquina
TitleState estat_actual_; // Estat actual de la màquina
float temps_acumulat_; // Temps acumulat per l'estat INIT
// Lletres del títol "ORNI ATTACK!"
+2 -2
View File
@@ -129,10 +129,10 @@ void StageManager::processar_level_start(float delta_time) {
void StageManager::processar_playing(float delta_time, bool pausar_spawn) {
// Update spawn controller (pauses when pausar_spawn = true)
// Note: The actual enemy array update happens in EscenaJoc::update()
// Note: The actual enemy array update happens in GameScene::update()
// This is just for internal timekeeping
(void)delta_time; // Spawn controller is updated externally
(void)pausar_spawn; // Passed to spawn_controller_.update() by EscenaJoc
(void)pausar_spawn; // Passed to spawn_controller_.update() by GameScene
}
void StageManager::processar_level_completed(float delta_time) {
+1 -1
View File
@@ -68,7 +68,7 @@ class ShipAnimator {
void update(float delta_time);
void draw() const;
// Control d'estat (cridat per EscenaTitol)
// Control d'estat (cridat per TitleScene)
void start_entry_animation();
void trigger_exit_animation(); // Anima totes les naus
void trigger_exit_animation_for_player(int jugador_id); // Anima només una nau (P1=1, P2=2)