layout de TITOL

This commit is contained in:
2025-12-17 11:32:37 +01:00
parent 886ec8ab1d
commit 3b432e6580
45 changed files with 880 additions and 745 deletions

View File

@@ -5,7 +5,9 @@ project(orni VERSION 0.6.0)
# Info del proyecto # Info del proyecto
set(PROJECT_LONG_NAME "Orni Attack") set(PROJECT_LONG_NAME "Orni Attack")
set(PROJECT_COPYRIGHT "© 1999 Visente i Sergi, 2025 Port") set(PROJECT_COPYRIGHT_ORIGINAL "© 1999 Visente i Sergi")
set(PROJECT_COPYRIGHT_PORT "© 2025 JailDesigner")
set(PROJECT_COPYRIGHT "${PROJECT_COPYRIGHT_ORIGINAL}, ${PROJECT_COPYRIGHT_PORT}")
# Establecer estándar de C++ # Establecer estándar de C++
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)

View File

@@ -3,10 +3,10 @@
#include "core/audio/audio_cache.hpp" #include "core/audio/audio_cache.hpp"
#include "core/resources/resource_helper.hpp"
#include <iostream> #include <iostream>
#include "core/resources/resource_helper.hpp"
// Inicialització de variables estàtiques // Inicialització de variables estàtiques
std::unordered_map<std::string, JA_Sound_t*> AudioCache::sounds_; std::unordered_map<std::string, JA_Sound_t*> AudioCache::sounds_;
std::unordered_map<std::string, JA_Music_t*> AudioCache::musics_; std::unordered_map<std::string, JA_Music_t*> AudioCache::musics_;

View File

@@ -28,12 +28,12 @@ namespace Zones {
// Totes les zones definides com a percentatges de Game::WIDTH (640) i Game::HEIGHT (480) // Totes les zones definides com a percentatges de Game::WIDTH (640) i Game::HEIGHT (480)
// Percentatges d'alçada (divisió vertical) // Percentatges d'alçada (divisió vertical)
constexpr float SCOREBOARD_TOP_HEIGHT_PERCENT = 0.02f; // 10% superior constexpr float SCOREBOARD_TOP_HEIGHT_PERCENT = 0.02f; // 10% superior
constexpr float MAIN_PLAYAREA_HEIGHT_PERCENT = 0.88f; // 80% central constexpr float MAIN_PLAYAREA_HEIGHT_PERCENT = 0.88f; // 80% central
constexpr float SCOREBOARD_BOTTOM_HEIGHT_PERCENT = 0.10f; // 10% inferior constexpr float SCOREBOARD_BOTTOM_HEIGHT_PERCENT = 0.10f; // 10% inferior
// Padding horizontal per a PLAYAREA (dins de MAIN_PLAYAREA) // Padding horizontal per a PLAYAREA (dins de MAIN_PLAYAREA)
constexpr float PLAYAREA_PADDING_HORIZONTAL_PERCENT = 0.015f; // 5% a cada costat constexpr float PLAYAREA_PADDING_HORIZONTAL_PERCENT = 0.015f; // 5% a cada costat
// --- CÀLCULS AUTOMÀTICS DE PÍXELS --- // --- CÀLCULS AUTOMÀTICS DE PÍXELS ---
// Càlculs automàtics a partir dels percentatges // Càlculs automàtics a partir dels percentatges
@@ -56,42 +56,42 @@ constexpr float PLAYAREA_PADDING_H = Game::WIDTH * PLAYAREA_PADDING_HORIZONTAL_P
// Marcador superior (reservat per a futur ús) // Marcador superior (reservat per a futur ús)
// Ocupa: 10% superior (0-48px) // Ocupa: 10% superior (0-48px)
constexpr SDL_FRect SCOREBOARD_TOP = { constexpr SDL_FRect SCOREBOARD_TOP = {
0.0f, // x = 0.0 0.0f, // x = 0.0
SCOREBOARD_TOP_Y, // y = 0.0 SCOREBOARD_TOP_Y, // y = 0.0
static_cast<float>(Game::WIDTH), // w = 640.0 static_cast<float>(Game::WIDTH), // w = 640.0
SCOREBOARD_TOP_H // h = 48.0 SCOREBOARD_TOP_H // h = 48.0
}; };
// Àrea de joc principal (contenidor del 80% central, sense padding) // Àrea de joc principal (contenidor del 80% central, sense padding)
// Ocupa: 10-90% (48-432px), ample complet // Ocupa: 10-90% (48-432px), ample complet
constexpr SDL_FRect MAIN_PLAYAREA = { constexpr SDL_FRect MAIN_PLAYAREA = {
0.0f, // x = 0.0 0.0f, // x = 0.0
MAIN_PLAYAREA_Y, // y = 48.0 MAIN_PLAYAREA_Y, // y = 48.0
static_cast<float>(Game::WIDTH), // w = 640.0 static_cast<float>(Game::WIDTH), // w = 640.0
MAIN_PLAYAREA_H // h = 384.0 MAIN_PLAYAREA_H // h = 384.0
}; };
// Zona de joc real (amb padding horizontal del 5%) // Zona de joc real (amb padding horizontal del 5%)
// Ocupa: dins de MAIN_PLAYAREA, amb marges laterals // Ocupa: dins de MAIN_PLAYAREA, amb marges laterals
// S'utilitza per a límits del joc, col·lisions, spawn // S'utilitza per a límits del joc, col·lisions, spawn
constexpr SDL_FRect PLAYAREA = { constexpr SDL_FRect PLAYAREA = {
PLAYAREA_PADDING_H, // x = 32.0 PLAYAREA_PADDING_H, // x = 32.0
MAIN_PLAYAREA_Y, // y = 48.0 (igual que MAIN_PLAYAREA) MAIN_PLAYAREA_Y, // y = 48.0 (igual que MAIN_PLAYAREA)
Game::WIDTH - 2.0f * PLAYAREA_PADDING_H, // w = 576.0 Game::WIDTH - 2.0f * PLAYAREA_PADDING_H, // w = 576.0
MAIN_PLAYAREA_H // h = 384.0 (igual que MAIN_PLAYAREA) MAIN_PLAYAREA_H // h = 384.0 (igual que MAIN_PLAYAREA)
}; };
// Marcador inferior (marcador actual) // Marcador inferior (marcador actual)
// Ocupa: 10% inferior (432-480px) // Ocupa: 10% inferior (432-480px)
constexpr SDL_FRect SCOREBOARD = { constexpr SDL_FRect SCOREBOARD = {
0.0f, // x = 0.0 0.0f, // x = 0.0
SCOREBOARD_BOTTOM_Y, // y = 432.0 SCOREBOARD_BOTTOM_Y, // y = 432.0
static_cast<float>(Game::WIDTH), // w = 640.0 static_cast<float>(Game::WIDTH), // w = 640.0
SCOREBOARD_BOTTOM_H // h = 48.0 SCOREBOARD_BOTTOM_H // h = 48.0
}; };
// Padding horizontal del marcador (per alinear zones esquerra/dreta amb PLAYAREA) // Padding horizontal del marcador (per alinear zones esquerra/dreta amb PLAYAREA)
constexpr float SCOREBOARD_PADDING_H = 0.0f;//Game::WIDTH * 0.015f; constexpr float SCOREBOARD_PADDING_H = 0.0f; // Game::WIDTH * 0.015f;
} // namespace Zones } // namespace Zones
// Objetos del juego // Objetos del juego
@@ -361,55 +361,112 @@ constexpr int MOLINILLO_SCORE = 200; // Molinillo (agressiu, 50 px/s)
// Title scene ship animations (naus 3D flotants a l'escena de títol) // Title scene ship animations (naus 3D flotants a l'escena de títol)
namespace Title { namespace Title {
namespace Ships { namespace Ships {
// Posicions clock (coordenades polars des del centre 320, 240) // ============================================================
// PARÀMETRES BASE (ajustar aquí per experimentar)
// ============================================================
// 1. Escala global de les naus
constexpr float SHIP_BASE_SCALE = 2.5f; // Multiplicador (1.0 = mida original del .shp)
// 2. Altura vertical (cercanía al centro)
// Ratio Y desde el centro de la pantalla (0.0 = centro, 1.0 = bottom de pantalla)
constexpr float TARGET_Y_RATIO = 0.15625f;
// 3. Radio orbital (distancia radial desde centro en coordenadas polares)
constexpr float CLOCK_RADIUS = 150.0f; // Distància des del centre
// 4. Ángulos de posición (clock positions en coordenadas polares)
// En coordenades de pantalla: 0° = dreta, 90° = baix, 180° = esquerra, 270° = dalt // En coordenades de pantalla: 0° = dreta, 90° = baix, 180° = esquerra, 270° = dalt
constexpr float CLOCK_8_ANGLE = 150.0f * Math::PI / 180.0f; // 8 o'clock = bottom-left constexpr float CLOCK_8_ANGLE = 150.0f * Math::PI / 180.0f; // 8 o'clock (bottom-left)
constexpr float CLOCK_4_ANGLE = 30.0f * Math::PI / 180.0f; // 4 o'clock = bottom-right constexpr float CLOCK_4_ANGLE = 30.0f * Math::PI / 180.0f; // 4 o'clock (bottom-right)
constexpr float CLOCK_RADIUS = 150.0f; // Distància des del centre
// P1 (8 o'clock, bottom-left) // 5. Radio máximo de la forma de la nave (para calcular offset automáticamente)
// 150° → cos(150°)=-0.866, sin(150°)=0.5 → X = 320 - 130 = 190, Y = 240 + 75 = 315 constexpr float SHIP_MAX_RADIUS = 30.0f; // Radi del cercle circumscrit a ship_p1.shp
constexpr float P1_TARGET_X = 190.0f;
constexpr float P1_TARGET_Y = 315.0f;
// P2 (4 o'clock, bottom-right) // 6. Margen de seguridad para offset de entrada
// 30° → cos(30°)=0.866, sin(30°)=0.5 → X = 320 + 130 = 450, Y = 240 + 75 = 315 constexpr float ENTRY_OFFSET_MARGIN = 227.5f; // Para offset total de ~340px (ajustado)
constexpr float P2_TARGET_X = 450.0f;
constexpr float P2_TARGET_Y = 315.0f;
// Escala base de les naus (ajusta aquí per fer-les més grans o petites) // ============================================================
constexpr float SHIP_BASE_SCALE = 2.5f; // Multiplicador global (1.0 = mida original) // VALORS DERIVATS (calculats automàticament - NO modificar)
// ============================================================
// Escales d'animació (perspectiva ja incorporada a les formes .shp) // Centre de la pantalla (punt de referència)
constexpr float ENTRY_SCALE_START = 1.5f * SHIP_BASE_SCALE; // Més gran per veure millor constexpr float CENTER_X = Game::WIDTH / 2.0f; // 320.0f
constexpr float FLOATING_SCALE = 1.0f * SHIP_BASE_SCALE; // Mida normal (més grans) constexpr float CENTER_Y = Game::HEIGHT / 2.0f; // 240.0f
// Animacions // Posicions target (calculades dinàmicament des dels paràmetres base)
constexpr float ENTRY_DURATION = 2.0f; // Entrada // Nota: std::cos/sin no són constexpr en C++20, però funcionen en runtime
constexpr float ENTRY_OFFSET = 340.0f; // Offset fora de pantalla (considera radi màxim 30px * escala 3.75 + marge) // Les funcions inline són optimitzades pel compilador (zero overhead)
constexpr float EXIT_DURATION = 1.0f; // Sortida (configurable) inline float P1_TARGET_X() {
return CENTER_X + CLOCK_RADIUS * std::cos(CLOCK_8_ANGLE);
}
inline float P1_TARGET_Y() {
return CENTER_Y + (Game::HEIGHT / 2.0f) * TARGET_Y_RATIO;
}
inline float P2_TARGET_X() {
return CENTER_X + CLOCK_RADIUS * std::cos(CLOCK_4_ANGLE);
}
inline float P2_TARGET_Y() {
return CENTER_Y + (Game::HEIGHT / 2.0f) * TARGET_Y_RATIO;
}
// Escales d'animació (relatives a SHIP_BASE_SCALE)
constexpr float ENTRY_SCALE_START = 1.5f * SHIP_BASE_SCALE; // Entrada: 50% més gran
constexpr float FLOATING_SCALE = 1.0f * SHIP_BASE_SCALE; // Flotant: escala base
// Offset d'entrada (ajustat automàticament a l'escala)
// Fórmula: (radi màxim de la nau * escala d'entrada) + marge
constexpr float ENTRY_OFFSET = (SHIP_MAX_RADIUS * ENTRY_SCALE_START) + ENTRY_OFFSET_MARGIN;
// Punt de fuga (centre per a l'animació de sortida)
constexpr float VANISHING_POINT_X = CENTER_X; // 320.0f
constexpr float VANISHING_POINT_Y = CENTER_Y; // 240.0f
// ============================================================
// ANIMACIONS (durades, oscil·lacions, delays)
// ============================================================
// Durades d'animació
constexpr float ENTRY_DURATION = 2.0f; // Entrada (segons)
constexpr float EXIT_DURATION = 1.0f; // Sortida (segons)
// Flotació (oscil·lació reduïda i diferenciada per nau) // Flotació (oscil·lació reduïda i diferenciada per nau)
constexpr float FLOAT_AMPLITUDE_X = 4.0f; // Era 6.0f constexpr float FLOAT_AMPLITUDE_X = 4.0f; // Amplitud X (píxels)
constexpr float FLOAT_AMPLITUDE_Y = 2.5f; // Era 4.0f constexpr float FLOAT_AMPLITUDE_Y = 2.5f; // Amplitud Y (píxels)
// Freqüències base // Freqüències base
constexpr float FLOAT_FREQUENCY_X_BASE = 0.5f; constexpr float FLOAT_FREQUENCY_X_BASE = 0.5f; // Hz
constexpr float FLOAT_FREQUENCY_Y_BASE = 0.7f; constexpr float FLOAT_FREQUENCY_Y_BASE = 0.7f; // Hz
constexpr float FLOAT_PHASE_OFFSET = 1.57f; // π/2 (90°) constexpr float FLOAT_PHASE_OFFSET = 1.57f; // π/2 (90°)
// Delays d'entrada // Delays d'entrada (per a entrada escalonada)
constexpr float P1_ENTRY_DELAY = 0.0f; // P1 entra immediatament constexpr float P1_ENTRY_DELAY = 0.0f; // P1 entra immediatament
constexpr float P2_ENTRY_DELAY = 0.5f; // P2 entra 0.5s després constexpr float P2_ENTRY_DELAY = 0.5f; // P2 entra 0.5s després
// Multiplicadors de freqüència per a cada nau (variació sutil ±12%) // Multiplicadors de freqüència per a cada nau (variació sutil ±12%)
constexpr float P1_FREQUENCY_MULTIPLIER = 0.88f; // 12% més lenta constexpr float P1_FREQUENCY_MULTIPLIER = 0.88f; // 12% més lenta
constexpr float P2_FREQUENCY_MULTIPLIER = 1.12f; // 12% més ràpida constexpr float P2_FREQUENCY_MULTIPLIER = 1.12f; // 12% més ràpida
// Punt de fuga
constexpr float VANISHING_POINT_X = Game::WIDTH / 2.0f; // 320.0f
constexpr float VANISHING_POINT_Y = Game::HEIGHT / 2.0f; // 240.0f
} // namespace Ships } // namespace Ships
namespace Layout {
// Posicions verticals (anclatges des del TOP de pantalla lògica, 0.0-1.0)
constexpr float LOGO_POS = 0.20f; // Logo "ORNI"
constexpr float PRESS_START_POS = 0.73f; // "PRESS START TO PLAY"
constexpr float COPYRIGHT1_POS = 0.87f; // Primera línia copyright
// Separacions relatives (proporció respecte Game::HEIGHT = 480px)
constexpr float LOGO_LINE_SPACING = 0.02f; // Entre "ORNI" i "ATTACK!" (10px)
constexpr float COPYRIGHT_LINE_SPACING = 0.0f; // Entre línies copyright (5px)
// Factors d'escala
constexpr float LOGO_SCALE = 0.6f; // Escala "ORNI ATTACK!"
constexpr float PRESS_START_SCALE = 1.0f; // Escala "PRESS START TO PLAY"
constexpr float COPYRIGHT_SCALE = 0.5f; // Escala copyright
// Espaiat entre caràcters (usat per VectorText)
constexpr float TEXT_SPACING = 2.0f;
} // namespace Layout
} // namespace Title } // namespace Title
// Floating score numbers (números flotants de puntuació) // Floating score numbers (números flotants de puntuació)

View File

@@ -3,10 +3,10 @@
#include "core/graphics/shape_loader.hpp" #include "core/graphics/shape_loader.hpp"
#include "core/resources/resource_helper.hpp"
#include <iostream> #include <iostream>
#include "core/resources/resource_helper.hpp"
namespace Graphics { namespace Graphics {
// Inicialització de variables estàtiques // Inicialització de variables estàtiques

View File

@@ -106,7 +106,7 @@ float Starfield::calcular_brightness(const Estrella& estrella) const {
// distancia_centre: 0.0 (centre, llunyanes) → 1.0 (vora, properes) // distancia_centre: 0.0 (centre, llunyanes) → 1.0 (vora, properes)
float brightness_base = Defaults::Brightness::STARFIELD_MIN + float brightness_base = Defaults::Brightness::STARFIELD_MIN +
(Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) * (Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) *
estrella.distancia_centre; estrella.distancia_centre;
// Aplicar multiplicador i limitar a 1.0 // Aplicar multiplicador i limitar a 1.0
return std::min(1.0f, brightness_base * multiplicador_brightness_); return std::min(1.0f, brightness_base * multiplicador_brightness_);

View File

@@ -72,10 +72,10 @@ class Starfield {
SDL_Renderer* renderer_; SDL_Renderer* renderer_;
// Configuració // Configuració
Punt punt_fuga_; // Punt d'origen de les estrelles Punt punt_fuga_; // Punt d'origen de les estrelles
SDL_FRect area_; // Àrea activa SDL_FRect area_; // Àrea activa
float radi_max_; // Distància màxima del centre al límit de pantalla float radi_max_; // Distància màxima del centre al límit de pantalla
int densitat_; // Nombre total d'estrelles int densitat_; // Nombre total d'estrelles
float multiplicador_brightness_{1.0f}; // Multiplicador de brillantor (1.0 = default) float multiplicador_brightness_{1.0f}; // Multiplicador de brillantor (1.0 = default)
}; };

View File

@@ -129,8 +129,8 @@ class Input {
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr Sint16 AXIS_THRESHOLD = 30000; // Umbral para ejes analógicos static constexpr Sint16 AXIS_THRESHOLD = 30000; // Umbral para ejes analógicos
static constexpr Sint16 TRIGGER_THRESHOLD = 16384; // Umbral para triggers (50% del rango) static constexpr Sint16 TRIGGER_THRESHOLD = 16384; // Umbral para triggers (50% del rango)
static constexpr std::array<Action, 1> BUTTON_INPUTS = {Action::SHOOT}; // Inputs que usan botones static constexpr std::array<Action, 1> BUTTON_INPUTS = {Action::SHOOT}; // Inputs que usan botones
// --- Métodos --- // --- Métodos ---

View File

@@ -4,57 +4,57 @@
// Definición de los mapas // Definición de los mapas
const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = { const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
{InputAction::LEFT, "LEFT"}, {InputAction::LEFT, "LEFT"},
{InputAction::RIGHT, "RIGHT"}, {InputAction::RIGHT, "RIGHT"},
{InputAction::THRUST, "THRUST"}, {InputAction::THRUST, "THRUST"},
{InputAction::SHOOT, "SHOOT"}, {InputAction::SHOOT, "SHOOT"},
{InputAction::WINDOW_INC_ZOOM, "WINDOW_INC_ZOOM"}, {InputAction::WINDOW_INC_ZOOM, "WINDOW_INC_ZOOM"},
{InputAction::WINDOW_DEC_ZOOM, "WINDOW_DEC_ZOOM"}, {InputAction::WINDOW_DEC_ZOOM, "WINDOW_DEC_ZOOM"},
{InputAction::TOGGLE_FULLSCREEN, "TOGGLE_FULLSCREEN"}, {InputAction::TOGGLE_FULLSCREEN, "TOGGLE_FULLSCREEN"},
{InputAction::TOGGLE_VSYNC, "TOGGLE_VSYNC"}, {InputAction::TOGGLE_VSYNC, "TOGGLE_VSYNC"},
{InputAction::EXIT, "EXIT"}, {InputAction::EXIT, "EXIT"},
{InputAction::NONE, "NONE"}}; {InputAction::NONE, "NONE"}};
const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = { const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
{"LEFT", InputAction::LEFT}, {"LEFT", InputAction::LEFT},
{"RIGHT", InputAction::RIGHT}, {"RIGHT", InputAction::RIGHT},
{"THRUST", InputAction::THRUST}, {"THRUST", InputAction::THRUST},
{"SHOOT", InputAction::SHOOT}, {"SHOOT", InputAction::SHOOT},
{"WINDOW_INC_ZOOM", InputAction::WINDOW_INC_ZOOM}, {"WINDOW_INC_ZOOM", InputAction::WINDOW_INC_ZOOM},
{"WINDOW_DEC_ZOOM", InputAction::WINDOW_DEC_ZOOM}, {"WINDOW_DEC_ZOOM", InputAction::WINDOW_DEC_ZOOM},
{"TOGGLE_FULLSCREEN", InputAction::TOGGLE_FULLSCREEN}, {"TOGGLE_FULLSCREEN", InputAction::TOGGLE_FULLSCREEN},
{"TOGGLE_VSYNC", InputAction::TOGGLE_VSYNC}, {"TOGGLE_VSYNC", InputAction::TOGGLE_VSYNC},
{"EXIT", InputAction::EXIT}, {"EXIT", InputAction::EXIT},
{"NONE", InputAction::NONE}}; {"NONE", InputAction::NONE}};
const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING = { const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING = {
{SDL_GAMEPAD_BUTTON_WEST, "WEST"}, {SDL_GAMEPAD_BUTTON_WEST, "WEST"},
{SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, {SDL_GAMEPAD_BUTTON_NORTH, "NORTH"},
{SDL_GAMEPAD_BUTTON_EAST, "EAST"}, {SDL_GAMEPAD_BUTTON_EAST, "EAST"},
{SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, {SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"},
{SDL_GAMEPAD_BUTTON_START, "START"}, {SDL_GAMEPAD_BUTTON_START, "START"},
{SDL_GAMEPAD_BUTTON_BACK, "BACK"}, {SDL_GAMEPAD_BUTTON_BACK, "BACK"},
{SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"}, {SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"}, {SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"}, {SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"},
{SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"}, {SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"},
{SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"}, {SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"},
{SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"}, {SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"},
{static_cast<SDL_GamepadButton>(100), "L2_AS_BUTTON"}, {static_cast<SDL_GamepadButton>(100), "L2_AS_BUTTON"},
{static_cast<SDL_GamepadButton>(101), "R2_AS_BUTTON"}}; {static_cast<SDL_GamepadButton>(101), "R2_AS_BUTTON"}};
const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON = { const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON = {
{"WEST", SDL_GAMEPAD_BUTTON_WEST}, {"WEST", SDL_GAMEPAD_BUTTON_WEST},
{"NORTH", SDL_GAMEPAD_BUTTON_NORTH}, {"NORTH", SDL_GAMEPAD_BUTTON_NORTH},
{"EAST", SDL_GAMEPAD_BUTTON_EAST}, {"EAST", SDL_GAMEPAD_BUTTON_EAST},
{"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH}, {"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH},
{"START", SDL_GAMEPAD_BUTTON_START}, {"START", SDL_GAMEPAD_BUTTON_START},
{"BACK", SDL_GAMEPAD_BUTTON_BACK}, {"BACK", SDL_GAMEPAD_BUTTON_BACK},
{"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, {"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
{"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}, {"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
{"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP}, {"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP},
{"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN}, {"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN},
{"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT}, {"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT},
{"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT}, {"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
{"L2_AS_BUTTON", static_cast<SDL_GamepadButton>(100)}, {"L2_AS_BUTTON", static_cast<SDL_GamepadButton>(100)},
{"R2_AS_BUTTON", static_cast<SDL_GamepadButton>(101)}}; {"R2_AS_BUTTON", static_cast<SDL_GamepadButton>(101)}};

View File

@@ -8,23 +8,23 @@
// --- Enums --- // --- Enums ---
enum class InputAction : int { // Acciones de entrada posibles en el juego enum class InputAction : int { // Acciones de entrada posibles en el juego
// Inputs de juego (movimiento y acción) // Inputs de juego (movimiento y acción)
LEFT, // Rotar izquierda LEFT, // Rotar izquierda
RIGHT, // Rotar derecha RIGHT, // Rotar derecha
THRUST, // Acelerar THRUST, // Acelerar
SHOOT, // Disparar SHOOT, // Disparar
START, // Empezar partida START, // Empezar partida
// Inputs de sistema (globales) // Inputs de sistema (globales)
WINDOW_INC_ZOOM, // F2 WINDOW_INC_ZOOM, // F2
WINDOW_DEC_ZOOM, // F1 WINDOW_DEC_ZOOM, // F1
TOGGLE_FULLSCREEN, // F3 TOGGLE_FULLSCREEN, // F3
TOGGLE_VSYNC, // F4 TOGGLE_VSYNC, // F4
EXIT, // ESC EXIT, // ESC
// Input obligatorio // Input obligatorio
NONE, NONE,
SIZE, SIZE,
}; };
// --- Variables --- // --- Variables ---
@@ -36,7 +36,6 @@ extern const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON
// --- Constantes --- // --- Constantes ---
// Physical arcade buttons (excludes directional controls LEFT/RIGHT) // Physical arcade buttons (excludes directional controls LEFT/RIGHT)
static constexpr std::array<InputAction, 3> ARCADE_BUTTONS = { static constexpr std::array<InputAction, 3> ARCADE_BUTTONS = {
InputAction::SHOOT, InputAction::SHOOT,
InputAction::THRUST, InputAction::THRUST,
InputAction::START InputAction::START};
};

View File

@@ -13,8 +13,8 @@
class SDLManager { class SDLManager {
public: public:
SDLManager(); // Constructor per defecte (usa Defaults::) SDLManager(); // Constructor per defecte (usa Defaults::)
SDLManager(int width, int height, bool fullscreen); // Constructor amb configuració SDLManager(int width, int height, bool fullscreen); // Constructor amb configuració
~SDLManager(); ~SDLManager();
// No permetre còpia ni assignació // No permetre còpia ni assignació
@@ -22,11 +22,11 @@ class SDLManager {
SDLManager& operator=(const SDLManager&) = delete; SDLManager& operator=(const SDLManager&) = delete;
// [NUEVO] Gestió de finestra dinàmica // [NUEVO] Gestió de finestra dinàmica
void increaseWindowSize(); // F2: +100px void increaseWindowSize(); // F2: +100px
void decreaseWindowSize(); // F1: -100px void decreaseWindowSize(); // F1: -100px
void toggleFullscreen(); // F3 void toggleFullscreen(); // F3
void toggleVSync(); // F4 void toggleVSync(); // F4
bool handleWindowEvent(const SDL_Event& event); // Per a SDL_EVENT_WINDOW_RESIZED bool handleWindowEvent(const SDL_Event& event); // Per a SDL_EVENT_WINDOW_RESIZED
// Funcions principals (renderitzat) // Funcions principals (renderitzat)
void neteja(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0); void neteja(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0);

View File

@@ -14,16 +14,22 @@ namespace Rendering {
// Estructura per rotacions 3D (pitch, yaw, roll) // Estructura per rotacions 3D (pitch, yaw, roll)
struct Rotation3D { struct Rotation3D {
float pitch; // Rotació eix X (cabeceo arriba/baix) float pitch; // Rotació eix X (cabeceo arriba/baix)
float yaw; // Rotació eix Y (guiñada esquerra/dreta) float yaw; // Rotació eix Y (guiñada esquerra/dreta)
float roll; // Rotació eix Z (alabeo lateral) float roll; // Rotació eix Z (alabeo lateral)
Rotation3D() : pitch(0.0f), yaw(0.0f), roll(0.0f) {} Rotation3D()
Rotation3D(float p, float y, float r) : pitch(p), yaw(y), roll(r) {} : pitch(0.0f),
yaw(0.0f),
roll(0.0f) {}
Rotation3D(float p, float y, float r)
: pitch(p),
yaw(y),
roll(r) {}
bool has_rotation() const { bool has_rotation() const {
return pitch != 0.0f || yaw != 0.0f || roll != 0.0f; return pitch != 0.0f || yaw != 0.0f || roll != 0.0f;
} }
}; };
// Renderitzar forma amb transformacions // Renderitzar forma amb transformacions

View File

@@ -3,11 +3,11 @@
#include "resource_helper.hpp" #include "resource_helper.hpp"
#include "resource_loader.hpp"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include "resource_loader.hpp"
namespace Resource { namespace Resource {
namespace Helper { namespace Helper {

View File

@@ -4,50 +4,50 @@
#pragma once #pragma once
#include "resource_pack.hpp"
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "resource_pack.hpp"
namespace Resource { namespace Resource {
// Singleton per gestionar la càrrega de recursos // Singleton per gestionar la càrrega de recursos
class Loader { class Loader {
public: public:
// Singleton // Singleton
static Loader& get(); static Loader& get();
// Inicialització // Inicialització
bool initialize(const std::string& pack_file, bool enable_fallback); bool initialize(const std::string& pack_file, bool enable_fallback);
// Càrrega de recursos // Càrrega de recursos
std::vector<uint8_t> loadResource(const std::string& filename); std::vector<uint8_t> loadResource(const std::string& filename);
bool resourceExists(const std::string& filename); bool resourceExists(const std::string& filename);
// Validació // Validació
bool validatePack(); bool validatePack();
bool isPackLoaded() const; bool isPackLoaded() const;
// Estat // Estat
void setBasePath(const std::string& path); void setBasePath(const std::string& path);
std::string getBasePath() const; std::string getBasePath() const;
private: private:
Loader() = default; Loader() = default;
~Loader() = default; ~Loader() = default;
// No es pot copiar ni moure // No es pot copiar ni moure
Loader(const Loader&) = delete; Loader(const Loader&) = delete;
Loader& operator=(const Loader&) = delete; Loader& operator=(const Loader&) = delete;
// Dades // Dades
std::unique_ptr<Pack> pack_; std::unique_ptr<Pack> pack_;
bool fallback_enabled_ = false; bool fallback_enabled_ = false;
std::string base_path_; std::string base_path_;
// Funcions auxiliars // Funcions auxiliars
std::vector<uint8_t> loadFromFilesystem(const std::string& filename); std::vector<uint8_t> loadFromFilesystem(const std::string& filename);
}; };
} // namespace Resource } // namespace Resource

View File

@@ -13,55 +13,55 @@ namespace Resource {
// Capçalera del fitxer de paquet // Capçalera del fitxer de paquet
struct PackHeader { struct PackHeader {
char magic[4]; // "ORNI" char magic[4]; // "ORNI"
uint32_t version; // Versió del format (1) uint32_t version; // Versió del format (1)
}; };
// Entrada de recurs dins el paquet // Entrada de recurs dins el paquet
struct ResourceEntry { struct ResourceEntry {
std::string filename; // Nom del recurs (amb barres normals) std::string filename; // Nom del recurs (amb barres normals)
uint64_t offset; // Posició dins el bloc de dades uint64_t offset; // Posició dins el bloc de dades
uint64_t size; // Mida en bytes uint64_t size; // Mida en bytes
uint32_t checksum; // Checksum CRC32 per verificació uint32_t checksum; // Checksum CRC32 per verificació
}; };
// Classe principal per gestionar paquets de recursos // Classe principal per gestionar paquets de recursos
class Pack { class Pack {
public: public:
Pack() = default; Pack() = default;
~Pack() = default; ~Pack() = default;
// Afegir fitxers al paquet // Afegir fitxers al paquet
bool addFile(const std::string& filepath, const std::string& pack_name); bool addFile(const std::string& filepath, const std::string& pack_name);
bool addDirectory(const std::string& dir_path, const std::string& base_path = ""); bool addDirectory(const std::string& dir_path, const std::string& base_path = "");
// Guardar i carregar paquets // Guardar i carregar paquets
bool savePack(const std::string& pack_file); bool savePack(const std::string& pack_file);
bool loadPack(const std::string& pack_file); bool loadPack(const std::string& pack_file);
// Accés a recursos // Accés a recursos
std::vector<uint8_t> getResource(const std::string& filename); std::vector<uint8_t> getResource(const std::string& filename);
bool hasResource(const std::string& filename) const; bool hasResource(const std::string& filename) const;
std::vector<std::string> getResourceList() const; std::vector<std::string> getResourceList() const;
// Validació // Validació
bool validatePack() const; bool validatePack() const;
private: private:
// Constants // Constants
static constexpr const char* MAGIC_HEADER = "ORNI"; static constexpr const char* MAGIC_HEADER = "ORNI";
static constexpr uint32_t VERSION = 1; static constexpr uint32_t VERSION = 1;
static constexpr const char* DEFAULT_ENCRYPT_KEY = "ORNI_RESOURCES_2025"; static constexpr const char* DEFAULT_ENCRYPT_KEY = "ORNI_RESOURCES_2025";
// Dades del paquet // Dades del paquet
std::unordered_map<std::string, ResourceEntry> resources_; std::unordered_map<std::string, ResourceEntry> resources_;
std::vector<uint8_t> data_; std::vector<uint8_t> data_;
// Funcions auxiliars // Funcions auxiliars
std::vector<uint8_t> readFile(const std::string& filepath); std::vector<uint8_t> readFile(const std::string& filepath);
uint32_t calculateChecksum(const std::vector<uint8_t>& data) const; uint32_t calculateChecksum(const std::vector<uint8_t>& data) const;
void encryptData(std::vector<uint8_t>& data, const std::string& key); void encryptData(std::vector<uint8_t>& data, const std::string& key);
void decryptData(std::vector<uint8_t>& data, const std::string& key); void decryptData(std::vector<uint8_t>& data, const std::string& key);
}; };
} // namespace Resource } // namespace Resource

View File

@@ -10,70 +10,70 @@ namespace GestorEscenes {
// Context de transició entre escenes // Context de transició entre escenes
// Conté l'escena destinació i opcions específiques per aquella escena // Conté l'escena destinació i opcions específiques per aquella escena
class ContextEscenes { class ContextEscenes {
public: public:
// Tipus d'escena del joc // Tipus d'escena del joc
enum class Escena { enum class Escena {
LOGO, // Pantalla d'inici (logo JAILGAMES) LOGO, // Pantalla d'inici (logo JAILGAMES)
TITOL, // Pantalla de títol amb menú TITOL, // Pantalla de títol amb menú
JOC, // Joc principal (Asteroids) JOC, // Joc principal (Asteroids)
EIXIR // Sortir del programa EIXIR // Sortir del programa
}; };
// Opcions específiques per a cada escena // Opcions específiques per a cada escena
enum class Opcio { enum class Opcio {
NONE, // Sense opcions especials (comportament per defecte) NONE, // Sense opcions especials (comportament per defecte)
JUMP_TO_TITLE_MAIN, // TITOL: Saltar directament a MAIN (starfield instantani) JUMP_TO_TITLE_MAIN, // TITOL: Saltar directament a MAIN (starfield instantani)
// MODE_DEMO, // JOC: Mode demostració amb IA (futur) // MODE_DEMO, // JOC: Mode demostració amb IA (futur)
}; };
// Constructor inicial amb escena LOGO i sense opcions // Constructor inicial amb escena LOGO i sense opcions
ContextEscenes() ContextEscenes()
: escena_desti_(Escena::LOGO), : escena_desti_(Escena::LOGO),
opcio_(Opcio::NONE) {} opcio_(Opcio::NONE) {}
// Canviar escena amb opció específica // Canviar escena amb opció específica
void canviar_escena(Escena nova_escena, Opcio opcio = Opcio::NONE) { void canviar_escena(Escena nova_escena, Opcio opcio = Opcio::NONE) {
escena_desti_ = nova_escena; escena_desti_ = nova_escena;
opcio_ = opcio; opcio_ = opcio;
} }
// Consultar escena destinació // Consultar escena destinació
[[nodiscard]] auto escena_desti() const -> Escena { [[nodiscard]] auto escena_desti() const -> Escena {
return escena_desti_; return escena_desti_;
} }
// Consultar opció actual // Consultar opció actual
[[nodiscard]] auto opcio() const -> Opcio { [[nodiscard]] auto opcio() const -> Opcio {
return opcio_; return opcio_;
} }
// Consumir opció (retorna valor i reseteja a NONE) // Consumir opció (retorna valor i reseteja a NONE)
// Utilitzar quan l'escena processa l'opció // Utilitzar quan l'escena processa l'opció
[[nodiscard]] auto consumir_opcio() -> Opcio { [[nodiscard]] auto consumir_opcio() -> Opcio {
Opcio valor = opcio_; Opcio valor = opcio_;
opcio_ = Opcio::NONE; opcio_ = Opcio::NONE;
return valor; return valor;
} }
// Reset opció a NONE (sense retornar valor) // Reset opció a NONE (sense retornar valor)
void reset_opcio() { void reset_opcio() {
opcio_ = Opcio::NONE; opcio_ = Opcio::NONE;
} }
// Configurar partida abans de transicionar a JOC // Configurar partida abans de transicionar a JOC
void set_config_partida(const GameConfig::ConfigPartida& config) { void set_config_partida(const GameConfig::ConfigPartida& config) {
config_partida_ = config; config_partida_ = config;
} }
// Obtenir configuració de partida (consumit per EscenaJoc) // Obtenir configuració de partida (consumit per EscenaJoc)
[[nodiscard]] const GameConfig::ConfigPartida& get_config_partida() const { [[nodiscard]] const GameConfig::ConfigPartida& get_config_partida() const {
return config_partida_; return config_partida_;
} }
private: private:
Escena escena_desti_; // Escena a la qual transicionar Escena escena_desti_; // Escena a la qual transicionar
Opcio opcio_; // Opció específica per l'escena Opcio opcio_; // Opció específica per l'escena
GameConfig::ConfigPartida config_partida_; // Configuració de partida (jugadors actius, mode) GameConfig::ConfigPartida config_partida_; // Configuració de partida (jugadors actius, mode)
}; };
// Variable global inline per gestionar l'escena actual (backward compatibility) // Variable global inline per gestionar l'escena actual (backward compatibility)

View File

@@ -7,6 +7,7 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include "context_escenes.hpp"
#include "core/audio/audio.hpp" #include "core/audio/audio.hpp"
#include "core/audio/audio_cache.hpp" #include "core/audio/audio_cache.hpp"
#include "core/defaults.hpp" #include "core/defaults.hpp"
@@ -20,7 +21,6 @@
#include "game/escenes/escena_logo.hpp" #include "game/escenes/escena_logo.hpp"
#include "game/escenes/escena_titol.hpp" #include "game/escenes/escena_titol.hpp"
#include "game/options.hpp" #include "game/options.hpp"
#include "context_escenes.hpp"
#include "project.h" #include "project.h"
#ifndef _WIN32 #ifndef _WIN32
@@ -240,7 +240,7 @@ auto Director::run() -> int {
// Crear context d'escenes // Crear context d'escenes
ContextEscenes context; ContextEscenes context;
#ifdef _DEBUG #ifdef _DEBUG
context.canviar_escena(Escena::JOC); context.canviar_escena(Escena::TITOL);
#else #else
context.canviar_escena(Escena::LOGO); context.canviar_escena(Escena::LOGO);
#endif #endif

View File

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

View File

@@ -5,10 +5,10 @@
#include <iostream> #include <iostream>
#include "context_escenes.hpp"
#include "core/input/input.hpp" #include "core/input/input.hpp"
#include "core/input/mouse.hpp" #include "core/input/mouse.hpp"
#include "core/rendering/sdl_manager.hpp" #include "core/rendering/sdl_manager.hpp"
#include "context_escenes.hpp"
// Using declarations per simplificar el codi // Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes; using GestorEscenes::ContextEscenes;

View File

@@ -8,7 +8,9 @@
// Forward declarations // Forward declarations
class SDLManager; class SDLManager;
namespace GestorEscenes { class ContextEscenes; } namespace GestorEscenes {
class ContextEscenes;
}
namespace GlobalEvents { namespace GlobalEvents {
// Processa events globals (F1/F2/F3/ESC/QUIT) // Processa events globals (F1/F2/F3/ESC/QUIT)

View File

@@ -18,9 +18,9 @@ struct Debris {
float acceleracio; // Acceleració negativa (fricció) en px/s² float acceleracio; // Acceleració negativa (fricció) en px/s²
// Rotació // Rotació
float angle_rotacio; // Angle de rotació acumulat (radians) float angle_rotacio; // Angle de rotació acumulat (radians)
float velocitat_rot; // Velocitat de rotació de TRAYECTORIA (rad/s) float velocitat_rot; // Velocitat de rotació de TRAYECTORIA (rad/s)
float velocitat_rot_visual; // Velocitat de rotació VISUAL del segment (rad/s) float velocitat_rot_visual; // Velocitat de rotació VISUAL del segment (rad/s)
// Estat de vida // Estat de vida
float temps_vida; // Temps transcorregut (segons) float temps_vida; // Temps transcorregut (segons)

View File

@@ -8,7 +8,8 @@
namespace Effects { namespace Effects {
GestorPuntuacioFlotant::GestorPuntuacioFlotant(SDL_Renderer* renderer) GestorPuntuacioFlotant::GestorPuntuacioFlotant(SDL_Renderer* renderer)
: renderer_(renderer), text_(renderer) { : renderer_(renderer),
text_(renderer) {
// Inicialitzar tots els slots com inactius // Inicialitzar tots els slots com inactius
for (auto& pf : pool_) { for (auto& pf : pool_) {
pf.actiu = false; pf.actiu = false;
@@ -25,7 +26,7 @@ void GestorPuntuacioFlotant::crear(int punts, const Punt& posicio) {
pf->text = std::to_string(punts); pf->text = std::to_string(punts);
pf->posicio = posicio; pf->posicio = posicio;
pf->velocitat = {Defaults::FloatingScore::VELOCITY_X, pf->velocitat = {Defaults::FloatingScore::VELOCITY_X,
Defaults::FloatingScore::VELOCITY_Y}; Defaults::FloatingScore::VELOCITY_Y};
pf->temps_vida = 0.0f; pf->temps_vida = 0.0f;
pf->temps_max = Defaults::FloatingScore::LIFETIME; pf->temps_max = Defaults::FloatingScore::LIFETIME;
pf->brightness = 1.0f; pf->brightness = 1.0f;

View File

@@ -17,38 +17,38 @@ namespace Effects {
// Gestor de números de puntuació flotants // Gestor de números de puntuació flotants
// Manté un pool de PuntuacioFlotant i gestiona el seu cicle de vida // Manté un pool de PuntuacioFlotant i gestiona el seu cicle de vida
class GestorPuntuacioFlotant { class GestorPuntuacioFlotant {
public: public:
explicit GestorPuntuacioFlotant(SDL_Renderer* renderer); explicit GestorPuntuacioFlotant(SDL_Renderer* renderer);
// Crear número flotant // Crear número flotant
// - punts: valor numèric (100, 150, 200) // - punts: valor numèric (100, 150, 200)
// - posicio: on apareix (normalment centre d'enemic destruït) // - posicio: on apareix (normalment centre d'enemic destruït)
void crear(int punts, const Punt& posicio); void crear(int punts, const Punt& posicio);
// Actualitzar tots els números actius // Actualitzar tots els números actius
void actualitzar(float delta_time); void actualitzar(float delta_time);
// Dibuixar tots els números actius // Dibuixar tots els números actius
void dibuixar(); void dibuixar();
// Reiniciar tots (neteja) // Reiniciar tots (neteja)
void reiniciar(); void reiniciar();
// Obtenir número actius (debug) // Obtenir número actius (debug)
int get_num_actius() const; int get_num_actius() const;
private: private:
SDL_Renderer* renderer_; SDL_Renderer* renderer_;
Graphics::VectorText text_; // Sistema de text vectorial Graphics::VectorText text_; // Sistema de text vectorial
// Pool de números flotants (màxim concurrent) // Pool de números flotants (màxim concurrent)
// Màxim 15 enemics simultanis = màxim 15 números // Màxim 15 enemics simultanis = màxim 15 números
static constexpr int MAX_PUNTUACIONS = static constexpr int MAX_PUNTUACIONS =
Defaults::FloatingScore::MAX_CONCURRENT; Defaults::FloatingScore::MAX_CONCURRENT;
std::array<PuntuacioFlotant, MAX_PUNTUACIONS> pool_; std::array<PuntuacioFlotant, MAX_PUNTUACIONS> pool_;
// Trobar primer slot inactiu // Trobar primer slot inactiu
PuntuacioFlotant* trobar_slot_lliure(); PuntuacioFlotant* trobar_slot_lliure();
}; };
} // namespace Effects } // namespace Effects

View File

@@ -12,22 +12,22 @@ namespace Effects {
// PuntuacioFlotant: text animat que mostra punts guanyats // PuntuacioFlotant: text animat que mostra punts guanyats
// S'activa quan es destrueix un enemic i s'esvaeix després d'un temps // S'activa quan es destrueix un enemic i s'esvaeix després d'un temps
struct PuntuacioFlotant { struct PuntuacioFlotant {
// Text a mostrar (e.g., "100", "150", "200") // Text a mostrar (e.g., "100", "150", "200")
std::string text; std::string text;
// Posició actual (coordenades mundials) // Posició actual (coordenades mundials)
Punt posicio; Punt posicio;
// Animació de moviment // Animació de moviment
Punt velocitat; // px/s (normalment cap amunt: {0.0f, -30.0f}) Punt velocitat; // px/s (normalment cap amunt: {0.0f, -30.0f})
// Animació de fade // Animació de fade
float temps_vida; // Temps transcorregut (segons) float temps_vida; // Temps transcorregut (segons)
float temps_max; // Temps de vida màxim (segons) float temps_max; // Temps de vida màxim (segons)
float brightness; // Brillantor calculada (0.0-1.0) float brightness; // Brillantor calculada (0.0-1.0)
// Estat // Estat
bool actiu; bool actiu;
}; };
} // namespace Effects } // namespace Effects

View File

@@ -25,7 +25,7 @@ Enemic::Enemic(SDL_Renderer* renderer)
tipus_(TipusEnemic::PENTAGON), tipus_(TipusEnemic::PENTAGON),
tracking_timer_(0.0f), tracking_timer_(0.0f),
ship_position_(nullptr), ship_position_(nullptr),
tracking_strength_(0.5f), // Default tracking strength tracking_strength_(0.5f), // Default tracking strength
timer_invulnerabilitat_(0.0f) { // Start vulnerable timer_invulnerabilitat_(0.0f) { // Start vulnerable
// [NUEVO] Forma es carrega a inicialitzar() segons el tipus // [NUEVO] Forma es carrega a inicialitzar() segons el tipus
// Constructor no carrega forma per permetre tipus diferents // Constructor no carrega forma per permetre tipus diferents
@@ -126,7 +126,7 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Punt* ship_pos) {
// [NEW] Inicialitzar invulnerabilitat // [NEW] Inicialitzar invulnerabilitat
timer_invulnerabilitat_ = Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; // 3.0s timer_invulnerabilitat_ = Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; // 3.0s
brightness_ = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; // 0.3f brightness_ = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; // 0.3f
// Activar // Activar
esta_ = true; esta_ = true;
@@ -144,7 +144,7 @@ void Enemic::actualitzar(float delta_time) {
// [NEW] Update brightness with LERP during invulnerability // [NEW] Update brightness with LERP during invulnerability
float t_inv = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; float t_inv = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION;
float t = 1.0f - t_inv; // 0.0 → 1.0 float t = 1.0f - t_inv; // 0.0 → 1.0
float smooth_t = t * t * (3.0f - 2.0f * t); // smoothstep float smooth_t = t * t * (3.0f - 2.0f * t); // smoothstep
constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START;

View File

@@ -54,8 +54,7 @@ class Enemic {
Punt get_velocitat_vector() const { Punt get_velocitat_vector() const {
return { return {
velocitat_ * std::cos(angle_ - Constants::PI / 2.0f), velocitat_ * std::cos(angle_ - Constants::PI / 2.0f),
velocitat_ * std::sin(angle_ - Constants::PI / 2.0f) velocitat_ * std::sin(angle_ - Constants::PI / 2.0f)};
};
} }
// Set ship position reference for tracking behavior // Set ship position reference for tracking behavior
@@ -68,7 +67,10 @@ class Enemic {
// [NEW] Setters for difficulty multipliers (stage system) // [NEW] Setters for difficulty multipliers (stage system)
void set_velocity(float vel) { velocitat_ = vel; } void set_velocity(float vel) { velocitat_ = vel; }
void set_rotation(float rot) { drotacio_ = rot; animacio_.drotacio_base = rot; } void set_rotation(float rot) {
drotacio_ = rot;
animacio_.drotacio_base = rot;
}
void set_tracking_strength(float strength); void set_tracking_strength(float strength);
// [NEW] Invulnerability queries // [NEW] Invulnerability queries

View File

@@ -140,7 +140,7 @@ void Nau::dibuixar() const {
if (es_invulnerable()) { if (es_invulnerable()) {
// Calcular ciclo de parpadeo // Calcular ciclo de parpadeo
float blink_cycle = Defaults::Ship::BLINK_VISIBLE_TIME + float blink_cycle = Defaults::Ship::BLINK_VISIBLE_TIME +
Defaults::Ship::BLINK_INVISIBLE_TIME; Defaults::Ship::BLINK_INVISIBLE_TIME;
float time_in_cycle = std::fmod(invulnerable_timer_, blink_cycle); float time_in_cycle = std::fmod(invulnerable_timer_, blink_cycle);
// Si estamos en fase invisible, no dibujar // Si estamos en fase invisible, no dibujar

View File

@@ -33,8 +33,7 @@ class Nau {
Punt get_velocitat_vector() const { Punt get_velocitat_vector() const {
return { return {
velocitat_ * std::cos(angle_ - Constants::PI / 2.0f), velocitat_ * std::cos(angle_ - Constants::PI / 2.0f),
velocitat_ * std::sin(angle_ - Constants::PI / 2.0f) velocitat_ * std::sin(angle_ - Constants::PI / 2.0f)};
};
} }
// Setters // Setters
@@ -55,7 +54,7 @@ class Nau {
float angle_; // Angle d'orientació float angle_; // Angle d'orientació
float velocitat_; // Velocitat (px/s) float velocitat_; // Velocitat (px/s)
bool esta_tocada_; bool esta_tocada_;
float brightness_; // Factor de brillantor (0.0-1.0) float brightness_; // Factor de brillantor (0.0-1.0)
float invulnerable_timer_; // 0.0f = vulnerable, >0.0f = invulnerable float invulnerable_timer_; // 0.0f = vulnerable, >0.0f = invulnerable
void aplicar_fisica(float delta_time); void aplicar_fisica(float delta_time);

View File

@@ -323,22 +323,19 @@ void EscenaJoc::actualitzar(float delta_time) {
} }
// Calcular global progress (0.0 al inicio → 1.0 al final) // Calcular global progress (0.0 al inicio → 1.0 al final)
float global_progress = 1.0f - (stage_manager_->get_timer_transicio() / float global_progress = 1.0f - (stage_manager_->get_timer_transicio() / Defaults::Game::INIT_HUD_DURATION);
Defaults::Game::INIT_HUD_DURATION);
global_progress = std::min(1.0f, global_progress); global_progress = std::min(1.0f, global_progress);
// [NEW] Calcular progress independiente para cada nave // [NEW] Calcular progress independiente para cada nave
float ship1_progress = calcular_progress_rango( float ship1_progress = calcular_progress_rango(
global_progress, global_progress,
Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP1_RATIO_END Defaults::Game::INIT_HUD_SHIP1_RATIO_END);
);
float ship2_progress = calcular_progress_rango( float ship2_progress = calcular_progress_rango(
global_progress, global_progress,
Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP2_RATIO_END Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
);
// [MODIFICAT] Animar AMBAS naus con sus progress respectivos // [MODIFICAT] Animar AMBAS naus con sus progress respectivos
if (config_partida_.jugador1_actiu && ship1_progress < 1.0f) { if (config_partida_.jugador1_actiu && ship1_progress < 1.0f) {
@@ -501,26 +498,22 @@ void EscenaJoc::dibuixar() {
float rect_progress = calcular_progress_rango( float rect_progress = calcular_progress_rango(
global_progress, global_progress,
Defaults::Game::INIT_HUD_RECT_RATIO_INIT, Defaults::Game::INIT_HUD_RECT_RATIO_INIT,
Defaults::Game::INIT_HUD_RECT_RATIO_END Defaults::Game::INIT_HUD_RECT_RATIO_END);
);
float score_progress = calcular_progress_rango( float score_progress = calcular_progress_rango(
global_progress, global_progress,
Defaults::Game::INIT_HUD_SCORE_RATIO_INIT, Defaults::Game::INIT_HUD_SCORE_RATIO_INIT,
Defaults::Game::INIT_HUD_SCORE_RATIO_END Defaults::Game::INIT_HUD_SCORE_RATIO_END);
);
float ship1_progress = calcular_progress_rango( float ship1_progress = calcular_progress_rango(
global_progress, global_progress,
Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP1_RATIO_END Defaults::Game::INIT_HUD_SHIP1_RATIO_END);
);
float ship2_progress = calcular_progress_rango( float ship2_progress = calcular_progress_rango(
global_progress, global_progress,
Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP2_RATIO_END Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
);
// Dibuixar elements animats // Dibuixar elements animats
if (rect_progress > 0.0f) { if (rect_progress > 0.0f) {
@@ -643,15 +636,15 @@ void EscenaJoc::tocado(uint8_t player_id) {
Punt vel_nau_80 = {vel_nau.x * 0.8f, vel_nau.y * 0.8f}; Punt vel_nau_80 = {vel_nau.x * 0.8f, vel_nau.y * 0.8f};
debris_manager_.explotar( debris_manager_.explotar(
naus_[player_id].get_forma(), // Ship shape (3 lines) naus_[player_id].get_forma(), // Ship shape (3 lines)
ship_pos, // Center position ship_pos, // Center position
ship_angle, // Ship orientation ship_angle, // Ship orientation
1.0f, // Normal scale 1.0f, // Normal scale
Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s
naus_[player_id].get_brightness(), // Heredar brightness naus_[player_id].get_brightness(), // Heredar brightness
vel_nau_80, // Heredar 80% velocitat vel_nau_80, // Heredar 80% velocitat
0.0f, // Nave: trayectorias rectas (sin drotacio) 0.0f, // Nave: trayectorias rectas (sin drotacio)
0.0f // Sin herencia visual (rotación aleatoria) 0.0f // Sin herencia visual (rotación aleatoria)
); );
Audio::get()->playSound(Defaults::Sound::EXPLOSION, Audio::Group::GAME); Audio::get()->playSound(Defaults::Sound::EXPLOSION, Audio::Group::GAME);
@@ -863,7 +856,7 @@ std::string EscenaJoc::construir_marcador() const {
// Nivell (2 dígits) // Nivell (2 dígits)
uint8_t stage_num = stage_manager_->get_stage_actual(); uint8_t stage_num = stage_manager_->get_stage_actual();
std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num) std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num)
: std::to_string(stage_num); : std::to_string(stage_num);
// Puntuació P2 (6 dígits) - mostrar zeros si inactiu // Puntuació P2 (6 dígits) - mostrar zeros si inactiu
std::string score_p2; std::string score_p2;
@@ -946,15 +939,15 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() {
// 2. Crear explosió de fragments // 2. Crear explosió de fragments
Punt vel_enemic = enemic.get_velocitat_vector(); Punt vel_enemic = enemic.get_velocitat_vector();
debris_manager_.explotar( debris_manager_.explotar(
enemic.get_forma(), // Forma vectorial del pentàgon enemic.get_forma(), // Forma vectorial del pentàgon
pos_enemic, // Posició central pos_enemic, // Posició central
0.0f, // Angle (enemic té rotació interna) 0.0f, // Angle (enemic té rotació interna)
1.0f, // Escala normal 1.0f, // Escala normal
VELOCITAT_EXPLOSIO, // 50 px/s (explosió suau) VELOCITAT_EXPLOSIO, // 50 px/s (explosió suau)
enemic.get_brightness(), // Heredar brightness enemic.get_brightness(), // Heredar brightness
vel_enemic, // Heredar velocitat vel_enemic, // Heredar velocitat
enemic.get_drotacio(), // Heredar velocitat angular (trayectorias curvas) enemic.get_drotacio(), // Heredar velocitat angular (trayectorias curvas)
0.0f // Sin herencia visual (rotación aleatoria) 0.0f // Sin herencia visual (rotación aleatoria)
); );
// 3. Desactivar bala // 3. Desactivar bala
@@ -1063,8 +1056,8 @@ void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
// Auto-scale if text exceeds max width // Auto-scale if text exceeds max width
float escala = (text_width_at_base <= max_width) float escala = (text_width_at_base <= max_width)
? escala_base ? escala_base
: max_width / text_width_at_base; : max_width / text_width_at_base;
// Recalculate dimensions with final scale (using FULL message for centering) // Recalculate dimensions with final scale (using FULL message for centering)
float full_text_width = text_.get_text_width(missatge, escala, spacing); float full_text_width = text_.get_text_width(missatge, escala, spacing);
@@ -1099,8 +1092,7 @@ Punt EscenaJoc::obtenir_punt_spawn(uint8_t player_id) const {
return { return {
zona.x + zona.w * x_ratio, zona.x + zona.w * x_ratio,
zona.y + zona.h * Defaults::Game::SPAWN_Y_RATIO zona.y + zona.h * Defaults::Game::SPAWN_Y_RATIO};
};
} }
void EscenaJoc::disparar_bala(uint8_t player_id) { void EscenaJoc::disparar_bala(uint8_t player_id) {

View File

@@ -9,6 +9,7 @@
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <memory>
#include "../constants.hpp" #include "../constants.hpp"
#include "../effects/debris_manager.hpp" #include "../effects/debris_manager.hpp"
@@ -23,8 +24,6 @@
#include "core/system/game_config.hpp" #include "core/system/game_config.hpp"
#include "core/types.hpp" #include "core/types.hpp"
#include <memory>
// Classe principal del joc (escena) // Classe principal del joc (escena)
class EscenaJoc { class EscenaJoc {
public: public:
@@ -57,7 +56,7 @@ class EscenaJoc {
bool game_over_; // Game over state flag bool game_over_; // Game over state flag
float game_over_timer_; // Countdown timer for auto-return (seconds) float game_over_timer_; // Countdown timer for auto-return (seconds)
// Punt punt_spawn_; // DEPRECATED: usar obtenir_punt_spawn(player_id) // Punt punt_spawn_; // DEPRECATED: usar obtenir_punt_spawn(player_id)
Punt punt_mort_; // Death position (for respawn, legacy) Punt punt_mort_; // Death position (for respawn, legacy)
std::array<int, 2> puntuacio_per_jugador_; // [0]=P1, [1]=P2 std::array<int, 2> puntuacio_per_jugador_; // [0]=P1, [1]=P2
// Text vectorial // Text vectorial
@@ -72,20 +71,20 @@ class EscenaJoc {
// Funcions privades // Funcions privades
void tocado(uint8_t player_id); void tocado(uint8_t player_id);
void detectar_col·lisions_bales_enemics(); // Col·lisions bala-enemic void detectar_col·lisions_bales_enemics(); // Col·lisions bala-enemic
void detectar_col·lisio_naus_enemics(); // Ship-enemy collision detection (plural) void detectar_col·lisio_naus_enemics(); // Ship-enemy collision detection (plural)
void dibuixar_marges() const; // Dibuixar vores de la zona de joc void dibuixar_marges() const; // Dibuixar vores de la zona de joc
void dibuixar_marcador(); // Dibuixar marcador de puntuació void dibuixar_marcador(); // Dibuixar marcador de puntuació
void disparar_bala(uint8_t player_id); // Shoot bullet from player void disparar_bala(uint8_t player_id); // Shoot bullet from player
Punt obtenir_punt_spawn(uint8_t player_id) const; // Get spawn position for player Punt obtenir_punt_spawn(uint8_t player_id) const; // Get spawn position for player
// [NEW] Stage system helpers // [NEW] Stage system helpers
void dibuixar_missatge_stage(const std::string& missatge); void dibuixar_missatge_stage(const std::string& missatge);
// [NEW] Funcions d'animació per INIT_HUD // [NEW] Funcions d'animació per INIT_HUD
void dibuixar_marges_animat(float progress) const; // Rectangle amb creixement uniforme void dibuixar_marges_animat(float progress) const; // Rectangle amb creixement uniforme
void dibuixar_marcador_animat(float progress); // Marcador que puja des de baix void dibuixar_marcador_animat(float progress); // Marcador que puja des de baix
Punt calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const; // Posició animada de la nau Punt calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const; // Posició animada de la nau
// [NEW] Función helper del sistema de animación INIT_HUD // [NEW] Función helper del sistema de animación INIT_HUD
float calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const; float calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const;

View File

@@ -220,10 +220,8 @@ void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) {
std::random_device rd; std::random_device rd;
std::mt19937 g(rd()); std::mt19937 g(rd());
std::shuffle(ordre_explosio_.begin(), ordre_explosio_.end(), g); std::shuffle(ordre_explosio_.begin(), ordre_explosio_.end(), g);
} } else if (nou_estat == EstatAnimacio::POST_EXPLOSION) {
else if (nou_estat == EstatAnimacio::POST_EXPLOSION) Audio::get()->playMusic("title.ogg");
{
Audio::get()->playMusic("title.ogg");
} }
std::cout << "[EscenaLogo] Canvi a estat: " << static_cast<int>(nou_estat) std::cout << "[EscenaLogo] Canvi a estat: " << static_cast<int>(nou_estat)
@@ -246,13 +244,13 @@ void EscenaLogo::actualitzar_explosions(float delta_time) {
const auto& lletra = lletres_[index_actual]; const auto& lletra = lletres_[index_actual];
debris_manager_->explotar( debris_manager_->explotar(
lletra.forma, // Forma a explotar lletra.forma, // Forma a explotar
lletra.posicio, // Posició lletra.posicio, // Posició
0.0f, // Angle (sense rotació) 0.0f, // Angle (sense rotació)
ESCALA_FINAL, // Escala (lletres a escala final) ESCALA_FINAL, // Escala (lletres a escala final)
VELOCITAT_EXPLOSIO, // Velocitat base VELOCITAT_EXPLOSIO, // Velocitat base
1.0f, // Brightness màxim (per defecte) 1.0f, // Brightness màxim (per defecte)
{0.0f, 0.0f} // Sense velocitat (per defecte) {0.0f, 0.0f} // Sense velocitat (per defecte)
); );
std::cout << "[EscenaLogo] Explota lletra " << lletra_explosio_index_ << "\n"; std::cout << "[EscenaLogo] Explota lletra " << lletra_explosio_index_ << "\n";

View File

@@ -10,18 +10,18 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "game/effects/debris_manager.hpp"
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/graphics/shape.hpp" #include "core/graphics/shape.hpp"
#include "core/input/input_types.hpp" #include "core/input/input_types.hpp"
#include "core/rendering/sdl_manager.hpp" #include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp" #include "core/system/context_escenes.hpp"
#include "core/types.hpp" #include "core/types.hpp"
#include "game/effects/debris_manager.hpp"
class EscenaLogo { class EscenaLogo {
public: public:
explicit EscenaLogo(SDLManager& sdl, GestorEscenes::ContextEscenes& context); explicit EscenaLogo(SDLManager& sdl, GestorEscenes::ContextEscenes& context);
~EscenaLogo(); // Destructor per aturar sons ~EscenaLogo(); // Destructor per aturar sons
void executar(); // Bucle principal de l'escena void executar(); // Bucle principal de l'escena
private: private:

View File

@@ -140,10 +140,10 @@ void EscenaTitol::inicialitzar_titol() {
float ancho_sin_escalar = max_x - min_x; float ancho_sin_escalar = max_x - min_x;
float altura_sin_escalar = max_y - min_y; float altura_sin_escalar = max_y - min_y;
// Escalar ancho, altura i offset amb ESCALA_TITULO // Escalar ancho, altura i offset amb LOGO_SCALE
float ancho = ancho_sin_escalar * ESCALA_TITULO; float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float altura = altura_sin_escalar * ESCALA_TITULO; float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float offset_centre = (forma->get_centre().x - min_x) * ESCALA_TITULO; float offset_centre = (forma->get_centre().x - min_x) * Defaults::Title::Layout::LOGO_SCALE;
lletres_orni_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre}); lletres_orni_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre});
@@ -159,7 +159,7 @@ void EscenaTitol::inicialitzar_titol() {
for (auto& lletra : lletres_orni_) { for (auto& lletra : lletres_orni_) {
lletra.posicio.x = x_actual + lletra.offset_centre; lletra.posicio.x = x_actual + lletra.offset_centre;
lletra.posicio.y = Y_ORNI; lletra.posicio.y = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS;
x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
} }
@@ -169,7 +169,9 @@ void EscenaTitol::inicialitzar_titol() {
// === Calcular posició Y dinàmica per "ATTACK!" === // === Calcular posició Y dinàmica per "ATTACK!" ===
// Totes les lletres ORNI tenen la mateixa altura, utilitzem la primera // Totes les lletres ORNI tenen la mateixa altura, utilitzem la primera
float altura_orni = lletres_orni_.empty() ? 50.0f : lletres_orni_[0].altura; float altura_orni = lletres_orni_.empty() ? 50.0f : lletres_orni_[0].altura;
y_attack_dinamica_ = Y_ORNI + altura_orni + SEPARACION_LINEAS; float y_orni = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS;
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 << "[EscenaTitol] Altura ORNI: " << altura_orni
<< " px, Y_ATTACK dinàmica: " << y_attack_dinamica_ << " px\n"; << " px, Y_ATTACK dinàmica: " << y_attack_dinamica_ << " px\n";
@@ -212,10 +214,10 @@ void EscenaTitol::inicialitzar_titol() {
float ancho_sin_escalar = max_x - min_x; float ancho_sin_escalar = max_x - min_x;
float altura_sin_escalar = max_y - min_y; float altura_sin_escalar = max_y - min_y;
// Escalar ancho, altura i offset amb ESCALA_TITULO // Escalar ancho, altura i offset amb LOGO_SCALE
float ancho = ancho_sin_escalar * ESCALA_TITULO; float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float altura = altura_sin_escalar * ESCALA_TITULO; float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float offset_centre = (forma->get_centre().x - min_x) * ESCALA_TITULO; float offset_centre = (forma->get_centre().x - min_x) * Defaults::Title::Layout::LOGO_SCALE;
lletres_attack_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre}); lletres_attack_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre});
@@ -326,9 +328,9 @@ void EscenaTitol::actualitzar(float delta_time) {
// Actualitzar naus (quan visibles) // Actualitzar naus (quan visibles)
if (ship_animator_ && if (ship_animator_ &&
(estat_actual_ == EstatTitol::STARFIELD_FADE_IN || (estat_actual_ == EstatTitol::STARFIELD_FADE_IN ||
estat_actual_ == EstatTitol::STARFIELD || estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) { estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
ship_animator_->actualitzar(delta_time); ship_animator_->actualitzar(delta_time);
} }
@@ -346,7 +348,7 @@ void EscenaTitol::actualitzar(float delta_time) {
// Transició a STARFIELD quan el fade es completa // Transició a STARFIELD quan el fade es completa
if (temps_acumulat_ >= DURACIO_FADE_IN) { if (temps_acumulat_ >= DURACIO_FADE_IN) {
estat_actual_ = EstatTitol::STARFIELD; estat_actual_ = EstatTitol::STARFIELD;
temps_acumulat_ = 0.0f; // Reset timer per al següent estat temps_acumulat_ = 0.0f; // Reset timer per al següent estat
starfield_->set_brightness(BRIGHTNESS_STARFIELD); // Assegurar valor final starfield_->set_brightness(BRIGHTNESS_STARFIELD); // Assegurar valor final
} }
break; break;
@@ -538,9 +540,9 @@ void EscenaTitol::dibuixar() {
// Dibuixar naus (després starfield, abans logo) // Dibuixar naus (després starfield, abans logo)
if (ship_animator_ && if (ship_animator_ &&
(estat_actual_ == EstatTitol::STARFIELD_FADE_IN || (estat_actual_ == EstatTitol::STARFIELD_FADE_IN ||
estat_actual_ == EstatTitol::STARFIELD || estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) { estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
ship_animator_->dibuixar(); ship_animator_->dibuixar();
} }
@@ -580,7 +582,7 @@ void EscenaTitol::dibuixar() {
lletres_orni_[i].forma, lletres_orni_[i].forma,
pos_shadow, pos_shadow,
0.0f, 0.0f,
ESCALA_TITULO, Defaults::Title::Layout::LOGO_SCALE,
true, true,
1.0f, // progress = 1.0 (totalment visible) 1.0f, // progress = 1.0 (totalment visible)
SHADOW_BRIGHTNESS // brightness = 0.4 (brillantor reduïda) SHADOW_BRIGHTNESS // brightness = 0.4 (brillantor reduïda)
@@ -598,7 +600,7 @@ void EscenaTitol::dibuixar() {
lletres_attack_[i].forma, lletres_attack_[i].forma,
pos_shadow, pos_shadow,
0.0f, 0.0f,
ESCALA_TITULO, Defaults::Title::Layout::LOGO_SCALE,
true, true,
1.0f, // progress = 1.0 (totalment visible) 1.0f, // progress = 1.0 (totalment visible)
SHADOW_BRIGHTNESS); SHADOW_BRIGHTNESS);
@@ -614,7 +616,7 @@ void EscenaTitol::dibuixar() {
lletra.forma, lletra.forma,
lletra.posicio, lletra.posicio,
0.0f, 0.0f,
ESCALA_TITULO, Defaults::Title::Layout::LOGO_SCALE,
true, true,
1.0f // Brillantor completa 1.0f // Brillantor completa
); );
@@ -627,7 +629,7 @@ void EscenaTitol::dibuixar() {
lletra.forma, lletra.forma,
lletra.posicio, lletra.posicio,
0.0f, 0.0f,
ESCALA_TITULO, Defaults::Title::Layout::LOGO_SCALE,
true, true,
1.0f // Brillantor completa 1.0f // Brillantor completa
); );
@@ -637,7 +639,7 @@ void EscenaTitol::dibuixar() {
// En estat MAIN: sempre visible // En estat MAIN: sempre visible
// En estat TRANSITION: parpellejant (blink amb sinusoide) // En estat TRANSITION: parpellejant (blink amb sinusoide)
const float spacing = 2.0f; // Espai entre caràcters (usat també per copyright) const float spacing = Defaults::Title::Layout::TEXT_SPACING;
bool mostrar_text = true; bool mostrar_text = true;
if (estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) { if (estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) {
@@ -648,34 +650,46 @@ void EscenaTitol::dibuixar() {
if (mostrar_text) { if (mostrar_text) {
const std::string main_text = "PRESS START TO PLAY"; const std::string main_text = "PRESS START TO PLAY";
const float escala_main = 1.0f; const float escala_main = Defaults::Title::Layout::PRESS_START_SCALE;
float text_width = text_.get_text_width(main_text, escala_main, spacing); float text_width = text_.get_text_width(main_text, escala_main, spacing);
float x_center = (Defaults::Game::WIDTH - text_width) / 2.0f; float x_center = (Defaults::Game::WIDTH - text_width) / 2.0f;
float altura_attack = lletres_attack_.empty() ? 50.0f : lletres_attack_[0].altura; float y_center = Defaults::Game::HEIGHT * Defaults::Title::Layout::PRESS_START_POS;
float y_center = y_attack_dinamica_ + altura_attack + 70.0f;
text_.render(main_text, Punt{x_center, y_center}, escala_main, spacing); text_.render(main_text, Punt{x_center, y_center}, escala_main, spacing);
} }
// === Copyright a la part inferior (centrat horitzontalment) === // === Copyright a la part inferior (centrat horitzontalment, dues línies) ===
// Convert to uppercase since VectorText only supports A-Z const float escala_copy = Defaults::Title::Layout::COPYRIGHT_SCALE;
std::string copyright = Project::COPYRIGHT; const float copy_height = text_.get_text_height(escala_copy);
for (char& c : copyright) { const float line_spacing = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT_LINE_SPACING;
if (c >= 'a' && c <= 'z') {
c = c - 32; // Convert to uppercase // Línea 1: Original (© 1999 Visente i Sergi)
} std::string copyright_original = Project::COPYRIGHT_ORIGINAL;
for (char& c : copyright_original) {
if (c >= 'a' && c <= 'z') c = c - 32; // Uppercase
} }
const float escala_copy = 0.6f;
float copy_width = text_.get_text_width(copyright, escala_copy, spacing); // Línea 2: Port (© 2025 jaildesigner)
float copy_height = text_.get_text_height(escala_copy); std::string copyright_port = Project::COPYRIGHT_PORT;
for (char& c : copyright_port) {
if (c >= 'a' && c <= 'z') c = c - 32; // Uppercase
}
float x_copy = (Defaults::Game::WIDTH - copy_width) / 2.0f; // Calcular posicions (anclatge des del top + separació)
float y_copy = Defaults::Game::HEIGHT - copy_height - 20.0f; // 20px des del fons float y_line1 = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS;
float y_line2 = y_line1 + copy_height + line_spacing; // Línea 2 debajo de línea 1
text_.render(copyright, Punt{x_copy, y_copy}, escala_copy, spacing); // Renderitzar línea 1 (original)
float width_line1 = text_.get_text_width(copyright_original, escala_copy, spacing);
float x_line1 = (Defaults::Game::WIDTH - width_line1) / 2.0f;
text_.render(copyright_original, Punt{x_line1, y_line1}, escala_copy, spacing);
// Renderitzar línea 2 (port)
float width_line2 = text_.get_text_width(copyright_port, escala_copy, spacing);
float x_line2 = (Defaults::Game::WIDTH - width_line2) / 2.0f;
text_.render(copyright_port, Punt{x_line2, y_line2}, escala_copy, spacing);
} }
} }

View File

@@ -23,8 +23,7 @@
// Botones para INICIAR PARTIDA desde MAIN (solo START) // Botones para INICIAR PARTIDA desde MAIN (solo START)
static constexpr std::array<InputAction, 1> START_GAME_BUTTONS = { static constexpr std::array<InputAction, 1> START_GAME_BUTTONS = {
InputAction::START InputAction::START};
};
class EscenaTitol { class EscenaTitol {
public: public:
@@ -53,12 +52,12 @@ class EscenaTitol {
SDLManager& sdl_; SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_; GestorEscenes::ContextEscenes& context_;
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
Graphics::VectorText text_; // Sistema de text vectorial Graphics::VectorText text_; // Sistema de text vectorial
std::unique_ptr<Graphics::Starfield> starfield_; // Camp d'estrelles de fons std::unique_ptr<Graphics::Starfield> starfield_; // Camp d'estrelles de fons
std::unique_ptr<Title::ShipAnimator> ship_animator_; // Naus 3D flotants std::unique_ptr<Title::ShipAnimator> ship_animator_; // Naus 3D flotants
EstatTitol estat_actual_; // Estat actual de la màquina EstatTitol estat_actual_; // Estat actual de la màquina
float temps_acumulat_; // Temps acumulat per l'estat INIT float temps_acumulat_; // Temps acumulat per l'estat INIT
// Lletres del títol "ORNI ATTACK!" // Lletres del títol "ORNI ATTACK!"
std::vector<LetraLogo> lletres_orni_; // Lletres de "ORNI" (línia 1) std::vector<LetraLogo> lletres_orni_; // Lletres de "ORNI" (línia 1)
@@ -80,10 +79,7 @@ class EscenaTitol {
static constexpr float DURACIO_FADE_IN = 3.0f; // Duració del fade-in del starfield (1.5 segons) static constexpr float DURACIO_FADE_IN = 3.0f; // Duració del fade-in del starfield (1.5 segons)
static constexpr float DURACIO_INIT = 4.0f; // Duració de l'estat INIT (2 segons) static constexpr float DURACIO_INIT = 4.0f; // Duració de l'estat INIT (2 segons)
static constexpr float DURACIO_TRANSITION = 2.5f; // Duració de la transició (1.5 segons) static constexpr float DURACIO_TRANSITION = 2.5f; // Duració de la transició (1.5 segons)
static constexpr float ESCALA_TITULO = 0.6f; // Escala per les lletres del títol (50%)
static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espai entre lletres static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espai entre lletres
static constexpr float Y_ORNI = 150.0f; // Posició Y de "ORNI"
static constexpr float SEPARACION_LINEAS = 10.0f; // Separació entre "ORNI" i "ATTACK!" (0.0f = pegades)
static constexpr float BLINK_FREQUENCY = 3.0f; // Freqüència de parpelleig (3 Hz) static constexpr float BLINK_FREQUENCY = 3.0f; // Freqüència de parpelleig (3 Hz)
static constexpr float DURACIO_BLACK_SCREEN = 2.0f; // Duració pantalla negra (2 segons) static constexpr float DURACIO_BLACK_SCREEN = 2.0f; // Duració pantalla negra (2 segons)
static constexpr int MUSIC_FADE = 1500; // Duracio del fade de la musica del titol al començar a jugar static constexpr int MUSIC_FADE = 1500; // Duracio del fade de la musica del titol al començar a jugar

View File

@@ -15,107 +15,169 @@ namespace Options {
// Mapa de SDL_Scancode a string // Mapa de SDL_Scancode a string
static const std::unordered_map<SDL_Scancode, std::string> SCANCODE_TO_STRING = { static const std::unordered_map<SDL_Scancode, std::string> SCANCODE_TO_STRING = {
{SDL_SCANCODE_A, "A"}, {SDL_SCANCODE_B, "B"}, {SDL_SCANCODE_C, "C"}, {SDL_SCANCODE_D, "D"}, {SDL_SCANCODE_A, "A"},
{SDL_SCANCODE_E, "E"}, {SDL_SCANCODE_F, "F"}, {SDL_SCANCODE_G, "G"}, {SDL_SCANCODE_H, "H"}, {SDL_SCANCODE_B, "B"},
{SDL_SCANCODE_I, "I"}, {SDL_SCANCODE_J, "J"}, {SDL_SCANCODE_K, "K"}, {SDL_SCANCODE_L, "L"}, {SDL_SCANCODE_C, "C"},
{SDL_SCANCODE_M, "M"}, {SDL_SCANCODE_N, "N"}, {SDL_SCANCODE_O, "O"}, {SDL_SCANCODE_P, "P"}, {SDL_SCANCODE_D, "D"},
{SDL_SCANCODE_Q, "Q"}, {SDL_SCANCODE_R, "R"}, {SDL_SCANCODE_S, "S"}, {SDL_SCANCODE_T, "T"}, {SDL_SCANCODE_E, "E"},
{SDL_SCANCODE_U, "U"}, {SDL_SCANCODE_V, "V"}, {SDL_SCANCODE_W, "W"}, {SDL_SCANCODE_X, "X"}, {SDL_SCANCODE_F, "F"},
{SDL_SCANCODE_Y, "Y"}, {SDL_SCANCODE_Z, "Z"}, {SDL_SCANCODE_G, "G"},
{SDL_SCANCODE_1, "1"}, {SDL_SCANCODE_2, "2"}, {SDL_SCANCODE_3, "3"}, {SDL_SCANCODE_4, "4"}, {SDL_SCANCODE_H, "H"},
{SDL_SCANCODE_5, "5"}, {SDL_SCANCODE_6, "6"}, {SDL_SCANCODE_7, "7"}, {SDL_SCANCODE_8, "8"}, {SDL_SCANCODE_I, "I"},
{SDL_SCANCODE_9, "9"}, {SDL_SCANCODE_0, "0"}, {SDL_SCANCODE_J, "J"},
{SDL_SCANCODE_RETURN, "RETURN"}, {SDL_SCANCODE_ESCAPE, "ESCAPE"}, {SDL_SCANCODE_K, "K"},
{SDL_SCANCODE_BACKSPACE, "BACKSPACE"}, {SDL_SCANCODE_TAB, "TAB"}, {SDL_SCANCODE_L, "L"},
{SDL_SCANCODE_SPACE, "SPACE"}, {SDL_SCANCODE_M, "M"},
{SDL_SCANCODE_UP, "UP"}, {SDL_SCANCODE_DOWN, "DOWN"}, {SDL_SCANCODE_N, "N"},
{SDL_SCANCODE_LEFT, "LEFT"}, {SDL_SCANCODE_RIGHT, "RIGHT"}, {SDL_SCANCODE_O, "O"},
{SDL_SCANCODE_LSHIFT, "LSHIFT"}, {SDL_SCANCODE_RSHIFT, "RSHIFT"}, {SDL_SCANCODE_P, "P"},
{SDL_SCANCODE_LCTRL, "LCTRL"}, {SDL_SCANCODE_RCTRL, "RCTRL"}, {SDL_SCANCODE_Q, "Q"},
{SDL_SCANCODE_LALT, "LALT"}, {SDL_SCANCODE_RALT, "RALT"} {SDL_SCANCODE_R, "R"},
}; {SDL_SCANCODE_S, "S"},
{SDL_SCANCODE_T, "T"},
{SDL_SCANCODE_U, "U"},
{SDL_SCANCODE_V, "V"},
{SDL_SCANCODE_W, "W"},
{SDL_SCANCODE_X, "X"},
{SDL_SCANCODE_Y, "Y"},
{SDL_SCANCODE_Z, "Z"},
{SDL_SCANCODE_1, "1"},
{SDL_SCANCODE_2, "2"},
{SDL_SCANCODE_3, "3"},
{SDL_SCANCODE_4, "4"},
{SDL_SCANCODE_5, "5"},
{SDL_SCANCODE_6, "6"},
{SDL_SCANCODE_7, "7"},
{SDL_SCANCODE_8, "8"},
{SDL_SCANCODE_9, "9"},
{SDL_SCANCODE_0, "0"},
{SDL_SCANCODE_RETURN, "RETURN"},
{SDL_SCANCODE_ESCAPE, "ESCAPE"},
{SDL_SCANCODE_BACKSPACE, "BACKSPACE"},
{SDL_SCANCODE_TAB, "TAB"},
{SDL_SCANCODE_SPACE, "SPACE"},
{SDL_SCANCODE_UP, "UP"},
{SDL_SCANCODE_DOWN, "DOWN"},
{SDL_SCANCODE_LEFT, "LEFT"},
{SDL_SCANCODE_RIGHT, "RIGHT"},
{SDL_SCANCODE_LSHIFT, "LSHIFT"},
{SDL_SCANCODE_RSHIFT, "RSHIFT"},
{SDL_SCANCODE_LCTRL, "LCTRL"},
{SDL_SCANCODE_RCTRL, "RCTRL"},
{SDL_SCANCODE_LALT, "LALT"},
{SDL_SCANCODE_RALT, "RALT"}};
// Mapa invers: string a SDL_Scancode // Mapa invers: string a SDL_Scancode
static const std::unordered_map<std::string, SDL_Scancode> STRING_TO_SCANCODE = { static const std::unordered_map<std::string, SDL_Scancode> STRING_TO_SCANCODE = {
{"A", SDL_SCANCODE_A}, {"B", SDL_SCANCODE_B}, {"C", SDL_SCANCODE_C}, {"D", SDL_SCANCODE_D}, {"A", SDL_SCANCODE_A},
{"E", SDL_SCANCODE_E}, {"F", SDL_SCANCODE_F}, {"G", SDL_SCANCODE_G}, {"H", SDL_SCANCODE_H}, {"B", SDL_SCANCODE_B},
{"I", SDL_SCANCODE_I}, {"J", SDL_SCANCODE_J}, {"K", SDL_SCANCODE_K}, {"L", SDL_SCANCODE_L}, {"C", SDL_SCANCODE_C},
{"M", SDL_SCANCODE_M}, {"N", SDL_SCANCODE_N}, {"O", SDL_SCANCODE_O}, {"P", SDL_SCANCODE_P}, {"D", SDL_SCANCODE_D},
{"Q", SDL_SCANCODE_Q}, {"R", SDL_SCANCODE_R}, {"S", SDL_SCANCODE_S}, {"T", SDL_SCANCODE_T}, {"E", SDL_SCANCODE_E},
{"U", SDL_SCANCODE_U}, {"V", SDL_SCANCODE_V}, {"W", SDL_SCANCODE_W}, {"X", SDL_SCANCODE_X}, {"F", SDL_SCANCODE_F},
{"Y", SDL_SCANCODE_Y}, {"Z", SDL_SCANCODE_Z}, {"G", SDL_SCANCODE_G},
{"1", SDL_SCANCODE_1}, {"2", SDL_SCANCODE_2}, {"3", SDL_SCANCODE_3}, {"4", SDL_SCANCODE_4}, {"H", SDL_SCANCODE_H},
{"5", SDL_SCANCODE_5}, {"6", SDL_SCANCODE_6}, {"7", SDL_SCANCODE_7}, {"8", SDL_SCANCODE_8}, {"I", SDL_SCANCODE_I},
{"9", SDL_SCANCODE_9}, {"0", SDL_SCANCODE_0}, {"J", SDL_SCANCODE_J},
{"RETURN", SDL_SCANCODE_RETURN}, {"ESCAPE", SDL_SCANCODE_ESCAPE}, {"K", SDL_SCANCODE_K},
{"BACKSPACE", SDL_SCANCODE_BACKSPACE}, {"TAB", SDL_SCANCODE_TAB}, {"L", SDL_SCANCODE_L},
{"SPACE", SDL_SCANCODE_SPACE}, {"M", SDL_SCANCODE_M},
{"UP", SDL_SCANCODE_UP}, {"DOWN", SDL_SCANCODE_DOWN}, {"N", SDL_SCANCODE_N},
{"LEFT", SDL_SCANCODE_LEFT}, {"RIGHT", SDL_SCANCODE_RIGHT}, {"O", SDL_SCANCODE_O},
{"LSHIFT", SDL_SCANCODE_LSHIFT}, {"RSHIFT", SDL_SCANCODE_RSHIFT}, {"P", SDL_SCANCODE_P},
{"LCTRL", SDL_SCANCODE_LCTRL}, {"RCTRL", SDL_SCANCODE_RCTRL}, {"Q", SDL_SCANCODE_Q},
{"LALT", SDL_SCANCODE_LALT}, {"RALT", SDL_SCANCODE_RALT} {"R", SDL_SCANCODE_R},
}; {"S", SDL_SCANCODE_S},
{"T", SDL_SCANCODE_T},
{"U", SDL_SCANCODE_U},
{"V", SDL_SCANCODE_V},
{"W", SDL_SCANCODE_W},
{"X", SDL_SCANCODE_X},
{"Y", SDL_SCANCODE_Y},
{"Z", SDL_SCANCODE_Z},
{"1", SDL_SCANCODE_1},
{"2", SDL_SCANCODE_2},
{"3", SDL_SCANCODE_3},
{"4", SDL_SCANCODE_4},
{"5", SDL_SCANCODE_5},
{"6", SDL_SCANCODE_6},
{"7", SDL_SCANCODE_7},
{"8", SDL_SCANCODE_8},
{"9", SDL_SCANCODE_9},
{"0", SDL_SCANCODE_0},
{"RETURN", SDL_SCANCODE_RETURN},
{"ESCAPE", SDL_SCANCODE_ESCAPE},
{"BACKSPACE", SDL_SCANCODE_BACKSPACE},
{"TAB", SDL_SCANCODE_TAB},
{"SPACE", SDL_SCANCODE_SPACE},
{"UP", SDL_SCANCODE_UP},
{"DOWN", SDL_SCANCODE_DOWN},
{"LEFT", SDL_SCANCODE_LEFT},
{"RIGHT", SDL_SCANCODE_RIGHT},
{"LSHIFT", SDL_SCANCODE_LSHIFT},
{"RSHIFT", SDL_SCANCODE_RSHIFT},
{"LCTRL", SDL_SCANCODE_LCTRL},
{"RCTRL", SDL_SCANCODE_RCTRL},
{"LALT", SDL_SCANCODE_LALT},
{"RALT", SDL_SCANCODE_RALT}};
// Mapa de botó de gamepad (int) a string // Mapa de botó de gamepad (int) a string
static const std::unordered_map<int, std::string> BUTTON_TO_STRING = { static const std::unordered_map<int, std::string> BUTTON_TO_STRING = {
{SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, // A (Xbox), Cross (PS) {SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, // A (Xbox), Cross (PS)
{SDL_GAMEPAD_BUTTON_EAST, "EAST"}, // B (Xbox), Circle (PS) {SDL_GAMEPAD_BUTTON_EAST, "EAST"}, // B (Xbox), Circle (PS)
{SDL_GAMEPAD_BUTTON_WEST, "WEST"}, // X (Xbox), Square (PS) {SDL_GAMEPAD_BUTTON_WEST, "WEST"}, // X (Xbox), Square (PS)
{SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, // Y (Xbox), Triangle (PS) {SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, // Y (Xbox), Triangle (PS)
{SDL_GAMEPAD_BUTTON_BACK, "BACK"}, {SDL_GAMEPAD_BUTTON_BACK, "BACK"},
{SDL_GAMEPAD_BUTTON_START, "START"}, {SDL_GAMEPAD_BUTTON_START, "START"},
{SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"}, {SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"}, {SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"}, {SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"},
{SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"}, {SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"},
{SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"}, {SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"},
{SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"}, {SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"},
{100, "L2_AS_BUTTON"}, // Trigger L2 com a botó digital {100, "L2_AS_BUTTON"}, // Trigger L2 com a botó digital
{101, "R2_AS_BUTTON"} // Trigger R2 com a botó digital {101, "R2_AS_BUTTON"} // Trigger R2 com a botó digital
}; };
// Mapa invers: string a botó de gamepad // Mapa invers: string a botó de gamepad
static const std::unordered_map<std::string, int> STRING_TO_BUTTON = { static const std::unordered_map<std::string, int> STRING_TO_BUTTON = {
{"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH}, {"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH},
{"EAST", SDL_GAMEPAD_BUTTON_EAST}, {"EAST", SDL_GAMEPAD_BUTTON_EAST},
{"WEST", SDL_GAMEPAD_BUTTON_WEST}, {"WEST", SDL_GAMEPAD_BUTTON_WEST},
{"NORTH", SDL_GAMEPAD_BUTTON_NORTH}, {"NORTH", SDL_GAMEPAD_BUTTON_NORTH},
{"BACK", SDL_GAMEPAD_BUTTON_BACK}, {"BACK", SDL_GAMEPAD_BUTTON_BACK},
{"START", SDL_GAMEPAD_BUTTON_START}, {"START", SDL_GAMEPAD_BUTTON_START},
{"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, {"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
{"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}, {"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
{"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP}, {"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP},
{"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN}, {"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN},
{"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT}, {"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT},
{"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT}, {"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
{"L2_AS_BUTTON", 100}, {"L2_AS_BUTTON", 100},
{"R2_AS_BUTTON", 101} {"R2_AS_BUTTON", 101}};
};
static auto scancodeToString(SDL_Scancode code) -> std::string { static auto scancodeToString(SDL_Scancode code) -> std::string {
auto it = SCANCODE_TO_STRING.find(code); auto it = SCANCODE_TO_STRING.find(code);
return (it != SCANCODE_TO_STRING.end()) ? it->second : "UNKNOWN"; return (it != SCANCODE_TO_STRING.end()) ? it->second : "UNKNOWN";
} }
static auto stringToScancode(const std::string& str) -> SDL_Scancode { static auto stringToScancode(const std::string& str) -> SDL_Scancode {
auto it = STRING_TO_SCANCODE.find(str); auto it = STRING_TO_SCANCODE.find(str);
return (it != STRING_TO_SCANCODE.end()) ? it->second : SDL_SCANCODE_UNKNOWN; return (it != STRING_TO_SCANCODE.end()) ? it->second : SDL_SCANCODE_UNKNOWN;
} }
static auto buttonToString(int button) -> std::string { static auto buttonToString(int button) -> std::string {
auto it = BUTTON_TO_STRING.find(button); auto it = BUTTON_TO_STRING.find(button);
return (it != BUTTON_TO_STRING.end()) ? it->second : "UNKNOWN"; return (it != BUTTON_TO_STRING.end()) ? it->second : "UNKNOWN";
} }
static auto stringToButton(const std::string& str) -> int { static auto stringToButton(const std::string& str) -> int {
auto it = STRING_TO_BUTTON.find(str); auto it = STRING_TO_BUTTON.find(str);
return (it != STRING_TO_BUTTON.end()) ? it->second : SDL_GAMEPAD_BUTTON_INVALID; return (it != STRING_TO_BUTTON.end()) ? it->second : SDL_GAMEPAD_BUTTON_INVALID;
} }
// ========== FI FUNCIONS AUXILIARS ========== // ========== FI FUNCIONS AUXILIARS ==========
// Inicialitzar opcions amb valors per defecte de Defaults:: // Inicialitzar opcions amb valors per defecte de Defaults::
void init() { void init() {
#ifdef _DEBUG #ifdef _DEBUG

View File

@@ -63,8 +63,8 @@ struct KeyboardControls {
struct GamepadControls { struct GamepadControls {
int button_left{SDL_GAMEPAD_BUTTON_DPAD_LEFT}; int button_left{SDL_GAMEPAD_BUTTON_DPAD_LEFT};
int button_right{SDL_GAMEPAD_BUTTON_DPAD_RIGHT}; int button_right{SDL_GAMEPAD_BUTTON_DPAD_RIGHT};
int button_thrust{SDL_GAMEPAD_BUTTON_WEST}; // X button int button_thrust{SDL_GAMEPAD_BUTTON_WEST}; // X button
int button_shoot{SDL_GAMEPAD_BUTTON_SOUTH}; // A button int button_shoot{SDL_GAMEPAD_BUTTON_SOUTH}; // A button
}; };
struct PlayerControls { struct PlayerControls {
@@ -85,23 +85,23 @@ inline Audio audio{};
// Controles per jugador // Controles per jugador
inline PlayerControls player1{ inline PlayerControls player1{
.keyboard = .keyboard =
{.key_left = SDL_SCANCODE_LEFT, {.key_left = SDL_SCANCODE_LEFT,
.key_right = SDL_SCANCODE_RIGHT, .key_right = SDL_SCANCODE_RIGHT,
.key_thrust = SDL_SCANCODE_UP, .key_thrust = SDL_SCANCODE_UP,
.key_shoot = SDL_SCANCODE_SPACE, .key_shoot = SDL_SCANCODE_SPACE,
.key_start = SDL_SCANCODE_1}, .key_start = SDL_SCANCODE_1},
.gamepad_name = "" // Primer gamepad disponible .gamepad_name = "" // Primer gamepad disponible
}; };
inline PlayerControls player2{ inline PlayerControls player2{
.keyboard = .keyboard =
{.key_left = SDL_SCANCODE_A, {.key_left = SDL_SCANCODE_A,
.key_right = SDL_SCANCODE_D, .key_right = SDL_SCANCODE_D,
.key_thrust = SDL_SCANCODE_W, .key_thrust = SDL_SCANCODE_W,
.key_shoot = SDL_SCANCODE_LSHIFT, .key_shoot = SDL_SCANCODE_LSHIFT,
.key_start = SDL_SCANCODE_2}, .key_start = SDL_SCANCODE_2},
.gamepad_name = "" // Segon gamepad disponible .gamepad_name = "" // Segon gamepad disponible
}; };
// Per compatibilitat amb pollo (no utilitzat en orni, però necessari per Input) // Per compatibilitat amb pollo (no utilitzat en orni, però necessari per Input)

View File

@@ -9,7 +9,10 @@
namespace StageSystem { namespace StageSystem {
SpawnController::SpawnController() SpawnController::SpawnController()
: config_(nullptr), temps_transcorregut_(0.0f), index_spawn_actual_(0), ship_position_(nullptr) {} : config_(nullptr),
temps_transcorregut_(0.0f),
index_spawn_actual_(0),
ship_position_(nullptr) {}
void SpawnController::configurar(const ConfigStage* config) { void SpawnController::configurar(const ConfigStage* config) {
config_ = config; config_ = config;
@@ -114,7 +117,7 @@ void SpawnController::generar_spawn_events() {
for (uint8_t i = 0; i < config_->total_enemics; i++) { for (uint8_t i = 0; i < config_->total_enemics; i++) {
float spawn_time = config_->config_spawn.delay_inicial + float spawn_time = config_->config_spawn.delay_inicial +
(i * config_->config_spawn.interval_spawn); (i * config_->config_spawn.interval_spawn);
TipusEnemic tipus = seleccionar_tipus_aleatori(); TipusEnemic tipus = seleccionar_tipus_aleatori();

View File

@@ -15,44 +15,44 @@ namespace StageSystem {
// Informació de spawn planificat // Informació de spawn planificat
struct SpawnEvent { struct SpawnEvent {
float temps_spawn; // Temps absolut (segons) per spawnejar float temps_spawn; // Temps absolut (segons) per spawnejar
TipusEnemic tipus; // Tipus d'enemic TipusEnemic tipus; // Tipus d'enemic
bool spawnejat; // Ja s'ha processat? bool spawnejat; // Ja s'ha processat?
}; };
class SpawnController { class SpawnController {
public: public:
SpawnController(); SpawnController();
// Configuration // Configuration
void configurar(const ConfigStage* config); // Set stage config void configurar(const ConfigStage* config); // Set stage config
void iniciar(); // Generate spawn schedule void iniciar(); // Generate spawn schedule
void reset(); // Clear all pending spawns void reset(); // Clear all pending spawns
// Update // Update
void actualitzar(float delta_time, std::array<Enemic, 15>& orni_array, bool pausar = false); void actualitzar(float delta_time, std::array<Enemic, 15>& orni_array, bool pausar = false);
// Status queries // Status queries
bool tots_enemics_spawnejats() const; bool tots_enemics_spawnejats() const;
bool tots_enemics_destruits(const std::array<Enemic, 15>& orni_array) const; bool tots_enemics_destruits(const std::array<Enemic, 15>& orni_array) const;
uint8_t get_enemics_vius(const std::array<Enemic, 15>& orni_array) const; uint8_t get_enemics_vius(const std::array<Enemic, 15>& orni_array) const;
uint8_t get_enemics_spawnejats() const; uint8_t get_enemics_spawnejats() const;
// [NEW] Set ship position reference for safe spawn // [NEW] Set ship position reference for safe spawn
void set_ship_position(const Punt* ship_pos) { ship_position_ = ship_pos; } void set_ship_position(const Punt* ship_pos) { ship_position_ = ship_pos; }
private: private:
const ConfigStage* config_; // Non-owning pointer to current stage config const ConfigStage* config_; // Non-owning pointer to current stage config
std::vector<SpawnEvent> spawn_queue_; std::vector<SpawnEvent> spawn_queue_;
float temps_transcorregut_; // Elapsed time since stage start float temps_transcorregut_; // Elapsed time since stage start
uint8_t index_spawn_actual_; // Next spawn to process uint8_t index_spawn_actual_; // Next spawn to process
// Spawn generation // Spawn generation
void generar_spawn_events(); void generar_spawn_events();
TipusEnemic seleccionar_tipus_aleatori() const; TipusEnemic seleccionar_tipus_aleatori() const;
void spawn_enemic(Enemic& enemic, TipusEnemic tipus, const Punt* ship_pos = nullptr); void spawn_enemic(Enemic& enemic, TipusEnemic tipus, const Punt* ship_pos = nullptr);
void aplicar_multiplicadors(Enemic& enemic) const; void aplicar_multiplicadors(Enemic& enemic) const;
const Punt* ship_position_; // [NEW] Non-owning pointer to ship position const Punt* ship_position_; // [NEW] Non-owning pointer to ship position
}; };
} // namespace StageSystem } // namespace StageSystem

View File

@@ -19,82 +19,81 @@ enum class ModeSpawn {
// Configuració de spawn // Configuració de spawn
struct ConfigSpawn { struct ConfigSpawn {
ModeSpawn mode; ModeSpawn mode;
float delay_inicial; // Segons abans del primer spawn float delay_inicial; // Segons abans del primer spawn
float interval_spawn; // Segons entre spawns consecutius float interval_spawn; // Segons entre spawns consecutius
}; };
// Distribució de tipus d'enemics (percentatges) // Distribució de tipus d'enemics (percentatges)
struct DistribucioEnemics { struct DistribucioEnemics {
uint8_t pentagon; // 0-100 uint8_t pentagon; // 0-100
uint8_t quadrat; // 0-100 uint8_t quadrat; // 0-100
uint8_t molinillo; // 0-100 uint8_t molinillo; // 0-100
// Suma ha de ser 100, validat en StageLoader // Suma ha de ser 100, validat en StageLoader
}; };
// Multiplicadors de dificultat // Multiplicadors de dificultat
struct MultiplicadorsDificultat { struct MultiplicadorsDificultat {
float velocitat; // 0.5-2.0 típic float velocitat; // 0.5-2.0 típic
float rotacio; // 0.5-2.0 típic float rotacio; // 0.5-2.0 típic
float tracking_strength; // 0.0-1.5 (aplicat a Quadrat) float tracking_strength; // 0.0-1.5 (aplicat a Quadrat)
}; };
// Metadades del fitxer YAML // Metadades del fitxer YAML
struct MetadataStages { struct MetadataStages {
std::string version; std::string version;
uint8_t total_stages; uint8_t total_stages;
std::string descripcio; std::string descripcio;
}; };
// Configuració completa d'un stage // Configuració completa d'un stage
struct ConfigStage { struct ConfigStage {
uint8_t stage_id; // 1-10 uint8_t stage_id; // 1-10
uint8_t total_enemics; // 5-15 uint8_t total_enemics; // 5-15
ConfigSpawn config_spawn; ConfigSpawn config_spawn;
DistribucioEnemics distribucio; DistribucioEnemics distribucio;
MultiplicadorsDificultat multiplicadors; MultiplicadorsDificultat multiplicadors;
// Validació // Validació
bool es_valid() const { bool es_valid() const {
return stage_id >= 1 && stage_id <= 255 && return stage_id >= 1 && stage_id <= 255 &&
total_enemics > 0 && total_enemics <= 15 && total_enemics > 0 && total_enemics <= 15 &&
distribucio.pentagon + distribucio.quadrat + distribucio.molinillo == 100; distribucio.pentagon + distribucio.quadrat + distribucio.molinillo == 100;
} }
}; };
// Configuració completa del sistema (carregada des de YAML) // Configuració completa del sistema (carregada des de YAML)
struct ConfigSistemaStages { struct ConfigSistemaStages {
MetadataStages metadata; MetadataStages metadata;
std::vector<ConfigStage> stages; // Índex [0] = stage 1 std::vector<ConfigStage> stages; // Índex [0] = stage 1
// Obtenir configuració d'un stage específic // Obtenir configuració d'un stage específic
const ConfigStage* obte_stage(uint8_t stage_id) const { const ConfigStage* obte_stage(uint8_t stage_id) const {
if (stage_id < 1 || stage_id > stages.size()) { if (stage_id < 1 || stage_id > stages.size()) {
return nullptr; return nullptr;
}
return &stages[stage_id - 1];
} }
return &stages[stage_id - 1];
}
}; };
// Constants per missatges de transició // Constants per missatges de transició
namespace Constants { namespace Constants {
// Pool de missatges per inici de level (selecció aleatòria) // Pool de missatges per inici de level (selecció aleatòria)
inline constexpr std::array<const char*, 12> MISSATGES_LEVEL_START = { inline constexpr std::array<const char*, 12> MISSATGES_LEVEL_START = {
"ORNI ALERT!", "ORNI ALERT!",
"INCOMING ORNIS!", "INCOMING ORNIS!",
"ROLLING THREAT!", "ROLLING THREAT!",
"ENEMY WAVE!", "ENEMY WAVE!",
"WAVE OF ORNIS DETECTED!", "WAVE OF ORNIS DETECTED!",
"NEXT SWARM APPROACHING!", "NEXT SWARM APPROACHING!",
"BRACE FOR THE NEXT WAVE!", "BRACE FOR THE NEXT WAVE!",
"ANOTHER ATTACK INCOMING!", "ANOTHER ATTACK INCOMING!",
"SENSORS DETECT HOSTILE ORNIS...", "SENSORS DETECT HOSTILE ORNIS...",
"UNIDENTIFIED ROLLING OBJECTS INBOUND!", "UNIDENTIFIED ROLLING OBJECTS INBOUND!",
"ENEMY FORCES MOBILIZING!", "ENEMY FORCES MOBILIZING!",
"PREPARE FOR IMPACT!" "PREPARE FOR IMPACT!"};
};
constexpr const char* MISSATGE_LEVEL_COMPLETED = "GOOD JOB COMMANDER!"; constexpr const char* MISSATGE_LEVEL_COMPLETED = "GOOD JOB COMMANDER!";
} } // namespace Constants
} // namespace StageSystem } // namespace StageSystem

View File

@@ -3,13 +3,13 @@
#include "stage_loader.hpp" #include "stage_loader.hpp"
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
namespace StageSystem { namespace StageSystem {
std::unique_ptr<ConfigSistemaStages> StageLoader::carregar(const std::string& path) { std::unique_ptr<ConfigSistemaStages> StageLoader::carregar(const std::string& path) {
@@ -88,8 +88,8 @@ bool StageLoader::parse_metadata(const fkyaml::node& yaml, MetadataStages& meta)
meta.version = yaml["version"].get_value<std::string>(); meta.version = yaml["version"].get_value<std::string>();
meta.total_stages = yaml["total_stages"].get_value<uint8_t>(); meta.total_stages = yaml["total_stages"].get_value<uint8_t>();
meta.descripcio = yaml.contains("description") meta.descripcio = yaml.contains("description")
? yaml["description"].get_value<std::string>() ? yaml["description"].get_value<std::string>()
: ""; : "";
return true; return true;
} catch (const std::exception& e) { } catch (const std::exception& e) {

View File

@@ -5,28 +5,29 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "external/fkyaml_node.hpp" #include "external/fkyaml_node.hpp"
#include "stage_config.hpp" #include "stage_config.hpp"
namespace StageSystem { namespace StageSystem {
class StageLoader { class StageLoader {
public: public:
// Carregar configuració des de fitxer YAML // Carregar configuració des de fitxer YAML
// Retorna nullptr si hi ha errors // Retorna nullptr si hi ha errors
static std::unique_ptr<ConfigSistemaStages> carregar(const std::string& path); static std::unique_ptr<ConfigSistemaStages> carregar(const std::string& path);
private: private:
// Parsing helpers (implementats en .cpp) // Parsing helpers (implementats en .cpp)
static bool parse_metadata(const fkyaml::node& yaml, MetadataStages& meta); static bool parse_metadata(const fkyaml::node& yaml, MetadataStages& meta);
static bool parse_stage(const fkyaml::node& yaml, ConfigStage& stage); static bool parse_stage(const fkyaml::node& yaml, ConfigStage& stage);
static bool parse_spawn_config(const fkyaml::node& yaml, ConfigSpawn& config); static bool parse_spawn_config(const fkyaml::node& yaml, ConfigSpawn& config);
static bool parse_distribution(const fkyaml::node& yaml, DistribucioEnemics& dist); static bool parse_distribution(const fkyaml::node& yaml, DistribucioEnemics& dist);
static bool parse_multipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult); static bool parse_multipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult);
static ModeSpawn parse_spawn_mode(const std::string& mode_str); static ModeSpawn parse_spawn_mode(const std::string& mode_str);
// Validació // Validació
static bool validar_config(const ConfigSistemaStages& config); static bool validar_config(const ConfigSistemaStages& config);
}; };
} // namespace StageSystem } // namespace StageSystem

View File

@@ -57,8 +57,8 @@ void StageManager::stage_completat() {
bool StageManager::tot_completat() const { bool StageManager::tot_completat() const {
return stage_actual_ >= config_->metadata.total_stages && return stage_actual_ >= config_->metadata.total_stages &&
estat_ == EstatStage::LEVEL_COMPLETED && estat_ == EstatStage::LEVEL_COMPLETED &&
timer_transicio_ <= 0.0f; timer_transicio_ <= 0.0f;
} }
const ConfigStage* StageManager::get_config_actual() const { const ConfigStage* StageManager::get_config_actual() const {
@@ -127,8 +127,8 @@ void StageManager::processar_playing(float delta_time, bool pausar_spawn) {
// Update spawn controller (pauses when pausar_spawn = true) // Update spawn controller (pauses when pausar_spawn = true)
// Note: The actual enemy array update happens in EscenaJoc::actualitzar() // Note: The actual enemy array update happens in EscenaJoc::actualitzar()
// This is just for internal timekeeping // This is just for internal timekeeping
(void)delta_time; // Spawn controller is updated externally (void)delta_time; // Spawn controller is updated externally
(void)pausar_spawn; // Passed to spawn_controller_.actualitzar() by EscenaJoc (void)pausar_spawn; // Passed to spawn_controller_.actualitzar() by EscenaJoc
} }
void StageManager::processar_level_completed(float delta_time) { void StageManager::processar_level_completed(float delta_time) {

View File

@@ -13,51 +13,51 @@ namespace StageSystem {
// Estats del stage system // Estats del stage system
enum class EstatStage { enum class EstatStage {
INIT_HUD, // Animació inicial del HUD (3s) INIT_HUD, // Animació inicial del HUD (3s)
LEVEL_START, // Pantalla "ENEMY INCOMING" (3s) LEVEL_START, // Pantalla "ENEMY INCOMING" (3s)
PLAYING, // Gameplay normal PLAYING, // Gameplay normal
LEVEL_COMPLETED // Pantalla "GOOD JOB COMMANDER!" (3s) LEVEL_COMPLETED // Pantalla "GOOD JOB COMMANDER!" (3s)
}; };
class StageManager { class StageManager {
public: public:
explicit StageManager(const ConfigSistemaStages* config); explicit StageManager(const ConfigSistemaStages* config);
// Lifecycle // Lifecycle
void inicialitzar(); // Reset to stage 1 void inicialitzar(); // Reset to stage 1
void actualitzar(float delta_time, bool pausar_spawn = false); void actualitzar(float delta_time, bool pausar_spawn = false);
// Stage progression // Stage progression
void stage_completat(); // Call when all enemies destroyed void stage_completat(); // Call when all enemies destroyed
bool tot_completat() const; // All 10 stages done? bool tot_completat() const; // All 10 stages done?
// Current state queries // Current state queries
EstatStage get_estat() const { return estat_; } EstatStage get_estat() const { return estat_; }
uint8_t get_stage_actual() const { return stage_actual_; } uint8_t get_stage_actual() const { return stage_actual_; }
const ConfigStage* get_config_actual() const; const ConfigStage* get_config_actual() const;
float get_timer_transicio() const { return timer_transicio_; } float get_timer_transicio() const { return timer_transicio_; }
const std::string& get_missatge_level_start() const { return missatge_level_start_actual_; } const std::string& get_missatge_level_start() const { return missatge_level_start_actual_; }
// Spawn control (delegate to SpawnController) // Spawn control (delegate to SpawnController)
SpawnController& get_spawn_controller() { return spawn_controller_; } SpawnController& get_spawn_controller() { return spawn_controller_; }
const SpawnController& get_spawn_controller() const { return spawn_controller_; } const SpawnController& get_spawn_controller() const { return spawn_controller_; }
private: private:
const ConfigSistemaStages* config_; // Non-owning pointer const ConfigSistemaStages* config_; // Non-owning pointer
SpawnController spawn_controller_; SpawnController spawn_controller_;
EstatStage estat_; EstatStage estat_;
uint8_t stage_actual_; // 1-10 uint8_t stage_actual_; // 1-10
float timer_transicio_; // Timer for LEVEL_START/LEVEL_COMPLETED (3.0s → 0.0s) float timer_transicio_; // Timer for LEVEL_START/LEVEL_COMPLETED (3.0s → 0.0s)
std::string missatge_level_start_actual_; // Missatge seleccionat per al level actual std::string missatge_level_start_actual_; // Missatge seleccionat per al level actual
// State transitions // State transitions
void canviar_estat(EstatStage nou_estat); void canviar_estat(EstatStage nou_estat);
void processar_init_hud(float delta_time); void processar_init_hud(float delta_time);
void processar_level_start(float delta_time); void processar_level_start(float delta_time);
void processar_playing(float delta_time, bool pausar_spawn); void processar_playing(float delta_time, bool pausar_spawn);
void processar_level_completed(float delta_time); void processar_level_completed(float delta_time);
void carregar_stage(uint8_t stage_id); void carregar_stage(uint8_t stage_id);
}; };
} // namespace StageSystem } // namespace StageSystem

View File

@@ -17,8 +17,8 @@ ShipAnimator::ShipAnimator(SDL_Renderer* renderer)
void ShipAnimator::inicialitzar() { void ShipAnimator::inicialitzar() {
// Carregar formes de naus amb perspectiva pre-calculada // Carregar formes de naus amb perspectiva pre-calculada
auto forma_p1 = Graphics::ShapeLoader::load("ship_p1.shp"); // Perspectiva esquerra auto forma_p1 = Graphics::ShapeLoader::load("ship_p1.shp"); // Perspectiva esquerra
auto forma_p2 = Graphics::ShapeLoader::load("ship2_p2.shp"); // Perspectiva dreta auto forma_p2 = Graphics::ShapeLoader::load("ship2_p2.shp"); // Perspectiva dreta
// Configurar nau P1 // Configurar nau P1
naus_[0].jugador_id = 1; naus_[0].jugador_id = 1;
@@ -59,11 +59,11 @@ void ShipAnimator::dibuixar() const {
renderer_, renderer_,
nau.forma, nau.forma,
nau.posicio_actual, nau.posicio_actual,
0.0f, // angle (rotació 2D no utilitzada) 0.0f, // angle (rotació 2D no utilitzada)
nau.escala_actual, nau.escala_actual,
true, // dibuixar true, // dibuixar
1.0f, // progress (sempre visible) 1.0f, // progress (sempre visible)
1.0f // brightness (brillantor màxima) 1.0f // brightness (brillantor màxima)
); );
} }
} }
@@ -227,7 +227,7 @@ void ShipAnimator::configurar_nau_p1(NauTitol& nau) {
nau.temps_estat = 0.0f; nau.temps_estat = 0.0f;
// Posicions (clock 8, bottom-left) // Posicions (clock 8, bottom-left)
nau.posicio_objectiu = {P1_TARGET_X, P1_TARGET_Y}; nau.posicio_objectiu = {P1_TARGET_X(), P1_TARGET_Y()};
// Calcular posició inicial (fora de pantalla) // Calcular posició inicial (fora de pantalla)
nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_8_ANGLE); nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_8_ANGLE);
@@ -262,7 +262,7 @@ void ShipAnimator::configurar_nau_p2(NauTitol& nau) {
nau.temps_estat = 0.0f; nau.temps_estat = 0.0f;
// Posicions (clock 4, bottom-right) // Posicions (clock 4, bottom-right)
nau.posicio_objectiu = {P2_TARGET_X, P2_TARGET_Y}; nau.posicio_objectiu = {P2_TARGET_X(), P2_TARGET_Y()};
// Calcular posició inicial (fora de pantalla) // Calcular posició inicial (fora de pantalla)
nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_4_ANGLE); nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_4_ANGLE);
@@ -293,7 +293,8 @@ Punt ShipAnimator::calcular_posicio_fora_pantalla(float angle_rellotge) const {
using namespace Defaults::Title::Ships; using namespace Defaults::Title::Ships;
// Convertir angle del rellotge a radians (per exemple: 240° per clock 8) // Convertir angle del rellotge a radians (per exemple: 240° per clock 8)
// Calcular posició en direcció radial des del centre, però més lluny (+ ENTRY_OFFSET) // Calcular posició en direcció radial des del centre, però més lluny
// ENTRY_OFFSET es calcula automàticament: (SHIP_MAX_RADIUS * ENTRY_SCALE_START) + ENTRY_OFFSET_MARGIN
float extended_radius = CLOCK_RADIUS + ENTRY_OFFSET; float extended_radius = CLOCK_RADIUS + ENTRY_OFFSET;
float x = Defaults::Game::WIDTH / 2.0f + extended_radius * std::cos(angle_rellotge); float x = Defaults::Game::WIDTH / 2.0f + extended_radius * std::cos(angle_rellotge);

View File

@@ -23,74 +23,74 @@ enum class EstatNau {
// Dades d'una nau individual al títol // Dades d'una nau individual al títol
struct NauTitol { struct NauTitol {
// Identificació // Identificació
int jugador_id; // 1 o 2 int jugador_id; // 1 o 2
// Estat // Estat
EstatNau estat; EstatNau estat;
float temps_estat; // Temps acumulat en l'estat actual float temps_estat; // Temps acumulat en l'estat actual
// Posicions // Posicions
Punt posicio_inicial; // Posició d'inici (fora de pantalla per ENTERING) Punt posicio_inicial; // Posició d'inici (fora de pantalla per ENTERING)
Punt posicio_objectiu; // Posició objectiu (rellotge 8 o 4) Punt posicio_objectiu; // Posició objectiu (rellotge 8 o 4)
Punt posicio_actual; // Posició interpolada actual Punt posicio_actual; // Posició interpolada actual
// Escales (simulació eix Z) // Escales (simulació eix Z)
float escala_inicial; // Escala d'inici (més gran = més a prop) float escala_inicial; // Escala d'inici (més gran = més a prop)
float escala_objectiu; // Escala objectiu (mida flotació) float escala_objectiu; // Escala objectiu (mida flotació)
float escala_actual; // Escala interpolada actual float escala_actual; // Escala interpolada actual
// Flotació // Flotació
float fase_oscilacio; // Acumulador de fase per moviment sinusoïdal float fase_oscilacio; // Acumulador de fase per moviment sinusoïdal
// Paràmetres d'entrada // Paràmetres d'entrada
float entry_delay; // Delay abans d'entrar (0.0 per P1, 0.5 per P2) float entry_delay; // Delay abans d'entrar (0.0 per P1, 0.5 per P2)
// Paràmetres d'oscil·lació per nau // Paràmetres d'oscil·lació per nau
float amplitude_x; float amplitude_x;
float amplitude_y; float amplitude_y;
float frequency_x; float frequency_x;
float frequency_y; float frequency_y;
// Forma // Forma
std::shared_ptr<Graphics::Shape> forma; std::shared_ptr<Graphics::Shape> forma;
// Visibilitat // Visibilitat
bool visible; bool visible;
}; };
// Gestor d'animació de naus per a l'escena de títol // Gestor d'animació de naus per a l'escena de títol
class ShipAnimator { class ShipAnimator {
public: public:
explicit ShipAnimator(SDL_Renderer* renderer); explicit ShipAnimator(SDL_Renderer* renderer);
// Cicle de vida // Cicle de vida
void inicialitzar(); void inicialitzar();
void actualitzar(float delta_time); void actualitzar(float delta_time);
void dibuixar() const; void dibuixar() const;
// Control d'estat (cridat per EscenaTitol) // Control d'estat (cridat per EscenaTitol)
void start_entry_animation(); void start_entry_animation();
void trigger_exit_animation(); // Anima totes les naus 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) void trigger_exit_animation_for_player(int jugador_id); // Anima només una nau (P1=1, P2=2)
// Control de visibilitat // Control de visibilitat
void set_visible(bool visible); void set_visible(bool visible);
bool is_animation_complete() const; bool is_animation_complete() const;
private: private:
SDL_Renderer* renderer_; SDL_Renderer* renderer_;
std::array<NauTitol, 2> naus_; // Naus P1 i P2 std::array<NauTitol, 2> naus_; // Naus P1 i P2
// Mètodes d'animació // Mètodes d'animació
void actualitzar_entering(NauTitol& nau, float delta_time); void actualitzar_entering(NauTitol& nau, float delta_time);
void actualitzar_floating(NauTitol& nau, float delta_time); void actualitzar_floating(NauTitol& nau, float delta_time);
void actualitzar_exiting(NauTitol& nau, float delta_time); void actualitzar_exiting(NauTitol& nau, float delta_time);
// Configuració // Configuració
void configurar_nau_p1(NauTitol& nau); void configurar_nau_p1(NauTitol& nau);
void configurar_nau_p2(NauTitol& nau); void configurar_nau_p2(NauTitol& nau);
Punt calcular_posicio_fora_pantalla(float angle_rellotge) const; Punt calcular_posicio_fora_pantalla(float angle_rellotge) const;
}; };
} // namespace Title } // namespace Title

View File

@@ -5,5 +5,7 @@ constexpr const char* NAME = "@PROJECT_NAME@";
constexpr const char* LONG_NAME = "@PROJECT_LONG_NAME@"; constexpr const char* LONG_NAME = "@PROJECT_LONG_NAME@";
constexpr const char* VERSION = "@PROJECT_VERSION@"; constexpr const char* VERSION = "@PROJECT_VERSION@";
constexpr const char* COPYRIGHT = "@PROJECT_COPYRIGHT@"; constexpr const char* COPYRIGHT = "@PROJECT_COPYRIGHT@";
constexpr const char* COPYRIGHT_ORIGINAL = "@PROJECT_COPYRIGHT_ORIGINAL@";
constexpr const char* COPYRIGHT_PORT = "@PROJECT_COPYRIGHT_PORT@";
constexpr const char* GIT_HASH = "@GIT_HASH@"; constexpr const char* GIT_HASH = "@GIT_HASH@";
} // namespace Project } // namespace Project