diff --git a/source/core/defaults.hpp b/source/core/defaults.hpp index a2c7670..04b752a 100644 --- a/source/core/defaults.hpp +++ b/source/core/defaults.hpp @@ -16,7 +16,9 @@ #include "core/defaults/entities.hpp" #include "core/defaults/floating_score.hpp" #include "core/defaults/game.hpp" +#include "core/defaults/hud.hpp" #include "core/defaults/math.hpp" +#include "core/defaults/notifier.hpp" #include "core/defaults/palette.hpp" #include "core/defaults/physics.hpp" #include "core/defaults/rendering.hpp" diff --git a/source/core/defaults/enemies.hpp b/source/core/defaults/enemies.hpp index b4c81ee..d321966 100644 --- a/source/core/defaults/enemies.hpp +++ b/source/core/defaults/enemies.hpp @@ -7,19 +7,30 @@ namespace Defaults::Enemies { + // Cuerpo físico común (valores por defecto del constructor) + namespace Body { + constexpr float DEFAULT_MASS = 5.0F; // Más liviano que la nave (10.0) + constexpr float RESTITUTION = 1.0F; // Rebote elástico perfecto contra paredes + constexpr float LINEAR_DAMPING = 0.0F; // Sin fricción: mantienen velocidad + constexpr float ANGULAR_DAMPING = 0.0F; + } // namespace Body + // Pentagon (esquivador - zigzag evasion) namespace Pentagon { - constexpr float VELOCITAT = 35.0F; // px/s (slightly slower) - 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 DROTACIO_MIN = 0.75F; // Min visual rotation (rad/s) [+50%] - constexpr float DROTACIO_MAX = 3.75F; // Max visual rotation (rad/s) [+50%] + constexpr float VELOCITAT = 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 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 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%] @@ -30,6 +41,7 @@ namespace Defaults::Enemies { // 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%] diff --git a/source/core/defaults/game.hpp b/source/core/defaults/game.hpp index e5f4c9d..64d86ab 100644 --- a/source/core/defaults/game.hpp +++ b/source/core/defaults/game.hpp @@ -20,6 +20,7 @@ namespace Defaults::Game { constexpr bool FRIENDLY_FIRE_ENABLED = true; // Activar friendly fire constexpr float COLLISION_BULLET_PLAYER_AMPLIFIER = 1.0F; // Hitbox exacto (100%) constexpr float BULLET_GRACE_PERIOD = 0.2F; // Inmunidad post-disparo (s) + constexpr float BULLET_SPEED = 140.0F; // Velocidad escalar (px/s). Pascal: 7 px/frame × 20 FPS // Transición LEVEL_START (mensajes aleatorios PRE-level) constexpr float LEVEL_START_DURATION = 3.0F; // Duración total diff --git a/source/core/defaults/hud.hpp b/source/core/defaults/hud.hpp new file mode 100644 index 0000000..f97843c --- /dev/null +++ b/source/core/defaults/hud.hpp @@ -0,0 +1,13 @@ +// hud.hpp - Configuració visual del HUD (marcador, etc.) +// © 2026 JailDesigner + +#pragma once + +namespace Defaults::Hud { + + // Marcador (scoreboard inferior). Usado por GameScene::drawScoreboard() + // y por la animación de entrada en init_hud_animator. + constexpr float SCOREBOARD_TEXT_SCALE = 0.85F; + constexpr float SCOREBOARD_TEXT_SPACING = 0.0F; + +} // namespace Defaults::Hud diff --git a/source/core/defaults/notifier.hpp b/source/core/defaults/notifier.hpp new file mode 100644 index 0000000..a3938df --- /dev/null +++ b/source/core/defaults/notifier.hpp @@ -0,0 +1,31 @@ +// notifier.hpp - Configuració del cuadre de notificacions toast (System::Notifier) +// © 2026 JailDesigner + +#pragma once + +#include + +namespace Defaults::Notifier { + + // Geometria del cuadre en coordenades lògiques (1280×720). + constexpr float CANVAS_WIDTH = 1280.0F; + constexpr float MARGIN_TOP = 40.0F; + constexpr float PADDING_H = 16.0F; + constexpr float PADDING_V = 10.0F; + constexpr float BORDER_THICKNESS = 2.0F; + constexpr float TEXT_SCALE = 0.55F; + constexpr float TEXT_SPACING = 2.0F; + constexpr float BORDER_BRIGHTNESS = 1.0F; + + // Cinemàtica del slide. + constexpr float SLIDE_DURATION_S = 0.30F; + + // Presets per als atajos semàntics. + constexpr SDL_Color COLOR_INFO{.r = 80, .g = 230, .b = 255, .a = 255}; + constexpr SDL_Color COLOR_WARN{.r = 255, .g = 180, .b = 40, .a = 255}; + constexpr SDL_Color COLOR_EXIT{.r = 255, .g = 80, .b = 80, .a = 255}; + constexpr float DURATION_INFO = 2.0F; + constexpr float DURATION_WARN = 3.0F; + constexpr float DURATION_EXIT = 3.0F; + +} // namespace Defaults::Notifier diff --git a/source/core/defaults/ship.hpp b/source/core/defaults/ship.hpp index 6156a3d..75e07b9 100644 --- a/source/core/defaults/ship.hpp +++ b/source/core/defaults/ship.hpp @@ -13,4 +13,15 @@ namespace Defaults::Ship { constexpr float BLINK_INVISIBLE_TIME = 0.1F; // Tiempo invisible (segundos) // Frecuencia total: 0.2s/ciclo = 5 Hz (~15 parpadeos en 3s) + // Cuerpo físico + constexpr float MASS = 10.0F; // Masa de referencia para choques + constexpr float RESTITUTION = 0.6F; // Rebote moderado contra paredes + constexpr float LINEAR_DAMPING = 1.5F; // Fricción exponencial (s⁻¹) + constexpr float ANGULAR_DAMPING = 0.0F; // Rotación 100% por input (no inercial) + + // Empuje visual: escala proporcional a la velocidad (0..200 px/s → 1.0..1.5) + // Mantiene la sensación del Pascal original. + constexpr float VISUAL_PUSH_DIVISOR = 33.33F; // SPEED / DIVISOR = empuje visual + constexpr float VISUAL_SCALE_DIVISOR = 12.0F; // SCALE = 1 + (PUSH / DIVISOR) + } // namespace Defaults::Ship diff --git a/source/core/system/notifier.cpp b/source/core/system/notifier.cpp index b44f756..ccf390d 100644 --- a/source/core/system/notifier.cpp +++ b/source/core/system/notifier.cpp @@ -2,24 +2,15 @@ #include "core/system/notifier.hpp" +#include "core/defaults.hpp" #include "core/rendering/gpu/gpu_frame_renderer.hpp" #include "core/utils/easing.hpp" namespace System { namespace { - // Geometria del cuadre en coordenades lògiques (1280×720). - constexpr float CANVAS_WIDTH = 1280.0F; - constexpr float MARGIN_TOP = 40.0F; - constexpr float PADDING_H = 16.0F; - constexpr float PADDING_V = 10.0F; - constexpr float BORDER_THICKNESS = 2.0F; - constexpr float TEXT_SCALE = 0.55F; - constexpr float TEXT_SPACING = 2.0F; - constexpr float BORDER_BRIGHTNESS = 1.0F; - - // Cinemàtica del slide. - constexpr float SLIDE_DURATION_S = 0.30F; + // Alias d'àmbit local per accedir compactament als defaults del notifier. + namespace Cfg = Defaults::Notifier; // Conversió color SDL → float [0,1]. constexpr auto toUnit(Uint8 v) -> float { @@ -47,14 +38,6 @@ namespace System { .b = toUnit(c.b) * DARKEN, .a = BG_ALPHA}; } - - // Presets per als atajos semàntics. - constexpr SDL_Color COLOR_INFO{.r = 80, .g = 230, .b = 255, .a = 255}; - constexpr SDL_Color COLOR_WARN{.r = 255, .g = 180, .b = 40, .a = 255}; - constexpr SDL_Color COLOR_EXIT{.r = 255, .g = 80, .b = 80, .a = 255}; - constexpr float DURATION_INFO = 2.0F; - constexpr float DURATION_WARN = 3.0F; - constexpr float DURATION_EXIT = 3.0F; } // namespace std::unique_ptr Notifier::instance; @@ -79,15 +62,15 @@ namespace System { hold_remaining_s_ = duration_s; current_is_exit_ = false; - const float TEXT_W = Graphics::VectorText::getTextWidth(text, TEXT_SCALE, TEXT_SPACING); - const float TEXT_H = Graphics::VectorText::getTextHeight(TEXT_SCALE); + const float TEXT_W = Graphics::VectorText::getTextWidth(text, Cfg::TEXT_SCALE, Cfg::TEXT_SPACING); + const float TEXT_H = Graphics::VectorText::getTextHeight(Cfg::TEXT_SCALE); - box_w_ = TEXT_W + (PADDING_H * 2.0F); - box_h_ = TEXT_H + (PADDING_V * 2.0F); - text_x_ = (CANVAS_WIDTH - TEXT_W) * 0.5F; + box_w_ = TEXT_W + (Cfg::PADDING_H * 2.0F); + box_h_ = TEXT_H + (Cfg::PADDING_V * 2.0F); + text_x_ = (Cfg::CANVAS_WIDTH - TEXT_W) * 0.5F; - y_on_ = MARGIN_TOP; - y_off_ = -(box_h_ + BORDER_THICKNESS); + y_on_ = Cfg::MARGIN_TOP; + y_off_ = -(box_h_ + Cfg::BORDER_THICKNESS); // Si ja es veu, reseteja el slide-in des de la posició actual perquè // la transició sembli continua. Si està amagat, arrenc des de fora. @@ -96,13 +79,13 @@ namespace System { } status_ = Status::ENTERING; slide_elapsed_s_ = 0.0F; - text_scale_ = TEXT_SCALE; + text_scale_ = Cfg::TEXT_SCALE; } - void Notifier::notifyInfo(const std::string& text) { notify(text, COLOR_INFO, DURATION_INFO); } - void Notifier::notifyWarn(const std::string& text) { notify(text, COLOR_WARN, DURATION_WARN); } + void Notifier::notifyInfo(const std::string& text) { notify(text, Cfg::COLOR_INFO, Cfg::DURATION_INFO); } + void Notifier::notifyWarn(const std::string& text) { notify(text, Cfg::COLOR_WARN, Cfg::DURATION_WARN); } void Notifier::notifyExit(const std::string& text) { - notify(text, COLOR_EXIT, DURATION_EXIT); + notify(text, Cfg::COLOR_EXIT, Cfg::DURATION_EXIT); current_is_exit_ = true; // notify() ho ha posat a false; restaurem. } @@ -110,12 +93,12 @@ namespace System { switch (status_) { case Status::ENTERING: { slide_elapsed_s_ += delta_time; - if (slide_elapsed_s_ >= SLIDE_DURATION_S) { + if (slide_elapsed_s_ >= Cfg::SLIDE_DURATION_S) { y_current_ = y_on_; status_ = Status::HOLDING; slide_elapsed_s_ = 0.0F; } else { - const float T = slide_elapsed_s_ / SLIDE_DURATION_S; + const float T = slide_elapsed_s_ / Cfg::SLIDE_DURATION_S; const float K = Utils::Easing::outCubic(T); y_current_ = y_off_ + ((y_on_ - y_off_) * K); } @@ -131,11 +114,11 @@ namespace System { } case Status::EXITING: { slide_elapsed_s_ += delta_time; - if (slide_elapsed_s_ >= SLIDE_DURATION_S) { + if (slide_elapsed_s_ >= Cfg::SLIDE_DURATION_S) { y_current_ = y_off_; status_ = Status::HIDDEN; } else { - const float T = slide_elapsed_s_ / SLIDE_DURATION_S; + const float T = slide_elapsed_s_ / Cfg::SLIDE_DURATION_S; const float K = Utils::Easing::inCubic(T); y_current_ = y_on_ + ((y_off_ - y_on_) * K); } @@ -152,7 +135,7 @@ namespace System { return; } - const float BOX_X = (CANVAS_WIDTH - box_w_) * 0.5F; + const float BOX_X = (Cfg::CANVAS_WIDTH - box_w_) * 0.5F; const float BOX_Y = y_current_; const UnitRGBA TC = textColorFloat(current_color_); const UnitRGBA BG = bgColorFloat(current_color_); @@ -167,19 +150,19 @@ namespace System { const float Y1 = BOX_Y; const float X2 = BOX_X + box_w_; const float Y2 = BOX_Y + box_h_; - gpu->pushLine(X1, Y1, X2, Y1, BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // top - gpu->pushLine(X1, Y2, X2, Y2, BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // bottom - gpu->pushLine(X1, Y1, X1, Y2, BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // left - gpu->pushLine(X2, Y1, X2, Y2, BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // right + gpu->pushLine(X1, Y1, X2, Y1, Cfg::BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // top + gpu->pushLine(X1, Y2, X2, Y2, Cfg::BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // bottom + gpu->pushLine(X1, Y1, X1, Y2, Cfg::BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // left + gpu->pushLine(X2, Y1, X2, Y2, Cfg::BORDER_THICKNESS, TC.r, TC.g, TC.b, TC.a); // right // 3. Text centrat dins la caixa, amb color explícit (l'alpha != 0 // li diu al renderShape que no agafe l'oscil·lador global de color). - const float TEXT_Y = BOX_Y + PADDING_V; + const float TEXT_Y = BOX_Y + Cfg::PADDING_V; text_.render(current_text_, Vec2{.x = text_x_, .y = TEXT_Y}, text_scale_, - TEXT_SPACING, - BORDER_BRIGHTNESS, + Cfg::TEXT_SPACING, + Cfg::BORDER_BRIGHTNESS, current_color_); } diff --git a/source/game/entities/bullet.cpp b/source/game/entities/bullet.cpp index 6434263..32186b4 100644 --- a/source/game/entities/bullet.cpp +++ b/source/game/entities/bullet.cpp @@ -16,12 +16,6 @@ #include "core/types.hpp" #include "game/constants.hpp" -namespace { - // Velocidad escalar de las balas (px/s). Conserva el feel del Pascal original - // (7 px/frame × 20 FPS = 140 px/s). - constexpr float BULLET_SPEED = 140.0F; -} // namespace - Bullet::Bullet(Rendering::Renderer* renderer) : Entity(renderer) { // Brightness específico para balas @@ -80,7 +74,7 @@ void Bullet::disparar(const Vec2& position, float angle, uint8_t owner_id) { body_.angle = angle; const float DIR_X = std::cos(angle - (Constants::PI / 2.0F)); const float DIR_Y = std::sin(angle - (Constants::PI / 2.0F)); - body_.velocity = Vec2{.x = DIR_X * BULLET_SPEED, .y = DIR_Y * BULLET_SPEED}; + body_.velocity = Vec2{.x = DIR_X * Defaults::Game::BULLET_SPEED, .y = DIR_Y * Defaults::Game::BULLET_SPEED}; body_.angular_velocity = 0.0F; body_.clearAccumulators(); diff --git a/source/game/entities/enemy.cpp b/source/game/entities/enemy.cpp index 1617366..033b942 100644 --- a/source/game/entities/enemy.cpp +++ b/source/game/entities/enemy.cpp @@ -41,16 +41,16 @@ namespace { Enemy::Enemy(Rendering::Renderer* renderer) : Entity(renderer), - tracking_strength_(0.5F) { + tracking_strength_(Defaults::Enemies::Cuadrado::TRACKING_STRENGTH) { brightness_ = Defaults::Brightness::ENEMIC; // Configuración del cuerpo físico — defaults para enemy genérico. // init() ajusta velocidad y masa según el tipo (Pentagon/Quadrat/Molinillo). - body_.setMass(5.0F); // Más liviano que la nave (10.0) - body_.radius = 0.0F; // 0 hasta spawn (no colisiona inactivo) - body_.restitution = 1.0F; // Rebote elástico perfecto contra paredes - body_.linear_damping = 0.0F; // Sin fricción: mantienen velocidad - body_.angular_damping = 0.0F; // Idem + body_.setMass(Defaults::Enemies::Body::DEFAULT_MASS); + body_.radius = 0.0F; // 0 hasta spawn (no colisiona inactivo) + body_.restitution = Defaults::Enemies::Body::RESTITUTION; + body_.linear_damping = Defaults::Enemies::Body::LINEAR_DAMPING; + body_.angular_damping = Defaults::Enemies::Body::ANGULAR_DAMPING; } void Enemy::init(EnemyType type, const Vec2* ship_pos) { @@ -60,7 +60,7 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { float base_speed = 0.0F; float drotacio_min = 0.0F; float drotacio_max = 0.0F; - float type_mass = 5.0F; + float type_mass = Defaults::Enemies::Body::DEFAULT_MASS; switch (type_) { case EnemyType::PENTAGON: @@ -68,7 +68,7 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { base_speed = Defaults::Enemies::Pentagon::VELOCITAT; drotacio_min = Defaults::Enemies::Pentagon::DROTACIO_MIN; drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX; - type_mass = 5.0F; + type_mass = Defaults::Enemies::Pentagon::MASS; break; case EnemyType::QUADRAT: @@ -76,7 +76,7 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { base_speed = Defaults::Enemies::Cuadrado::VELOCITAT; drotacio_min = Defaults::Enemies::Cuadrado::DROTACIO_MIN; drotacio_max = Defaults::Enemies::Cuadrado::DROTACIO_MAX; - type_mass = 8.0F; // Más pesado, "tanque" + type_mass = Defaults::Enemies::Cuadrado::MASS; tracking_timer_ = 0.0F; break; @@ -85,7 +85,7 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) { base_speed = Defaults::Enemies::Molinillo::VELOCITAT; drotacio_min = Defaults::Enemies::Molinillo::DROTACIO_MIN; drotacio_max = Defaults::Enemies::Molinillo::DROTACIO_MAX; - type_mass = 4.0F; // Más liviano, ágil + type_mass = Defaults::Enemies::Molinillo::MASS; break; default: @@ -268,9 +268,8 @@ void Enemy::behaviorPentagon(float delta_time) { // Probabilidad de zigzag por segundo (calibrada para sensación equivalente // a la versión vieja que disparaba en cada toque de pared). - constexpr float ZIGZAG_PROB_PER_SECOND = 0.8F; const float RAND_VAL = static_cast(std::rand()) / static_cast(RAND_MAX); - if (RAND_VAL < ZIGZAG_PROB_PER_SECOND * 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; diff --git a/source/game/entities/ship.cpp b/source/game/entities/ship.cpp index f4b129e..f021eb4 100644 --- a/source/game/entities/ship.cpp +++ b/source/game/entities/ship.cpp @@ -25,11 +25,11 @@ Ship::Ship(Rendering::Renderer* renderer, const char* shape_file) brightness_ = Defaults::Brightness::NAU; // Configuración del cuerpo físico - body_.setMass(10.0F); // Masa de referencia para choques - body_.radius = Defaults::Entities::SHIP_RADIUS; // Radio de colisión - body_.restitution = 0.6F; // Rebote moderado contra paredes - body_.linear_damping = 1.5F; // Fricción exponencial (s⁻¹) - body_.angular_damping = 0.0F; // La rotación es 100% por input, no inercial + body_.setMass(Defaults::Ship::MASS); + body_.radius = Defaults::Entities::SHIP_RADIUS; + body_.restitution = Defaults::Ship::RESTITUTION; + body_.linear_damping = Defaults::Ship::LINEAR_DAMPING; + body_.angular_damping = Defaults::Ship::ANGULAR_DAMPING; // Cargar shape compartida desde archivo shape_ = Graphics::ShapeLoader::load(shape_file); @@ -154,8 +154,8 @@ void Ship::draw() const { // Efecto visual de empuje: escala proporcional a la velocidad. // 0..200 px/s → escala 1.0..1.5 (manteniendo la sensación del Pascal original). const float SPEED = getSpeed(); - const float VISUAL_PUSH = SPEED / 33.33F; - const float SCALE = 1.0F + (VISUAL_PUSH / 12.0F); + const float VISUAL_PUSH = SPEED / Defaults::Ship::VISUAL_PUSH_DIVISOR; + const float SCALE = 1.0F + (VISUAL_PUSH / Defaults::Ship::VISUAL_SCALE_DIVISOR); Rendering::renderShape(renderer_, shape_, center_, angle_, SCALE, 1.0F, brightness_, Defaults::Palette::SHIP); } diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 2c7170c..136b833 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -674,8 +674,8 @@ void GameScene::drawScoreboard() { std::string text = buildScoreboard(); // Parámetros de renderització - const float SCALE = 0.85F; - const float SPACING = 0.0F; + const float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE; + const float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING; // Calcular centro de la zona del marcador const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD; diff --git a/source/game/systems/init_hud_animator.cpp b/source/game/systems/init_hud_animator.cpp index a5e9c04..480315a 100644 --- a/source/game/systems/init_hud_animator.cpp +++ b/source/game/systems/init_hud_animator.cpp @@ -13,87 +13,88 @@ namespace Systems::InitHud { -auto computeRangeProgress(float global_progress, - float ratio_init, - float ratio_end) -> float { - if (ratio_init >= ratio_end) { - return (global_progress >= ratio_end) ? 1.0F : 0.0F; - } - if (global_progress < ratio_init) { - return 0.0F; - } - if (global_progress > ratio_end) { - return 1.0F; - } - return (global_progress - ratio_init) / (ratio_end - ratio_init); -} - -auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2 { - const float EASED = Easing::easeOutQuad(progress); - const SDL_FRect& zone = Defaults::Zones::PLAYAREA; - // Y inicial: 50 px bajo la zona de juego. - const float Y_INI = zone.y + zone.h + 50.0F; - const float Y_ANIM = Y_INI + ((final_position.y - Y_INI) * EASED); - return Vec2{.x = final_position.x, .y = Y_ANIM}; -} - -void drawBordersAnimated(Rendering::Renderer* renderer, float progress) { - const SDL_FRect& zone = Defaults::Zones::PLAYAREA; - const float EASED = Easing::easeOutQuad(progress); - - 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 CX = (X1 + X2) / 2; - - constexpr float PHASE_1_END = 0.33F; - constexpr float PHASE_2_END = 0.66F; - - // Fase 1: línea superior crece desde el centro hacia los lados. - if (EASED > 0.0F) { - const float P = std::min(EASED / PHASE_1_END, 1.0F); - const int X_LEFT = static_cast(CX - ((CX - X1) * P)); - const int X_RIGHT = static_cast(CX + ((X2 - CX) * P)); - Rendering::linea(renderer, CX, Y1, X_LEFT, Y1); - Rendering::linea(renderer, CX, Y1, X_RIGHT, Y1); + auto computeRangeProgress(float global_progress, + float ratio_init, + float ratio_end) -> float { + if (ratio_init >= ratio_end) { + return (global_progress >= ratio_end) ? 1.0F : 0.0F; + } + if (global_progress < ratio_init) { + return 0.0F; + } + if (global_progress > ratio_end) { + return 1.0F; + } + return (global_progress - ratio_init) / (ratio_end - ratio_init); } - // Fase 2: laterales bajan. - if (EASED > PHASE_1_END) { - const float P = std::min((EASED - PHASE_1_END) / (PHASE_2_END - PHASE_1_END), 1.0F); - const int Y_BOTTOM = static_cast(Y1 + ((Y2 - Y1) * P)); - Rendering::linea(renderer, X1, Y1, X1, Y_BOTTOM); - Rendering::linea(renderer, X2, Y1, X2, Y_BOTTOM); + auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2 { + const float EASED = Easing::easeOutQuad(progress); + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + // Y inicial: 50 px bajo la zona de juego. + const float Y_INI = zone.y + zone.h + 50.0F; + const float Y_ANIM = Y_INI + ((final_position.y - Y_INI) * EASED); + return Vec2{.x = final_position.x, .y = Y_ANIM}; } - // Fase 3: línea inferior crece desde los lados hacia el centro. - if (EASED > PHASE_2_END) { - const float P = (EASED - PHASE_2_END) / (1.0F - PHASE_2_END); - const int X_LEFT = static_cast(X1 + ((CX - X1) * P)); - const int X_RIGHT = static_cast(X2 - ((X2 - CX) * P)); - Rendering::linea(renderer, X1, Y2, X_LEFT, Y2); - Rendering::linea(renderer, X2, Y2, X_RIGHT, Y2); + void drawBordersAnimated(Rendering::Renderer* renderer, float progress) { + const SDL_FRect& zone = Defaults::Zones::PLAYAREA; + const float EASED = Easing::easeOutQuad(progress); + + 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 CX = (X1 + X2) / 2; + + constexpr float PHASE_1_END = 0.33F; + constexpr float PHASE_2_END = 0.66F; + + // Fase 1: línea superior crece desde el centro hacia los lados. + if (EASED > 0.0F) { + const float P = std::min(EASED / PHASE_1_END, 1.0F); + const int X_LEFT = static_cast(CX - ((CX - X1) * P)); + const int X_RIGHT = static_cast(CX + ((X2 - CX) * P)); + Rendering::linea(renderer, CX, Y1, X_LEFT, Y1); + Rendering::linea(renderer, CX, Y1, X_RIGHT, Y1); + } + + // Fase 2: laterales bajan. + if (EASED > PHASE_1_END) { + const float P = std::min((EASED - PHASE_1_END) / (PHASE_2_END - PHASE_1_END), 1.0F); + const int Y_BOTTOM = static_cast(Y1 + ((Y2 - Y1) * P)); + Rendering::linea(renderer, X1, Y1, X1, Y_BOTTOM); + Rendering::linea(renderer, X2, Y1, X2, Y_BOTTOM); + } + + // Fase 3: línea inferior crece desde los lados hacia el centro. + if (EASED > PHASE_2_END) { + const float P = (EASED - PHASE_2_END) / (1.0F - PHASE_2_END); + const int X_LEFT = static_cast(X1 + ((CX - X1) * P)); + const int X_RIGHT = static_cast(X2 - ((X2 - CX) * P)); + Rendering::linea(renderer, X1, Y2, X_LEFT, Y2); + Rendering::linea(renderer, X2, Y2, X_RIGHT, Y2); + } } -} -void drawScoreboardAnimated(const Graphics::VectorText& text, - const std::string& scoreboard_text, - float progress) { - const float EASED = Easing::easeOutQuad(progress); + void drawScoreboardAnimated(const Graphics::VectorText& text, + const std::string& scoreboard_text, + float progress) { + const float EASED = Easing::easeOutQuad(progress); - constexpr float SCALE = 0.85F; - constexpr float SPACING = 0.0F; - const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD; - const float CENTRE_X = scoreboard_zone.w / 2.0F; - const float Y_FINAL = scoreboard_zone.y + (scoreboard_zone.h / 2.0F); - // Posición inicial: fuera de la pantalla por debajo. - const auto Y_INI = static_cast(Defaults::Game::HEIGHT); - const float Y_ANIM = Y_INI + ((Y_FINAL - Y_INI) * EASED); + constexpr float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE; + constexpr float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING; + const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD; + const float CENTRE_X = scoreboard_zone.w / 2.0F; + const float Y_FINAL = scoreboard_zone.y + (scoreboard_zone.h / 2.0F); + // Posición inicial: fuera de la pantalla por debajo. + const auto Y_INI = static_cast(Defaults::Game::HEIGHT); + const float Y_ANIM = Y_INI + ((Y_FINAL - Y_INI) * EASED); - text.renderCentered(scoreboard_text, - Vec2{.x = CENTRE_X, .y = Y_ANIM}, - SCALE, SPACING); -} + text.renderCentered(scoreboard_text, + Vec2{.x = CENTRE_X, .y = Y_ANIM}, + SCALE, + SPACING); + } } // namespace Systems::InitHud