refactor(defaults): centralitza constants de bullet, ship, enemy, hud i notifier

This commit is contained in:
2026-05-21 09:39:36 +02:00
parent 61ae211dab
commit 08100f60e8
12 changed files with 197 additions and 150 deletions
+2
View File
@@ -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"
+17 -5
View File
@@ -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%]
+1
View File
@@ -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
+13
View File
@@ -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
+31
View File
@@ -0,0 +1,31 @@
// notifier.hpp - Configuració del cuadre de notificacions toast (System::Notifier)
// © 2026 JailDesigner
#pragma once
#include <SDL3/SDL.h>
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
+11
View File
@@ -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
+26 -43
View File
@@ -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> 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_);
}
+1 -7
View File
@@ -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();
+11 -12
View File
@@ -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<float>(std::rand()) / static_cast<float>(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<float>(std::rand()) / static_cast<float>(RAND_MAX)) *
Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX;
+7 -7
View File
@@ -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);
}
+2 -2
View File
@@ -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;
+75 -74
View File
@@ -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<int>(zone.x);
const int Y1 = static_cast<int>(zone.y);
const int X2 = static_cast<int>(zone.x + zone.w);
const int Y2 = static_cast<int>(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<int>(CX - ((CX - X1) * P));
const int X_RIGHT = static_cast<int>(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<int>(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<int>(X1 + ((CX - X1) * P));
const int X_RIGHT = static_cast<int>(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<int>(zone.x);
const int Y1 = static_cast<int>(zone.y);
const int X2 = static_cast<int>(zone.x + zone.w);
const int Y2 = static_cast<int>(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<int>(CX - ((CX - X1) * P));
const int X_RIGHT = static_cast<int>(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<int>(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<int>(X1 + ((CX - X1) * P));
const int X_RIGHT = static_cast<int>(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<float>(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<float>(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