Merge branch 'feat/centralize-defaults'
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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%]
|
||||
|
||||
@@ -10,9 +10,13 @@ namespace Defaults::Game {
|
||||
constexpr int HEIGHT = 720;
|
||||
|
||||
// Regles de partida
|
||||
constexpr int STARTING_LIVES = 3; // Initial lives
|
||||
constexpr float DEATH_DURATION = 3.0F; // Seconds of death animation
|
||||
constexpr float GAME_OVER_DURATION = 5.0F; // Seconds to display game over
|
||||
constexpr int STARTING_LIVES = 3; // Initial lives
|
||||
constexpr float DEATH_DURATION = 3.0F; // Seconds of death animation
|
||||
constexpr float GAME_OVER_DURATION = 5.0F; // Seconds to display game over
|
||||
|
||||
// Valores centinela del temporitzador de mort per-jugador.
|
||||
constexpr float HIT_TIMER_INACTIVE_PLAYER = 999.0F; // Jugador permanentment inactiu
|
||||
constexpr float HIT_TIMER_TRIGGER_DEATH = 0.001F; // Trigger inicial post-impacte (>0 sense disparar regla)
|
||||
constexpr float COLLISION_SHIP_ENEMY_AMPLIFIER = 0.80F; // 80% hitbox (generous)
|
||||
constexpr float COLLISION_BULLET_ENEMY_AMPLIFIER = 1.15F; // 115% hitbox (generous)
|
||||
|
||||
@@ -20,6 +24,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
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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;
|
||||
|
||||
// Animación de entrada del HUD (init_hud_animator).
|
||||
namespace InitAnim {
|
||||
// Spawn vertical de la nave: 50 px bajo la PLAYAREA (sale desde fuera).
|
||||
constexpr float SHIP_SPAWN_Y_OFFSET = 50.0F;
|
||||
|
||||
// Bordes: ratios de las tres fases (top → laterales → bottom).
|
||||
constexpr float BORDER_PHASE_1_END = 0.33F; // Fin de la fase top
|
||||
constexpr float BORDER_PHASE_2_END = 0.66F; // Fin de la fase laterales
|
||||
} // namespace InitAnim
|
||||
|
||||
// Indicadores ("tips") sobre los enemigos enganchados a la nave.
|
||||
// Offset local al frame de la nave (apunta hacia delante, eje Y negativo).
|
||||
namespace Tips {
|
||||
constexpr float LOCAL_X = 0.0F;
|
||||
constexpr float LOCAL_Y = -12.0F;
|
||||
} // namespace Tips
|
||||
|
||||
// Overlay de debug (FPS, métriques) en coordenades lògiques (1280×720).
|
||||
namespace DebugOverlay {
|
||||
constexpr float X = 12.0F;
|
||||
constexpr float Y_FPS = 12.0F;
|
||||
constexpr float LINE_HEIGHT = 18.0F; // separació entre línies (scale 0.4 → ~16 px alt)
|
||||
constexpr float TEXT_SCALE = 0.4F;
|
||||
constexpr float TEXT_SPACING = 2.0F;
|
||||
constexpr float BRIGHTNESS = 1.0F;
|
||||
constexpr float FPS_UPDATE_INTERVAL = 0.5F; // Cadencia d'actualització del FPS visible
|
||||
} // namespace DebugOverlay
|
||||
|
||||
} // namespace Defaults::Hud
|
||||
@@ -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
|
||||
@@ -26,6 +26,9 @@ namespace Defaults::Physics {
|
||||
constexpr float FACTOR_HERENCIA_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;
|
||||
|
||||
// Angular velocity sin for trajectory inheritance
|
||||
// Excess above this threshold is converted to tangential linear velocity
|
||||
// Prevents "vortex trap" problem with high-rotation enemies
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace Defaults::Rendering {
|
||||
constexpr int VSYNC_DEFAULT = 1; // 0=disabled, 1=enabled
|
||||
constexpr int ANTIALIAS_DEFAULT = 1; // 0=disabled, 1=enabled (AA geomètric a les línies)
|
||||
|
||||
// Grosor global per defecte de les línies. 1.5 dóna línia visible i crujent;
|
||||
// 1.0 es veu massa fi en pantalles grans. Configurable via setLineThickness.
|
||||
constexpr float LINE_THICKNESS_DEFAULT = 1.5F;
|
||||
|
||||
// Resolució del render target offscreen. El tamany lògic del joc roman a
|
||||
// 1280×720 (coordenades dels objectes); aquesta és la resolució física a
|
||||
// la qual es rasteritzen les línies abans de la composició final.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,52 +3,56 @@
|
||||
|
||||
#include "core/rendering/line_renderer.hpp"
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
// Color global compartido para líneas sin paleta propia (HUD, debug, texto
|
||||
// genérico). Equivale al "color máximo" de la antigua oscilación CPU: verde
|
||||
// fósforo CRT. El pulso de brillo lo aplica ahora el shader de postpro.
|
||||
SDL_Color g_current_line_color = {100, 255, 100, 255};
|
||||
// Color global compartido para líneas sin paleta propia (HUD, debug, texto
|
||||
// genérico). Equivale al "color máximo" de la antigua oscilación CPU: verde
|
||||
// fósforo CRT. El pulso de brillo lo aplica ahora el shader de postpro.
|
||||
SDL_Color g_current_line_color = {100, 255, 100, 255};
|
||||
|
||||
// Grosor global por defecto. Configurable via setLineThickness.
|
||||
// 1.5 da una línea visible y crujiente; 1.0 se ve demasiado fino en pantallas grandes.
|
||||
float g_current_line_thickness = 1.5F;
|
||||
// Grosor global por defecto. Configurable via setLineThickness.
|
||||
float g_current_line_thickness = Defaults::Rendering::LINE_THICKNESS_DEFAULT;
|
||||
|
||||
void linea(Renderer* renderer,
|
||||
int x1, int y1, int x2, int y2,
|
||||
float brightness,
|
||||
float thickness,
|
||||
SDL_Color color) {
|
||||
if (renderer == nullptr) {
|
||||
return;
|
||||
void linea(Renderer* renderer,
|
||||
int x1,
|
||||
int y1,
|
||||
int x2,
|
||||
int y2,
|
||||
float brightness,
|
||||
float thickness,
|
||||
SDL_Color color) {
|
||||
if (renderer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Coords lógicas (1280×720). El shader hace el mapeo a NDC; el viewport
|
||||
// del SDLManager hace el letterbox a píxeles físicos.
|
||||
const auto FX1 = static_cast<float>(x1);
|
||||
const auto FY1 = static_cast<float>(y1);
|
||||
const auto FX2 = static_cast<float>(x2);
|
||||
const auto FY2 = static_cast<float>(y2);
|
||||
|
||||
// color.alpha==0 → usar color global (verde fósforo). alpha>0 → color directo.
|
||||
const SDL_Color SOURCE = (color.a > 0) ? color : g_current_line_color;
|
||||
const float R = (static_cast<float>(SOURCE.r) * brightness) / 255.0F;
|
||||
const float G = (static_cast<float>(SOURCE.g) * brightness) / 255.0F;
|
||||
const float B = (static_cast<float>(SOURCE.b) * brightness) / 255.0F;
|
||||
|
||||
const float W = (thickness > 0.0F) ? thickness : g_current_line_thickness;
|
||||
|
||||
renderer->pushLine(FX1, FY1, FX2, FY2, W, R, G, B, 1.0F);
|
||||
}
|
||||
|
||||
// Coords lógicas (1280×720). El shader hace el mapeo a NDC; el viewport
|
||||
// del SDLManager hace el letterbox a píxeles físicos.
|
||||
const auto FX1 = static_cast<float>(x1);
|
||||
const auto FY1 = static_cast<float>(y1);
|
||||
const auto FX2 = static_cast<float>(x2);
|
||||
const auto FY2 = static_cast<float>(y2);
|
||||
void setLineColor(SDL_Color color) { g_current_line_color = color; }
|
||||
|
||||
// color.alpha==0 → usar color global (verde fósforo). alpha>0 → color directo.
|
||||
const SDL_Color SOURCE = (color.a > 0) ? color : g_current_line_color;
|
||||
const float R = (static_cast<float>(SOURCE.r) * brightness) / 255.0F;
|
||||
const float G = (static_cast<float>(SOURCE.g) * brightness) / 255.0F;
|
||||
const float B = (static_cast<float>(SOURCE.b) * brightness) / 255.0F;
|
||||
|
||||
const float W = (thickness > 0.0F) ? thickness : g_current_line_thickness;
|
||||
|
||||
renderer->pushLine(FX1, FY1, FX2, FY2, W, R, G, B, 1.0F);
|
||||
}
|
||||
|
||||
void setLineColor(SDL_Color color) { g_current_line_color = color; }
|
||||
|
||||
void setLineThickness(float thickness) {
|
||||
if (thickness > 0.0F) {
|
||||
g_current_line_thickness = thickness;
|
||||
void setLineThickness(float thickness) {
|
||||
if (thickness > 0.0F) {
|
||||
g_current_line_thickness = thickness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto getLineThickness() -> float { return g_current_line_thickness; }
|
||||
auto getLineThickness() -> float { return g_current_line_thickness; }
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -4,21 +4,13 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/types.hpp"
|
||||
|
||||
namespace System {
|
||||
|
||||
namespace {
|
||||
// Posición y tamaño del overlay en coordenadas lógicas (1280×720).
|
||||
constexpr float OVERLAY_X = 12.0F;
|
||||
constexpr float OVERLAY_Y_FPS = 12.0F;
|
||||
constexpr float OVERLAY_LINE_HEIGHT = 18.0F; // separación entre líneas (scale 0.4 → ~16 px alto)
|
||||
constexpr float OVERLAY_SCALE = 0.4F;
|
||||
constexpr float OVERLAY_SPACING = 2.0F;
|
||||
constexpr float OVERLAY_BRIGHTNESS = 1.0F;
|
||||
|
||||
// Cadencia de actualización del valor de FPS mostrado.
|
||||
constexpr float FPS_UPDATE_INTERVAL = 0.5F;
|
||||
namespace Cfg = Defaults::Hud::DebugOverlay;
|
||||
} // namespace
|
||||
|
||||
DebugOverlay::DebugOverlay(Rendering::Renderer* renderer,
|
||||
@@ -30,7 +22,7 @@ namespace System {
|
||||
fps_accumulator_ += delta_time;
|
||||
fps_frame_count_++;
|
||||
|
||||
if (fps_accumulator_ >= FPS_UPDATE_INTERVAL) {
|
||||
if (fps_accumulator_ >= Cfg::FPS_UPDATE_INTERVAL) {
|
||||
fps_display_ = static_cast<int>(fps_frame_count_ / fps_accumulator_);
|
||||
fps_frame_count_ = 0;
|
||||
fps_accumulator_ = 0.0F;
|
||||
@@ -47,20 +39,20 @@ namespace System {
|
||||
const std::string AA_TEXT = std::string("AA: ") + (rendering_cfg_->antialias == 1 ? "ON" : "OFF");
|
||||
|
||||
text_.render(FPS_TEXT,
|
||||
Vec2{.x = OVERLAY_X, .y = OVERLAY_Y_FPS},
|
||||
OVERLAY_SCALE,
|
||||
OVERLAY_SPACING,
|
||||
OVERLAY_BRIGHTNESS);
|
||||
Vec2{.x = Cfg::X, .y = Cfg::Y_FPS},
|
||||
Cfg::TEXT_SCALE,
|
||||
Cfg::TEXT_SPACING,
|
||||
Cfg::BRIGHTNESS);
|
||||
text_.render(VSYNC_TEXT,
|
||||
Vec2{.x = OVERLAY_X, .y = OVERLAY_Y_FPS + OVERLAY_LINE_HEIGHT},
|
||||
OVERLAY_SCALE,
|
||||
OVERLAY_SPACING,
|
||||
OVERLAY_BRIGHTNESS);
|
||||
Vec2{.x = Cfg::X, .y = Cfg::Y_FPS + Cfg::LINE_HEIGHT},
|
||||
Cfg::TEXT_SCALE,
|
||||
Cfg::TEXT_SPACING,
|
||||
Cfg::BRIGHTNESS);
|
||||
text_.render(AA_TEXT,
|
||||
Vec2{.x = OVERLAY_X, .y = OVERLAY_Y_FPS + (2.0F * OVERLAY_LINE_HEIGHT)},
|
||||
OVERLAY_SCALE,
|
||||
OVERLAY_SPACING,
|
||||
OVERLAY_BRIGHTNESS);
|
||||
Vec2{.x = Cfg::X, .y = Cfg::Y_FPS + (2.0F * Cfg::LINE_HEIGHT)},
|
||||
Cfg::TEXT_SCALE,
|
||||
Cfg::TEXT_SPACING,
|
||||
Cfg::BRIGHTNESS);
|
||||
}
|
||||
|
||||
} // namespace System
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context)
|
||||
} else {
|
||||
// Jugador inactiu: marcar como a mort permanent
|
||||
ships_[i].markHit();
|
||||
hit_timer_per_player_[i] = 999.0F; // Valor sentinella (permanent inactiu)
|
||||
lives_per_player_[i] = 0; // Sin vides
|
||||
hit_timer_per_player_[i] = Defaults::Game::HIT_TIMER_INACTIVE_PLAYER;
|
||||
lives_per_player_[i] = 0; // Sin vides
|
||||
std::cout << "[GameScene] Jugador " << (i + 1) << " inactiu\n";
|
||||
}
|
||||
}
|
||||
@@ -201,8 +201,8 @@ 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] != 999.0F) ||
|
||||
(match_config_.jugador2_actiu && hit_timer_per_player_[1] != 999.0F);
|
||||
(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);
|
||||
if (!ALGU_VIU) {
|
||||
return;
|
||||
}
|
||||
@@ -211,7 +211,7 @@ void GameScene::stepMidGameJoin() {
|
||||
for (uint8_t pid = 0; pid < 2; pid++) {
|
||||
const bool ACTIU = (pid == 0) ? match_config_.jugador1_actiu
|
||||
: match_config_.jugador2_actiu;
|
||||
const bool MUERTO_SIN_VIDAS = hit_timer_per_player_[pid] == 999.0F;
|
||||
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
|
||||
}
|
||||
@@ -284,7 +284,7 @@ auto GameScene::stepGameOver(float delta_time) -> bool {
|
||||
void GameScene::stepDeathSequence(float delta_time) {
|
||||
bool algun_mort = false;
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
if (hit_timer_per_player_[i] <= 0.0F || hit_timer_per_player_[i] >= 999.0F) {
|
||||
if (hit_timer_per_player_[i] <= 0.0F || hit_timer_per_player_[i] >= Defaults::Game::HIT_TIMER_INACTIVE_PLAYER) {
|
||||
continue;
|
||||
}
|
||||
algun_mort = true;
|
||||
@@ -304,7 +304,7 @@ void GameScene::stepDeathSequence(float delta_time) {
|
||||
}
|
||||
|
||||
// Sin vidas: marcar definitivamente muerto y comprobar transición a CONTINUE.
|
||||
hit_timer_per_player_[i] = 999.0F;
|
||||
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;
|
||||
if (P1_DEAD && P2_DEAD) {
|
||||
@@ -628,8 +628,9 @@ void GameScene::tocado(uint8_t player_id) {
|
||||
const Vec2& ship_pos = ships_[player_id].getCenter();
|
||||
float ship_angle = ships_[player_id].getAngle();
|
||||
Vec2 vel_nau = ships_[player_id].getVelocityVector();
|
||||
// Reduir a 80% la velocity heretada per la ship (més realista)
|
||||
Vec2 vel_nau_80 = {.x = vel_nau.x * 0.8F, .y = vel_nau.y * 0.8F};
|
||||
// Reduir la velocity heretada per la ship segons defaults (més realista)
|
||||
constexpr float INHERIT = Defaults::Physics::Debris::SHIP_VELOCITY_INHERITANCE;
|
||||
Vec2 vel_nau_80 = {.x = vel_nau.x * INHERIT, .y = vel_nau.y * INHERIT};
|
||||
|
||||
debris_manager_.explode(
|
||||
ships_[player_id].getShape(), // Ship shape (3 lines)
|
||||
@@ -646,7 +647,7 @@ void GameScene::tocado(uint8_t player_id) {
|
||||
);
|
||||
|
||||
// Start death timer (non-zero to avoid re-triggering)
|
||||
hit_timer_per_player_[player_id] = 0.001F;
|
||||
hit_timer_per_player_[player_id] = Defaults::Game::HIT_TIMER_TRIGGER_DEATH;
|
||||
}
|
||||
// Phase 2 is automatic (debris updates in update())
|
||||
// Phase 3 is handled in update() when hit_timer_per_player_ >= DEATH_DURATION
|
||||
@@ -674,8 +675,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;
|
||||
@@ -828,8 +829,8 @@ void GameScene::fireBullet(uint8_t player_id) {
|
||||
const Vec2& ship_centre = ships_[player_id].getCenter();
|
||||
float ship_angle = ships_[player_id].getAngle();
|
||||
|
||||
constexpr float LOCAL_TIP_X = 0.0F;
|
||||
constexpr float LOCAL_TIP_Y = -12.0F;
|
||||
constexpr float LOCAL_TIP_X = Defaults::Hud::Tips::LOCAL_X;
|
||||
constexpr float LOCAL_TIP_Y = Defaults::Hud::Tips::LOCAL_Y;
|
||||
float cos_a = std::cos(ship_angle);
|
||||
float sin_a = std::sin(ship_angle);
|
||||
float tip_x = (LOCAL_TIP_X * cos_a) - (LOCAL_TIP_Y * sin_a) + ship_centre.x;
|
||||
|
||||
@@ -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: bajo la zona 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};
|
||||
}
|
||||
|
||||
// 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 = Defaults::Hud::InitAnim::BORDER_PHASE_1_END;
|
||||
constexpr float PHASE_2_END = Defaults::Hud::InitAnim::BORDER_PHASE_2_END;
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user