diff --git a/source/core/defaults/effects.hpp b/source/core/defaults/effects.hpp index 83ffb14..cfe7e17 100644 --- a/source/core/defaults/effects.hpp +++ b/source/core/defaults/effects.hpp @@ -9,8 +9,8 @@ namespace Defaults::FX::Glow { // Neon glow per outline gruixut, aplicat automàticament per renderShape. // Els gruixos d'halo són RÀTIOS del bounding_radius de la shape (escalat - // per scale), de manera que un pentàgon (radi 20) té halo gros i una bala - // (radi 3) té halo subtil. El core (últim pass) usa el gruix de línia + // per scale), de manera que un pentàgon (radius 20) té halo gros i una bala + // (radius 3) té halo subtil. El core (últim pass) usa el gruix de línia // global (1.5px) — no escala amb la shape. // // Cap superior: si la shape és molt gran (logos del títol, intro), el diff --git a/source/core/defaults/enemies.hpp b/source/core/defaults/enemies.hpp index 1d6a960..0834164 100644 --- a/source/core/defaults/enemies.hpp +++ b/source/core/defaults/enemies.hpp @@ -1,4 +1,4 @@ -// enemies.hpp - Configuració per tipus d'enemic (Pentagon/Cuadrado/Molinillo), spawn i scoring +// enemies.hpp - Configuració per tipus d'enemic (Pentagon/Square/Molinillo), spawn i scoring // © 2026 JailDesigner #pragma once @@ -17,57 +17,57 @@ namespace Defaults::Enemies { // Pentagon (esquivador - zigzag evasion) namespace Pentagon { - constexpr float VELOCITAT = 35.0F; // px/s (slightly slower) + constexpr float SPEED = 35.0F; // px/s (slightly slower) constexpr float MASS = 5.0F; // Masa estándar - constexpr float CANVI_ANGLE_PROB = 0.20F; // 20% per wall hit (frequent zigzag) - constexpr float CANVI_ANGLE_MAX = 1.0F; // Max random angle change (rad) + constexpr float ANGLE_CHANGE_PROB = 0.20F; // 20% per wall hit (frequent zigzag) + constexpr float ANGLE_CHANGE_MAX = 1.0F; // Max random angle change (rad) constexpr float ZIGZAG_PROB_PER_SECOND = 0.8F; // Probabilidad de zigzag por segundo - constexpr float DROTACIO_MIN = 0.75F; // Min visual rotation (rad/s) [+50%] - constexpr float DROTACIO_MAX = 3.75F; // Max visual rotation (rad/s) [+50%] + constexpr float ROTATION_DELTA_MIN = 0.75F; // Min visual rotation (rad/s) [+50%] + constexpr float ROTATION_DELTA_MAX = 3.75F; // Max visual rotation (rad/s) [+50%] constexpr const char* SHAPE_FILE = "enemy_pentagon.shp"; } // namespace Pentagon - // Cuadrado (perseguidor - tracks player) - namespace Cuadrado { - constexpr float VELOCITAT = 40.0F; // px/s (medium speed) - constexpr float MASS = 8.0F; // Más pesado, "tanque" - constexpr float TRACKING_STRENGTH = 0.5F; // Interpolation toward player (0.0-1.0) - constexpr float TRACKING_INTERVAL = 1.0F; // Seconds between angle updates - constexpr float DROTACIO_MIN = 0.3F; // Slow rotation [+50%] - constexpr float DROTACIO_MAX = 1.5F; // [+50%] + // Square (perseguidor - tracks player) + namespace Square { + constexpr float SPEED = 40.0F; // px/s (medium speed) + constexpr float MASS = 8.0F; // Más pesado, "tanque" + constexpr float TRACKING_STRENGTH = 0.5F; // Interpolation toward player (0.0-1.0) + constexpr float TRACKING_INTERVAL = 1.0F; // Seconds between angle updates + constexpr float ROTATION_DELTA_MIN = 0.3F; // Slow rotation [+50%] + constexpr float ROTATION_DELTA_MAX = 1.5F; // [+50%] constexpr const char* SHAPE_FILE = "enemy_square.shp"; - } // namespace Cuadrado + } // namespace Square // Molinillo (agressiu - fast straight lines, proximity spin-up) - namespace Molinillo { - constexpr float VELOCITAT = 50.0F; // px/s (fastest) - constexpr float MASS = 4.0F; // Más liviano, ágil - constexpr float CANVI_ANGLE_PROB = 0.05F; // 5% per wall hit (rare direction change) - constexpr float CANVI_ANGLE_MAX = 0.3F; // Small angle adjustments - constexpr float DROTACIO_MIN = 3.0F; // Base rotation (rad/s) [+50%] - constexpr float DROTACIO_MAX = 6.0F; // [+50%] - constexpr float DROTACIO_PROXIMITY_MULTIPLIER = 3.0F; // Spin-up multiplier when near ship - constexpr float PROXIMITY_DISTANCE = 100.0F; // Distance threshold (px) + namespace Pinwheel { + constexpr float SPEED = 50.0F; // px/s (fastest) + constexpr float MASS = 4.0F; // Más liviano, ágil + constexpr float ANGLE_CHANGE_PROB = 0.05F; // 5% per wall hit (rare direction change) + constexpr float ANGLE_CHANGE_MAX = 0.3F; // Small angle adjustments + constexpr float ROTATION_DELTA_MIN = 3.0F; // Base rotation (rad/s) [+50%] + constexpr float ROTATION_DELTA_MAX = 6.0F; // [+50%] + constexpr float ROTATION_DELTA_PROXIMITY_MULTIPLIER = 3.0F; // Spin-up multiplier when near ship + constexpr float PROXIMITY_DISTANCE = 100.0F; // Distance threshold (px) constexpr const char* SHAPE_FILE = "enemy_pinwheel.shp"; - } // namespace Molinillo + } // namespace Pinwheel // Animation parameters (shared) namespace Animation { // Palpitation - constexpr float PALPITACIO_TRIGGER_PROB = 0.01F; // 1% chance per second - constexpr float PALPITACIO_DURACIO_MIN = 1.0F; // Min duration (seconds) - constexpr float PALPITACIO_DURACIO_MAX = 3.0F; // Max duration (seconds) - constexpr float PALPITACIO_AMPLITUD_MIN = 0.08F; // Min scale variation - constexpr float PALPITACIO_AMPLITUD_MAX = 0.20F; // Max scale variation - constexpr float PALPITACIO_FREQ_MIN = 1.5F; // Min frequency (Hz) - constexpr float PALPITACIO_FREQ_MAX = 3.0F; // Max frequency (Hz) + constexpr float PULSE_TRIGGER_PROB = 0.01F; // 1% chance per second + constexpr float PULSE_DURATION_MIN = 1.0F; // Min duration (seconds) + constexpr float PULSE_DURATION_MAX = 3.0F; // Max duration (seconds) + constexpr float PULSE_AMPLITUD_MIN = 0.08F; // Min scale variation + constexpr float PULSE_AMPLITUD_MAX = 0.20F; // Max scale variation + constexpr float PULSE_FREQ_MIN = 1.5F; // Min frequency (Hz) + constexpr float PULSE_FREQ_MAX = 3.0F; // Max frequency (Hz) // Rotation acceleration - constexpr float ROTACIO_ACCEL_TRIGGER_PROB = 0.02F; // 2% chance per second [4x more frequent] - constexpr float ROTACIO_ACCEL_DURACIO_MIN = 3.0F; // Min transition time - constexpr float ROTACIO_ACCEL_DURACIO_MAX = 8.0F; // Max transition time - constexpr float ROTACIO_ACCEL_MULTIPLIER_MIN = 0.3F; // Min speed multiplier [more dramatic] - constexpr float ROTACIO_ACCEL_MULTIPLIER_MAX = 4.0F; // Max speed multiplier [more dramatic] + constexpr float ROTATION_ACCEL_TRIGGER_PROB = 0.02F; // 2% chance per second [4x more frequent] + constexpr float ROTATION_ACCEL_DURATION_MIN = 3.0F; // Min transition time + constexpr float ROTATION_ACCEL_DURATION_MAX = 8.0F; // Max transition time + constexpr float ROTATION_ACCEL_MULTIPLIER_MIN = 0.3F; // Min speed multiplier [more dramatic] + constexpr float ROTATION_ACCEL_MULTIPLIER_MAX = 4.0F; // Max speed multiplier [more dramatic] } // namespace Animation // Wounded state (entre primer impacto y explosión) @@ -93,9 +93,9 @@ namespace Defaults::Enemies { // Scoring system (puntuación per type de enemy) namespace Scoring { - constexpr int PENTAGON_SCORE = 100; // Pentágono (esquivador, 35 px/s) - constexpr int QUADRAT_SCORE = 150; // Cuadrado (perseguidor, 40 px/s) - constexpr int MOLINILLO_SCORE = 200; // Molinillo (agressiu, 50 px/s) + constexpr int PENTAGON_SCORE = 100; // Pentágono (esquivador, 35 px/s) + constexpr int SQUARE_SCORE = 150; // Square (perseguidor, 40 px/s) + constexpr int PINWHEEL_SCORE = 200; // Molinillo (agressiu, 50 px/s) } // namespace Scoring } // namespace Defaults::Enemies diff --git a/source/core/defaults/entities.hpp b/source/core/defaults/entities.hpp index 56b5cd1..a1e2178 100644 --- a/source/core/defaults/entities.hpp +++ b/source/core/defaults/entities.hpp @@ -6,7 +6,7 @@ namespace Defaults::Entities { constexpr int MAX_ORNIS = 15; - constexpr int MAX_BALES = 50; + constexpr int MAX_BULLETS = 50; constexpr float SHIP_RADIUS = 12.0F; constexpr float ENEMY_RADIUS = 20.0F; diff --git a/source/core/defaults/game.hpp b/source/core/defaults/game.hpp index 5c08046..a7299c2 100644 --- a/source/core/defaults/game.hpp +++ b/source/core/defaults/game.hpp @@ -59,7 +59,7 @@ namespace Defaults::Game { constexpr float INIT_HUD_SHIP2_RATIO_INIT = 0.20F; constexpr float INIT_HUD_SHIP2_RATIO_END = 1.0F; - // Posición inicial de la nave en INIT_HUD (75% de altura de zona de juego) + // Posición inicial de la nave en INIT_HUD (75% de altura de zone de juego) constexpr float INIT_HUD_SHIP_START_Y_RATIO = 0.75F; // 75% desde el top de PLAYAREA // Spawn positions (distribución horizontal para 2 jugadores) diff --git a/source/core/defaults/palette.hpp b/source/core/defaults/palette.hpp index 6aef5b7..310ea8c 100644 --- a/source/core/defaults/palette.hpp +++ b/source/core/defaults/palette.hpp @@ -14,11 +14,11 @@ namespace Defaults::Palette { // brillantor perceptual sota el bloom (sense alterar la identitat de color). // El canal dominant es manté a 255 a cada color per maximitzar la saturació // visible quan el halo s'expandeix. - constexpr SDL_Color SHIP = {.r = 255, .g = 255, .b = 255, .a = 255}; // Blanco neutro - constexpr SDL_Color BULLET = {.r = 155, .g = 255, .b = 175, .a = 255}; // Verde laser - constexpr SDL_Color PENTAGON = {.r = 0, .g = 255, .b = 255, .a = 255}; // Cyan pur "esquivador" - constexpr SDL_Color QUADRAT = {.r = 255, .g = 0, .b = 0, .a = 255}; // Roig pur "tank" - constexpr SDL_Color MOLINILLO = {.r = 255, .g = 0, .b = 255, .a = 255}; // Magenta pur "agressiu" - constexpr SDL_Color WOUNDED = {.r = 255, .g = 220, .b = 60, .a = 255}; // Dorado: enemigo herido + constexpr SDL_Color SHIP = {.r = 255, .g = 255, .b = 255, .a = 255}; // Blanco neutro + constexpr SDL_Color BULLET = {.r = 155, .g = 255, .b = 175, .a = 255}; // Verde laser + constexpr SDL_Color PENTAGON = {.r = 0, .g = 255, .b = 255, .a = 255}; // Cyan pur "esquivador" + constexpr SDL_Color SQUARE = {.r = 255, .g = 0, .b = 0, .a = 255}; // Roig pur "tank" + constexpr SDL_Color PINWHEEL = {.r = 255, .g = 0, .b = 255, .a = 255}; // Magenta pur "agressiu" + constexpr SDL_Color WOUNDED = {.r = 255, .g = 220, .b = 60, .a = 255}; // Dorado: enemigo herido } // namespace Defaults::Palette diff --git a/source/core/defaults/physics.hpp b/source/core/defaults/physics.hpp index a515610..3fa8f7a 100644 --- a/source/core/defaults/physics.hpp +++ b/source/core/defaults/physics.hpp @@ -28,14 +28,14 @@ namespace Defaults::Physics { // Explosions (debris physics) namespace Debris { - constexpr float VELOCITAT_BASE = 80.0F; // Velocidad inicial (px/s) - constexpr float VARIACIO_VELOCITAT = 40.0F; // ±variació aleatòria (px/s) - constexpr float ACCELERACIO = -60.0F; // Fricció/desacceleració (px/s²) - constexpr float ROTACIO_MIN = 0.1F; // Rotación mínima (rad/s ~5.7°/s) - constexpr float ROTACIO_MAX = 0.3F; // Rotación màxima (rad/s ~17.2°/s) - constexpr float TEMPS_VIDA = 2.0F; // Vida mínima garantida (s) — després pot morir per velocitat baixa - constexpr float TEMPS_VIDA_NAU = 3.0F; // Ship debris min lifetime (matches DEATH_DURATION) - constexpr float SHRINK_RATE = 1.0F; // Reducció de mida (1.0 = encoge a 0 al final del min_lifetime) + constexpr float SPEED_BASE = 80.0F; // Velocidad inicial (px/s) + constexpr float VARIACIO_SPEED = 40.0F; // ±variació aleatòria (px/s) + constexpr float ACCELERACIO = -60.0F; // Fricció/desacceleració (px/s²) + constexpr float ROTATION_MIN = 0.1F; // Rotación mínima (rad/s ~5.7°/s) + constexpr float ROTATION_MAX = 0.3F; // Rotación màxima (rad/s ~17.2°/s) + constexpr float TEMPS_VIDA = 2.0F; // Vida mínima garantida (s) — després pot morir per velocitat baixa + constexpr float TEMPS_VIDA_NAU = 3.0F; // Ship debris min lifetime (matches DEATH_DURATION) + constexpr float SHRINK_RATE = 1.0F; // Reducció de mida (1.0 = encoge a 0 al final del min_lifetime) // Política de mort: passat el min_lifetime, el fragment mor quan la // seva velocity cau per sota d'aquest llindar. Així els fragments @@ -48,9 +48,9 @@ namespace Defaults::Physics { constexpr float RESTITUTION_BOUNDS = 0.7F; // Herència de velocity angular (trayectorias curvas) - constexpr float FACTOR_HERENCIA_MIN = 0.7F; // Mínimo 70% del drotacio heredat - constexpr float FACTOR_HERENCIA_MAX = 1.0F; // Màxim 100% del drotacio heredat - constexpr float FRICCIO_ANGULAR = 0.5F; // Desacceleració angular (rad/s²) + constexpr float INHERITANCE_FACTOR_MIN = 0.7F; // Mínimo 70% del drotacio heredat + constexpr float INHERITANCE_FACTOR_MAX = 1.0F; // Màxim 100% del drotacio heredat + constexpr float FRICCIO_ANGULAR = 0.5F; // Desacceleració angular (rad/s²) // Velocity heredada de la nau a l'explosió (80% del feel original). constexpr float SHIP_VELOCITY_INHERITANCE = 0.8F; @@ -68,7 +68,7 @@ namespace Defaults::Physics { // Angular velocity sin for trajectory inheritance // Excess above this threshold is converted to tangential linear velocity // Prevents "vortex trap" problem with high-rotation enemies - constexpr float VELOCITAT_ROT_MAX = 1.5F; // rad/s (~86°/s) + constexpr float SPEED_ROT_MAX = 1.5F; // rad/s (~86°/s) } // namespace Debris } // namespace Defaults::Physics diff --git a/source/core/defaults/title.hpp b/source/core/defaults/title.hpp index 02c1af8..8f5c3ff 100644 --- a/source/core/defaults/title.hpp +++ b/source/core/defaults/title.hpp @@ -68,7 +68,7 @@ namespace Defaults::Title { constexpr float FLOATING_SCALE = 1.0F * SHIP_BASE_SCALE; // Flotante: scale base // Offset de entrada (ajustat automáticoament a l'scale) - // Fórmula: (radi màxim de la ship * scale de entrada) + margen + // Fórmula: (radius màxim de la ship * scale de entrada) + margen constexpr float ENTRY_OFFSET = (SHIP_MAX_RADIUS * ENTRY_SCALE_START) + ENTRY_OFFSET_MARGIN; // Vec2 de fuga (centro para l'animación de salida) diff --git a/source/core/graphics/border.cpp b/source/core/graphics/border.cpp index 1c67aac..ccc7f08 100644 --- a/source/core/graphics/border.cpp +++ b/source/core/graphics/border.cpp @@ -23,12 +23,12 @@ namespace Graphics { } void Border::bumpAt(Vec2 contact_point, float strength) { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; const std::array DISTANCES = { - /* TOP */ std::abs(contact_point.y - zona.y), - /* RIGHT */ std::abs((zona.x + zona.w) - contact_point.x), - /* BOTTOM */ std::abs((zona.y + zona.h) - contact_point.y), - /* LEFT */ std::abs(contact_point.x - zona.x)}; + /* TOP */ std::abs(contact_point.y - zone.y), + /* RIGHT */ std::abs((zone.x + zone.w) - contact_point.x), + /* BOTTOM */ std::abs((zone.y + zone.h) - contact_point.y), + /* LEFT */ std::abs(contact_point.x - zone.x)}; int closest_idx = 0; float closest_dist = DISTANCES[0]; @@ -71,11 +71,11 @@ namespace Graphics { } // namespace void Border::draw() const { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - const int X1 = static_cast(zona.x); - const int Y1 = static_cast(zona.y); - const int X2 = static_cast(zona.x + zona.w); - const int Y2 = static_cast(zona.y + zona.h); + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + const int X1 = static_cast(zone.x); + const int Y1 = static_cast(zone.y); + const int X2 = static_cast(zone.x + zone.w); + const int Y2 = static_cast(zone.y + zone.h); const int OFF_TOP = static_cast(sides_[SIDE_TOP].displacement_px); const int OFF_RIGHT = static_cast(sides_[SIDE_RIGHT].displacement_px); diff --git a/source/core/graphics/playfield.cpp b/source/core/graphics/playfield.cpp index 4358cb5..2c87e60 100644 --- a/source/core/graphics/playfield.cpp +++ b/source/core/graphics/playfield.cpp @@ -141,9 +141,9 @@ namespace Graphics { } void Playfield::buildLines() { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - const float CELL_W = zona.w / static_cast(Defaults::Playfield::COLUMNS); - const float CELL_H = zona.h / static_cast(Defaults::Playfield::ROWS); + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + const float CELL_W = zone.w / static_cast(Defaults::Playfield::COLUMNS); + const float CELL_H = zone.h / static_cast(Defaults::Playfield::ROWS); const float SUB_W = CELL_W / static_cast(Defaults::Playfield::SUBDIVISIONS); const float SUB_H = CELL_H / static_cast(Defaults::Playfield::SUBDIVISIONS); const int SUB_VERTS = Defaults::Playfield::COLUMNS * Defaults::Playfield::SUBDIVISIONS; @@ -154,14 +154,14 @@ namespace Graphics { // Verticals: posicions i ∈ [1, SUB_VERTS-1]. for (int i = 1; i < SUB_VERTS; i++) { - const float X = zona.x + (static_cast(i) * SUB_W); + const float X = zone.x + (static_cast(i) * SUB_W); const bool IS_MAIN = (i % Defaults::Playfield::SUBDIVISIONS) == 0; const float BRIGHTNESS = IS_MAIN ? Defaults::Playfield::GRID_BRIGHTNESS : Defaults::Playfield::SUBGRID_BRIGHTNESS; verticals.push_back(Line{ - .start = {.x = X, .y = zona.y}, - .end = {.x = X, .y = zona.y + zona.h}, + .start = {.x = X, .y = zone.y}, + .end = {.x = X, .y = zone.y + zone.h}, .brightness = BRIGHTNESS, .spawn_time_s = 0.0F, .is_vertical = true}); @@ -169,14 +169,14 @@ namespace Graphics { // Horitzontals: posicions j ∈ [1, SUB_HORIZ-1]. for (int j = 1; j < SUB_HORIZ; j++) { - const float Y = zona.y + (static_cast(j) * SUB_H); + const float Y = zone.y + (static_cast(j) * SUB_H); const bool IS_MAIN = (j % Defaults::Playfield::SUBDIVISIONS) == 0; const float BRIGHTNESS = IS_MAIN ? Defaults::Playfield::GRID_BRIGHTNESS : Defaults::Playfield::SUBGRID_BRIGHTNESS; horizontals.push_back(Line{ - .start = {.x = zona.x, .y = Y}, - .end = {.x = zona.x + zona.w, .y = Y}, + .start = {.x = zone.x, .y = Y}, + .end = {.x = zone.x + zone.w, .y = Y}, .brightness = BRIGHTNESS, .spawn_time_s = 0.0F, .is_vertical = false}); diff --git a/source/core/graphics/starfield_parallax.cpp b/source/core/graphics/starfield_parallax.cpp index 8735ace..c6b0813 100644 --- a/source/core/graphics/starfield_parallax.cpp +++ b/source/core/graphics/starfield_parallax.cpp @@ -25,11 +25,11 @@ namespace Graphics { } void StarfieldParallax::buildStars() { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - const float MIN_X = zona.x; - const float MAX_X = zona.x + zona.w; - const float MIN_Y = zona.y; - const float MAX_Y = zona.y + zona.h; + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + const float MIN_X = zone.x; + const float MAX_X = zone.x + zone.w; + const float MIN_Y = zone.y; + const float MAX_Y = zone.y + zone.h; // Color únic per a totes les estrelles: el mateix blanc-blau gel // del starfield del títol (Defaults::Title::Colors::STARFIELD). @@ -89,13 +89,13 @@ namespace Graphics { } void StarfieldParallax::update(float delta_time, Vec2 world_velocity) { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - const float MIN_X = zona.x; - const float MAX_X = zona.x + zona.w; - const float MIN_Y = zona.y; - const float MAX_Y = zona.y + zona.h; - const float W = zona.w; - const float H = zona.h; + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + const float MIN_X = zone.x; + const float MAX_X = zone.x + zone.w; + const float MIN_Y = zone.y; + const float MAX_Y = zone.y + zone.h; + const float W = zone.w; + const float H = zone.h; for (auto& star : stars_) { const float FACTOR = layerParallax(star.layer); diff --git a/source/core/graphics/vector_text.cpp b/source/core/graphics/vector_text.cpp index c89c383..2e30810 100644 --- a/source/core/graphics/vector_text.cpp +++ b/source/core/graphics/vector_text.cpp @@ -235,19 +235,19 @@ namespace Graphics { } } - void VectorText::renderCentered(const std::string& text, const Vec2& centre_punt, float scale, float spacing, float brightness, SDL_Color color) const { + void VectorText::renderCentered(const std::string& text, const Vec2& centre_point, float scale, float spacing, float brightness, SDL_Color color) const { // Calcular dimensions del text float text_width = getTextWidth(text, scale, spacing); float text_height = getTextHeight(scale); // Calcular posición de l'esquina superior izquierda // restant la meitat de las dimensions del point central - Vec2 posicio_esquerra = { - .x = centre_punt.x - (text_width / 2.0F), - .y = centre_punt.y - (text_height / 2.0F)}; + Vec2 top_left_position = { + .x = centre_point.x - (text_width / 2.0F), + .y = centre_point.y - (text_height / 2.0F)}; // Delegar al método render() existent - render(text, posicio_esquerra, scale, spacing, brightness, color); + render(text, top_left_position, scale, spacing, brightness, color); } auto VectorText::getTextWidth(const std::string& text, float scale, float spacing) -> float { diff --git a/source/core/graphics/vector_text.hpp b/source/core/graphics/vector_text.hpp index f96a480..864390e 100644 --- a/source/core/graphics/vector_text.hpp +++ b/source/core/graphics/vector_text.hpp @@ -31,12 +31,12 @@ namespace Graphics { // Renderizar string centrado en un punto // - text: cadena a renderizar - // - centre_punt: punto central del texto (no esquina superior izquierda) + // - centre_point: punto central del texto (no esquina superior izquierda) // - scale: factor de scale (1.0 = 20×40 px por carácter) // - spacing: espacio entre caracteres en píxeles (a scale 1.0) // - brightness: factor de brightness (0.0-1.0, default 1.0 = màxima brightness) // - color: color RGBA explícit; si alpha==0 (default) s'usa l'oscil·lador global - void renderCentered(const std::string& text, const Vec2& centre_punt, float scale = 1.0F, float spacing = 2.0F, float brightness = 1.0F, SDL_Color color = {0, 0, 0, 0}) const; + void renderCentered(const std::string& text, const Vec2& centre_point, float scale = 1.0F, float spacing = 2.0F, float brightness = 1.0F, SDL_Color color = {0, 0, 0, 0}) const; // Calcular ancho total de un string (útil para centrado). // Es estático: no depende del estado del VectorText (el ancho viene de diff --git a/source/core/physics/collision.hpp b/source/core/physics/collision.hpp index 9f5adea..55c0ac3 100644 --- a/source/core/physics/collision.hpp +++ b/source/core/physics/collision.hpp @@ -17,7 +17,7 @@ namespace Physics { return false; } - // Calcular radi combinat (con amplificador per hitbox generós) + // Calcular radius combinat (con amplificador per hitbox generós) float suma_radis = (a.getCollisionRadius() + b.getCollisionRadius()) * amplifier; float suma_radis_sq = suma_radis * suma_radis; @@ -31,8 +31,8 @@ namespace Physics { return dist_sq <= suma_radis_sq; } - // Swept collision: una entitat mòbil (radi r_a) s'ha desplaçat de p0 a p1 aquest - // frame. Comprova si el segment expandit pel radi conjunt (r_a + radi de b, amb + // Swept collision: una entitat mòbil (radius r_a) s'ha desplaçat de p0 a p1 aquest + // frame. Comprova si el segment expandit pel radius conjunt (r_a + radius de b, amb // amplificador) toca el cercle de l'entity b. Equival al check discrete quan // p0 == p1 (sense moviment). Evita tunneling a velocitats altes. inline auto checkCollisionSwept(const Vec2& p0, const Vec2& p1, float r_a, const Entities::Entity& b, float amplifier = 1.0F) -> bool { diff --git a/source/core/system/game_config.hpp b/source/core/system/game_config.hpp index 5517b5c..25f26f6 100644 --- a/source/core/system/game_config.hpp +++ b/source/core/system/game_config.hpp @@ -4,52 +4,52 @@ namespace GameConfig { -// Mode de juego -enum class Mode : std::uint8_t { - NORMAL, // Partida normal - DEMO // Mode demostració (futur) -}; + // Mode de juego + enum class Mode : std::uint8_t { + NORMAL, // Partida normal + DEMO // Mode demostració (futur) + }; -// Configuración de una match -struct MatchConfig { - bool jugador1_actiu{false}; // Es active el player 1? - bool jugador2_actiu{false}; // Es active el player 2? + // Configuración de una match + struct MatchConfig { + bool player1_active{false}; // Es active el player 1? + bool player2_active{false}; // Es active el player 2? Mode mode{Mode::NORMAL}; // Mode de juego // Métodos auxiliars // Retorna true si solo hay un player active [[nodiscard]] auto isSinglePlayer() const -> bool { - return (jugador1_actiu && !jugador2_actiu) || - (!jugador1_actiu && jugador2_actiu); + return (player1_active && !player2_active) || + (!player1_active && player2_active); } // Retorna true si hay dos jugadors active [[nodiscard]] auto isCoop() const -> bool { - return jugador1_actiu && jugador2_actiu; + return player1_active && player2_active; } // Retorna true si no hay sin player active [[nodiscard]] auto hasNoPlayers() const -> bool { - return !jugador1_actiu && !jugador2_actiu; + return !player1_active && !player2_active; } // Compte de jugadors active (0, 1 o 2) [[nodiscard]] auto getPlayerCount() const -> uint8_t { - return (jugador1_actiu ? 1 : 0) + (jugador2_actiu ? 1 : 0); + return (player1_active ? 1 : 0) + (player2_active ? 1 : 0); } // Retorna l'ID de l'únic player active (0 o 1) // Solo vàlid si es_un_jugador() retorna true [[nodiscard]] auto getSinglePlayerId() const -> uint8_t { - if (jugador1_actiu && !jugador2_actiu) { + if (player1_active && !player2_active) { return 0; } - if (!jugador1_actiu && jugador2_actiu) { + if (!player1_active && player2_active) { return 1; } return 0; // Fallback (necesario comprovar es_un_jugador() primer) } -}; + }; } // namespace GameConfig diff --git a/source/game/constants.hpp b/source/game/constants.hpp index 0e7fda0..c86f17d 100644 --- a/source/game/constants.hpp +++ b/source/game/constants.hpp @@ -7,40 +7,40 @@ namespace Constants { // Límits de objectes constexpr int MAX_ORNIS = Defaults::Entities::MAX_ORNIS; - constexpr int MAX_BALES = Defaults::Entities::MAX_BALES; + constexpr int MAX_BULLETS = Defaults::Entities::MAX_BULLETS; // Matemàtiques constexpr float PI = Defaults::Math::PI; - // Helpers per comprovar límits de zona + // Helpers per comprovar límits de zone inline auto isInPlayArea(float x, float y) -> bool { const SDL_FPoint POINT = {x, y}; return SDL_PointInRectFloat(&POINT, &Defaults::Zones::PLAYAREA); } inline void getPlayAreaBounds(float& min_x, float& max_x, float& min_y, float& max_y) { - const auto& zona = Defaults::Zones::PLAYAREA; - min_x = zona.x; - max_x = zona.x + zona.w; - min_y = zona.y; - max_y = zona.y + zona.h; + const auto& zone = Defaults::Zones::PLAYAREA; + min_x = zone.x; + max_x = zone.x + zone.w; + min_y = zone.y; + max_y = zone.y + zone.h; } - // Obtenir límits segurs (compensant radi de l'entidad) - inline void getSafePlayAreaBounds(float radi, float& min_x, float& max_x, float& min_y, float& max_y) { - const auto& zona = Defaults::Zones::PLAYAREA; - constexpr float MARGE_SEGURETAT = 10.0F; // Safety margin + // Obtenir límits segurs (compensant radius de l'entidad) + inline void getSafePlayAreaBounds(float radius, float& min_x, float& max_x, float& min_y, float& max_y) { + const auto& zone = Defaults::Zones::PLAYAREA; + constexpr float SAFETY_MARGIN = 10.0F; // Safety margin - min_x = zona.x + radi + MARGE_SEGURETAT; - max_x = zona.x + zona.w - radi - MARGE_SEGURETAT; - min_y = zona.y + radi + MARGE_SEGURETAT; - max_y = zona.y + zona.h - radi - MARGE_SEGURETAT; + min_x = zone.x + radius + SAFETY_MARGIN; + max_x = zone.x + zone.w - radius - SAFETY_MARGIN; + min_y = zone.y + radius + SAFETY_MARGIN; + max_y = zone.y + zone.h - radius - SAFETY_MARGIN; } // Obtenir centro de l'àrea de juego - inline void getPlayAreaCenter(float& centre_x, float& centre_y) { - const auto& zona = Defaults::Zones::PLAYAREA; - centre_x = zona.x + (zona.w / 2.0F); - centre_y = zona.y + (zona.h / 2.0F); + inline void getPlayAreaCenter(float& center_x, float& center_y) { + const auto& zone = Defaults::Zones::PLAYAREA; + center_x = zone.x + (zone.w / 2.0F); + center_y = zone.y + (zone.h / 2.0F); } } // namespace Constants diff --git a/source/game/effects/debris.hpp b/source/game/effects/debris.hpp index 53eeb80..c08bde1 100644 --- a/source/game/effects/debris.hpp +++ b/source/game/effects/debris.hpp @@ -39,7 +39,7 @@ namespace Effects { // Política: viu sempre durant min_lifetime, després mor quan // |velocity| < MIN_SPEED_TO_DIE (definit en Defaults). Així els // fragments ràpids no "popen" en moviment. - float temps_vida; // Temps transcorregut (segons) + float elapsed_time; // Temps transcorregut (segons) float min_lifetime; // Temps mínim garantit (segons) bool active; // Està actiu? diff --git a/source/game/effects/debris_manager.cpp b/source/game/effects/debris_manager.cpp index 12f4cbc..2a5b900 100644 --- a/source/game/effects/debris_manager.cpp +++ b/source/game/effects/debris_manager.cpp @@ -135,7 +135,7 @@ namespace Effects { float speed = velocitat_base + (((std::rand() / static_cast(RAND_MAX)) * 2.0F - 1.0F) * - Defaults::Physics::Debris::VARIACIO_VELOCITAT); + Defaults::Physics::Debris::VARIACIO_SPEED); debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x; debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y; debris->acceleration = friction; @@ -150,7 +150,7 @@ namespace Effects { // Vida i shrinking — min_lifetime és el temps mínim garantit; després // el fragment mor quan |velocity| < MIN_SPEED_TO_DIE. - debris->temps_vida = 0.0F; + debris->elapsed_time = 0.0F; debris->min_lifetime = lifetime; debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE; @@ -170,16 +170,16 @@ namespace Effects { // FASE 1: Aplicar herència i variació float factor_herencia = - Defaults::Physics::Debris::FACTOR_HERENCIA_MIN + + Defaults::Physics::Debris::INHERITANCE_FACTOR_MIN + ((std::rand() / static_cast(RAND_MAX)) * - (Defaults::Physics::Debris::FACTOR_HERENCIA_MAX - - Defaults::Physics::Debris::FACTOR_HERENCIA_MIN)); + (Defaults::Physics::Debris::INHERITANCE_FACTOR_MAX - + Defaults::Physics::Debris::INHERITANCE_FACTOR_MIN)); float velocitat_ang_heretada = velocitat_angular * factor_herencia; float variacio = ((std::rand() / static_cast(RAND_MAX)) * 0.2F) - 0.1F; velocitat_ang_heretada *= (1.0F + variacio); // FASE 2: Cap a la velocity màxima; l'excés es converteix en tangencial - constexpr float CAP = Defaults::Physics::Debris::VELOCITAT_ROT_MAX; + constexpr float CAP = Defaults::Physics::Debris::SPEED_ROT_MAX; float abs_ang = std::abs(velocitat_ang_heretada); float sign_ang = (velocitat_ang_heretada >= 0.0F) ? 1.0F : -1.0F; @@ -213,10 +213,10 @@ namespace Effects { // Rotación visual aleatòria (factor = 0.0 o sin velocidad angular) debris.velocitat_rot_visual = - Defaults::Physics::Debris::ROTACIO_MIN + + Defaults::Physics::Debris::ROTATION_MIN + ((std::rand() / static_cast(RAND_MAX)) * - (Defaults::Physics::Debris::ROTACIO_MAX - - Defaults::Physics::Debris::ROTACIO_MIN)); + (Defaults::Physics::Debris::ROTATION_MAX - + Defaults::Physics::Debris::ROTATION_MIN)); // 50% probabilitat de rotación en sentit contrari if (std::rand() % 2 == 0) { @@ -266,12 +266,12 @@ namespace Effects { } // 1. Actualitzar time de vida - debris.temps_vida += delta_time; + debris.elapsed_time += delta_time; // Política de mort: viu sí o sí durant min_lifetime; després mor // quan la velocity cau per sota d'un llindar. Així els fragments // ràpids no desapareixen en moviment. - if (debris.temps_vida >= debris.min_lifetime) { + if (debris.elapsed_time >= debris.min_lifetime) { const float SPEED_SQ = (debris.velocity.x * debris.velocity.x) + (debris.velocity.y * debris.velocity.y); if (SPEED_SQ < Defaults::Physics::Debris::MIN_SPEED_TO_DIE_SQ) { @@ -344,7 +344,7 @@ namespace Effects { // 6. Shrink lineal sobre la longitud ORIGINAL (no iteratiu). // SHRINK_T va de 0 a 1 al llarg de min_lifetime; després queda // a 1 i el shrink_factor manté el valor mínim (1 - factor_shrink). - const float SHRINK_T = std::min(debris.temps_vida / debris.min_lifetime, 1.0F); + const float SHRINK_T = std::min(debris.elapsed_time / debris.min_lifetime, 1.0F); const float SHRINK_FACTOR = std::max(0.0F, 1.0F - (debris.factor_shrink * SHRINK_T)); // 7. Reconstruir p1/p2 des de la geometria autoritaritzada: diff --git a/source/game/effects/firework.hpp b/source/game/effects/firework.hpp index cbf4816..a748e78 100644 --- a/source/game/effects/firework.hpp +++ b/source/game/effects/firework.hpp @@ -16,7 +16,7 @@ namespace Effects { // tail = head − velocity_normalitzada × current_length. // // Cicle de vida: - // Fase 1 (temps_vida < grow_duration): current_length creix linealment + // Fase 1 (elapsed_time < grow_duration): current_length creix linealment // de 0 a max_length. Brillor al màxim. // Fase 2: current_length = max_length × (speed/initial_speed) i brillor // amb la mateixa proporció. Mor quan length o brightness cauen sota @@ -30,7 +30,7 @@ namespace Effects { float max_length; // Longitud màxima (final de la fase de creixement) float grow_duration; // Temps de creixement de 0 a max_length (s) - float temps_vida; // Acumulador (s) + float elapsed_time; // Acumulador (s) float initial_speed; // Speed inicial per a la proporció de fase 2 float brightness; // 0..1 diff --git a/source/game/effects/firework_manager.cpp b/source/game/effects/firework_manager.cpp index 5abb433..70f1dba 100644 --- a/source/game/effects/firework_manager.cpp +++ b/source/game/effects/firework_manager.cpp @@ -61,7 +61,7 @@ namespace Effects { } } - void FireworkManager::spawn(const Vec2& origen, + void FireworkManager::spawn(const Vec2& origin, SDL_Color color, float initial_speed, int n_points, @@ -74,7 +74,7 @@ namespace Effects { // Notificar als subscriptors (playfield pulses, etc.). if (spawn_callback_) { - spawn_callback_(origen); + spawn_callback_(origin); } const float ANGLE_STEP = 2.0F * Defaults::Math::PI / static_cast(n_points); @@ -94,7 +94,7 @@ namespace Effects { const float SPEED = initial_speed + (randSigned() * Defaults::FX::Firework::SPEED_VARIATION); - fw->head = origen; + fw->head = origin; fw->velocity = {.x = std::cos(ANGLE) * SPEED, .y = std::sin(ANGLE) * SPEED}; fw->acceleration = Defaults::FX::Firework::FRICTION; @@ -102,7 +102,7 @@ namespace Effects { fw->max_length = Defaults::FX::Firework::MAX_LENGTH; fw->grow_duration = Defaults::FX::Firework::GROW_DURATION; - fw->temps_vida = 0.0F; + fw->elapsed_time = 0.0F; fw->initial_speed = SPEED; fw->brightness = initial_brightness; @@ -119,7 +119,7 @@ namespace Effects { continue; } - fw.temps_vida += delta_time; + fw.elapsed_time += delta_time; // 1. Fricció lineal (aplicar en la direcció del movement). const float SPEED = std::sqrt( @@ -144,9 +144,9 @@ namespace Effects { bounceOffPlayArea(fw.head, fw.velocity); // 4. Calcular longitud i brillor segons fase. - if (fw.temps_vida < fw.grow_duration) { + if (fw.elapsed_time < fw.grow_duration) { // Fase 1: creixement lineal de 0 a max_length. - const float T = fw.temps_vida / fw.grow_duration; + const float T = fw.elapsed_time / fw.grow_duration; fw.current_length = fw.max_length * T; fw.brightness = Defaults::FX::Firework::INITIAL_BRIGHTNESS; } else { diff --git a/source/game/effects/firework_manager.hpp b/source/game/effects/firework_manager.hpp index 9fd031b..fb06179 100644 --- a/source/game/effects/firework_manager.hpp +++ b/source/game/effects/firework_manager.hpp @@ -21,7 +21,7 @@ namespace Effects { class FireworkManager { public: // Notificació opcional cada vegada que es genera un burst. - using SpawnCallback = std::function; + using SpawnCallback = std::function; explicit FireworkManager(Rendering::Renderer* renderer); @@ -37,7 +37,7 @@ namespace Effects { // initial_brightness: 0..1. // glow: si true, cada partícula es renderitza amb halo neon. // glow_color: color del halo. Si alpha==0, agafa el color de la línia. - void spawn(const Vec2& origen, + void spawn(const Vec2& origin, SDL_Color color = Defaults::FX::Firework::DEFAULT_COLOR, float initial_speed = Defaults::FX::Firework::SPEED, int n_points = Defaults::FX::Firework::N_POINTS, diff --git a/source/game/effects/floating_score.hpp b/source/game/effects/floating_score.hpp index 166be1e..6f3afda 100644 --- a/source/game/effects/floating_score.hpp +++ b/source/game/effects/floating_score.hpp @@ -9,9 +9,9 @@ namespace Effects { -// FloatingScore: text animat que muestra points guanyats -// S'activa cuando es destrueix un enemy i s'esvaeix después de un time -struct FloatingScore { + // FloatingScore: text animat que muestra points guanyats + // S'activa cuando es destrueix un enemy i s'esvaeix después de un time + struct FloatingScore { // Text a mostrar (e.g., "100", "150", "200") std::string text; @@ -22,12 +22,12 @@ struct FloatingScore { Vec2 velocity; // px/s (normalment sin amunt: {0.0f, -30.0f}) // Animación de fade - float temps_vida; // Temps transcorregut (segons) - float temps_max; // Temps de vida màxim (segons) - float brightness; // Brillantor calculada (0.0-1.0) + float elapsed_time; // Temps transcorregut (segons) + float max_lifetime; // Temps de vida màxim (segons) + float brightness; // Brillantor calculada (0.0-1.0) // Estat bool active; -}; + }; } // namespace Effects diff --git a/source/game/effects/floating_score_manager.cpp b/source/game/effects/floating_score_manager.cpp index efb7a14..40ce5b3 100644 --- a/source/game/effects/floating_score_manager.cpp +++ b/source/game/effects/floating_score_manager.cpp @@ -7,93 +7,93 @@ namespace Effects { -FloatingScoreManager::FloatingScoreManager(Rendering::Renderer* renderer) - : text_(renderer) { - // Inicialitzar todos los slots como inactius - for (auto& pf : pool_) { - pf.active = false; - } -} - -void FloatingScoreManager::crear(int points, const Vec2& position) { - // 1. Trobar slot lliure - FloatingScore* pf = findFreeSlot(); - if (pf == nullptr) { - return; // Pool ple (improbable) - } - - // 2. Inicialitzar puntuación flotante - pf->text = std::to_string(points); - pf->position = position; - pf->velocity = {.x = Defaults::FloatingScore::VELOCITY_X, - .y = Defaults::FloatingScore::VELOCITY_Y}; - pf->temps_vida = 0.0F; - pf->temps_max = Defaults::FloatingScore::LIFETIME; - pf->brightness = 1.0F; - pf->active = true; -} - -void FloatingScoreManager::update(float delta_time) { - for (auto& pf : pool_) { - if (!pf.active) { - continue; - } - - // 1. Actualitzar posición (deriva sin amunt) - pf.position.x += pf.velocity.x * delta_time; - pf.position.y += pf.velocity.y * delta_time; - - // 2. Actualitzar time de vida - pf.temps_vida += delta_time; - - // 3. Calcular brightness (fade lineal) - float progress = pf.temps_vida / pf.temps_max; // 0.0 → 1.0 - pf.brightness = 1.0F - progress; // 1.0 → 0.0 - - // 4. Desactivar cuando acaba el time - if (pf.temps_vida >= pf.temps_max) { + FloatingScoreManager::FloatingScoreManager(Rendering::Renderer* renderer) + : text_(renderer) { + // Inicialitzar todos los slots como inactius + for (auto& pf : pool_) { pf.active = false; } } -} -void FloatingScoreManager::draw() { - for (const auto& pf : pool_) { - if (!pf.active) { - continue; + void FloatingScoreManager::crear(int points, const Vec2& position) { + // 1. Trobar slot lliure + FloatingScore* pf = findFreeSlot(); + if (pf == nullptr) { + return; // Pool ple (improbable) } - // Renderizar centrat con brightness (fade) - constexpr float SCALE = Defaults::FloatingScore::SCALE; - constexpr float SPACING = Defaults::FloatingScore::SPACING; - - text_.renderCentered(pf.text, pf.position, SCALE, SPACING, pf.brightness); + // 2. Inicialitzar puntuación flotante + pf->text = std::to_string(points); + pf->position = position; + pf->velocity = {.x = Defaults::FloatingScore::VELOCITY_X, + .y = Defaults::FloatingScore::VELOCITY_Y}; + pf->elapsed_time = 0.0F; + pf->max_lifetime = Defaults::FloatingScore::LIFETIME; + pf->brightness = 1.0F; + pf->active = true; } -} -void FloatingScoreManager::reset() { - for (auto& pf : pool_) { - pf.active = false; - } -} + void FloatingScoreManager::update(float delta_time) { + for (auto& pf : pool_) { + if (!pf.active) { + continue; + } -auto FloatingScoreManager::getActiveCount() const -> int { - int count = 0; - for (const auto& pf : pool_) { - if (pf.active) { - count++; + // 1. Actualitzar posición (deriva sin amunt) + pf.position.x += pf.velocity.x * delta_time; + pf.position.y += pf.velocity.y * delta_time; + + // 2. Actualitzar time de vida + pf.elapsed_time += delta_time; + + // 3. Calcular brightness (fade lineal) + float progress = pf.elapsed_time / pf.max_lifetime; // 0.0 → 1.0 + pf.brightness = 1.0F - progress; // 1.0 → 0.0 + + // 4. Desactivar cuando acaba el time + if (pf.elapsed_time >= pf.max_lifetime) { + pf.active = false; + } } } - return count; -} -auto FloatingScoreManager::findFreeSlot() -> FloatingScore* { - for (auto& pf : pool_) { - if (!pf.active) { - return &pf; + void FloatingScoreManager::draw() { + for (const auto& pf : pool_) { + if (!pf.active) { + continue; + } + + // Renderizar centrat con brightness (fade) + constexpr float SCALE = Defaults::FloatingScore::SCALE; + constexpr float SPACING = Defaults::FloatingScore::SPACING; + + text_.renderCentered(pf.text, pf.position, SCALE, SPACING, pf.brightness); } } - return nullptr; // Pool ple -} + + void FloatingScoreManager::reset() { + for (auto& pf : pool_) { + pf.active = false; + } + } + + auto FloatingScoreManager::getActiveCount() const -> int { + int count = 0; + for (const auto& pf : pool_) { + if (pf.active) { + count++; + } + } + return count; + } + + auto FloatingScoreManager::findFreeSlot() -> FloatingScore* { + for (auto& pf : pool_) { + if (!pf.active) { + return &pf; + } + } + return nullptr; // Pool ple + } } // namespace Effects diff --git a/source/game/entities/bullet.cpp b/source/game/entities/bullet.cpp index dc77574..99187d3 100644 --- a/source/game/entities/bullet.cpp +++ b/source/game/entities/bullet.cpp @@ -53,7 +53,7 @@ void Bullet::init() { body_.clearAccumulators(); } -void Bullet::disparar(const Vec2& position, float angle, uint8_t owner_id) { +void Bullet::fire(const Vec2& position, float angle, uint8_t owner_id) { // Activar bullet is_active_ = true; @@ -80,7 +80,7 @@ void Bullet::disparar(const Vec2& position, float angle, uint8_t owner_id) { } void Bullet::update(float /*delta_time*/) { - // No-op: la desactivació per fora-de-zona viu a + // No-op: la desactivació per fora-de-zone viu a // Systems::Collision::desactivateOutOfBoundsBullets() perquè així té accés // al DebrisManager i pot generar el "trencament" visual de la bala alhora. // El moviment l'integra PhysicsWorld; postUpdate sincronitza center_ i prev_position_. diff --git a/source/game/entities/bullet.hpp b/source/game/entities/bullet.hpp index 7d9c8be..72c1a81 100644 --- a/source/game/entities/bullet.hpp +++ b/source/game/entities/bullet.hpp @@ -17,7 +17,7 @@ class Bullet : public Entities::Entity { explicit Bullet(Rendering::Renderer* renderer); void init() override; - void disparar(const Vec2& position, float angle, uint8_t owner_id); + void fire(const Vec2& position, float angle, uint8_t owner_id); void update(float delta_time) override; void postUpdate(float delta_time) override; void draw() const override; diff --git a/source/game/entities/enemy.cpp b/source/game/entities/enemy.cpp index b739635..3cd8948 100644 --- a/source/game/entities/enemy.cpp +++ b/source/game/entities/enemy.cpp @@ -41,7 +41,7 @@ namespace { Enemy::Enemy(Rendering::Renderer* renderer) : Entity(renderer), - tracking_strength_(Defaults::Enemies::Cuadrado::TRACKING_STRENGTH) { + tracking_strength_(Defaults::Enemies::Square::TRACKING_STRENGTH) { brightness_ = Defaults::Brightness::ENEMIC; // Configuración del cuerpo físico — defaults para enemy genérico. @@ -58,43 +58,43 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { const char* shape_file = nullptr; float base_speed = 0.0F; - float drotacio_min = 0.0F; - float drotacio_max = 0.0F; + float rotation_delta_min = 0.0F; + float rotation_delta_max = 0.0F; float type_mass = Defaults::Enemies::Body::DEFAULT_MASS; switch (type_) { case EnemyType::PENTAGON: shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE; - base_speed = Defaults::Enemies::Pentagon::VELOCITAT; - drotacio_min = Defaults::Enemies::Pentagon::DROTACIO_MIN; - drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX; + base_speed = Defaults::Enemies::Pentagon::SPEED; + rotation_delta_min = Defaults::Enemies::Pentagon::ROTATION_DELTA_MIN; + rotation_delta_max = Defaults::Enemies::Pentagon::ROTATION_DELTA_MAX; type_mass = Defaults::Enemies::Pentagon::MASS; break; - case EnemyType::QUADRAT: - shape_file = Defaults::Enemies::Cuadrado::SHAPE_FILE; - base_speed = Defaults::Enemies::Cuadrado::VELOCITAT; - drotacio_min = Defaults::Enemies::Cuadrado::DROTACIO_MIN; - drotacio_max = Defaults::Enemies::Cuadrado::DROTACIO_MAX; - type_mass = Defaults::Enemies::Cuadrado::MASS; + case EnemyType::SQUARE: + shape_file = Defaults::Enemies::Square::SHAPE_FILE; + base_speed = Defaults::Enemies::Square::SPEED; + rotation_delta_min = Defaults::Enemies::Square::ROTATION_DELTA_MIN; + rotation_delta_max = Defaults::Enemies::Square::ROTATION_DELTA_MAX; + type_mass = Defaults::Enemies::Square::MASS; tracking_timer_ = 0.0F; break; - case EnemyType::MOLINILLO: - shape_file = Defaults::Enemies::Molinillo::SHAPE_FILE; - base_speed = Defaults::Enemies::Molinillo::VELOCITAT; - drotacio_min = Defaults::Enemies::Molinillo::DROTACIO_MIN; - drotacio_max = Defaults::Enemies::Molinillo::DROTACIO_MAX; - type_mass = Defaults::Enemies::Molinillo::MASS; + case EnemyType::PINWHEEL: + shape_file = Defaults::Enemies::Pinwheel::SHAPE_FILE; + base_speed = Defaults::Enemies::Pinwheel::SPEED; + rotation_delta_min = Defaults::Enemies::Pinwheel::ROTATION_DELTA_MIN; + rotation_delta_max = Defaults::Enemies::Pinwheel::ROTATION_DELTA_MAX; + type_mass = Defaults::Enemies::Pinwheel::MASS; break; default: std::cerr << "[Enemy] Error: tipo desconocido (" << static_cast(type_) << "), usando PENTAGON\n"; shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE; - base_speed = Defaults::Enemies::Pentagon::VELOCITAT; - drotacio_min = Defaults::Enemies::Pentagon::DROTACIO_MIN; - drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX; + base_speed = Defaults::Enemies::Pentagon::SPEED; + rotation_delta_min = Defaults::Enemies::Pentagon::ROTATION_DELTA_MIN; + rotation_delta_max = Defaults::Enemies::Pentagon::ROTATION_DELTA_MAX; break; } @@ -131,7 +131,7 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { const int RANGE_Y = static_cast(max_y - min_y); center_.x = static_cast((std::rand() % RANGE_X) + static_cast(min_x)); center_.y = static_cast((std::rand() % RANGE_Y) + static_cast(min_y)); - std::cout << "[Enemy] Advertencia: spawn sin zona segura tras " + std::cout << "[Enemy] Advertencia: spawn sin zone segura tras " << Defaults::Enemies::Spawn::MAX_SPAWN_ATTEMPTS << " intentos\n"; } } else { @@ -152,28 +152,28 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { body_.clearAccumulators(); // Rotación visual aleatoria (independiente del body) - const float DROTACIO_RANGE = drotacio_max - drotacio_min; - drotacio_ = drotacio_min + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * DROTACIO_RANGE); - rotacio_ = 0.0F; + const float ROTATION_DELTA_RANGE = rotation_delta_max - rotation_delta_min; + rotation_delta_ = rotation_delta_min + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * ROTATION_DELTA_RANGE); + rotation_ = 0.0F; // Estado de animación - animacio_ = EnemyAnimation(); - animacio_.drotacio_base = drotacio_; - animacio_.drotacio_objetivo = drotacio_; - animacio_.drotacio_t = 1.0F; + animation_ = EnemyAnimation(); + animation_.rotation_delta_base = rotation_delta_; + animation_.rotation_delta_target = rotation_delta_; + animation_.rotation_delta_t = 1.0F; // Invulnerabilidad post-spawn - timer_invulnerabilitat_ = Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; + invulnerability_timer_ = Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; brightness_ = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; // Timer para próximo cambio de dirección (Pentagon) direction_change_timer_ = 0.0F; - esta_ = true; + is_active_ = true; } void Enemy::update(float delta_time) { - if (!esta_) { + if (!is_active_) { return; } @@ -189,11 +189,11 @@ void Enemy::update(float delta_time) { } // Decremento de invulnerabilidad + LERP de brightness - if (timer_invulnerabilitat_ > 0.0F) { - timer_invulnerabilitat_ -= delta_time; - timer_invulnerabilitat_ = std::max(timer_invulnerabilitat_, 0.0F); + if (invulnerability_timer_ > 0.0F) { + invulnerability_timer_ -= delta_time; + invulnerability_timer_ = std::max(invulnerability_timer_, 0.0F); - const float T_INV = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; + const float T_INV = invulnerability_timer_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; const float T = 1.0F - T_INV; const float SMOOTH_T = T * T * (3.0F - (2.0F * T)); constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; @@ -209,11 +209,11 @@ void Enemy::update(float delta_time) { case EnemyType::PENTAGON: behaviorPentagon(delta_time); break; - case EnemyType::QUADRAT: - behaviorQuadrat(delta_time); + case EnemyType::SQUARE: + behaviorSquare(delta_time); break; - case EnemyType::MOLINILLO: - behaviorMolinillo(delta_time); + case EnemyType::PINWHEEL: + behaviorPinwheel(delta_time); break; } } @@ -222,18 +222,18 @@ void Enemy::update(float delta_time) { updateAnimation(delta_time); // Rotación visual (decoración, no afecta movimiento) - rotacio_ += drotacio_ * delta_time; + rotation_ += rotation_delta_ * delta_time; } void Enemy::postUpdate(float /*delta_time*/) { // Sincronizar mirror tras la integración del world. - if (esta_) { + if (is_active_) { center_ = body_.position; } } void Enemy::draw() const { - if (!esta_ || !shape_) { + if (!is_active_ || !shape_) { return; } const float SCALE = computeCurrentScale(); @@ -242,11 +242,11 @@ void Enemy::draw() const { case EnemyType::PENTAGON: color = Defaults::Palette::PENTAGON; break; - case EnemyType::QUADRAT: - color = Defaults::Palette::QUADRAT; + case EnemyType::SQUARE: + color = Defaults::Palette::SQUARE; break; - case EnemyType::MOLINILLO: - color = Defaults::Palette::MOLINILLO; + case EnemyType::PINWHEEL: + color = Defaults::Palette::PINWHEEL; break; } @@ -260,11 +260,11 @@ void Enemy::draw() const { } } - Rendering::renderShape(renderer_, shape_, center_, rotacio_, SCALE, 1.0F, brightness_, color); + Rendering::renderShape(renderer_, shape_, center_, rotation_, SCALE, 1.0F, brightness_, color); } -void Enemy::destruir() { - esta_ = false; +void Enemy::destroy() { + is_active_ = false; body_.velocity = Vec2{}; body_.angular_velocity = 0.0F; body_.radius = 0.0F; // No colisiona mientras está inactivo @@ -273,7 +273,7 @@ void Enemy::destruir() { last_hit_by_ = 0xFF; } -void Enemy::herir(uint8_t shooter_id) { +void Enemy::hurt(uint8_t shooter_id) { wounded_timer_ = Defaults::Enemies::Wounded::DURATION; last_hit_by_ = shooter_id; // El so HIT ara el reprodueix la bala quan es trenca en debris @@ -312,7 +312,7 @@ void Enemy::behaviorPentagon(float delta_time) { if (RAND_VAL < Defaults::Enemies::Pentagon::ZIGZAG_PROB_PER_SECOND * delta_time) { const float CURRENT_ANGLE = velocityToAngle(body_.velocity); const float DELTA = (static_cast(std::rand()) / static_cast(RAND_MAX)) * - Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX; + Defaults::Enemies::Pentagon::ANGLE_CHANGE_MAX; const float NEW_ANGLE = CURRENT_ANGLE + ((std::rand() % 2 == 0) ? DELTA : -DELTA); const float SPEED = body_.velocity.length(); setVelocityFromAngle(NEW_ANGLE, SPEED); @@ -320,12 +320,12 @@ void Enemy::behaviorPentagon(float delta_time) { } } -// QUADRAT: tracking discreto cada TRACKING_INTERVAL. Ajusta dirección +// SQUARE: tracking discreto cada TRACKING_INTERVAL. Ajusta dirección // hacia el ship mezclando con tracking_strength_. -void Enemy::behaviorQuadrat(float delta_time) { +void Enemy::behaviorSquare(float delta_time) { tracking_timer_ += delta_time; - if (tracking_timer_ >= Defaults::Enemies::Cuadrado::TRACKING_INTERVAL && ship_position_ != nullptr) { + if (tracking_timer_ >= Defaults::Enemies::Square::TRACKING_INTERVAL && ship_position_ != nullptr) { tracking_timer_ = 0.0F; const Vec2 TO_SHIP = *ship_position_ - center_; @@ -348,89 +348,89 @@ void Enemy::behaviorQuadrat(float delta_time) { } } -// MOLINILLO: movimiento recto + boost de rotación visual cerca del ship. +// PINWHEEL: movimiento recto + boost de rotación visual cerca del ship. // Sin tracking — solo cambios de dirección raros (igual que Pentagon pero // con probabilidad mucho menor). -void Enemy::behaviorMolinillo(float /*delta_time*/) { +void Enemy::behaviorPinwheel(float /*delta_time*/) { // Boost de rotación visual por proximidad al ship if (ship_position_ != nullptr) { const Vec2 TO_SHIP = *ship_position_ - center_; const float DIST = TO_SHIP.length(); - if (DIST < Defaults::Enemies::Molinillo::PROXIMITY_DISTANCE) { - drotacio_ = animacio_.drotacio_base * Defaults::Enemies::Molinillo::DROTACIO_PROXIMITY_MULTIPLIER; + if (DIST < Defaults::Enemies::Pinwheel::PROXIMITY_DISTANCE) { + rotation_delta_ = animation_.rotation_delta_base * Defaults::Enemies::Pinwheel::ROTATION_DELTA_PROXIMITY_MULTIPLIER; } else { - drotacio_ = animacio_.drotacio_base; + rotation_delta_ = animation_.rotation_delta_base; } } // Movimiento lineal puro: el world se encarga de integrar y rebotar. } void Enemy::updateAnimation(float delta_time) { - updatePalpitation(delta_time); + updatePulse(delta_time); updateRotationAcceleration(delta_time); } -void Enemy::updatePalpitation(float delta_time) { - if (animacio_.palpitacio_activa) { - animacio_.palpitacio_fase += 2.0F * Constants::PI * animacio_.palpitacio_frequencia * delta_time; - animacio_.palpitacio_temps_restant -= delta_time; - if (animacio_.palpitacio_temps_restant <= 0.0F) { - animacio_.palpitacio_activa = false; +void Enemy::updatePulse(float delta_time) { + if (animation_.pulse_active) { + animation_.pulse_phase += 2.0F * Constants::PI * animation_.pulse_frequency * delta_time; + animation_.pulse_time_remaining -= delta_time; + if (animation_.pulse_time_remaining <= 0.0F) { + animation_.pulse_active = false; } } else { const float RAND_VAL = static_cast(std::rand()) / static_cast(RAND_MAX); - const float TRIGGER_PROB = Defaults::Enemies::Animation::PALPITACIO_TRIGGER_PROB * delta_time; + const float TRIGGER_PROB = Defaults::Enemies::Animation::PULSE_TRIGGER_PROB * delta_time; if (RAND_VAL < TRIGGER_PROB) { - animacio_.palpitacio_activa = true; - animacio_.palpitacio_fase = 0.0F; + animation_.pulse_active = true; + animation_.pulse_phase = 0.0F; - const float FREQ_RANGE = Defaults::Enemies::Animation::PALPITACIO_FREQ_MAX - - Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN; - animacio_.palpitacio_frequencia = Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN + + const float FREQ_RANGE = Defaults::Enemies::Animation::PULSE_FREQ_MAX - + Defaults::Enemies::Animation::PULSE_FREQ_MIN; + animation_.pulse_frequency = Defaults::Enemies::Animation::PULSE_FREQ_MIN + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * FREQ_RANGE); - const float AMP_RANGE = Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MAX - - Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN; - animacio_.palpitacio_amplitud = Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN + + const float AMP_RANGE = Defaults::Enemies::Animation::PULSE_AMPLITUD_MAX - + Defaults::Enemies::Animation::PULSE_AMPLITUD_MIN; + animation_.pulse_amplitude = Defaults::Enemies::Animation::PULSE_AMPLITUD_MIN + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * AMP_RANGE); - const float DUR_RANGE = Defaults::Enemies::Animation::PALPITACIO_DURACIO_MAX - - Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN; - animacio_.palpitacio_temps_restant = Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN + + const float DUR_RANGE = Defaults::Enemies::Animation::PULSE_DURATION_MAX - + Defaults::Enemies::Animation::PULSE_DURATION_MIN; + animation_.pulse_time_remaining = Defaults::Enemies::Animation::PULSE_DURATION_MIN + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * DUR_RANGE); } } } void Enemy::updateRotationAcceleration(float delta_time) { - if (animacio_.drotacio_t < 1.0F) { - animacio_.drotacio_t += delta_time / animacio_.drotacio_duracio; - if (animacio_.drotacio_t >= 1.0F) { - animacio_.drotacio_t = 1.0F; - animacio_.drotacio_base = animacio_.drotacio_objetivo; - drotacio_ = animacio_.drotacio_base; + if (animation_.rotation_delta_t < 1.0F) { + animation_.rotation_delta_t += delta_time / animation_.rotation_delta_duration; + if (animation_.rotation_delta_t >= 1.0F) { + animation_.rotation_delta_t = 1.0F; + animation_.rotation_delta_base = animation_.rotation_delta_target; + rotation_delta_ = animation_.rotation_delta_base; } else { - const float T = animacio_.drotacio_t; + const float T = animation_.rotation_delta_t; const float SMOOTH_T = T * T * (3.0F - (2.0F * T)); - const float INITIAL = animacio_.drotacio_base; - const float TARGET = animacio_.drotacio_objetivo; - drotacio_ = INITIAL + ((TARGET - INITIAL) * SMOOTH_T); + const float INITIAL = animation_.rotation_delta_base; + const float TARGET = animation_.rotation_delta_target; + rotation_delta_ = INITIAL + ((TARGET - INITIAL) * SMOOTH_T); } } else { const float RAND_VAL = static_cast(std::rand()) / static_cast(RAND_MAX); - const float TRIGGER_PROB = Defaults::Enemies::Animation::ROTACIO_ACCEL_TRIGGER_PROB * delta_time; + const float TRIGGER_PROB = Defaults::Enemies::Animation::ROTATION_ACCEL_TRIGGER_PROB * delta_time; if (RAND_VAL < TRIGGER_PROB) { - animacio_.drotacio_t = 0.0F; + animation_.rotation_delta_t = 0.0F; - const float MULT_RANGE = Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MAX - - Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN; - const float MULTIPLIER = Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN + + const float MULT_RANGE = Defaults::Enemies::Animation::ROTATION_ACCEL_MULTIPLIER_MAX - + Defaults::Enemies::Animation::ROTATION_ACCEL_MULTIPLIER_MIN; + const float MULTIPLIER = Defaults::Enemies::Animation::ROTATION_ACCEL_MULTIPLIER_MIN + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * MULT_RANGE); - animacio_.drotacio_objetivo = animacio_.drotacio_base * MULTIPLIER; + animation_.rotation_delta_target = animation_.rotation_delta_base * MULTIPLIER; - const float DUR_RANGE = Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MAX - - Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN; - animacio_.drotacio_duracio = Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN + + const float DUR_RANGE = Defaults::Enemies::Animation::ROTATION_ACCEL_DURATION_MAX - + Defaults::Enemies::Animation::ROTATION_ACCEL_DURATION_MIN; + animation_.rotation_delta_duration = Defaults::Enemies::Animation::ROTATION_ACCEL_DURATION_MIN + ((static_cast(std::rand()) / static_cast(RAND_MAX)) * DUR_RANGE); } } @@ -438,15 +438,15 @@ void Enemy::updateRotationAcceleration(float delta_time) { auto Enemy::computeCurrentScale() const -> float { float scale = 1.0F; - if (timer_invulnerabilitat_ > 0.0F) { - const float T_INV = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; + if (invulnerability_timer_ > 0.0F) { + const float T_INV = invulnerability_timer_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; const float T = 1.0F - T_INV; const float SMOOTH_T = T * T * (3.0F - (2.0F * T)); constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_SCALE_START; constexpr float END = Defaults::Enemies::Spawn::INVULNERABILITY_SCALE_END; scale = START + ((END - START) * SMOOTH_T); - } else if (animacio_.palpitacio_activa) { - scale += animacio_.palpitacio_amplitud * std::sin(animacio_.palpitacio_fase); + } else if (animation_.pulse_active) { + scale += animation_.pulse_amplitude * std::sin(animation_.pulse_phase); } return scale; } @@ -454,22 +454,22 @@ auto Enemy::computeCurrentScale() const -> float { auto Enemy::getBaseVelocity() const -> float { switch (type_) { case EnemyType::PENTAGON: - return Defaults::Enemies::Pentagon::VELOCITAT; - case EnemyType::QUADRAT: - return Defaults::Enemies::Cuadrado::VELOCITAT; - case EnemyType::MOLINILLO: - return Defaults::Enemies::Molinillo::VELOCITAT; + return Defaults::Enemies::Pentagon::SPEED; + case EnemyType::SQUARE: + return Defaults::Enemies::Square::SPEED; + case EnemyType::PINWHEEL: + return Defaults::Enemies::Pinwheel::SPEED; default: - return Defaults::Enemies::Pentagon::VELOCITAT; + return Defaults::Enemies::Pentagon::SPEED; } } auto Enemy::getBaseRotation() const -> float { - return animacio_.drotacio_base != 0.0F ? animacio_.drotacio_base : drotacio_; + return animation_.rotation_delta_base != 0.0F ? animation_.rotation_delta_base : rotation_delta_; } void Enemy::setTrackingStrength(float strength) { - if (type_ == EnemyType::QUADRAT) { + if (type_ == EnemyType::SQUARE) { tracking_strength_ = strength; } } diff --git a/source/game/entities/enemy.hpp b/source/game/entities/enemy.hpp index 0fd600f..76d6ae3 100644 --- a/source/game/entities/enemy.hpp +++ b/source/game/entities/enemy.hpp @@ -13,24 +13,24 @@ // Tipo de enemy enum class EnemyType : uint8_t { PENTAGON = 0, // Pentágono esquivador (zigzag) - QUADRAT = 1, // Cuadrado perseguidor (tracks ship) - MOLINILLO = 2 // Molinillo agresivo (rápido, girando) + SQUARE = 1, // Square perseguidor (tracks ship) + PINWHEEL = 2 // Molinillo agresivo (rápido, girando) }; // Estado de animación (palpitación + rotación acelerada) struct EnemyAnimation { // Palpitación (efecto respiración) - bool palpitacio_activa = false; - float palpitacio_fase = 0.0F; - float palpitacio_frequencia = 2.0F; - float palpitacio_amplitud = 0.15F; - float palpitacio_temps_restant = 0.0F; + bool pulse_active = false; + float pulse_phase = 0.0F; + float pulse_frequency = 2.0F; + float pulse_amplitude = 0.15F; + float pulse_time_remaining = 0.0F; // Aceleración de rotación visual (modulación a largo plazo) - float drotacio_base = 0.0F; - float drotacio_objetivo = 0.0F; - float drotacio_t = 0.0F; - float drotacio_duracio = 0.0F; + float rotation_delta_base = 0.0F; + float rotation_delta_target = 0.0F; + float rotation_delta_t = 0.0F; + float rotation_delta_duration = 0.0F; }; class Enemy : public Entities::Entity { @@ -46,7 +46,7 @@ class Enemy : public Entities::Entity { void draw() const override; // Override: Interfaz de Entity - [[nodiscard]] auto isActive() const -> bool override { return esta_; } + [[nodiscard]] auto isActive() const -> bool override { return is_active_; } // Override: Interfaz de colisión [[nodiscard]] auto getCollisionRadius() const -> float override { @@ -56,14 +56,14 @@ class Enemy : public Entities::Entity { // poden abatre i el cos físic rebota amb la nau. El damage a la nau // segueix filtrat per `isInvulnerable()` al detectShipEnemy. [[nodiscard]] auto isCollidable() const -> bool override { - return esta_; + return is_active_; } // Marcar destruido (desactiva el cuerpo físicamente: radius=0) - void destruir(); + void destroy(); // Getters - [[nodiscard]] auto getRotationDelta() const -> float { return drotacio_; } + [[nodiscard]] auto getRotationDelta() const -> float { return rotation_delta_; } [[nodiscard]] auto getVelocityVector() const -> Vec2 { return body_.velocity; } // Set ship position reference for tracking behavior @@ -79,18 +79,18 @@ class Enemy : public Entities::Entity { // actual del body_.velocity. void setVelocity(float speed); void setRotation(float rot) { - drotacio_ = rot; - animacio_.drotacio_base = rot; + rotation_delta_ = rot; + animation_.rotation_delta_base = rot; } void setTrackingStrength(float strength); // Invulnerabilidad - [[nodiscard]] auto isInvulnerable() const -> bool { return timer_invulnerabilitat_ > 0.0F; } - [[nodiscard]] auto getInvulnerabilityTime() const -> float { return timer_invulnerabilitat_; } + [[nodiscard]] auto isInvulnerable() const -> bool { return invulnerability_timer_ > 0.0F; } + [[nodiscard]] auto getInvulnerabilityTime() const -> float { return invulnerability_timer_; } // Estado "herido": entre primer impacto de bala y explosión diferida. // shooter_id: id del jugador que herí; 0xFF = sin atribución (cadena, etc.). - void herir(uint8_t shooter_id = 0xFF); + void hurt(uint8_t shooter_id = 0xFF); [[nodiscard]] auto isWounded() const -> bool { return wounded_timer_ > 0.0F; } [[nodiscard]] auto getWoundedTimer() const -> float { return wounded_timer_; } [[nodiscard]] auto woundExpiredThisFrame() const -> bool { return wound_expired_this_frame_; } @@ -104,12 +104,12 @@ class Enemy : public Entities::Entity { // Miembros específicos (heredados: renderer_, shape_, center_, angle_, brightness_, body_). // Inicializados en la declaración: el ctor por defecto deja al enemy en estado "inactivo // como pentágono", coherente con lo que harán init() o el ctor con renderer al activarlo. - float drotacio_{0.0F}; // Velocidad angular visual (rad/s) — solo decoración, separada de body_.angular_velocity - float rotacio_{0.0F}; // Rotación visual acumulada (no afecta movimiento) - bool esta_{false}; + float rotation_delta_{0.0F}; // Velocidad angular visual (rad/s) — solo decoración, separada de body_.angular_velocity + float rotation_{0.0F}; // Rotación visual acumulada (no afecta movimiento) + bool is_active_{false}; EnemyType type_{EnemyType::PENTAGON}; - EnemyAnimation animacio_; + EnemyAnimation animation_; // Comportamiento type-specific float tracking_timer_{0.0F}; // Quadrat: tiempo desde último update de dirección @@ -118,7 +118,7 @@ class Enemy : public Entities::Entity { float direction_change_timer_{0.0F}; // Pentagon: tiempo para próximo cambio de dirección // Invulnerabilidad post-spawn - float timer_invulnerabilitat_{0.0F}; + float invulnerability_timer_{0.0F}; // Estado "herido": timer cuenta atrás; al cruzar 0 se marca expiración. float wounded_timer_{0.0F}; @@ -127,11 +127,11 @@ class Enemy : public Entities::Entity { // Métodos privados void updateAnimation(float delta_time); - void updatePalpitation(float delta_time); + void updatePulse(float delta_time); void updateRotationAcceleration(float delta_time); void behaviorPentagon(float delta_time); - void behaviorQuadrat(float delta_time); - void behaviorMolinillo(float delta_time); + void behaviorSquare(float delta_time); + void behaviorPinwheel(float delta_time); [[nodiscard]] auto computeCurrentScale() const -> float; // Estático: solo opera sobre ship_pos pasado; no consulta estado del enemy. static auto attemptSafeSpawn(const Vec2& ship_pos, float& out_x, float& out_y) -> bool; diff --git a/source/game/entities/ship.cpp b/source/game/entities/ship.cpp index 996948d..4313b01 100644 --- a/source/game/entities/ship.cpp +++ b/source/game/entities/ship.cpp @@ -44,10 +44,10 @@ void Ship::init(const Vec2* spawn_point, bool activar_invulnerabilitat) { if (spawn_point != nullptr) { center_ = *spawn_point; } else { - float centre_x; - float centre_y; - Constants::getPlayAreaCenter(centre_x, centre_y); - center_ = {.x = centre_x, .y = centre_y}; + float center_x; + float center_y; + Constants::getPlayAreaCenter(center_x, center_y); + center_ = {.x = center_x, .y = center_y}; } // Reset orientación @@ -180,7 +180,7 @@ void Ship::draw() const { Rendering::renderShape(renderer_, shape_, center_, angle_, SCALE, 1.0F, brightness_, color); } -void Ship::herir() { +void Ship::hurt() { hurt_timer_ = Defaults::Ship::Hurt::DURATION; Audio::get()->playSound(Defaults::Sound::HURT, Audio::Group::GAME); } diff --git a/source/game/entities/ship.hpp b/source/game/entities/ship.hpp index 2ff1696..a5b64cc 100644 --- a/source/game/entities/ship.hpp +++ b/source/game/entities/ship.hpp @@ -54,7 +54,7 @@ class Ship : public Entities::Entity { } // Estat "ferit": primera col·lisió amb enemic dispara HURT; segona durant HURT mata. - void herir(); + void hurt(); [[nodiscard]] auto isHurt() const -> bool { return hurt_timer_ > 0.0F; } [[nodiscard]] auto getHurtTimer() const -> float { return hurt_timer_; } diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 87cd6c0..0ce0e0c 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -38,9 +38,9 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context) // Debug output de la configuración std::cout << "[GameScene] Configuración de match - P1: " - << (match_config_.jugador1_actiu ? "ACTIU" : "INACTIU") + << (match_config_.player1_active ? "ACTIU" : "INACTIU") << ", P2: " - << (match_config_.jugador2_actiu ? "ACTIU" : "INACTIU") + << (match_config_.player2_active ? "ACTIU" : "INACTIU") << '\n'; // Consumir opciones (preparació per MODE_DEMO futur) @@ -61,7 +61,7 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context) // Basat en el codi Pascal original: line 376 std::srand(static_cast(std::time(nullptr))); - // Configurar el mundo físico con los límites de la zona de juego. + // Configurar el mundo físico con los límites de la zone de juego. physics_world_.clear(); physics_world_.setBounds(Defaults::Zones::PLAYAREA); @@ -77,17 +77,17 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context) }); // Fireworks generen una ripple gran al playfield (ona d'aigua centrada al burst). - firework_manager_.setSpawnCallback([this](Vec2 origen) { - playfield_.notifyExplosion(origen); + firework_manager_.setSpawnCallback([this](Vec2 origin) { + playfield_.notifyExplosion(origin); }); // Explosions properes a una paret també generen bump (falloff lineal amb la distància). debris_manager_.setExplosionCallback([this](Vec2 center) { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - const float DIST_LEFT = std::abs(center.x - zona.x); - const float DIST_RIGHT = std::abs((zona.x + zona.w) - center.x); - const float DIST_TOP = std::abs(center.y - zona.y); - const float DIST_BOTTOM = std::abs((zona.y + zona.h) - center.y); + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + const float DIST_LEFT = std::abs(center.x - zone.x); + const float DIST_RIGHT = std::abs((zone.x + zone.w) - center.x); + const float DIST_TOP = std::abs(center.y - zone.y); + const float DIST_BOTTOM = std::abs((zone.y + zone.h) - center.y); const float MIN_DIST = std::min({DIST_LEFT, DIST_RIGHT, DIST_TOP, DIST_BOTTOM}); if (MIN_DIST > Defaults::Border::EXPLOSION_FALLOFF_PX) { return; @@ -130,9 +130,9 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context) // Inicialitzar naves segons configuración (solo jugadors active) for (uint8_t i = 0; i < 2; i++) { - bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu; + bool player_active = (i == 0) ? match_config_.player1_active : match_config_.player2_active; - if (jugador_actiu) { + if (player_active) { // Jugador active: init normalment Vec2 spawn_pos = getSpawnPoint(i); ships_[i].init(&spawn_pos, false); // No invulnerability at start @@ -248,11 +248,11 @@ void GameScene::stepPhysics(float delta_time) { void GameScene::stepShootingInput() { auto* input = Input::get(); - if (match_config_.jugador1_actiu && + if (match_config_.player1_active && input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) { fireBullet(0); } - if (match_config_.jugador2_actiu && + if (match_config_.player2_active && input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) { fireBullet(1); } @@ -267,16 +267,16 @@ void GameScene::stepMidGameJoin() { // Solo se permite join si hay al menos un jugador vivo (no se puede // hacer join en pantalla vacía). const bool ALGU_VIU = - (match_config_.jugador1_actiu && hit_timer_per_player_[0] != Defaults::Game::HIT_TIMER_INACTIVE_PLAYER) || - (match_config_.jugador2_actiu && hit_timer_per_player_[1] != Defaults::Game::HIT_TIMER_INACTIVE_PLAYER); + (match_config_.player1_active && hit_timer_per_player_[0] != Defaults::Game::HIT_TIMER_INACTIVE_PLAYER) || + (match_config_.player2_active && hit_timer_per_player_[1] != Defaults::Game::HIT_TIMER_INACTIVE_PLAYER); if (!ALGU_VIU) { return; } auto* input = Input::get(); for (uint8_t pid = 0; pid < 2; pid++) { - const bool ACTIU = (pid == 0) ? match_config_.jugador1_actiu - : match_config_.jugador2_actiu; + const bool ACTIU = (pid == 0) ? match_config_.player1_active + : match_config_.player2_active; const bool MUERTO_SIN_VIDAS = hit_timer_per_player_[pid] == Defaults::Game::HIT_TIMER_INACTIVE_PLAYER; if (ACTIU && !MUERTO_SIN_VIDAS) { continue; // jugador ya está jugando @@ -375,8 +375,8 @@ void GameScene::stepDeathSequence(float delta_time) { // Sin vidas: marcar definitivamente muerto y comprobar transición a CONTINUE. hit_timer_per_player_[i] = Defaults::Game::HIT_TIMER_INACTIVE_PLAYER; - const bool P1_DEAD = !match_config_.jugador1_actiu || lives_per_player_[0] <= 0; - const bool P2_DEAD = !match_config_.jugador2_actiu || lives_per_player_[1] <= 0; + const bool P1_DEAD = !match_config_.player1_active || lives_per_player_[0] <= 0; + const bool P2_DEAD = !match_config_.player2_active || lives_per_player_[1] <= 0; if (P1_DEAD && P2_DEAD) { game_over_state_ = GameOverState::CONTINUE; continue_counter_ = Defaults::Game::CONTINUE_COUNT_START; @@ -440,10 +440,10 @@ void GameScene::runStageInitHud(float delta_time) { Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP2_RATIO_END); - if (match_config_.jugador1_actiu && SHIP1_P < 1.0F) { + if (match_config_.player1_active && SHIP1_P < 1.0F) { ships_[0].setCenter(Systems::InitHud::computeShipPosition(SHIP1_P, getSpawnPoint(0))); } - if (match_config_.jugador2_actiu && SHIP2_P < 1.0F) { + if (match_config_.player2_active && SHIP2_P < 1.0F) { ships_[1].setCenter(Systems::InitHud::computeShipPosition(SHIP2_P, getSpawnPoint(1))); } } @@ -453,7 +453,7 @@ void GameScene::runStageLevelStart(float delta_time) { // Ambas naves pueden moverse y disparar durante el intro. for (uint8_t i = 0; i < 2; i++) { - const bool ACTIU = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu; + const bool ACTIU = (i == 0) ? match_config_.player1_active : match_config_.player2_active; if (ACTIU && hit_timer_per_player_[i] == 0.0F) { ships_[i].processInput(delta_time, i); ships_[i].update(delta_time); @@ -481,7 +481,7 @@ void GameScene::runStagePlaying(float delta_time) { // Gameplay normal: ships activos + entidades + colisiones + efectos. for (uint8_t i = 0; i < 2; i++) { - const bool ACTIU = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu; + const bool ACTIU = (i == 0) ? match_config_.player1_active : match_config_.player2_active; if (ACTIU && hit_timer_per_player_[i] == 0.0F) { ships_[i].processInput(delta_time, i); ships_[i].update(delta_time); @@ -491,7 +491,7 @@ void GameScene::runStagePlaying(float delta_time) { enemy.update(delta_time); } - // Col·lisions primer, després desactivació per fora-de-zona: així una bala que + // Col·lisions primer, després desactivació per fora-de-zone: així una bala que // el mateix frame xoca amb un enemic i alhora surt del PLAYAREA es compta com a // impacte abans no se la trenqui per sortir. runCollisionDetections(); @@ -507,7 +507,7 @@ void GameScene::runStagePlaying(float delta_time) { void GameScene::runStageLevelCompleted(float delta_time) { stage_manager_->update(delta_time); for (uint8_t i = 0; i < 2; i++) { - const bool ACTIU = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu; + const bool ACTIU = (i == 0) ? match_config_.player1_active : match_config_.player2_active; if (ACTIU && hit_timer_per_player_[i] == 0.0F) { ships_[i].processInput(delta_time, i); ships_[i].update(delta_time); @@ -580,8 +580,8 @@ void GameScene::drawBullets() const { void GameScene::drawActiveShipsAlive() const { for (uint8_t i = 0; i < 2; i++) { - bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu; - if (jugador_actiu && hit_timer_per_player_[i] == 0.0F) { + bool player_active = (i == 0) ? match_config_.player1_active : match_config_.player2_active; + if (player_active && hit_timer_per_player_[i] == 0.0F) { ships_[i].draw(); } } @@ -613,10 +613,10 @@ void GameScene::drawGameOverState() { constexpr float SPACING = Defaults::Game::GameOverScreen::TEXT_SPACING; const SDL_FRect& play_area = Defaults::Zones::PLAYAREA; - float centre_x = play_area.x + (play_area.w / 2.0F); - float centre_y = play_area.y + (play_area.h / 2.0F); + float center_x = play_area.x + (play_area.w / 2.0F); + float center_y = play_area.y + (play_area.h / 2.0F); - text_.renderCentered(GAME_OVER_TEXT, {.x = centre_x, .y = centre_y}, SCALE, SPACING); + text_.renderCentered(GAME_OVER_TEXT, {.x = center_x, .y = center_y}, SCALE, SPACING); drawScoreboard(); } @@ -663,11 +663,11 @@ void GameScene::drawInitHudState() { Systems::InitHud::drawScoreboardAnimated(text_, buildScoreboard(), score_progress); } - if (ship1_progress > 0.0F && match_config_.jugador1_actiu && ships_[0].isActive()) { + if (ship1_progress > 0.0F && match_config_.player1_active && ships_[0].isActive()) { ships_[0].draw(); } - if (ship2_progress > 0.0F && match_config_.jugador2_actiu && ships_[1].isActive()) { + if (ship2_progress > 0.0F && match_config_.player2_active && ships_[1].isActive()) { ships_[1].draw(); } } @@ -743,7 +743,7 @@ void GameScene::tocado(uint8_t player_id) { SHIP_POS, SHIP_ANGLE, 1.0F, - Defaults::Physics::Debris::VELOCITAT_BASE, + Defaults::Physics::Debris::SPEED_BASE, SHIP_BRIGHT, INHERITED_VEL, 0.0F, // sense herència angular @@ -769,20 +769,20 @@ void GameScene::drawScoreboard() { const float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE; const float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING; - // Calcular centro de la zona del marcador + // Calcular centro de la zone del marcador const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD; - float centre_x = scoreboard_zone.w / 2.0F; - float centre_y = scoreboard_zone.y + (scoreboard_zone.h / 2.0F); + float center_x = scoreboard_zone.w / 2.0F; + float center_y = scoreboard_zone.y + (scoreboard_zone.h / 2.0F); // Renderizar centrat - text_.renderCentered(text, {.x = centre_x, .y = centre_y}, SCALE, SPACING); + text_.renderCentered(text, {.x = center_x, .y = center_y}, SCALE, SPACING); } auto GameScene::buildScoreboard() const -> std::string { // Puntuación P1 (6 dígits) - mostrar zeros si inactiu std::string score_p1; std::string vides_p1; - if (match_config_.jugador1_actiu) { + if (match_config_.player1_active) { score_p1 = std::to_string(score_per_player_[0]); score_p1 = std::string(6 - std::min(6, static_cast(score_p1.length())), '0') + score_p1; vides_p1 = (lives_per_player_[0] < 10) @@ -801,7 +801,7 @@ auto GameScene::buildScoreboard() const -> std::string { // Puntuación P2 (6 dígits) - mostrar zeros si inactiu std::string score_p2; std::string vides_p2; - if (match_config_.jugador2_actiu) { + if (match_config_.player2_active) { score_p2 = std::to_string(score_per_player_[1]); score_p2 = std::string(6 - std::min(6, static_cast(score_p2.length())), '0') + score_p2; vides_p2 = (lives_per_player_[1] < 10) @@ -890,7 +890,7 @@ void GameScene::drawStageMessage(const std::string& message) { // ======================================== auto GameScene::getSpawnPoint(uint8_t player_id) const -> Vec2 { - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; float x_ratio; if (match_config_.isSinglePlayer()) { @@ -904,8 +904,8 @@ auto GameScene::getSpawnPoint(uint8_t player_id) const -> Vec2 { } return { - .x = zona.x + (zona.w * x_ratio), - .y = zona.y + (zona.h * Defaults::Game::SPAWN_Y_RATIO)}; + .x = zone.x + (zone.w * x_ratio), + .y = zone.y + (zone.h * Defaults::Game::SPAWN_Y_RATIO)}; } void GameScene::fireBullet(uint8_t player_id) { @@ -927,15 +927,15 @@ void GameScene::fireBullet(uint8_t player_id) { float sin_a = std::sin(ship_angle); float tip_x = (LOCAL_TIP_X * cos_a) - (LOCAL_TIP_Y * sin_a) + ship_centre.x; float tip_y = (LOCAL_TIP_X * sin_a) + (LOCAL_TIP_Y * cos_a) + ship_centre.y; - Vec2 posicio_dispar = {.x = tip_x, .y = tip_y}; + Vec2 fire_position = {.x = tip_x, .y = tip_y}; // Buscar primera bullet inactiva en el pool del player. - // El pool global té MAX_BALES slots per jugador (P1=[0..MAX-1], P2=[MAX..2*MAX-1]). - constexpr int SLOTS_PER_PLAYER = Defaults::Entities::MAX_BALES; + // El pool global té MAX_BULLETS slots per jugador (P1=[0..MAX-1], P2=[MAX..2*MAX-1]). + constexpr int SLOTS_PER_PLAYER = Defaults::Entities::MAX_BULLETS; const int START_IDX = player_id * SLOTS_PER_PLAYER; for (int i = START_IDX; i < START_IDX + SLOTS_PER_PLAYER; i++) { if (!bullets_[i].isActive()) { - bullets_[i].disparar(posicio_dispar, ship_angle, player_id); + bullets_[i].fire(fire_position, ship_angle, player_id); break; } } @@ -950,10 +950,10 @@ void GameScene::drawContinue() { float escala_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_SCALE; float y_ratio_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_Y_RATIO; - float centre_x = play_area.x + (play_area.w / 2.0F); + float center_x = play_area.x + (play_area.w / 2.0F); float centre_y_continue = play_area.y + (play_area.h * y_ratio_continue); - text_.renderCentered(CONTINUE_TEXT, {.x = centre_x, .y = centre_y_continue}, escala_continue, SPACING); + text_.renderCentered(CONTINUE_TEXT, {.x = center_x, .y = centre_y_continue}, escala_continue, SPACING); // Countdown number (using constants) const std::string COUNTER_STR = std::to_string(continue_counter_); @@ -962,7 +962,7 @@ void GameScene::drawContinue() { float centre_y_counter = play_area.y + (play_area.h * y_ratio_counter); - text_.renderCentered(COUNTER_STR, {.x = centre_x, .y = centre_y_counter}, escala_counter, SPACING); + text_.renderCentered(COUNTER_STR, {.x = center_x, .y = centre_y_counter}, escala_counter, SPACING); // "CONTINUES LEFT" (conditional + using constants) if (!Defaults::Game::INFINITE_CONTINUES) { @@ -972,16 +972,16 @@ void GameScene::drawContinue() { float centre_y_info = play_area.y + (play_area.h * y_ratio_info); - text_.renderCentered(CONTINUES_TEXT, {.x = centre_x, .y = centre_y_info}, escala_info, SPACING); + text_.renderCentered(CONTINUES_TEXT, {.x = center_x, .y = centre_y_info}, escala_info, SPACING); } } void GameScene::joinPlayer(uint8_t player_id) { // Activate player if (player_id == 0) { - match_config_.jugador1_actiu = true; + match_config_.player1_active = true; } else { - match_config_.jugador2_actiu = true; + match_config_.player2_active = true; } // Reset stats diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index 09688d8..d634f81 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -67,7 +67,7 @@ class GameScene final : public Scene { std::array enemies_; // 6 balas: P1=[0,1,2], P2=[3,4,5]. El cast a size_t evita la // widening conversion implícita que detecta clang-tidy. - std::array(Constants::MAX_BALES) * 2> bullets_; + std::array(Constants::MAX_BULLETS) * 2> bullets_; std::array hit_timer_per_player_; // Death timers per player (seconds) // Lives and game over system diff --git a/source/game/scenes/logo_scene.cpp b/source/game/scenes/logo_scene.cpp index 5166bc3..ea1d787 100644 --- a/source/game/scenes/logo_scene.cpp +++ b/source/game/scenes/logo_scene.cpp @@ -20,18 +20,18 @@ using SceneManager::SceneContext; using SceneType = SceneContext::SceneType; using Option = SceneContext::Option; -// Helper: calcular el progrés individual de una lletra +// Helper: calcular el progrés individual de una letter // en función del progrés global (efecte seqüencial) -static auto computeLetterProgress(size_t letra_index, size_t num_letras, float global_progress, float threshold) -> float { - if (num_letras == 0) { +static auto computeLetterProgress(size_t letter_index, size_t num_letters, float global_progress, float threshold) -> float { + if (num_letters == 0) { return 1.0F; } - // Calcular time per lletra - float duration_per_letra = 1.0F / static_cast(num_letras); - float step = threshold * duration_per_letra; - float start = static_cast(letra_index) * step; - float end = start + duration_per_letra; + // Calcular time per letter + float duration_per_letter = 1.0F / static_cast(num_letters); + float step = threshold * duration_per_letter; + float start = static_cast(letter_index) * step; + float end = start + duration_per_letter; // Interpolar progrés if (global_progress < start) { @@ -46,16 +46,15 @@ static auto computeLetterProgress(size_t letra_index, size_t num_letras, float g LogoScene::LogoScene(SDLManager& sdl, SceneContext& context) : sdl_(sdl), context_(context), - - debris_manager_(std::make_unique(sdl.getRenderer())) - { + + debris_manager_(std::make_unique(sdl.getRenderer())) { std::cout << "SceneType Logo: Inicialitzant...\n"; // Consumir opciones (LOGO no processa opciones actualment) auto option = context_.consumeOption(); (void)option; // Suprimir warning - so_reproduit_.fill(false); // Inicialitzar seguiment de sons + sound_played_.fill(false); // Inicialitzar seguiment de sons initLetters(); } @@ -91,7 +90,7 @@ void LogoScene::initLetters() { "logo/letra_s.shp"}; // Pas 1: Carregar todas las formes i calcular amplades - float ancho_total = 0.0F; + float total_width = 0.0F; for (const auto& file : archivos) { auto shape = ShapeLoader::load(file); @@ -111,66 +110,66 @@ void LogoScene::initLetters() { } } - float ancho_sin_escalar = max_x - min_x; + float width_unscaled = max_x - min_x; - // IMPORTANT: Escalar ancho i offset con ESCALA_FINAL + // IMPORTANT: Escalar ancho i offset con FINAL_SCALE // per que las posicions finals coincideixin con la mida real de las lletres - float ancho = ancho_sin_escalar * ESCALA_FINAL; - float offset_centre = (shape->getCenter().x - min_x) * ESCALA_FINAL; + float width = width_unscaled * FINAL_SCALE; + float center_offset = (shape->getCenter().x - min_x) * FINAL_SCALE; - lletres_.push_back({shape, + letters_.push_back({shape, {.x = 0.0F, .y = 0.0F}, // Posición es calcularà después - ancho, - offset_centre}); + width, + center_offset}); - ancho_total += ancho; + total_width += width; } // Pas 2: Añadir espaiat entre lletres - ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1); + total_width += LETTER_SPACING * (letters_.size() - 1); // Pas 3: Calcular posición inicial (centrat horitzontal) - constexpr auto PANTALLA_ANCHO = static_cast(Defaults::Game::WIDTH); + constexpr auto SCREEN_WIDTH = static_cast(Defaults::Game::WIDTH); constexpr auto PANTALLA_ALTO = static_cast(Defaults::Game::HEIGHT); - float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0F; + float x_inicial = (SCREEN_WIDTH - total_width) / 2.0F; float y_centre = PANTALLA_ALTO / 2.0F; - // Pas 4: Assignar posicions a cada lletra + // Pas 4: Assignar posicions a cada letter float x_actual = x_inicial; - for (auto& lletra : lletres_) { + for (auto& letter : letters_) { // Posicionar el centro de la shape (shape_centre) en pantalla - // Usar offset_centre en lloc de ancho/2 perquè shape_centre + // Usar center_offset en lloc de ancho/2 perquè shape_centre // pot no estar exactament al mig del bounding box - lletra.position.x = x_actual + lletra.offset_centre; - lletra.position.y = y_centre; + letter.position.x = x_actual + letter.center_offset; + letter.position.y = y_centre; - // Avançar para següent lletra - x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; + // Avançar para següent letter + x_actual += letter.width + LETTER_SPACING; } - std::cout << "[LogoScene] " << lletres_.size() - << " lletres carregades, ancho total: " << ancho_total << " px\n"; + std::cout << "[LogoScene] " << letters_.size() + << " lletres carregades, ancho total: " << total_width << " px\n"; } void LogoScene::changeState(AnimationState nou_estat) { - estat_actual_ = nou_estat; - temps_estat_actual_ = 0.0F; // Reset time + current_state_ = nou_estat; + temps_current_state_ = 0.0F; // Reset time // Inicialitzar state de explosión if (nou_estat == AnimationState::EXPLOSION) { - lletra_explosio_index_ = 0; - temps_des_ultima_explosio_ = 0.0F; + letter_explosion_index_ = 0; + time_since_last_explosion_ = 0.0F; // Generar ordre aleatori de explosions - ordre_explosio_.clear(); - for (size_t i = 0; i < lletres_.size(); i++) { - ordre_explosio_.push_back(i); + explosion_order_.clear(); + for (size_t i = 0; i < letters_.size(); i++) { + explosion_order_.push_back(i); } std::random_device rd; std::mt19937 g(rd()); - std::shuffle(ordre_explosio_.begin(), ordre_explosio_.end(), g); + std::shuffle(explosion_order_.begin(), explosion_order_.end(), g); } else if (nou_estat == AnimationState::POST_EXPLOSION) { Audio::get()->playMusic("title.ogg"); } @@ -180,35 +179,35 @@ void LogoScene::changeState(AnimationState nou_estat) { } auto LogoScene::allLettersComplete() const -> bool { - // Cuando global_progress = 1.0, todas las lletres tenen letra_progress = 1.0 - return temps_estat_actual_ >= DURACIO_ZOOM; + // Cuando global_progress = 1.0, todas las lletres tenen letter_progress = 1.0 + return temps_current_state_ >= DURATION_ZOOM; } void LogoScene::updateExplosions(float delta_time) { - temps_des_ultima_explosio_ += delta_time; + time_since_last_explosion_ += delta_time; - // Comprovar si es el moment de explode la següent lletra - if (temps_des_ultima_explosio_ >= DELAY_ENTRE_EXPLOSIONS) { - if (lletra_explosio_index_ < lletres_.size()) { - // Explotar lletra actual (en ordre aleatori) - size_t index_actual = ordre_explosio_[lletra_explosio_index_]; - const auto& lletra = lletres_[index_actual]; + // Comprovar si es el moment de explode la següent letter + if (time_since_last_explosion_ >= DELAY_ENTRE_EXPLOSIONS) { + if (letter_explosion_index_ < letters_.size()) { + // Explotar letter actual (en ordre aleatori) + size_t index_actual = explosion_order_[letter_explosion_index_]; + const auto& letter = letters_[index_actual]; debris_manager_->explode( - lletra.shape, // Forma a explode - lletra.position, // Posición + letter.shape, // Forma a explode + letter.position, // Posición 0.0F, // Angle (sin rotación) - ESCALA_FINAL, // Escala (lletres a scale final) - VELOCITAT_EXPLOSIO, // Velocidad base + FINAL_SCALE, // Escala (lletres a scale final) + SPEED_EXPLOSIO, // Velocidad base 1.0F, // Brightness màxim (per defecte) {.x = 0.0F, .y = 0.0F} // Sin velocity (per defecte) ); - std::cout << "[LogoScene] Explota lletra " << lletra_explosio_index_ << "\n"; + std::cout << "[LogoScene] Explota letter " << letter_explosion_index_ << "\n"; - // Passar a la següent lletra - lletra_explosio_index_++; - temps_des_ultima_explosio_ = 0.0F; + // Passar a la següent letter + letter_explosion_index_++; + time_since_last_explosion_ = 0.0F; } else { // Todas las lletres han explotat, transición a POST_EXPLOSION changeState(AnimationState::POST_EXPLOSION); @@ -217,31 +216,31 @@ void LogoScene::updateExplosions(float delta_time) { } void LogoScene::update(float delta_time) { - temps_estat_actual_ += delta_time; + temps_current_state_ += delta_time; - switch (estat_actual_) { + switch (current_state_) { case AnimationState::PRE_ANIMATION: - if (temps_estat_actual_ >= DURACIO_PRE) { + if (temps_current_state_ >= DURATION_PRE) { changeState(AnimationState::ANIMATION); } break; case AnimationState::ANIMATION: { - // Reproduir so per cada lletra cuando comença a aparèixer - float global_progress = std::min(temps_estat_actual_ / DURACIO_ZOOM, 1.0F); + // Reproduir so per cada letter cuando comença a aparèixer + float global_progress = std::min(temps_current_state_ / DURATION_ZOOM, 1.0F); - for (size_t i = 0; i < lletres_.size() && i < so_reproduit_.size(); i++) { - if (!so_reproduit_[i]) { - float letra_progress = computeLetterProgress( + for (size_t i = 0; i < letters_.size() && i < sound_played_.size(); i++) { + if (!sound_played_[i]) { + float letter_progress = computeLetterProgress( i, - lletres_.size(), + letters_.size(), global_progress, - THRESHOLD_LETRA); + LETTER_THRESHOLD); - // Reproduir so cuando la lletra comença a aparèixer (progress > 0) - if (letra_progress > 0.0F) { + // Reproduir so cuando la letter comença a aparèixer (progress > 0) + if (letter_progress > 0.0F) { Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME); - so_reproduit_[i] = true; + sound_played_[i] = true; } } } @@ -253,7 +252,7 @@ void LogoScene::update(float delta_time) { } case AnimationState::POST_ANIMATION: - if (temps_estat_actual_ >= DURACIO_POST_ANIMATION) { + if (temps_current_state_ >= DURATION_POST_ANIMATION) { changeState(AnimationState::EXPLOSION); } break; @@ -263,7 +262,7 @@ void LogoScene::update(float delta_time) { break; case AnimationState::POST_EXPLOSION: - if (temps_estat_actual_ >= DURACIO_POST_EXPLOSION) { + if (temps_current_state_ >= DURATION_POST_EXPLOSION) { // Transición a pantalla de título context_.setNextScene(SceneType::TITLE); } @@ -283,47 +282,47 @@ void LogoScene::draw() { // Director ha hecho el clear; aquí solo pintamos lo de la escena. // PRE_ANIMATION: Solo pantalla negra (no se pinta nada). - if (estat_actual_ == AnimationState::PRE_ANIMATION) { + if (current_state_ == AnimationState::PRE_ANIMATION) { return; } // ANIMATION o POST_ANIMATION: Dibuixar lletres con animación - if (estat_actual_ == AnimationState::ANIMATION || - estat_actual_ == AnimationState::POST_ANIMATION) { + if (current_state_ == AnimationState::ANIMATION || + current_state_ == AnimationState::POST_ANIMATION) { float global_progress = - (estat_actual_ == AnimationState::ANIMATION) - ? std::min(temps_estat_actual_ / DURACIO_ZOOM, 1.0F) + (current_state_ == AnimationState::ANIMATION) + ? std::min(temps_current_state_ / DURATION_ZOOM, 1.0F) : 1.0F; // POST: mantenir al 100% - const Vec2 ORIGEN_ZOOM = {.x = ORIGEN_ZOOM_X, .y = ORIGEN_ZOOM_Y}; + const Vec2 ZOOM_ORIGIN = {.x = ZOOM_ORIGIN_X, .y = ZOOM_ORIGIN_Y}; - for (size_t i = 0; i < lletres_.size(); i++) { - const auto& lletra = lletres_[i]; + for (size_t i = 0; i < letters_.size(); i++) { + const auto& letter = letters_[i]; - float letra_progress = computeLetterProgress( + float letter_progress = computeLetterProgress( i, - lletres_.size(), + letters_.size(), global_progress, - THRESHOLD_LETRA); + LETTER_THRESHOLD); - if (letra_progress <= 0.0F) { + if (letter_progress <= 0.0F) { continue; } Vec2 pos_actual; pos_actual.x = - ORIGEN_ZOOM.x + ((lletra.position.x - ORIGEN_ZOOM.x) * letra_progress); + ZOOM_ORIGIN.x + ((letter.position.x - ZOOM_ORIGIN.x) * letter_progress); pos_actual.y = - ORIGEN_ZOOM.y + ((lletra.position.y - ORIGEN_ZOOM.y) * letra_progress); + ZOOM_ORIGIN.y + ((letter.position.y - ZOOM_ORIGIN.y) * letter_progress); - float t = letra_progress; + float t = letter_progress; float ease_factor = 1.0F - ((1.0F - t) * (1.0F - t)); float current_scale = - ESCALA_INICIAL + ((ESCALA_FINAL - ESCALA_INICIAL) * ease_factor); + INITIAL_SCALE + ((FINAL_SCALE - INITIAL_SCALE) * ease_factor); Rendering::renderShape( sdl_.getRenderer(), - lletra.shape, + letter.shape, pos_actual, 0.0F, current_scale, @@ -332,24 +331,24 @@ void LogoScene::draw() { } // EXPLOSION: Dibuixar solo lletres que aún no han explotat - if (estat_actual_ == AnimationState::EXPLOSION) { + if (current_state_ == AnimationState::EXPLOSION) { // Crear conjunt de lletres ya explotades std::set explotades; - for (size_t i = 0; i < lletra_explosio_index_; i++) { - explotades.insert(ordre_explosio_[i]); + for (size_t i = 0; i < letter_explosion_index_; i++) { + explotades.insert(explosion_order_[i]); } // Dibuixar solo lletres que NO han explotat - for (size_t i = 0; i < lletres_.size(); i++) { + for (size_t i = 0; i < letters_.size(); i++) { if (!explotades.contains(i)) { - const auto& lletra = lletres_[i]; + const auto& letter = letters_[i]; Rendering::renderShape( sdl_.getRenderer(), - lletra.shape, - lletra.position, + letter.shape, + letter.position, 0.0F, - ESCALA_FINAL, + FINAL_SCALE, 1.0F); } } diff --git a/source/game/scenes/logo_scene.hpp b/source/game/scenes/logo_scene.hpp index 418d1b2..ca75643 100644 --- a/source/game/scenes/logo_scene.hpp +++ b/source/game/scenes/logo_scene.hpp @@ -20,76 +20,76 @@ #include "game/effects/debris_manager.hpp" class LogoScene final : public Scene { - public: - explicit LogoScene(SDLManager& sdl, SceneManager::SceneContext& context); - ~LogoScene() override; // Destructor per aturar sons + public: + explicit LogoScene(SDLManager& sdl, SceneManager::SceneContext& context); + ~LogoScene() override; // Destructor per aturar sons - // Scene interface - void handleEvent(const SDL_Event& event) override; - void update(float delta_time) override; - void draw() override; - [[nodiscard]] auto isFinished() const -> bool override; + // Scene interface + void handleEvent(const SDL_Event& event) override; + void update(float delta_time) override; + void draw() override; + [[nodiscard]] auto isFinished() const -> bool override; - private: - // Màquina de estats per l'animación - enum class AnimationState : std::uint8_t { - PRE_ANIMATION, // Pantalla negra inicial - ANIMATION, // Animación de zoom de lletres - POST_ANIMATION, // Logo complet visible - EXPLOSION, // Explosión seqüencial de lletres - POST_EXPLOSION // Espera después de l'última explosión - }; + private: + // Màquina de estats per l'animación + enum class AnimationState : std::uint8_t { + PRE_ANIMATION, // Pantalla negra inicial + ANIMATION, // Animación de zoom de lletres + POST_ANIMATION, // Logo complet visible + EXPLOSION, // Explosión seqüencial de lletres + POST_EXPLOSION // Espera después de l'última explosión + }; - SDLManager& sdl_; - SceneManager::SceneContext& context_; - AnimationState estat_actual_{AnimationState::PRE_ANIMATION}; // Estat actual de la màquina - float - temps_estat_actual_{0.0F}; // Temps en l'state actual (reset en cada transición) + SDLManager& sdl_; + SceneManager::SceneContext& context_; + AnimationState current_state_{AnimationState::PRE_ANIMATION}; // Estat actual de la màquina + float + temps_current_state_{0.0F}; // Temps en l'state actual (reset en cada transición) - // Gestor de fragments de explosions - std::unique_ptr debris_manager_; + // Gestor de fragments de explosions + std::unique_ptr debris_manager_; - // Seguiment de explosions seqüencials - size_t lletra_explosio_index_{0}; // Índex de la següent lletra a explode - float temps_des_ultima_explosio_{0.0F}; // Temps desde l'última explosión - std::vector ordre_explosio_; // Ordre aleatori de índexs de lletres + // Seguiment de explosions seqüencials + size_t letter_explosion_index_{0}; // Índex de la següent letter a explode + float time_since_last_explosion_{0.0F}; // Temps desde l'última explosión + std::vector explosion_order_; // Ordre aleatori de índexs de lletres - // Estructura para cada lletra del logo - struct LetraLogo { - std::shared_ptr shape; - Vec2 position; // Posición final en pantalla - float ancho; // Ancho del bounding box - float offset_centre; // Distancia de min_x a shape_centre.x - }; + // Estructura para cada letter del logo + struct LogoLetter { + std::shared_ptr shape; + Vec2 position; // Posición final en pantalla + float width; // Ancho del bounding box + float center_offset; // Distancia de min_x a shape_centre.x + }; - std::vector lletres_; // 9 lletres: J-A-I-L-G-A-M-E-S + std::vector letters_; // 9 lletres: J-A-I-L-G-A-M-E-S - // Seguiment de sons de lletres (evitar reproduccions repetides) - std::array so_reproduit_; // Track si cada lletra ya ha reproduit el so + // Seguiment de sons de lletres (evitar reproduccions repetides) + std::array sound_played_; // Track si cada letter ya ha reproduit el so - // Constants de animación - static constexpr float DURACIO_PRE = 1.5F; // Duració PRE_ANIMATION (pantalla negra) - static constexpr float DURACIO_ZOOM = 4.0F; // Duració del zoom (segons) - static constexpr float DURACIO_POST_ANIMATION = 3.0F; // Duració POST_ANIMATION (logo complet) - static constexpr float DURACIO_POST_EXPLOSION = 3.0F; // Duració POST_EXPLOSION (espera final) - static constexpr float DELAY_ENTRE_EXPLOSIONS = 0.1F; // Temps entre explosions de lletres - static constexpr float VELOCITAT_EXPLOSIO = 240.0F; // Velocidad base fragments (px/s) - static constexpr float ESCALA_INICIAL = 0.1F; // Escala inicial (10%) - static constexpr float ESCALA_FINAL = 0.8F; // Escala final (80%) - static constexpr float ESPAI_ENTRE_LLETRES = 10.0F; // Espaiat entre lletres + // Constants de animación + static constexpr float DURATION_PRE = 1.5F; // Duració PRE_ANIMATION (pantalla negra) + static constexpr float DURATION_ZOOM = 4.0F; // Duració del zoom (segons) + static constexpr float DURATION_POST_ANIMATION = 3.0F; // Duració POST_ANIMATION (logo complet) + static constexpr float DURATION_POST_EXPLOSION = 3.0F; // Duració POST_EXPLOSION (espera final) + static constexpr float DELAY_ENTRE_EXPLOSIONS = 0.1F; // Temps entre explosions de lletres + static constexpr float SPEED_EXPLOSIO = 240.0F; // Velocidad base fragments (px/s) + static constexpr float INITIAL_SCALE = 0.1F; // Escala inicial (10%) + static constexpr float FINAL_SCALE = 0.8F; // Escala final (80%) + static constexpr float LETTER_SPACING = 10.0F; // Espaiat entre lletres - // Constants de animación seqüencial - static constexpr float THRESHOLD_LETRA = 0.6F; // Umbral per activar següent lletra (0.0-1.0) - static constexpr float ORIGEN_ZOOM_X = Defaults::Game::WIDTH * 0.5F; // Vec2 inicial X del zoom - static constexpr float ORIGEN_ZOOM_Y = Defaults::Game::HEIGHT * 0.4F; // Vec2 inicial Y del zoom + // Constants de animación seqüencial + static constexpr float LETTER_THRESHOLD = 0.6F; // Umbral per activar següent letter (0.0-1.0) + static constexpr float ZOOM_ORIGIN_X = Defaults::Game::WIDTH * 0.5F; // Vec2 inicial X del zoom + static constexpr float ZOOM_ORIGIN_Y = Defaults::Game::HEIGHT * 0.4F; // Vec2 inicial Y del zoom - // Métodos privats - void initLetters(); - void updateExplosions(float delta_time); - // Estático: solo consulta Input (singleton), no estado de la escena. - static auto checkSkipButtonPressed() -> bool; + // Métodos privats + void initLetters(); + void updateExplosions(float delta_time); + // Estático: solo consulta Input (singleton), no estado de la escena. + static auto checkSkipButtonPressed() -> bool; - // Métodos de gestió de estats - void changeState(AnimationState nou_estat); - [[nodiscard]] auto allLettersComplete() const -> bool; + // Métodos de gestió de estats + void changeState(AnimationState nou_estat); + [[nodiscard]] auto allLettersComplete() const -> bool; }; diff --git a/source/game/scenes/title_scene.cpp b/source/game/scenes/title_scene.cpp index 7012384..b755e96 100644 --- a/source/game/scenes/title_scene.cpp +++ b/source/game/scenes/title_scene.cpp @@ -36,15 +36,15 @@ TitleScene::TitleScene(SDLManager& sdl, SceneContext& context) text_(sdl.getRenderer()) { std::cout << "SceneType Titol: Inicialitzant...\n"; - match_config_.jugador1_actiu = false; - match_config_.jugador2_actiu = false; + match_config_.player1_active = false; + match_config_.player2_active = false; match_config_.mode = GameConfig::Mode::NORMAL; auto option = context_.consumeOption(); if (option == Option::JUMP_TO_TITLE_MAIN) { std::cout << "SceneType Titol: Opció JUMP_TO_TITLE_MAIN activada\n"; - estat_actual_ = TitleState::MAIN; - temps_estat_main_ = 0.0F; + current_state_ = TitleState::MAIN; + state_time_main_ = 0.0F; } // Càmera 3D: posicionada a l'origen, mirant cap a +Z, amb Y cap amunt. @@ -61,7 +61,7 @@ TitleScene::TitleScene(SDLManager& sdl, SceneContext& context) camera_.get(), 200); starfield_->setColor(Defaults::Title::Colors::STARFIELD); - if (estat_actual_ == TitleState::MAIN) { + if (current_state_ == TitleState::MAIN) { starfield_->setBrightness(BRIGHTNESS_STARFIELD); } else { starfield_->setBrightness(0.0F); @@ -103,7 +103,7 @@ void TitleScene::initTitle() { "title/letra_n.shp", "title/letra_i.shp"}; - float ancho_total_orni = 0.0F; + float total_width_orni = 0.0F; for (const auto& file : FITXERS_ORNI) { auto shape = ShapeLoader::load(file); if (!shape || !shape->isValid()) { @@ -122,25 +122,25 @@ void TitleScene::initTitle() { max_y = std::max(max_y, point.y); } } - const float ANCHO = (max_x - min_x) * Defaults::Title::Layout::LOGO_SCALE; - const float ALTURA = (max_y - min_y) * Defaults::Title::Layout::LOGO_SCALE; - const float OFFSET_CENTRE = (shape->getCenter().x - min_x) * Defaults::Title::Layout::LOGO_SCALE; - lletres_orni_.push_back({shape, {.x = 0.0F, .y = 0.0F}, ANCHO, ALTURA, OFFSET_CENTRE}); - ancho_total_orni += ANCHO; + const float WIDTH = (max_x - min_x) * Defaults::Title::Layout::LOGO_SCALE; + const float HEIGHT = (max_y - min_y) * Defaults::Title::Layout::LOGO_SCALE; + const float CENTER_OFFSET = (shape->getCenter().x - min_x) * Defaults::Title::Layout::LOGO_SCALE; + letters_orni_.push_back({shape, {.x = 0.0F, .y = 0.0F}, WIDTH, HEIGHT, CENTER_OFFSET}); + total_width_orni += WIDTH; } - ancho_total_orni += ESPAI_ENTRE_LLETRES * static_cast(lletres_orni_.size() - 1); + total_width_orni += LETTER_SPACING * static_cast(letters_orni_.size() - 1); - float x_actual = (Defaults::Game::WIDTH - ancho_total_orni) / 2.0F; - for (auto& lletra : lletres_orni_) { - lletra.position.x = x_actual + lletra.offset_centre; - lletra.position.y = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS; - x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; + float x_actual = (Defaults::Game::WIDTH - total_width_orni) / 2.0F; + for (auto& letter : letters_orni_) { + letter.position.x = x_actual + letter.center_offset; + letter.position.y = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS; + x_actual += letter.width + LETTER_SPACING; } - const float ALTURA_ORNI = lletres_orni_.empty() ? 50.0F : lletres_orni_[0].altura; + const float ORNI_HEIGHT = letters_orni_.empty() ? 50.0F : letters_orni_[0].height; const float Y_ORNI = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS; const float SEPARACION = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_LINE_SPACING; - y_attack_dinamica_ = Y_ORNI + ALTURA_ORNI + SEPARACION; + dynamic_attack_y_ = Y_ORNI + ORNI_HEIGHT + SEPARACION; const std::vector FITXERS_ATTACK = { "title/letra_a.shp", @@ -151,7 +151,7 @@ void TitleScene::initTitle() { "title/letra_k.shp", "title/letra_exclamacion.shp"}; - float ancho_total_attack = 0.0F; + float total_width_attack = 0.0F; for (const auto& file : FITXERS_ATTACK) { auto shape = ShapeLoader::load(file); if (!shape || !shape->isValid()) { @@ -170,28 +170,28 @@ void TitleScene::initTitle() { max_y = std::max(max_y, point.y); } } - const float ANCHO = (max_x - min_x) * Defaults::Title::Layout::LOGO_SCALE; - const float ALTURA = (max_y - min_y) * Defaults::Title::Layout::LOGO_SCALE; - const float OFFSET_CENTRE = (shape->getCenter().x - min_x) * Defaults::Title::Layout::LOGO_SCALE; - lletres_attack_.push_back({shape, {.x = 0.0F, .y = 0.0F}, ANCHO, ALTURA, OFFSET_CENTRE}); - ancho_total_attack += ANCHO; + const float WIDTH = (max_x - min_x) * Defaults::Title::Layout::LOGO_SCALE; + const float HEIGHT = (max_y - min_y) * Defaults::Title::Layout::LOGO_SCALE; + const float CENTER_OFFSET = (shape->getCenter().x - min_x) * Defaults::Title::Layout::LOGO_SCALE; + letters_attack_.push_back({shape, {.x = 0.0F, .y = 0.0F}, WIDTH, HEIGHT, CENTER_OFFSET}); + total_width_attack += WIDTH; } - ancho_total_attack += ESPAI_ENTRE_LLETRES * static_cast(lletres_attack_.size() - 1); + total_width_attack += LETTER_SPACING * static_cast(letters_attack_.size() - 1); - x_actual = (Defaults::Game::WIDTH - ancho_total_attack) / 2.0F; - for (auto& lletra : lletres_attack_) { - lletra.position.x = x_actual + lletra.offset_centre; - lletra.position.y = y_attack_dinamica_; - x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; + x_actual = (Defaults::Game::WIDTH - total_width_attack) / 2.0F; + for (auto& letter : letters_attack_) { + letter.position.x = x_actual + letter.center_offset; + letter.position.y = dynamic_attack_y_; + x_actual += letter.width + LETTER_SPACING; } - posicions_originals_orni_.clear(); - for (const auto& lletra : lletres_orni_) { - posicions_originals_orni_.push_back(lletra.position); + original_positions_orni_.clear(); + for (const auto& letter : letters_orni_) { + original_positions_orni_.push_back(letter.position); } - posicions_originals_attack_.clear(); - for (const auto& lletra : lletres_attack_) { - posicions_originals_attack_.push_back(lletra.position); + original_positions_attack_.clear(); + for (const auto& letter : letters_attack_) { + original_positions_attack_.push_back(letter.position); } } @@ -211,8 +211,8 @@ void TitleScene::inicialitzarJailgames() { constexpr float SCALE = Defaults::Title::Layout::JAILGAMES_SCALE; - float ancho_total = 0.0F; - float altura_max = 0.0F; + float total_width = 0.0F; + float max_height = 0.0F; for (const auto& file : FITXERS) { auto shape = ShapeLoader::load(file); if (!shape || !shape->isValid()) { @@ -231,28 +231,28 @@ void TitleScene::inicialitzarJailgames() { max_y = std::max(max_y, point.y); } } - const float ANCHO = (max_x - min_x) * SCALE; - const float ALTURA = (max_y - min_y) * SCALE; - const float OFFSET_CENTRE = (shape->getCenter().x - min_x) * SCALE; - lletres_jailgames_.push_back({shape, {.x = 0.0F, .y = 0.0F}, ANCHO, ALTURA, OFFSET_CENTRE}); - ancho_total += ANCHO; - altura_max = std::max(altura_max, ALTURA); + const float WIDTH = (max_x - min_x) * SCALE; + const float HEIGHT = (max_y - min_y) * SCALE; + const float CENTER_OFFSET = (shape->getCenter().x - min_x) * SCALE; + letters_jailgames_.push_back({shape, {.x = 0.0F, .y = 0.0F}, WIDTH, HEIGHT, CENTER_OFFSET}); + total_width += WIDTH; + max_height = std::max(max_height, HEIGHT); } - constexpr float ESPAI_JAILGAMES = ESPAI_ENTRE_LLETRES * SCALE; - if (!lletres_jailgames_.empty()) { - ancho_total += ESPAI_JAILGAMES * static_cast(lletres_jailgames_.size() - 1); + constexpr float JAILGAMES_SPACING = LETTER_SPACING * SCALE; + if (!letters_jailgames_.empty()) { + total_width += JAILGAMES_SPACING * static_cast(letters_jailgames_.size() - 1); } const float Y_COPY = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS; const float GAP = Defaults::Game::HEIGHT * Defaults::Title::Layout::JAILGAMES_COPYRIGHT_GAP; - const float Y_CENTRE = Y_COPY - GAP - (altura_max / 2.0F); - const float X_INICIAL = (Defaults::Game::WIDTH - ancho_total) / 2.0F; + const float Y_CENTRE = Y_COPY - GAP - (max_height / 2.0F); + const float X_INICIAL = (Defaults::Game::WIDTH - total_width) / 2.0F; float x_actual = X_INICIAL; - for (auto& lletra : lletres_jailgames_) { - lletra.position.x = x_actual + lletra.offset_centre; - lletra.position.y = Y_CENTRE; - x_actual += lletra.ancho + ESPAI_JAILGAMES; + for (auto& letter : letters_jailgames_) { + letter.position.x = x_actual + letter.center_offset; + letter.position.y = Y_CENTRE; + x_actual += letter.width + JAILGAMES_SPACING; } } @@ -268,12 +268,12 @@ void TitleScene::dibuixarPeuTitol(float spacing) const { const float COPYRIGHT_S = std::lerp(S::FOOTER_INTRO_SCALE_START, 1.0F, Easing::easeOutQuad(intro_copyright_progress_)); const float JAILGAMES_RENDER_SCALE = Defaults::Title::Layout::JAILGAMES_SCALE * JAILGAMES_S; - for (const auto& lletra : lletres_jailgames_) { + for (const auto& letter : letters_jailgames_) { const Vec2 POS{ - .x = SCREEN_CENTRE_X + (JAILGAMES_S * (lletra.position.x - SCREEN_CENTRE_X)), - .y = SCREEN_CENTRE_Y + (JAILGAMES_S * (lletra.position.y - SCREEN_CENTRE_Y)), + .x = SCREEN_CENTRE_X + (JAILGAMES_S * (letter.position.x - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (JAILGAMES_S * (letter.position.y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, JAILGAMES_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::JAILGAMES_LOGO); + Rendering::renderShape(sdl_.getRenderer(), letter.shape, POS, 0.0F, JAILGAMES_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::JAILGAMES_LOGO); } std::string copyright = Project::COPYRIGHT; for (char& c : copyright) { @@ -297,15 +297,15 @@ void TitleScene::update(float delta_time) { starfield_->update(delta_time); } if (ship_animator_ && - (estat_actual_ == TitleState::STARFIELD_FADE_IN || - estat_actual_ == TitleState::STARFIELD || - estat_actual_ == TitleState::MAIN || - estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) { + (current_state_ == TitleState::STARFIELD_FADE_IN || + current_state_ == TitleState::STARFIELD || + current_state_ == TitleState::MAIN || + current_state_ == TitleState::PLAYER_JOIN_PHASE)) { ship_animator_->update(delta_time); } updateFlashes(delta_time); - switch (estat_actual_) { + switch (current_state_) { case TitleState::STARFIELD_FADE_IN: updateStarfieldFadeInState(delta_time); break; @@ -329,10 +329,10 @@ void TitleScene::update(float delta_time) { void TitleScene::updateStarfieldFadeInState(float delta_time) { temps_acumulat_ += delta_time; - const float PROGRESS = std::min(1.0F, temps_acumulat_ / DURACIO_FADE_IN); + const float PROGRESS = std::min(1.0F, temps_acumulat_ / DURATION_FADE_IN); starfield_->setBrightness(PROGRESS * BRIGHTNESS_STARFIELD); - if (temps_acumulat_ >= DURACIO_FADE_IN) { - estat_actual_ = TitleState::STARFIELD; + if (temps_acumulat_ >= DURATION_FADE_IN) { + current_state_ = TitleState::STARFIELD; temps_acumulat_ = 0.0F; starfield_->setBrightness(BRIGHTNESS_STARFIELD); } @@ -340,16 +340,16 @@ void TitleScene::updateStarfieldFadeInState(float delta_time) { void TitleScene::updateStarfieldState(float delta_time) { temps_acumulat_ += delta_time; - if (temps_acumulat_ >= DURACIO_INIT) { - estat_actual_ = TitleState::MAIN; - temps_estat_main_ = 0.0F; - animacio_activa_ = false; - factor_lerp_ = 0.0F; + if (temps_acumulat_ >= DURATION_INIT) { + current_state_ = TitleState::MAIN; + state_time_main_ = 0.0F; + animation_active_ = false; + lerp_factor_ = 0.0F; } } void TitleScene::updateMainState(float delta_time) { - temps_estat_main_ += delta_time; + state_time_main_ += delta_time; namespace S = Defaults::Title::Sequence; namespace Sh = Defaults::Title::Ships; @@ -364,38 +364,38 @@ void TitleScene::updateMainState(float delta_time) { constexpr float T_SHIPS_LANDED = T_SHIPS_START + Sh::ENTRY_DURATION + Sh::P2_ENTRY_DELAY; constexpr float T_PRESS_START_VISIBLE = T_SHIPS_LANDED + S::PRESS_START_DELAY_AFTER_SHIPS; - intro_logo_progress_ = std::clamp(temps_estat_main_ / S::LOGO_ENTRY_DURATION, 0.0F, 1.0F); + intro_logo_progress_ = std::clamp(state_time_main_ / S::LOGO_ENTRY_DURATION, 0.0F, 1.0F); intro_jailgames_progress_ = std::clamp( - (temps_estat_main_ - T_FOOTER_START) / S::JAILGAMES_ENTRY_DURATION, + (state_time_main_ - T_FOOTER_START) / S::JAILGAMES_ENTRY_DURATION, 0.0F, 1.0F); intro_copyright_progress_ = std::clamp( - (temps_estat_main_ - T_COPY_START) / S::COPYRIGHT_ENTRY_DURATION, + (state_time_main_ - T_COPY_START) / S::COPYRIGHT_ENTRY_DURATION, 0.0F, 1.0F); - if (!ships_intro_launched_ && temps_estat_main_ >= T_SHIPS_START && + if (!ships_intro_launched_ && state_time_main_ >= T_SHIPS_START && ship_animator_ != nullptr) { ship_animator_->setVisible(true); ship_animator_->startEntryAnimation(); ships_intro_launched_ = true; } - if (!press_start_visible_ && temps_estat_main_ >= T_PRESS_START_VISIBLE) { + if (!press_start_visible_ && state_time_main_ >= T_PRESS_START_VISIBLE) { press_start_visible_ = true; } // L'oscil·lació suau del logo arrenca quan el logo ha aterrat. Així // l'amplitud creix gradualment (lerp) durant la resta de la intro. - if (temps_estat_main_ < S::LOGO_ENTRY_DURATION) { - factor_lerp_ = 0.0F; - animacio_activa_ = false; - } else if (temps_estat_main_ < S::LOGO_ENTRY_DURATION + DURACIO_LERP) { - factor_lerp_ = (temps_estat_main_ - S::LOGO_ENTRY_DURATION) / DURACIO_LERP; - animacio_activa_ = true; + if (state_time_main_ < S::LOGO_ENTRY_DURATION) { + lerp_factor_ = 0.0F; + animation_active_ = false; + } else if (state_time_main_ < S::LOGO_ENTRY_DURATION + DURATION_LERP) { + lerp_factor_ = (state_time_main_ - S::LOGO_ENTRY_DURATION) / DURATION_LERP; + animation_active_ = true; } else { - factor_lerp_ = 1.0F; - animacio_activa_ = true; + lerp_factor_ = 1.0F; + animation_active_ = true; } updateLogoAnimation(delta_time); } @@ -404,8 +404,8 @@ void TitleScene::updatePlayerJoinPhaseState(float delta_time) { temps_acumulat_ += delta_time; updateLogoAnimation(delta_time); - const bool P1_ABANS = match_config_.jugador1_actiu; - const bool P2_ABANS = match_config_.jugador2_actiu; + const bool P1_ABANS = match_config_.player1_active; + const bool P2_ABANS = match_config_.player2_active; if (checkStartGameButtonPressed()) { context_.setMatchConfig(match_config_); @@ -414,27 +414,27 @@ void TitleScene::updatePlayerJoinPhaseState(float delta_time) { temps_acumulat_ = 0.0F; } - if (temps_acumulat_ >= DURACIO_TRANSITION) { - estat_actual_ = TitleState::BLACK_SCREEN; + if (temps_acumulat_ >= DURATION_TRANSITION) { + current_state_ = TitleState::BLACK_SCREEN; temps_acumulat_ = 0.0F; } } void TitleScene::updateBlackScreenState(float delta_time) { temps_acumulat_ += delta_time; - if (temps_acumulat_ >= DURACIO_BLACK_SCREEN) { + if (temps_acumulat_ >= DURATION_BLACK_SCREEN) { context_.setNextScene(SceneType::GAME); } } void TitleScene::handleSkipInput() { - if (estat_actual_ != TitleState::STARFIELD_FADE_IN && estat_actual_ != TitleState::STARFIELD) { + if (current_state_ != TitleState::STARFIELD_FADE_IN && current_state_ != TitleState::STARFIELD) { return; } if (!checkSkipButtonPressed()) { return; } - estat_actual_ = TitleState::MAIN; + current_state_ = TitleState::MAIN; starfield_->setBrightness(BRIGHTNESS_STARFIELD); // Saltar la intro coreografiada: deixar tots els elements ja in-place. @@ -445,7 +445,7 @@ void TitleScene::handleSkipInput() { S::LOGO_ENTRY_DURATION + S::COPYRIGHT_STAGGER + S::COPYRIGHT_ENTRY_DURATION); constexpr float T_PRESS_START_VISIBLE = T_FOOTER_END + S::SHIPS_DELAY_AFTER_FOOTER + Sh::ENTRY_DURATION + Sh::P2_ENTRY_DELAY + S::PRESS_START_DELAY_AFTER_SHIPS; - temps_estat_main_ = T_PRESS_START_VISIBLE; + state_time_main_ = T_PRESS_START_VISIBLE; intro_logo_progress_ = 1.0F; intro_jailgames_progress_ = 1.0F; intro_copyright_progress_ = 1.0F; @@ -458,7 +458,7 @@ void TitleScene::handleSkipInput() { } void TitleScene::handleStartInput() { - if (estat_actual_ != TitleState::MAIN) { + if (current_state_ != TitleState::MAIN) { return; } // No acceptar START fins que la intro coreografiada haja conclòs i el @@ -466,8 +466,8 @@ void TitleScene::handleStartInput() { if (!press_start_visible_) { return; } - const bool P1_ABANS = match_config_.jugador1_actiu; - const bool P2_ABANS = match_config_.jugador2_actiu; + const bool P1_ABANS = match_config_.player1_active; + const bool P2_ABANS = match_config_.player2_active; if (!checkStartGameButtonPressed()) { return; @@ -479,7 +479,7 @@ void TitleScene::handleStartInput() { } context_.setMatchConfig(match_config_); - estat_actual_ = TitleState::PLAYER_JOIN_PHASE; + current_state_ = TitleState::PLAYER_JOIN_PHASE; temps_acumulat_ = 0.0F; triggerExitForJoinedPlayers(P1_ABANS, P2_ABANS, ""); @@ -492,55 +492,55 @@ void TitleScene::triggerExitForJoinedPlayers(bool p1_was_active, bool p2_was_act if (ship_animator_ == nullptr) { return; } - if (match_config_.jugador1_actiu && !p1_was_active) { + if (match_config_.player1_active && !p1_was_active) { ship_animator_->triggerExitAnimationForPlayer(1); std::cout << "[TitleScene] P1 " << log_prefix << "ship exiting\n"; } - if (match_config_.jugador2_actiu && !p2_was_active) { + if (match_config_.player2_active && !p2_was_active) { ship_animator_->triggerExitAnimationForPlayer(2); std::cout << "[TitleScene] P2 " << log_prefix << "ship exiting\n"; } } void TitleScene::updateLogoAnimation(float delta_time) { - if (!animacio_activa_) { + if (!animation_active_) { return; } - temps_animacio_ += delta_time * factor_lerp_; + animation_time_ += delta_time * lerp_factor_; const float TWO_PI = 2.0F * Defaults::Math::PI; - const float OFFSET_X = ORBIT_AMPLITUDE_X * std::sin(TWO_PI * ORBIT_FREQUENCY_X * temps_animacio_); - const float OFFSET_Y = ORBIT_AMPLITUDE_Y * std::sin((TWO_PI * ORBIT_FREQUENCY_Y * temps_animacio_) + ORBIT_PHASE_OFFSET); + const float OFFSET_X = ORBIT_AMPLITUDE_X * std::sin(TWO_PI * ORBIT_FREQUENCY_X * animation_time_); + const float OFFSET_Y = ORBIT_AMPLITUDE_Y * std::sin((TWO_PI * ORBIT_FREQUENCY_Y * animation_time_) + ORBIT_PHASE_OFFSET); - for (std::size_t i = 0; i < lletres_orni_.size(); ++i) { - lletres_orni_[i].position.x = posicions_originals_orni_[i].x + std::round(OFFSET_X); - lletres_orni_[i].position.y = posicions_originals_orni_[i].y + std::round(OFFSET_Y); + for (std::size_t i = 0; i < letters_orni_.size(); ++i) { + letters_orni_[i].position.x = original_positions_orni_[i].x + std::round(OFFSET_X); + letters_orni_[i].position.y = original_positions_orni_[i].y + std::round(OFFSET_Y); } - for (std::size_t i = 0; i < lletres_attack_.size(); ++i) { - lletres_attack_[i].position.x = posicions_originals_attack_[i].x + std::round(OFFSET_X); - lletres_attack_[i].position.y = posicions_originals_attack_[i].y + std::round(OFFSET_Y); + for (std::size_t i = 0; i < letters_attack_.size(); ++i) { + letters_attack_[i].position.x = original_positions_attack_[i].x + std::round(OFFSET_X); + letters_attack_[i].position.y = original_positions_attack_[i].y + std::round(OFFSET_Y); } } void TitleScene::draw() { - if (starfield_ && estat_actual_ != TitleState::BLACK_SCREEN) { + if (starfield_ && current_state_ != TitleState::BLACK_SCREEN) { starfield_->draw(); } if (ship_animator_ && - (estat_actual_ == TitleState::STARFIELD_FADE_IN || - estat_actual_ == TitleState::STARFIELD || - estat_actual_ == TitleState::MAIN || - estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) { + (current_state_ == TitleState::STARFIELD_FADE_IN || + current_state_ == TitleState::STARFIELD || + current_state_ == TitleState::MAIN || + current_state_ == TitleState::PLAYER_JOIN_PHASE)) { ship_animator_->draw(); } drawFlashes(); - if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) { + if (current_state_ == TitleState::STARFIELD_FADE_IN || current_state_ == TitleState::STARFIELD) { return; } - if (estat_actual_ != TitleState::MAIN && estat_actual_ != TitleState::PLAYER_JOIN_PHASE) { + if (current_state_ != TitleState::MAIN && current_state_ != TitleState::PLAYER_JOIN_PHASE) { return; } @@ -552,52 +552,52 @@ void TitleScene::draw() { const float SCREEN_CENTRE_Y = Defaults::Game::HEIGHT / 2.0F; const float LOGO_RENDER_SCALE = Defaults::Title::Layout::LOGO_SCALE * LOGO_S; - if (animacio_activa_) { - float temps_shadow = std::max(0.0F, temps_animacio_ - SHADOW_DELAY); + if (animation_active_) { + float temps_shadow = std::max(0.0F, animation_time_ - SHADOW_DELAY); const float TWO_PI = 2.0F * Defaults::Math::PI; const float SHADOW_OX = (ORBIT_AMPLITUDE_X * std::sin(TWO_PI * ORBIT_FREQUENCY_X * temps_shadow)) + SHADOW_OFFSET_X; const float SHADOW_OY = (ORBIT_AMPLITUDE_Y * std::sin((TWO_PI * ORBIT_FREQUENCY_Y * temps_shadow) + ORBIT_PHASE_OFFSET)) + SHADOW_OFFSET_Y; - for (std::size_t i = 0; i < lletres_orni_.size(); ++i) { - const float BASE_X = posicions_originals_orni_[i].x + std::round(SHADOW_OX); - const float BASE_Y = posicions_originals_orni_[i].y + std::round(SHADOW_OY); + for (std::size_t i = 0; i < letters_orni_.size(); ++i) { + const float BASE_X = original_positions_orni_[i].x + std::round(SHADOW_OX); + const float BASE_Y = original_positions_orni_[i].y + std::round(SHADOW_OY); const Vec2 POS_SHADOW{ .x = SCREEN_CENTRE_X + (LOGO_S * (BASE_X - SCREEN_CENTRE_X)), .y = SCREEN_CENTRE_Y + (LOGO_S * (BASE_Y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletres_orni_[i].shape, POS_SHADOW, 0.0F, LOGO_RENDER_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); + Rendering::renderShape(sdl_.getRenderer(), letters_orni_[i].shape, POS_SHADOW, 0.0F, LOGO_RENDER_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); } - for (std::size_t i = 0; i < lletres_attack_.size(); ++i) { - const float BASE_X = posicions_originals_attack_[i].x + std::round(SHADOW_OX); - const float BASE_Y = posicions_originals_attack_[i].y + std::round(SHADOW_OY); + for (std::size_t i = 0; i < letters_attack_.size(); ++i) { + const float BASE_X = original_positions_attack_[i].x + std::round(SHADOW_OX); + const float BASE_Y = original_positions_attack_[i].y + std::round(SHADOW_OY); const Vec2 POS_SHADOW{ .x = SCREEN_CENTRE_X + (LOGO_S * (BASE_X - SCREEN_CENTRE_X)), .y = SCREEN_CENTRE_Y + (LOGO_S * (BASE_Y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletres_attack_[i].shape, POS_SHADOW, 0.0F, LOGO_RENDER_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); + Rendering::renderShape(sdl_.getRenderer(), letters_attack_[i].shape, POS_SHADOW, 0.0F, LOGO_RENDER_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); } } - for (const auto& lletra : lletres_orni_) { + for (const auto& letter : letters_orni_) { const Vec2 POS{ - .x = SCREEN_CENTRE_X + (LOGO_S * (lletra.position.x - SCREEN_CENTRE_X)), - .y = SCREEN_CENTRE_Y + (LOGO_S * (lletra.position.y - SCREEN_CENTRE_Y)), + .x = SCREEN_CENTRE_X + (LOGO_S * (letter.position.x - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (LOGO_S * (letter.position.y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, LOGO_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); + Rendering::renderShape(sdl_.getRenderer(), letter.shape, POS, 0.0F, LOGO_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); } - for (const auto& lletra : lletres_attack_) { + for (const auto& letter : letters_attack_) { const Vec2 POS{ - .x = SCREEN_CENTRE_X + (LOGO_S * (lletra.position.x - SCREEN_CENTRE_X)), - .y = SCREEN_CENTRE_Y + (LOGO_S * (lletra.position.y - SCREEN_CENTRE_Y)), + .x = SCREEN_CENTRE_X + (LOGO_S * (letter.position.x - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (LOGO_S * (letter.position.y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, LOGO_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); + Rendering::renderShape(sdl_.getRenderer(), letter.shape, POS, 0.0F, LOGO_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); } const float SPACING = Defaults::Title::Layout::TEXT_SPACING; if (press_start_visible_) { bool mostrar_text = true; - if (estat_actual_ == TitleState::PLAYER_JOIN_PHASE) { + if (current_state_ == TitleState::PLAYER_JOIN_PHASE) { const float FASE = temps_acumulat_ * BLINK_FREQUENCY * 2.0F * std::numbers::pi_v; mostrar_text = (std::sin(FASE) > 0.0F); } @@ -622,14 +622,14 @@ auto TitleScene::checkStartGameButtonPressed() -> bool { bool any_pressed = false; for (auto action : START_GAME_BUTTONS) { if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT)) { - if (!match_config_.jugador1_actiu) { - match_config_.jugador1_actiu = true; + if (!match_config_.player1_active) { + match_config_.player1_active = true; any_pressed = true; } } if (input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) { - if (!match_config_.jugador2_actiu) { - match_config_.jugador2_actiu = true; + if (!match_config_.player2_active) { + match_config_.player2_active = true; any_pressed = true; } } diff --git a/source/game/scenes/title_scene.hpp b/source/game/scenes/title_scene.hpp index 6f03692..ac26c7d 100644 --- a/source/game/scenes/title_scene.hpp +++ b/source/game/scenes/title_scene.hpp @@ -48,12 +48,12 @@ class TitleScene final : public Scene { BLACK_SCREEN, }; - struct LetraLogo { + struct LogoLetter { std::shared_ptr shape; Vec2 position; - float ancho; - float altura; - float offset_centre; + float width; + float height; + float center_offset; }; SDLManager& sdl_; @@ -77,22 +77,22 @@ class TitleScene final : public Scene { void triggerFlash(Vec2 pos); void updateFlashes(float delta_time); void drawFlashes(); - TitleState estat_actual_{TitleState::STARFIELD_FADE_IN}; + TitleState current_state_{TitleState::STARFIELD_FADE_IN}; float temps_acumulat_{0.0F}; - std::vector lletres_orni_; - std::vector lletres_attack_; - float y_attack_dinamica_{0.0F}; + std::vector letters_orni_; + std::vector letters_attack_; + float dynamic_attack_y_{0.0F}; - std::vector lletres_jailgames_; + std::vector letters_jailgames_; - float temps_animacio_{0.0F}; - std::vector posicions_originals_orni_; - std::vector posicions_originals_attack_; + float animation_time_{0.0F}; + std::vector original_positions_orni_; + std::vector original_positions_attack_; - float temps_estat_main_{0.0F}; - bool animacio_activa_{false}; - float factor_lerp_{0.0F}; + float state_time_main_{0.0F}; + bool animation_active_{false}; + float lerp_factor_{0.0F}; // Progresos de la intro coreografiada al state MAIN. float intro_logo_progress_{0.0F}; @@ -102,12 +102,12 @@ class TitleScene final : public Scene { bool ships_intro_launched_{false}; static constexpr float BRIGHTNESS_STARFIELD = 1.2F; - static constexpr float DURACIO_FADE_IN = 3.0F; - static constexpr float DURACIO_INIT = 4.0F; - static constexpr float DURACIO_TRANSITION = 2.5F; - static constexpr float ESPAI_ENTRE_LLETRES = 10.0F; + static constexpr float DURATION_FADE_IN = 3.0F; + static constexpr float DURATION_INIT = 4.0F; + static constexpr float DURATION_TRANSITION = 2.5F; + static constexpr float LETTER_SPACING = 10.0F; static constexpr float BLINK_FREQUENCY = 3.0F; - static constexpr float DURACIO_BLACK_SCREEN = 2.0F; + static constexpr float DURATION_BLACK_SCREEN = 2.0F; static constexpr int MUSIC_FADE = 1500; static constexpr float ORBIT_AMPLITUDE_X = 4.0F; @@ -121,7 +121,7 @@ class TitleScene final : public Scene { static constexpr float SHADOW_OFFSET_X = 2.0F; static constexpr float SHADOW_OFFSET_Y = 2.0F; - static constexpr float DURACIO_LERP = 2.0F; + static constexpr float DURATION_LERP = 2.0F; // Càmera 3D: FOV vertical en radians. static constexpr float CAMERA_FOV_Y_RAD = 1.0472F; // 60° diff --git a/source/game/stage_system/spawn_controller.cpp b/source/game/stage_system/spawn_controller.cpp index 1aa67b6..82a769a 100644 --- a/source/game/stage_system/spawn_controller.cpp +++ b/source/game/stage_system/spawn_controller.cpp @@ -16,152 +16,152 @@ namespace StageSystem { -SpawnController::SpawnController() = default; + SpawnController::SpawnController() = default; -void SpawnController::configure(const StageConfig* config) { - config_ = config; -} - -void SpawnController::start() { - if (config_ == nullptr) { - std::cerr << "[SpawnController] Error: config_ es null" << '\n'; - return; + void SpawnController::configure(const StageConfig* config) { + config_ = config; } - reset(); - generateSpawnEvents(); - - std::cout << "[SpawnController] Stage " << static_cast(config_->stage_id) - << ": generats " << spawn_queue_.size() << " spawn events" << '\n'; -} - -void SpawnController::reset() { - spawn_queue_.clear(); - temps_transcorregut_ = 0.0F; - index_spawn_actual_ = 0; -} - -void SpawnController::update(float delta_time, std::array& orni_array, bool pausar) { - if ((config_ == nullptr) || spawn_queue_.empty()) { - return; - } - - // Increment timer only when not paused - if (!pausar) { - temps_transcorregut_ += delta_time; - } - - // Process spawn events - while (index_spawn_actual_ < spawn_queue_.size()) { - SpawnEvent& event = spawn_queue_[index_spawn_actual_]; - - if (event.spawnejat) { - index_spawn_actual_++; - continue; + void SpawnController::start() { + if (config_ == nullptr) { + std::cerr << "[SpawnController] Error: config_ es null" << '\n'; + return; } - if (temps_transcorregut_ >= event.temps_spawn) { - // Find first inactive enemy - for (auto& enemy : orni_array) { - if (!enemy.isActive()) { - spawnEnemy(enemy, event.type, ship_position_); - event.spawnejat = true; - index_spawn_actual_++; + reset(); + generateSpawnEvents(); + + std::cout << "[SpawnController] Stage " << static_cast(config_->stage_id) + << ": generats " << spawn_queue_.size() << " spawn events" << '\n'; + } + + void SpawnController::reset() { + spawn_queue_.clear(); + temps_transcorregut_ = 0.0F; + index_spawn_actual_ = 0; + } + + void SpawnController::update(float delta_time, std::array& orni_array, bool pausar) { + if ((config_ == nullptr) || spawn_queue_.empty()) { + return; + } + + // Increment timer only when not paused + if (!pausar) { + temps_transcorregut_ += delta_time; + } + + // Process spawn events + while (index_spawn_actual_ < spawn_queue_.size()) { + SpawnEvent& event = spawn_queue_[index_spawn_actual_]; + + if (event.spawnejat) { + index_spawn_actual_++; + continue; + } + + if (temps_transcorregut_ >= event.temps_spawn) { + // Find first inactive enemy + for (auto& enemy : orni_array) { + if (!enemy.isActive()) { + spawnEnemy(enemy, event.type, ship_position_); + event.spawnejat = true; + index_spawn_actual_++; + break; + } + } + + // If no slot available, try next frame + if (!event.spawnejat) { break; } - } - - // If no slot available, try next frame - if (!event.spawnejat) { + } else { + // Not yet time for this spawn break; } - } else { - // Not yet time for this spawn - break; } } -} -auto SpawnController::allEnemiesSpawned() const -> bool { - return index_spawn_actual_ >= spawn_queue_.size(); -} - -auto SpawnController::allEnemiesDestroyed(const std::array& orni_array) const -> bool { - if (!allEnemiesSpawned()) { - return false; + auto SpawnController::allEnemiesSpawned() const -> bool { + return index_spawn_actual_ >= spawn_queue_.size(); } - return std::ranges::all_of(orni_array, [](const Enemy& enemy) { return !enemy.isActive(); }); -} -auto SpawnController::getAliveEnemyCount(const std::array& orni_array) -> uint8_t { - uint8_t count = 0; - for (const auto& enemy : orni_array) { - if (enemy.isActive()) { - count++; + auto SpawnController::allEnemiesDestroyed(const std::array& orni_array) const -> bool { + if (!allEnemiesSpawned()) { + return false; + } + return std::ranges::all_of(orni_array, [](const Enemy& enemy) { return !enemy.isActive(); }); + } + + auto SpawnController::getAliveEnemyCount(const std::array& orni_array) -> uint8_t { + uint8_t count = 0; + for (const auto& enemy : orni_array) { + if (enemy.isActive()) { + count++; + } + } + return count; + } + + auto SpawnController::countSpawnedEnemies() const -> uint8_t { + return static_cast(index_spawn_actual_); + } + + void SpawnController::generateSpawnEvents() { + if (config_ == nullptr) { + return; + } + + for (uint8_t i = 0; i < config_->total_enemies; i++) { + float spawn_time = config_->config_spawn.delay_inicial + + (i * config_->config_spawn.interval_spawn); + + EnemyType type = selectRandomType(); + + spawn_queue_.push_back({spawn_time, type, false}); } } - return count; -} -auto SpawnController::countSpawnedEnemies() const -> uint8_t { - return static_cast(index_spawn_actual_); -} + auto SpawnController::selectRandomType() const -> EnemyType { + if (config_ == nullptr) { + return EnemyType::PENTAGON; + } -void SpawnController::generateSpawnEvents() { - if (config_ == nullptr) { - return; + // Weighted random selection based on distribution + int rand_val = std::rand() % 100; + + if (std::cmp_less(rand_val, config_->distribucio.pentagon)) { + return EnemyType::PENTAGON; + } + if (rand_val < config_->distribucio.pentagon + config_->distribucio.cuadrado) { + return EnemyType::SQUARE; + } + return EnemyType::PINWHEEL; } - for (uint8_t i = 0; i < config_->total_enemies; i++) { - float spawn_time = config_->config_spawn.delay_inicial + - (i * config_->config_spawn.interval_spawn); + void SpawnController::spawnEnemy(Enemy& enemy, EnemyType type, const Vec2* ship_pos) { + // Initialize enemy (with safe spawn if ship_pos provided) + enemy.init(type, ship_pos); - EnemyType type = selectRandomType(); - - spawn_queue_.push_back({spawn_time, type, false}); - } -} - -auto SpawnController::selectRandomType() const -> EnemyType { - if (config_ == nullptr) { - return EnemyType::PENTAGON; + // Apply difficulty multipliers + applyMultipliers(enemy); } - // Weighted random selection based on distribution - int rand_val = std::rand() % 100; + void SpawnController::applyMultipliers(Enemy& enemy) const { + if (config_ == nullptr) { + return; + } - if (std::cmp_less(rand_val, config_->distribucio.pentagon)) { - return EnemyType::PENTAGON; + // Apply velocity multiplier + float base_vel = enemy.getBaseVelocity(); + enemy.setVelocity(base_vel * config_->multiplicadors.velocity); + + // Apply rotation multiplier + float base_rot = enemy.getBaseRotation(); + enemy.setRotation(base_rot * config_->multiplicadors.rotation); + + // Apply tracking strength (only affects SQUARE) + enemy.setTrackingStrength(config_->multiplicadors.tracking_strength); } - if (rand_val < config_->distribucio.pentagon + config_->distribucio.cuadrado) { - return EnemyType::QUADRAT; - } - return EnemyType::MOLINILLO; -} - -void SpawnController::spawnEnemy(Enemy& enemy, EnemyType type, const Vec2* ship_pos) { - // Initialize enemy (with safe spawn if ship_pos provided) - enemy.init(type, ship_pos); - - // Apply difficulty multipliers - applyMultipliers(enemy); -} - -void SpawnController::applyMultipliers(Enemy& enemy) const { - if (config_ == nullptr) { - return; - } - - // Apply velocity multiplier - float base_vel = enemy.getBaseVelocity(); - enemy.setVelocity(base_vel * config_->multiplicadors.velocity); - - // Apply rotation multiplier - float base_rot = enemy.getBaseRotation(); - enemy.setRotation(base_rot * config_->multiplicadors.rotation); - - // Apply tracking strength (only affects QUADRAT) - enemy.setTrackingStrength(config_->multiplicadors.tracking_strength); -} } // namespace StageSystem diff --git a/source/game/stage_system/stage_config.hpp b/source/game/stage_system/stage_config.hpp index c8665db..9616e43 100644 --- a/source/game/stage_system/stage_config.hpp +++ b/source/game/stage_system/stage_config.hpp @@ -36,7 +36,7 @@ namespace StageSystem { struct MultiplicadorsDificultat { float velocity; // 0.5-2.0 típic float rotation; // 0.5-2.0 típic - float tracking_strength; // 0.0-1.5 (aplicat a Cuadrado) + float tracking_strength; // 0.0-1.5 (aplicat a Square) }; // Metadades del file YAML diff --git a/source/game/systems/collision_system.cpp b/source/game/systems/collision_system.cpp index 2185b35..3a6bbac 100644 --- a/source/game/systems/collision_system.cpp +++ b/source/game/systems/collision_system.cpp @@ -20,10 +20,10 @@ namespace Systems::Collision { switch (type) { case EnemyType::PENTAGON: return Defaults::Enemies::Scoring::PENTAGON_SCORE; - case EnemyType::QUADRAT: - return Defaults::Enemies::Scoring::QUADRAT_SCORE; - case EnemyType::MOLINILLO: - return Defaults::Enemies::Scoring::MOLINILLO_SCORE; + case EnemyType::SQUARE: + return Defaults::Enemies::Scoring::SQUARE_SCORE; + case EnemyType::PINWHEEL: + return Defaults::Enemies::Scoring::PINWHEEL_SCORE; } return 0; } @@ -32,10 +32,10 @@ namespace Systems::Collision { switch (type) { case EnemyType::PENTAGON: return Defaults::Palette::PENTAGON; - case EnemyType::QUADRAT: - return Defaults::Palette::QUADRAT; - case EnemyType::MOLINILLO: - return Defaults::Palette::MOLINILLO; + case EnemyType::SQUARE: + return Defaults::Palette::SQUARE; + case EnemyType::PINWHEEL: + return Defaults::Palette::PINWHEEL; } return SDL_Color{}; } @@ -58,16 +58,16 @@ namespace Systems::Collision { } ctx.floating_score_manager.crear(POINTS, ENEMY_POS); - enemy.destruir(); + enemy.destroy(); - constexpr float VELOCITAT_EXPLOSIO = 80.0F; // px/s (explosión suave) + constexpr float SPEED_EXPLOSIO = 80.0F; // px/s (explosión suave) const Vec2 INHERITED_VEL = ENEMY_VEL * Defaults::Physics::Debris::ENEMY_VELOCITY_INHERITANCE; ctx.debris_manager.explode( SHAPE, ENEMY_POS, 0.0F, // angle (rotación interna del enemy) 1.0F, // escala - VELOCITAT_EXPLOSIO, + SPEED_EXPLOSIO, BRIGHTNESS, INHERITED_VEL, 0.0F, // sense herència angular: evita que els 5 trossos curvin en bloc @@ -141,7 +141,7 @@ namespace Systems::Collision { explodeNow(ctx, enemy, SHOOTER); } else { // Primer impacto → entra en estado herido (explosión diferida). - enemy.herir(SHOOTER); + enemy.hurt(SHOOTER); } breakBullet(ctx.debris_manager, bullet); @@ -182,9 +182,9 @@ namespace Systems::Collision { } // El sano queda herido, propagando el shooter original. if (A_WOUNDED) { - b.herir(a.getLastHitBy()); + b.hurt(a.getLastHitBy()); } else { - a.herir(b.getLastHitBy()); + a.hurt(b.getLastHitBy()); } } } @@ -230,7 +230,7 @@ namespace Systems::Collision { } else { // Primer impacte → estat HURT (rebot físic ja resolt per PhysicsWorld; // l'enemic no rep dany per decisió de disseny). - ctx.ships[i].herir(); + ctx.ships[i].hurt(); } } ctx.ships[i].setTouchingEnemyPrevFrame(TOUCHING_NOW); @@ -262,8 +262,8 @@ namespace Systems::Collision { continue; } const bool JUGADOR_ACTIU = (player_id == 0) - ? ctx.match_config.jugador1_actiu - : ctx.match_config.jugador2_actiu; + ? ctx.match_config.player1_active + : ctx.match_config.player2_active; if (!JUGADOR_ACTIU) { continue; } @@ -297,7 +297,7 @@ namespace Systems::Collision { } void desactivateOutOfBoundsBullets( - std::array(Defaults::Entities::MAX_BALES) * 2>& bullets, + std::array(Defaults::Entities::MAX_BULLETS) * 2>& bullets, Effects::DebrisManager& debris_manager) { float min_x; float max_x; diff --git a/source/game/systems/collision_system.hpp b/source/game/systems/collision_system.hpp index 49f8647..1503603 100644 --- a/source/game/systems/collision_system.hpp +++ b/source/game/systems/collision_system.hpp @@ -32,7 +32,7 @@ namespace Systems::Collision { struct Context { std::array& ships; std::array& enemies; - std::array(Defaults::Entities::MAX_BALES) * 2>& bullets; + std::array(Defaults::Entities::MAX_BULLETS) * 2>& bullets; std::array& hit_timer_per_player; std::array& score_per_player; std::array& lives_per_player; @@ -74,7 +74,7 @@ namespace Systems::Collision { // (8 fragments de l'octàgon) i el so HIT. Cal cridar-la després de detectAll() // perquè una bala que el mateix frame xoca i alhora surt es comptabilitzi com a impacte. void desactivateOutOfBoundsBullets( - std::array(Defaults::Entities::MAX_BALES) * 2>& bullets, + std::array(Defaults::Entities::MAX_BULLETS) * 2>& bullets, Effects::DebrisManager& debris_manager); } // namespace Systems::Collision diff --git a/source/game/systems/continue_system.cpp b/source/game/systems/continue_system.cpp index f9a6dd4..ffe94a0 100644 --- a/source/game/systems/continue_system.cpp +++ b/source/game/systems/continue_system.cpp @@ -11,99 +11,99 @@ #include "game/scenes/game_scene.hpp" // GameOverState (definición completa) namespace Systems::ContinueScreen { -namespace { + namespace { -// Si el countdown ha bajado de 0, transiciona a GAME_OVER con su timer. -void checkAndApplyTimeout(Context& ctx) { - if (ctx.counter < 0) { - ctx.state = GameOverState::GAME_OVER; - ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION; - } -} + // Si el countdown ha bajado de 0, transiciona a GAME_OVER con su timer. + void checkAndApplyTimeout(Context& ctx) { + if (ctx.counter < 0) { + ctx.state = GameOverState::GAME_OVER; + ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION; + } + } -void revivePlayer(Context& ctx, uint8_t player_id) { - ctx.score_per_player[player_id] = 0; - ctx.lives_per_player[player_id] = Defaults::Game::STARTING_LIVES; - ctx.hit_timer_per_player[player_id] = 0.0F; + void revivePlayer(Context& ctx, uint8_t player_id) { + ctx.score_per_player[player_id] = 0; + ctx.lives_per_player[player_id] = Defaults::Game::STARTING_LIVES; + ctx.hit_timer_per_player[player_id] = 0.0F; - if (player_id == 0) { - ctx.match_config.jugador1_actiu = true; - } else { - ctx.match_config.jugador2_actiu = true; - } + if (player_id == 0) { + ctx.match_config.player1_active = true; + } else { + ctx.match_config.player2_active = true; + } - const Vec2 SPAWN = ctx.get_spawn_point(player_id); - ctx.ships[player_id].init(&SPAWN, /*activar_invulnerabilitat=*/true); -} + const Vec2 SPAWN = ctx.get_spawn_point(player_id); + ctx.ships[player_id].init(&SPAWN, /*activar_invulnerabilitat=*/true); + } -} // namespace + } // namespace -void update(Context& ctx, float delta_time) { - ctx.tick_timer -= delta_time; - if (ctx.tick_timer > 0.0F) { - return; - } - ctx.counter--; - ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION; - - checkAndApplyTimeout(ctx); - - // Solo pita el tick si seguimos en CONTINUE (no transitamos a GAME_OVER) - if (ctx.state == GameOverState::CONTINUE) { - Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME); - } -} - -void processInput(Context& ctx) { - auto* input = Input::get(); - - const bool P1_START = input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT); - const bool P2_START = input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT); - - if (P1_START || P2_START) { - // ¿Quedan continues? - if (!Defaults::Game::INFINITE_CONTINUES && - ctx.continues_used >= Defaults::Game::MAX_CONTINUES) { - ctx.state = GameOverState::GAME_OVER; - ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION; + void update(Context& ctx, float delta_time) { + ctx.tick_timer -= delta_time; + if (ctx.tick_timer > 0.0F) { return; } - if (!Defaults::Game::INFINITE_CONTINUES) { - ctx.continues_used++; - } - - const uint8_t PRIMARY = P1_START ? 0 : 1; - revivePlayer(ctx, PRIMARY); - // Si ambos pulsan START, revivimos a los dos. - if (P1_START && P2_START) { - revivePlayer(ctx, 1); - } - - // Reanudar partida. - ctx.state = GameOverState::NONE; - ctx.counter = 0; - ctx.tick_timer = 0.0F; - - Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME); - return; - } - - // THRUST/SHOOT acelera el countdown manualmente. - const bool ACCEL = input->checkActionPlayer1(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT) || - input->checkActionPlayer2(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT) || - input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT) || - input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT); - - if (ACCEL) { ctx.counter--; + ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION; + checkAndApplyTimeout(ctx); + // Solo pita el tick si seguimos en CONTINUE (no transitamos a GAME_OVER) if (ctx.state == GameOverState::CONTINUE) { Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME); } - - ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION; } -} + + void processInput(Context& ctx) { + auto* input = Input::get(); + + const bool P1_START = input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT); + const bool P2_START = input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT); + + if (P1_START || P2_START) { + // ¿Quedan continues? + if (!Defaults::Game::INFINITE_CONTINUES && + ctx.continues_used >= Defaults::Game::MAX_CONTINUES) { + ctx.state = GameOverState::GAME_OVER; + ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION; + return; + } + if (!Defaults::Game::INFINITE_CONTINUES) { + ctx.continues_used++; + } + + const uint8_t PRIMARY = P1_START ? 0 : 1; + revivePlayer(ctx, PRIMARY); + // Si ambos pulsan START, revivimos a los dos. + if (P1_START && P2_START) { + revivePlayer(ctx, 1); + } + + // Reanudar partida. + ctx.state = GameOverState::NONE; + ctx.counter = 0; + ctx.tick_timer = 0.0F; + + Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME); + return; + } + + // THRUST/SHOOT acelera el countdown manualmente. + const bool ACCEL = input->checkActionPlayer1(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT) || + input->checkActionPlayer2(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT) || + input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT) || + input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT); + + if (ACCEL) { + ctx.counter--; + checkAndApplyTimeout(ctx); + + if (ctx.state == GameOverState::CONTINUE) { + Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME); + } + + ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION; + } + } } // namespace Systems::ContinueScreen diff --git a/source/game/systems/init_hud_animator.cpp b/source/game/systems/init_hud_animator.cpp index 75e81eb..9e2011c 100644 --- a/source/game/systems/init_hud_animator.cpp +++ b/source/game/systems/init_hud_animator.cpp @@ -31,7 +31,7 @@ namespace Systems::InitHud { auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2 { const float EASED = Easing::easeOutQuad(progress); const SDL_FRect& zone = Defaults::Zones::PLAYAREA; - // Y inicial: bajo la zona de juego (sale desde fuera). + // Y inicial: bajo la zone de juego (sale desde fuera). const float Y_INI = zone.y + zone.h + Defaults::Hud::InitAnim::SHIP_SPAWN_Y_OFFSET; const float Y_ANIM = Y_INI + ((final_position.y - Y_INI) * EASED); return Vec2{.x = final_position.x, .y = Y_ANIM}; diff --git a/source/game/systems/init_hud_animator.hpp b/source/game/systems/init_hud_animator.hpp index 668f180..1775816 100644 --- a/source/game/systems/init_hud_animator.hpp +++ b/source/game/systems/init_hud_animator.hpp @@ -4,7 +4,7 @@ // Cubre la animación INIT_HUD del comienzo de cada partida/stage: // 1. Crecimiento de los marcos del PLAYAREA con efecto pincel en 3 fases. // 2. Marcador subiendo desde abajo. -// 3. Naves entrando desde la zona inferior hacia su spawn. +// 3. Naves entrando desde la zone inferior hacia su spawn. // // Todas las funciones son puras (sin estado interno propio). GameScene aporta // el contexto que necesitan: posiciones finales, texto del scoreboard y el @@ -21,29 +21,29 @@ namespace Systems::InitHud { -// Convierte un progreso global 0..1 al sub-progreso de un elemento que solo -// se anima en la ventana [ratio_init, ratio_end]. -// < ratio_init → 0.0 (no empezó) -// > ratio_end → 1.0 (terminó) -// en rango → interpolación lineal 0..1 -[[nodiscard]] auto computeRangeProgress(float global_progress, - float ratio_init, - float ratio_end) -> float; + // Convierte un progreso global 0..1 al sub-progreso de un elemento que solo + // se anima en la ventana [ratio_init, ratio_end]. + // < ratio_init → 0.0 (no empezó) + // > ratio_end → 1.0 (terminó) + // en rango → interpolación lineal 0..1 + [[nodiscard]] auto computeRangeProgress(float global_progress, + float ratio_init, + float ratio_end) -> float; -// Calcula posición Y animada de una nave durante INIT_HUD. La nave sube -// desde 50 px bajo el PLAYAREA hasta `final_position` con easing. -[[nodiscard]] auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2; + // Calcula posición Y animada de una nave durante INIT_HUD. La nave sube + // desde 50 px bajo el PLAYAREA hasta `final_position` con easing. + [[nodiscard]] auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2; -// Dibuja los 4 lados del PLAYAREA con efecto pincel en 3 fases: -// 0..33% → línea superior crece desde el centro hacia los lados. -// 33..66% → líneas verticales bajan por los laterales. -// 66..100% → línea inferior crece desde los lados hacia el centro. -void drawBordersAnimated(Rendering::Renderer* renderer, float progress); + // Dibuja los 4 lados del PLAYAREA con efecto pincel en 3 fases: + // 0..33% → línea superior crece desde el centro hacia los lados. + // 33..66% → líneas verticales bajan por los laterales. + // 66..100% → línea inferior crece desde los lados hacia el centro. + void drawBordersAnimated(Rendering::Renderer* renderer, float progress); -// Dibuja el scoreboard centrado, subiendo desde fuera de la pantalla -// hasta su posición final con easing. -void drawScoreboardAnimated(const Graphics::VectorText& text, - const std::string& scoreboard_text, - float progress); + // Dibuja el scoreboard centrado, subiendo desde fuera de la pantalla + // hasta su posición final con easing. + void drawScoreboardAnimated(const Graphics::VectorText& text, + const std::string& scoreboard_text, + float progress); } // namespace Systems::InitHud