refactor: renombra jugador*/zona/radi/MARGE/origen/letra residuals a anglès
This commit is contained in:
@@ -9,8 +9,8 @@ namespace Defaults::FX::Glow {
|
|||||||
|
|
||||||
// Neon glow per outline gruixut, aplicat automàticament per renderShape.
|
// 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
|
// 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
|
// per scale), de manera que un pentàgon (radius 20) té halo gros i una bala
|
||||||
// (radi 3) té halo subtil. El core (últim pass) usa el gruix de línia
|
// (radius 3) té halo subtil. El core (últim pass) usa el gruix de línia
|
||||||
// global (1.5px) — no escala amb la shape.
|
// global (1.5px) — no escala amb la shape.
|
||||||
//
|
//
|
||||||
// Cap superior: si la shape és molt gran (logos del títol, intro), el
|
// Cap superior: si la shape és molt gran (logos del títol, intro), el
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Defaults::Game {
|
|||||||
constexpr float INIT_HUD_SHIP2_RATIO_INIT = 0.20F;
|
constexpr float INIT_HUD_SHIP2_RATIO_INIT = 0.20F;
|
||||||
constexpr float INIT_HUD_SHIP2_RATIO_END = 1.0F;
|
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
|
constexpr float INIT_HUD_SHIP_START_Y_RATIO = 0.75F; // 75% desde el top de PLAYAREA
|
||||||
|
|
||||||
// Spawn positions (distribución horizontal para 2 jugadores)
|
// Spawn positions (distribución horizontal para 2 jugadores)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ namespace Defaults::Title {
|
|||||||
constexpr float FLOATING_SCALE = 1.0F * SHIP_BASE_SCALE; // Flotante: scale base
|
constexpr float FLOATING_SCALE = 1.0F * SHIP_BASE_SCALE; // Flotante: scale base
|
||||||
|
|
||||||
// Offset de entrada (ajustat automáticoament a l'scale)
|
// 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;
|
constexpr float ENTRY_OFFSET = (SHIP_MAX_RADIUS * ENTRY_SCALE_START) + ENTRY_OFFSET_MARGIN;
|
||||||
|
|
||||||
// Vec2 de fuga (centro para l'animación de salida)
|
// Vec2 de fuga (centro para l'animación de salida)
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ namespace Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Border::bumpAt(Vec2 contact_point, float strength) {
|
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<float, SIDE_COUNT> DISTANCES = {
|
const std::array<float, SIDE_COUNT> DISTANCES = {
|
||||||
/* TOP */ std::abs(contact_point.y - zona.y),
|
/* TOP */ std::abs(contact_point.y - zone.y),
|
||||||
/* RIGHT */ std::abs((zona.x + zona.w) - contact_point.x),
|
/* RIGHT */ std::abs((zone.x + zone.w) - contact_point.x),
|
||||||
/* BOTTOM */ std::abs((zona.y + zona.h) - contact_point.y),
|
/* BOTTOM */ std::abs((zone.y + zone.h) - contact_point.y),
|
||||||
/* LEFT */ std::abs(contact_point.x - zona.x)};
|
/* LEFT */ std::abs(contact_point.x - zone.x)};
|
||||||
|
|
||||||
int closest_idx = 0;
|
int closest_idx = 0;
|
||||||
float closest_dist = DISTANCES[0];
|
float closest_dist = DISTANCES[0];
|
||||||
@@ -71,11 +71,11 @@ namespace Graphics {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Border::draw() const {
|
void Border::draw() const {
|
||||||
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
|
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
|
||||||
const int X1 = static_cast<int>(zona.x);
|
const int X1 = static_cast<int>(zone.x);
|
||||||
const int Y1 = static_cast<int>(zona.y);
|
const int Y1 = static_cast<int>(zone.y);
|
||||||
const int X2 = static_cast<int>(zona.x + zona.w);
|
const int X2 = static_cast<int>(zone.x + zone.w);
|
||||||
const int Y2 = static_cast<int>(zona.y + zona.h);
|
const int Y2 = static_cast<int>(zone.y + zone.h);
|
||||||
|
|
||||||
const int OFF_TOP = static_cast<int>(sides_[SIDE_TOP].displacement_px);
|
const int OFF_TOP = static_cast<int>(sides_[SIDE_TOP].displacement_px);
|
||||||
const int OFF_RIGHT = static_cast<int>(sides_[SIDE_RIGHT].displacement_px);
|
const int OFF_RIGHT = static_cast<int>(sides_[SIDE_RIGHT].displacement_px);
|
||||||
|
|||||||
@@ -141,9 +141,9 @@ namespace Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Playfield::buildLines() {
|
void Playfield::buildLines() {
|
||||||
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
|
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
|
||||||
const float CELL_W = zona.w / static_cast<float>(Defaults::Playfield::COLUMNS);
|
const float CELL_W = zone.w / static_cast<float>(Defaults::Playfield::COLUMNS);
|
||||||
const float CELL_H = zona.h / static_cast<float>(Defaults::Playfield::ROWS);
|
const float CELL_H = zone.h / static_cast<float>(Defaults::Playfield::ROWS);
|
||||||
const float SUB_W = CELL_W / static_cast<float>(Defaults::Playfield::SUBDIVISIONS);
|
const float SUB_W = CELL_W / static_cast<float>(Defaults::Playfield::SUBDIVISIONS);
|
||||||
const float SUB_H = CELL_H / static_cast<float>(Defaults::Playfield::SUBDIVISIONS);
|
const float SUB_H = CELL_H / static_cast<float>(Defaults::Playfield::SUBDIVISIONS);
|
||||||
const int SUB_VERTS = Defaults::Playfield::COLUMNS * 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].
|
// Verticals: posicions i ∈ [1, SUB_VERTS-1].
|
||||||
for (int i = 1; i < SUB_VERTS; i++) {
|
for (int i = 1; i < SUB_VERTS; i++) {
|
||||||
const float X = zona.x + (static_cast<float>(i) * SUB_W);
|
const float X = zone.x + (static_cast<float>(i) * SUB_W);
|
||||||
const bool IS_MAIN = (i % Defaults::Playfield::SUBDIVISIONS) == 0;
|
const bool IS_MAIN = (i % Defaults::Playfield::SUBDIVISIONS) == 0;
|
||||||
const float BRIGHTNESS = IS_MAIN
|
const float BRIGHTNESS = IS_MAIN
|
||||||
? Defaults::Playfield::GRID_BRIGHTNESS
|
? Defaults::Playfield::GRID_BRIGHTNESS
|
||||||
: Defaults::Playfield::SUBGRID_BRIGHTNESS;
|
: Defaults::Playfield::SUBGRID_BRIGHTNESS;
|
||||||
verticals.push_back(Line{
|
verticals.push_back(Line{
|
||||||
.start = {.x = X, .y = zona.y},
|
.start = {.x = X, .y = zone.y},
|
||||||
.end = {.x = X, .y = zona.y + zona.h},
|
.end = {.x = X, .y = zone.y + zone.h},
|
||||||
.brightness = BRIGHTNESS,
|
.brightness = BRIGHTNESS,
|
||||||
.spawn_time_s = 0.0F,
|
.spawn_time_s = 0.0F,
|
||||||
.is_vertical = true});
|
.is_vertical = true});
|
||||||
@@ -169,14 +169,14 @@ namespace Graphics {
|
|||||||
|
|
||||||
// Horitzontals: posicions j ∈ [1, SUB_HORIZ-1].
|
// Horitzontals: posicions j ∈ [1, SUB_HORIZ-1].
|
||||||
for (int j = 1; j < SUB_HORIZ; j++) {
|
for (int j = 1; j < SUB_HORIZ; j++) {
|
||||||
const float Y = zona.y + (static_cast<float>(j) * SUB_H);
|
const float Y = zone.y + (static_cast<float>(j) * SUB_H);
|
||||||
const bool IS_MAIN = (j % Defaults::Playfield::SUBDIVISIONS) == 0;
|
const bool IS_MAIN = (j % Defaults::Playfield::SUBDIVISIONS) == 0;
|
||||||
const float BRIGHTNESS = IS_MAIN
|
const float BRIGHTNESS = IS_MAIN
|
||||||
? Defaults::Playfield::GRID_BRIGHTNESS
|
? Defaults::Playfield::GRID_BRIGHTNESS
|
||||||
: Defaults::Playfield::SUBGRID_BRIGHTNESS;
|
: Defaults::Playfield::SUBGRID_BRIGHTNESS;
|
||||||
horizontals.push_back(Line{
|
horizontals.push_back(Line{
|
||||||
.start = {.x = zona.x, .y = Y},
|
.start = {.x = zone.x, .y = Y},
|
||||||
.end = {.x = zona.x + zona.w, .y = Y},
|
.end = {.x = zone.x + zone.w, .y = Y},
|
||||||
.brightness = BRIGHTNESS,
|
.brightness = BRIGHTNESS,
|
||||||
.spawn_time_s = 0.0F,
|
.spawn_time_s = 0.0F,
|
||||||
.is_vertical = false});
|
.is_vertical = false});
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ namespace Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StarfieldParallax::buildStars() {
|
void StarfieldParallax::buildStars() {
|
||||||
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
|
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
|
||||||
const float MIN_X = zona.x;
|
const float MIN_X = zone.x;
|
||||||
const float MAX_X = zona.x + zona.w;
|
const float MAX_X = zone.x + zone.w;
|
||||||
const float MIN_Y = zona.y;
|
const float MIN_Y = zone.y;
|
||||||
const float MAX_Y = zona.y + zona.h;
|
const float MAX_Y = zone.y + zone.h;
|
||||||
|
|
||||||
// Color únic per a totes les estrelles: el mateix blanc-blau gel
|
// Color únic per a totes les estrelles: el mateix blanc-blau gel
|
||||||
// del starfield del títol (Defaults::Title::Colors::STARFIELD).
|
// del starfield del títol (Defaults::Title::Colors::STARFIELD).
|
||||||
@@ -89,13 +89,13 @@ namespace Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StarfieldParallax::update(float delta_time, Vec2 world_velocity) {
|
void StarfieldParallax::update(float delta_time, Vec2 world_velocity) {
|
||||||
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
|
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
|
||||||
const float MIN_X = zona.x;
|
const float MIN_X = zone.x;
|
||||||
const float MAX_X = zona.x + zona.w;
|
const float MAX_X = zone.x + zone.w;
|
||||||
const float MIN_Y = zona.y;
|
const float MIN_Y = zone.y;
|
||||||
const float MAX_Y = zona.y + zona.h;
|
const float MAX_Y = zone.y + zone.h;
|
||||||
const float W = zona.w;
|
const float W = zone.w;
|
||||||
const float H = zona.h;
|
const float H = zone.h;
|
||||||
|
|
||||||
for (auto& star : stars_) {
|
for (auto& star : stars_) {
|
||||||
const float FACTOR = layerParallax(star.layer);
|
const float FACTOR = layerParallax(star.layer);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Physics {
|
|||||||
return false;
|
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 = (a.getCollisionRadius() + b.getCollisionRadius()) * amplifier;
|
||||||
float suma_radis_sq = suma_radis * suma_radis;
|
float suma_radis_sq = suma_radis * suma_radis;
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ namespace Physics {
|
|||||||
return dist_sq <= suma_radis_sq;
|
return dist_sq <= suma_radis_sq;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swept collision: una entitat mòbil (radi r_a) s'ha desplaçat de p0 a p1 aquest
|
// 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 radi conjunt (r_a + radi de b, amb
|
// 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
|
// amplificador) toca el cercle de l'entity b. Equival al check discrete quan
|
||||||
// p0 == p1 (sense moviment). Evita tunneling a velocitats altes.
|
// 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 {
|
inline auto checkCollisionSwept(const Vec2& p0, const Vec2& p1, float r_a, const Entities::Entity& b, float amplifier = 1.0F) -> bool {
|
||||||
|
|||||||
@@ -4,52 +4,52 @@
|
|||||||
|
|
||||||
namespace GameConfig {
|
namespace GameConfig {
|
||||||
|
|
||||||
// Mode de juego
|
// Mode de juego
|
||||||
enum class Mode : std::uint8_t {
|
enum class Mode : std::uint8_t {
|
||||||
NORMAL, // Partida normal
|
NORMAL, // Partida normal
|
||||||
DEMO // Mode demostració (futur)
|
DEMO // Mode demostració (futur)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configuración de una match
|
// Configuración de una match
|
||||||
struct MatchConfig {
|
struct MatchConfig {
|
||||||
bool jugador1_actiu{false}; // Es active el player 1?
|
bool player1_active{false}; // Es active el player 1?
|
||||||
bool jugador2_actiu{false}; // Es active el player 2?
|
bool player2_active{false}; // Es active el player 2?
|
||||||
Mode mode{Mode::NORMAL}; // Mode de juego
|
Mode mode{Mode::NORMAL}; // Mode de juego
|
||||||
|
|
||||||
// Métodos auxiliars
|
// Métodos auxiliars
|
||||||
|
|
||||||
// Retorna true si solo hay un player active
|
// Retorna true si solo hay un player active
|
||||||
[[nodiscard]] auto isSinglePlayer() const -> bool {
|
[[nodiscard]] auto isSinglePlayer() const -> bool {
|
||||||
return (jugador1_actiu && !jugador2_actiu) ||
|
return (player1_active && !player2_active) ||
|
||||||
(!jugador1_actiu && jugador2_actiu);
|
(!player1_active && player2_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retorna true si hay dos jugadors active
|
// Retorna true si hay dos jugadors active
|
||||||
[[nodiscard]] auto isCoop() const -> bool {
|
[[nodiscard]] auto isCoop() const -> bool {
|
||||||
return jugador1_actiu && jugador2_actiu;
|
return player1_active && player2_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retorna true si no hay sin player active
|
// Retorna true si no hay sin player active
|
||||||
[[nodiscard]] auto hasNoPlayers() const -> bool {
|
[[nodiscard]] auto hasNoPlayers() const -> bool {
|
||||||
return !jugador1_actiu && !jugador2_actiu;
|
return !player1_active && !player2_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compte de jugadors active (0, 1 o 2)
|
// Compte de jugadors active (0, 1 o 2)
|
||||||
[[nodiscard]] auto getPlayerCount() const -> uint8_t {
|
[[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)
|
// Retorna l'ID de l'únic player active (0 o 1)
|
||||||
// Solo vàlid si es_un_jugador() retorna true
|
// Solo vàlid si es_un_jugador() retorna true
|
||||||
[[nodiscard]] auto getSinglePlayerId() const -> uint8_t {
|
[[nodiscard]] auto getSinglePlayerId() const -> uint8_t {
|
||||||
if (jugador1_actiu && !jugador2_actiu) {
|
if (player1_active && !player2_active) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!jugador1_actiu && jugador2_actiu) {
|
if (!player1_active && player2_active) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0; // Fallback (necesario comprovar es_un_jugador() primer)
|
return 0; // Fallback (necesario comprovar es_un_jugador() primer)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GameConfig
|
} // namespace GameConfig
|
||||||
|
|||||||
+18
-18
@@ -12,35 +12,35 @@ namespace Constants {
|
|||||||
// Matemàtiques
|
// Matemàtiques
|
||||||
constexpr float PI = Defaults::Math::PI;
|
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 {
|
inline auto isInPlayArea(float x, float y) -> bool {
|
||||||
const SDL_FPoint POINT = {x, y};
|
const SDL_FPoint POINT = {x, y};
|
||||||
return SDL_PointInRectFloat(&POINT, &Defaults::Zones::PLAYAREA);
|
return SDL_PointInRectFloat(&POINT, &Defaults::Zones::PLAYAREA);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void getPlayAreaBounds(float& min_x, float& max_x, float& min_y, float& max_y) {
|
inline void getPlayAreaBounds(float& min_x, float& max_x, float& min_y, float& max_y) {
|
||||||
const auto& zona = Defaults::Zones::PLAYAREA;
|
const auto& zone = Defaults::Zones::PLAYAREA;
|
||||||
min_x = zona.x;
|
min_x = zone.x;
|
||||||
max_x = zona.x + zona.w;
|
max_x = zone.x + zone.w;
|
||||||
min_y = zona.y;
|
min_y = zone.y;
|
||||||
max_y = zona.y + zona.h;
|
max_y = zone.y + zone.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtenir límits segurs (compensant radi de l'entidad)
|
// Obtenir límits segurs (compensant radius de l'entidad)
|
||||||
inline void getSafePlayAreaBounds(float radi, float& min_x, float& max_x, float& min_y, float& max_y) {
|
inline void getSafePlayAreaBounds(float radius, float& min_x, float& max_x, float& min_y, float& max_y) {
|
||||||
const auto& zona = Defaults::Zones::PLAYAREA;
|
const auto& zone = Defaults::Zones::PLAYAREA;
|
||||||
constexpr float MARGE_SEGURETAT = 10.0F; // Safety margin
|
constexpr float SAFETY_MARGIN = 10.0F; // Safety margin
|
||||||
|
|
||||||
min_x = zona.x + radi + MARGE_SEGURETAT;
|
min_x = zone.x + radius + SAFETY_MARGIN;
|
||||||
max_x = zona.x + zona.w - radi - MARGE_SEGURETAT;
|
max_x = zone.x + zone.w - radius - SAFETY_MARGIN;
|
||||||
min_y = zona.y + radi + MARGE_SEGURETAT;
|
min_y = zone.y + radius + SAFETY_MARGIN;
|
||||||
max_y = zona.y + zona.h - radi - MARGE_SEGURETAT;
|
max_y = zone.y + zone.h - radius - SAFETY_MARGIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtenir centro de l'àrea de juego
|
// Obtenir centro de l'àrea de juego
|
||||||
inline void getPlayAreaCenter(float& centre_x, float& centre_y) {
|
inline void getPlayAreaCenter(float& center_x, float& center_y) {
|
||||||
const auto& zona = Defaults::Zones::PLAYAREA;
|
const auto& zone = Defaults::Zones::PLAYAREA;
|
||||||
centre_x = zona.x + (zona.w / 2.0F);
|
center_x = zone.x + (zone.w / 2.0F);
|
||||||
centre_y = zona.y + (zona.h / 2.0F);
|
center_y = zone.y + (zone.h / 2.0F);
|
||||||
}
|
}
|
||||||
} // namespace Constants
|
} // namespace Constants
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace Effects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FireworkManager::spawn(const Vec2& origen,
|
void FireworkManager::spawn(const Vec2& origin,
|
||||||
SDL_Color color,
|
SDL_Color color,
|
||||||
float initial_speed,
|
float initial_speed,
|
||||||
int n_points,
|
int n_points,
|
||||||
@@ -74,7 +74,7 @@ namespace Effects {
|
|||||||
|
|
||||||
// Notificar als subscriptors (playfield pulses, etc.).
|
// Notificar als subscriptors (playfield pulses, etc.).
|
||||||
if (spawn_callback_) {
|
if (spawn_callback_) {
|
||||||
spawn_callback_(origen);
|
spawn_callback_(origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float ANGLE_STEP = 2.0F * Defaults::Math::PI / static_cast<float>(n_points);
|
const float ANGLE_STEP = 2.0F * Defaults::Math::PI / static_cast<float>(n_points);
|
||||||
@@ -94,7 +94,7 @@ namespace Effects {
|
|||||||
const float SPEED =
|
const float SPEED =
|
||||||
initial_speed + (randSigned() * Defaults::FX::Firework::SPEED_VARIATION);
|
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->velocity = {.x = std::cos(ANGLE) * SPEED, .y = std::sin(ANGLE) * SPEED};
|
||||||
fw->acceleration = Defaults::FX::Firework::FRICTION;
|
fw->acceleration = Defaults::FX::Firework::FRICTION;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Effects {
|
|||||||
class FireworkManager {
|
class FireworkManager {
|
||||||
public:
|
public:
|
||||||
// Notificació opcional cada vegada que es genera un burst.
|
// Notificació opcional cada vegada que es genera un burst.
|
||||||
using SpawnCallback = std::function<void(Vec2 origen)>;
|
using SpawnCallback = std::function<void(Vec2 origin)>;
|
||||||
|
|
||||||
explicit FireworkManager(Rendering::Renderer* renderer);
|
explicit FireworkManager(Rendering::Renderer* renderer);
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ namespace Effects {
|
|||||||
// initial_brightness: 0..1.
|
// initial_brightness: 0..1.
|
||||||
// glow: si true, cada partícula es renderitza amb halo neon.
|
// 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.
|
// 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,
|
SDL_Color color = Defaults::FX::Firework::DEFAULT_COLOR,
|
||||||
float initial_speed = Defaults::FX::Firework::SPEED,
|
float initial_speed = Defaults::FX::Firework::SPEED,
|
||||||
int n_points = Defaults::FX::Firework::N_POINTS,
|
int n_points = Defaults::FX::Firework::N_POINTS,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ void Bullet::fire(const Vec2& position, float angle, uint8_t owner_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bullet::update(float /*delta_time*/) {
|
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
|
// Systems::Collision::desactivateOutOfBoundsBullets() perquè així té accés
|
||||||
// al DebrisManager i pot generar el "trencament" visual de la bala alhora.
|
// al DebrisManager i pot generar el "trencament" visual de la bala alhora.
|
||||||
// El moviment l'integra PhysicsWorld; postUpdate sincronitza center_ i prev_position_.
|
// El moviment l'integra PhysicsWorld; postUpdate sincronitza center_ i prev_position_.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ void Enemy::init(EnemyType type, const Vec2* ship_pos) {
|
|||||||
const int RANGE_Y = static_cast<int>(max_y - min_y);
|
const int RANGE_Y = static_cast<int>(max_y - min_y);
|
||||||
center_.x = static_cast<float>((std::rand() % RANGE_X) + static_cast<int>(min_x));
|
center_.x = static_cast<float>((std::rand() % RANGE_X) + static_cast<int>(min_x));
|
||||||
center_.y = static_cast<float>((std::rand() % RANGE_Y) + static_cast<int>(min_y));
|
center_.y = static_cast<float>((std::rand() % RANGE_Y) + static_cast<int>(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";
|
<< Defaults::Enemies::Spawn::MAX_SPAWN_ATTEMPTS << " intentos\n";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ void Ship::init(const Vec2* spawn_point, bool activar_invulnerabilitat) {
|
|||||||
if (spawn_point != nullptr) {
|
if (spawn_point != nullptr) {
|
||||||
center_ = *spawn_point;
|
center_ = *spawn_point;
|
||||||
} else {
|
} else {
|
||||||
float centre_x;
|
float center_x;
|
||||||
float centre_y;
|
float center_y;
|
||||||
Constants::getPlayAreaCenter(centre_x, centre_y);
|
Constants::getPlayAreaCenter(center_x, center_y);
|
||||||
center_ = {.x = centre_x, .y = centre_y};
|
center_ = {.x = center_x, .y = center_y};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset orientación
|
// Reset orientación
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context)
|
|||||||
|
|
||||||
// Debug output de la configuración
|
// Debug output de la configuración
|
||||||
std::cout << "[GameScene] Configuración de match - P1: "
|
std::cout << "[GameScene] Configuración de match - P1: "
|
||||||
<< (match_config_.jugador1_actiu ? "ACTIU" : "INACTIU")
|
<< (match_config_.player1_active ? "ACTIU" : "INACTIU")
|
||||||
<< ", P2: "
|
<< ", P2: "
|
||||||
<< (match_config_.jugador2_actiu ? "ACTIU" : "INACTIU")
|
<< (match_config_.player2_active ? "ACTIU" : "INACTIU")
|
||||||
<< '\n';
|
<< '\n';
|
||||||
|
|
||||||
// Consumir opciones (preparació per MODE_DEMO futur)
|
// 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
|
// Basat en el codi Pascal original: line 376
|
||||||
std::srand(static_cast<unsigned>(std::time(nullptr)));
|
std::srand(static_cast<unsigned>(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_.clear();
|
||||||
physics_world_.setBounds(Defaults::Zones::PLAYAREA);
|
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).
|
// Fireworks generen una ripple gran al playfield (ona d'aigua centrada al burst).
|
||||||
firework_manager_.setSpawnCallback([this](Vec2 origen) {
|
firework_manager_.setSpawnCallback([this](Vec2 origin) {
|
||||||
playfield_.notifyExplosion(origen);
|
playfield_.notifyExplosion(origin);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Explosions properes a una paret també generen bump (falloff lineal amb la distància).
|
// Explosions properes a una paret també generen bump (falloff lineal amb la distància).
|
||||||
debris_manager_.setExplosionCallback([this](Vec2 center) {
|
debris_manager_.setExplosionCallback([this](Vec2 center) {
|
||||||
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
|
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
|
||||||
const float DIST_LEFT = std::abs(center.x - zona.x);
|
const float DIST_LEFT = std::abs(center.x - zone.x);
|
||||||
const float DIST_RIGHT = std::abs((zona.x + zona.w) - center.x);
|
const float DIST_RIGHT = std::abs((zone.x + zone.w) - center.x);
|
||||||
const float DIST_TOP = std::abs(center.y - zona.y);
|
const float DIST_TOP = std::abs(center.y - zone.y);
|
||||||
const float DIST_BOTTOM = std::abs((zona.y + zona.h) - center.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});
|
const float MIN_DIST = std::min({DIST_LEFT, DIST_RIGHT, DIST_TOP, DIST_BOTTOM});
|
||||||
if (MIN_DIST > Defaults::Border::EXPLOSION_FALLOFF_PX) {
|
if (MIN_DIST > Defaults::Border::EXPLOSION_FALLOFF_PX) {
|
||||||
return;
|
return;
|
||||||
@@ -130,9 +130,9 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context)
|
|||||||
|
|
||||||
// Inicialitzar naves segons configuración (solo jugadors active)
|
// Inicialitzar naves segons configuración (solo jugadors active)
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
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
|
// Jugador active: init normalment
|
||||||
Vec2 spawn_pos = getSpawnPoint(i);
|
Vec2 spawn_pos = getSpawnPoint(i);
|
||||||
ships_[i].init(&spawn_pos, false); // No invulnerability at start
|
ships_[i].init(&spawn_pos, false); // No invulnerability at start
|
||||||
@@ -248,11 +248,11 @@ void GameScene::stepPhysics(float delta_time) {
|
|||||||
|
|
||||||
void GameScene::stepShootingInput() {
|
void GameScene::stepShootingInput() {
|
||||||
auto* input = Input::get();
|
auto* input = Input::get();
|
||||||
if (match_config_.jugador1_actiu &&
|
if (match_config_.player1_active &&
|
||||||
input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
fireBullet(0);
|
fireBullet(0);
|
||||||
}
|
}
|
||||||
if (match_config_.jugador2_actiu &&
|
if (match_config_.player2_active &&
|
||||||
input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
fireBullet(1);
|
fireBullet(1);
|
||||||
}
|
}
|
||||||
@@ -267,16 +267,16 @@ void GameScene::stepMidGameJoin() {
|
|||||||
// Solo se permite join si hay al menos un jugador vivo (no se puede
|
// Solo se permite join si hay al menos un jugador vivo (no se puede
|
||||||
// hacer join en pantalla vacía).
|
// hacer join en pantalla vacía).
|
||||||
const bool ALGU_VIU =
|
const bool ALGU_VIU =
|
||||||
(match_config_.jugador1_actiu && hit_timer_per_player_[0] != Defaults::Game::HIT_TIMER_INACTIVE_PLAYER) ||
|
(match_config_.player1_active && 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_.player2_active && hit_timer_per_player_[1] != Defaults::Game::HIT_TIMER_INACTIVE_PLAYER);
|
||||||
if (!ALGU_VIU) {
|
if (!ALGU_VIU) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* input = Input::get();
|
auto* input = Input::get();
|
||||||
for (uint8_t pid = 0; pid < 2; pid++) {
|
for (uint8_t pid = 0; pid < 2; pid++) {
|
||||||
const bool ACTIU = (pid == 0) ? match_config_.jugador1_actiu
|
const bool ACTIU = (pid == 0) ? match_config_.player1_active
|
||||||
: match_config_.jugador2_actiu;
|
: match_config_.player2_active;
|
||||||
const bool MUERTO_SIN_VIDAS = hit_timer_per_player_[pid] == Defaults::Game::HIT_TIMER_INACTIVE_PLAYER;
|
const bool MUERTO_SIN_VIDAS = hit_timer_per_player_[pid] == Defaults::Game::HIT_TIMER_INACTIVE_PLAYER;
|
||||||
if (ACTIU && !MUERTO_SIN_VIDAS) {
|
if (ACTIU && !MUERTO_SIN_VIDAS) {
|
||||||
continue; // jugador ya está jugando
|
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.
|
// Sin vidas: marcar definitivamente muerto y comprobar transición a CONTINUE.
|
||||||
hit_timer_per_player_[i] = Defaults::Game::HIT_TIMER_INACTIVE_PLAYER;
|
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 P1_DEAD = !match_config_.player1_active || lives_per_player_[0] <= 0;
|
||||||
const bool P2_DEAD = !match_config_.jugador2_actiu || lives_per_player_[1] <= 0;
|
const bool P2_DEAD = !match_config_.player2_active || lives_per_player_[1] <= 0;
|
||||||
if (P1_DEAD && P2_DEAD) {
|
if (P1_DEAD && P2_DEAD) {
|
||||||
game_over_state_ = GameOverState::CONTINUE;
|
game_over_state_ = GameOverState::CONTINUE;
|
||||||
continue_counter_ = Defaults::Game::CONTINUE_COUNT_START;
|
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_INIT,
|
||||||
Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
|
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)));
|
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)));
|
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.
|
// Ambas naves pueden moverse y disparar durante el intro.
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
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) {
|
if (ACTIU && hit_timer_per_player_[i] == 0.0F) {
|
||||||
ships_[i].processInput(delta_time, i);
|
ships_[i].processInput(delta_time, i);
|
||||||
ships_[i].update(delta_time);
|
ships_[i].update(delta_time);
|
||||||
@@ -481,7 +481,7 @@ void GameScene::runStagePlaying(float delta_time) {
|
|||||||
|
|
||||||
// Gameplay normal: ships activos + entidades + colisiones + efectos.
|
// Gameplay normal: ships activos + entidades + colisiones + efectos.
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
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) {
|
if (ACTIU && hit_timer_per_player_[i] == 0.0F) {
|
||||||
ships_[i].processInput(delta_time, i);
|
ships_[i].processInput(delta_time, i);
|
||||||
ships_[i].update(delta_time);
|
ships_[i].update(delta_time);
|
||||||
@@ -491,7 +491,7 @@ void GameScene::runStagePlaying(float delta_time) {
|
|||||||
enemy.update(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
|
// el mateix frame xoca amb un enemic i alhora surt del PLAYAREA es compta com a
|
||||||
// impacte abans no se la trenqui per sortir.
|
// impacte abans no se la trenqui per sortir.
|
||||||
runCollisionDetections();
|
runCollisionDetections();
|
||||||
@@ -507,7 +507,7 @@ void GameScene::runStagePlaying(float delta_time) {
|
|||||||
void GameScene::runStageLevelCompleted(float delta_time) {
|
void GameScene::runStageLevelCompleted(float delta_time) {
|
||||||
stage_manager_->update(delta_time);
|
stage_manager_->update(delta_time);
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
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) {
|
if (ACTIU && hit_timer_per_player_[i] == 0.0F) {
|
||||||
ships_[i].processInput(delta_time, i);
|
ships_[i].processInput(delta_time, i);
|
||||||
ships_[i].update(delta_time);
|
ships_[i].update(delta_time);
|
||||||
@@ -580,8 +580,8 @@ void GameScene::drawBullets() const {
|
|||||||
|
|
||||||
void GameScene::drawActiveShipsAlive() const {
|
void GameScene::drawActiveShipsAlive() const {
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
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 && hit_timer_per_player_[i] == 0.0F) {
|
if (player_active && hit_timer_per_player_[i] == 0.0F) {
|
||||||
ships_[i].draw();
|
ships_[i].draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,10 +613,10 @@ void GameScene::drawGameOverState() {
|
|||||||
constexpr float SPACING = Defaults::Game::GameOverScreen::TEXT_SPACING;
|
constexpr float SPACING = Defaults::Game::GameOverScreen::TEXT_SPACING;
|
||||||
|
|
||||||
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
|
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
|
||||||
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 = play_area.y + (play_area.h / 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();
|
drawScoreboard();
|
||||||
}
|
}
|
||||||
@@ -663,11 +663,11 @@ void GameScene::drawInitHudState() {
|
|||||||
Systems::InitHud::drawScoreboardAnimated(text_, buildScoreboard(), score_progress);
|
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();
|
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();
|
ships_[1].draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -769,20 +769,20 @@ void GameScene::drawScoreboard() {
|
|||||||
const float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE;
|
const float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE;
|
||||||
const float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING;
|
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;
|
const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD;
|
||||||
float centre_x = scoreboard_zone.w / 2.0F;
|
float center_x = scoreboard_zone.w / 2.0F;
|
||||||
float centre_y = scoreboard_zone.y + (scoreboard_zone.h / 2.0F);
|
float center_y = scoreboard_zone.y + (scoreboard_zone.h / 2.0F);
|
||||||
|
|
||||||
// Renderizar centrat
|
// 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 {
|
auto GameScene::buildScoreboard() const -> std::string {
|
||||||
// Puntuación P1 (6 dígits) - mostrar zeros si inactiu
|
// Puntuación P1 (6 dígits) - mostrar zeros si inactiu
|
||||||
std::string score_p1;
|
std::string score_p1;
|
||||||
std::string vides_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::to_string(score_per_player_[0]);
|
||||||
score_p1 = std::string(6 - std::min(6, static_cast<int>(score_p1.length())), '0') + score_p1;
|
score_p1 = std::string(6 - std::min(6, static_cast<int>(score_p1.length())), '0') + score_p1;
|
||||||
vides_p1 = (lives_per_player_[0] < 10)
|
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
|
// Puntuación P2 (6 dígits) - mostrar zeros si inactiu
|
||||||
std::string score_p2;
|
std::string score_p2;
|
||||||
std::string vides_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::to_string(score_per_player_[1]);
|
||||||
score_p2 = std::string(6 - std::min(6, static_cast<int>(score_p2.length())), '0') + score_p2;
|
score_p2 = std::string(6 - std::min(6, static_cast<int>(score_p2.length())), '0') + score_p2;
|
||||||
vides_p2 = (lives_per_player_[1] < 10)
|
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 {
|
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;
|
float x_ratio;
|
||||||
if (match_config_.isSinglePlayer()) {
|
if (match_config_.isSinglePlayer()) {
|
||||||
@@ -904,8 +904,8 @@ auto GameScene::getSpawnPoint(uint8_t player_id) const -> Vec2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.x = zona.x + (zona.w * x_ratio),
|
.x = zone.x + (zone.w * x_ratio),
|
||||||
.y = zona.y + (zona.h * Defaults::Game::SPAWN_Y_RATIO)};
|
.y = zone.y + (zone.h * Defaults::Game::SPAWN_Y_RATIO)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::fireBullet(uint8_t player_id) {
|
void GameScene::fireBullet(uint8_t player_id) {
|
||||||
@@ -950,10 +950,10 @@ void GameScene::drawContinue() {
|
|||||||
float escala_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_SCALE;
|
float escala_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_SCALE;
|
||||||
float y_ratio_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_Y_RATIO;
|
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);
|
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)
|
// Countdown number (using constants)
|
||||||
const std::string COUNTER_STR = std::to_string(continue_counter_);
|
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);
|
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)
|
// "CONTINUES LEFT" (conditional + using constants)
|
||||||
if (!Defaults::Game::INFINITE_CONTINUES) {
|
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);
|
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) {
|
void GameScene::joinPlayer(uint8_t player_id) {
|
||||||
// Activate player
|
// Activate player
|
||||||
if (player_id == 0) {
|
if (player_id == 0) {
|
||||||
match_config_.jugador1_actiu = true;
|
match_config_.player1_active = true;
|
||||||
} else {
|
} else {
|
||||||
match_config_.jugador2_actiu = true;
|
match_config_.player2_active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset stats
|
// Reset stats
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ using Option = SceneContext::Option;
|
|||||||
|
|
||||||
// Helper: calcular el progrés individual de una letter
|
// Helper: calcular el progrés individual de una letter
|
||||||
// en función del progrés global (efecte seqüencial)
|
// 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 {
|
static auto computeLetterProgress(size_t letter_index, size_t num_letters, float global_progress, float threshold) -> float {
|
||||||
if (num_letras == 0) {
|
if (num_letters == 0) {
|
||||||
return 1.0F;
|
return 1.0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular time per letter
|
// Calcular time per letter
|
||||||
float duration_per_letra = 1.0F / static_cast<float>(num_letras);
|
float duration_per_letter = 1.0F / static_cast<float>(num_letters);
|
||||||
float step = threshold * duration_per_letra;
|
float step = threshold * duration_per_letter;
|
||||||
float start = static_cast<float>(letra_index) * step;
|
float start = static_cast<float>(letter_index) * step;
|
||||||
float end = start + duration_per_letra;
|
float end = start + duration_per_letter;
|
||||||
|
|
||||||
// Interpolar progrés
|
// Interpolar progrés
|
||||||
if (global_progress < start) {
|
if (global_progress < start) {
|
||||||
@@ -179,7 +179,7 @@ void LogoScene::changeState(AnimationState nou_estat) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto LogoScene::allLettersComplete() const -> bool {
|
auto LogoScene::allLettersComplete() const -> bool {
|
||||||
// Cuando global_progress = 1.0, todas las lletres tenen letra_progress = 1.0
|
// Cuando global_progress = 1.0, todas las lletres tenen letter_progress = 1.0
|
||||||
return temps_current_state_ >= DURATION_ZOOM;
|
return temps_current_state_ >= DURATION_ZOOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,14 +231,14 @@ void LogoScene::update(float delta_time) {
|
|||||||
|
|
||||||
for (size_t i = 0; i < letters_.size() && i < sound_played_.size(); i++) {
|
for (size_t i = 0; i < letters_.size() && i < sound_played_.size(); i++) {
|
||||||
if (!sound_played_[i]) {
|
if (!sound_played_[i]) {
|
||||||
float letra_progress = computeLetterProgress(
|
float letter_progress = computeLetterProgress(
|
||||||
i,
|
i,
|
||||||
letters_.size(),
|
letters_.size(),
|
||||||
global_progress,
|
global_progress,
|
||||||
LETTER_THRESHOLD);
|
LETTER_THRESHOLD);
|
||||||
|
|
||||||
// Reproduir so cuando la letter comença a aparèixer (progress > 0)
|
// Reproduir so cuando la letter comença a aparèixer (progress > 0)
|
||||||
if (letra_progress > 0.0F) {
|
if (letter_progress > 0.0F) {
|
||||||
Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME);
|
Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME);
|
||||||
sound_played_[i] = true;
|
sound_played_[i] = true;
|
||||||
}
|
}
|
||||||
@@ -294,28 +294,28 @@ void LogoScene::draw() {
|
|||||||
? std::min(temps_current_state_ / DURATION_ZOOM, 1.0F)
|
? std::min(temps_current_state_ / DURATION_ZOOM, 1.0F)
|
||||||
: 1.0F; // POST: mantenir al 100%
|
: 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 < letters_.size(); i++) {
|
for (size_t i = 0; i < letters_.size(); i++) {
|
||||||
const auto& letter = letters_[i];
|
const auto& letter = letters_[i];
|
||||||
|
|
||||||
float letra_progress = computeLetterProgress(
|
float letter_progress = computeLetterProgress(
|
||||||
i,
|
i,
|
||||||
letters_.size(),
|
letters_.size(),
|
||||||
global_progress,
|
global_progress,
|
||||||
LETTER_THRESHOLD);
|
LETTER_THRESHOLD);
|
||||||
|
|
||||||
if (letra_progress <= 0.0F) {
|
if (letter_progress <= 0.0F) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 pos_actual;
|
Vec2 pos_actual;
|
||||||
pos_actual.x =
|
pos_actual.x =
|
||||||
ORIGEN_ZOOM.x + ((letter.position.x - ORIGEN_ZOOM.x) * letra_progress);
|
ZOOM_ORIGIN.x + ((letter.position.x - ZOOM_ORIGIN.x) * letter_progress);
|
||||||
pos_actual.y =
|
pos_actual.y =
|
||||||
ORIGEN_ZOOM.y + ((letter.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 ease_factor = 1.0F - ((1.0F - t) * (1.0F - t));
|
||||||
float current_scale =
|
float current_scale =
|
||||||
INITIAL_SCALE + ((FINAL_SCALE - INITIAL_SCALE) * ease_factor);
|
INITIAL_SCALE + ((FINAL_SCALE - INITIAL_SCALE) * ease_factor);
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ class LogoScene final : public Scene {
|
|||||||
|
|
||||||
// Constants de animación seqüencial
|
// 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 LETTER_THRESHOLD = 0.6F; // Umbral per activar següent letter (0.0-1.0)
|
||||||
static constexpr float ORIGEN_ZOOM_X = Defaults::Game::WIDTH * 0.5F; // Vec2 inicial X del zoom
|
static constexpr float ZOOM_ORIGIN_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
|
static constexpr float ZOOM_ORIGIN_Y = Defaults::Game::HEIGHT * 0.4F; // Vec2 inicial Y del zoom
|
||||||
|
|
||||||
// Métodos privats
|
// Métodos privats
|
||||||
void initLetters();
|
void initLetters();
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ TitleScene::TitleScene(SDLManager& sdl, SceneContext& context)
|
|||||||
text_(sdl.getRenderer()) {
|
text_(sdl.getRenderer()) {
|
||||||
std::cout << "SceneType Titol: Inicialitzant...\n";
|
std::cout << "SceneType Titol: Inicialitzant...\n";
|
||||||
|
|
||||||
match_config_.jugador1_actiu = false;
|
match_config_.player1_active = false;
|
||||||
match_config_.jugador2_actiu = false;
|
match_config_.player2_active = false;
|
||||||
match_config_.mode = GameConfig::Mode::NORMAL;
|
match_config_.mode = GameConfig::Mode::NORMAL;
|
||||||
|
|
||||||
auto option = context_.consumeOption();
|
auto option = context_.consumeOption();
|
||||||
@@ -404,8 +404,8 @@ void TitleScene::updatePlayerJoinPhaseState(float delta_time) {
|
|||||||
temps_acumulat_ += delta_time;
|
temps_acumulat_ += delta_time;
|
||||||
updateLogoAnimation(delta_time);
|
updateLogoAnimation(delta_time);
|
||||||
|
|
||||||
const bool P1_ABANS = match_config_.jugador1_actiu;
|
const bool P1_ABANS = match_config_.player1_active;
|
||||||
const bool P2_ABANS = match_config_.jugador2_actiu;
|
const bool P2_ABANS = match_config_.player2_active;
|
||||||
|
|
||||||
if (checkStartGameButtonPressed()) {
|
if (checkStartGameButtonPressed()) {
|
||||||
context_.setMatchConfig(match_config_);
|
context_.setMatchConfig(match_config_);
|
||||||
@@ -466,8 +466,8 @@ void TitleScene::handleStartInput() {
|
|||||||
if (!press_start_visible_) {
|
if (!press_start_visible_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bool P1_ABANS = match_config_.jugador1_actiu;
|
const bool P1_ABANS = match_config_.player1_active;
|
||||||
const bool P2_ABANS = match_config_.jugador2_actiu;
|
const bool P2_ABANS = match_config_.player2_active;
|
||||||
|
|
||||||
if (!checkStartGameButtonPressed()) {
|
if (!checkStartGameButtonPressed()) {
|
||||||
return;
|
return;
|
||||||
@@ -492,11 +492,11 @@ void TitleScene::triggerExitForJoinedPlayers(bool p1_was_active, bool p2_was_act
|
|||||||
if (ship_animator_ == nullptr) {
|
if (ship_animator_ == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (match_config_.jugador1_actiu && !p1_was_active) {
|
if (match_config_.player1_active && !p1_was_active) {
|
||||||
ship_animator_->triggerExitAnimationForPlayer(1);
|
ship_animator_->triggerExitAnimationForPlayer(1);
|
||||||
std::cout << "[TitleScene] P1 " << log_prefix << "ship exiting\n";
|
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);
|
ship_animator_->triggerExitAnimationForPlayer(2);
|
||||||
std::cout << "[TitleScene] P2 " << log_prefix << "ship exiting\n";
|
std::cout << "[TitleScene] P2 " << log_prefix << "ship exiting\n";
|
||||||
}
|
}
|
||||||
@@ -622,14 +622,14 @@ auto TitleScene::checkStartGameButtonPressed() -> bool {
|
|||||||
bool any_pressed = false;
|
bool any_pressed = false;
|
||||||
for (auto action : START_GAME_BUTTONS) {
|
for (auto action : START_GAME_BUTTONS) {
|
||||||
if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT)) {
|
if (input->checkActionPlayer1(action, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
if (!match_config_.jugador1_actiu) {
|
if (!match_config_.player1_active) {
|
||||||
match_config_.jugador1_actiu = true;
|
match_config_.player1_active = true;
|
||||||
any_pressed = true;
|
any_pressed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) {
|
if (input->checkActionPlayer2(action, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
if (!match_config_.jugador2_actiu) {
|
if (!match_config_.player2_active) {
|
||||||
match_config_.jugador2_actiu = true;
|
match_config_.player2_active = true;
|
||||||
any_pressed = true;
|
any_pressed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,8 +262,8 @@ namespace Systems::Collision {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const bool JUGADOR_ACTIU = (player_id == 0)
|
const bool JUGADOR_ACTIU = (player_id == 0)
|
||||||
? ctx.match_config.jugador1_actiu
|
? ctx.match_config.player1_active
|
||||||
: ctx.match_config.jugador2_actiu;
|
: ctx.match_config.player2_active;
|
||||||
if (!JUGADOR_ACTIU) {
|
if (!JUGADOR_ACTIU) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,99 +11,99 @@
|
|||||||
#include "game/scenes/game_scene.hpp" // GameOverState (definición completa)
|
#include "game/scenes/game_scene.hpp" // GameOverState (definición completa)
|
||||||
|
|
||||||
namespace Systems::ContinueScreen {
|
namespace Systems::ContinueScreen {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Si el countdown ha bajado de 0, transiciona a GAME_OVER con su timer.
|
// Si el countdown ha bajado de 0, transiciona a GAME_OVER con su timer.
|
||||||
void checkAndApplyTimeout(Context& ctx) {
|
void checkAndApplyTimeout(Context& ctx) {
|
||||||
if (ctx.counter < 0) {
|
if (ctx.counter < 0) {
|
||||||
ctx.state = GameOverState::GAME_OVER;
|
ctx.state = GameOverState::GAME_OVER;
|
||||||
ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION;
|
ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void revivePlayer(Context& ctx, uint8_t player_id) {
|
void revivePlayer(Context& ctx, uint8_t player_id) {
|
||||||
ctx.score_per_player[player_id] = 0;
|
ctx.score_per_player[player_id] = 0;
|
||||||
ctx.lives_per_player[player_id] = Defaults::Game::STARTING_LIVES;
|
ctx.lives_per_player[player_id] = Defaults::Game::STARTING_LIVES;
|
||||||
ctx.hit_timer_per_player[player_id] = 0.0F;
|
ctx.hit_timer_per_player[player_id] = 0.0F;
|
||||||
|
|
||||||
if (player_id == 0) {
|
if (player_id == 0) {
|
||||||
ctx.match_config.jugador1_actiu = true;
|
ctx.match_config.player1_active = true;
|
||||||
} else {
|
} else {
|
||||||
ctx.match_config.jugador2_actiu = true;
|
ctx.match_config.player2_active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vec2 SPAWN = ctx.get_spawn_point(player_id);
|
const Vec2 SPAWN = ctx.get_spawn_point(player_id);
|
||||||
ctx.ships[player_id].init(&SPAWN, /*activar_invulnerabilitat=*/true);
|
ctx.ships[player_id].init(&SPAWN, /*activar_invulnerabilitat=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void update(Context& ctx, float delta_time) {
|
void update(Context& ctx, float delta_time) {
|
||||||
ctx.tick_timer -= delta_time;
|
ctx.tick_timer -= delta_time;
|
||||||
if (ctx.tick_timer > 0.0F) {
|
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;
|
|
||||||
return;
|
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.counter--;
|
||||||
|
ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION;
|
||||||
|
|
||||||
checkAndApplyTimeout(ctx);
|
checkAndApplyTimeout(ctx);
|
||||||
|
|
||||||
|
// Solo pita el tick si seguimos en CONTINUE (no transitamos a GAME_OVER)
|
||||||
if (ctx.state == GameOverState::CONTINUE) {
|
if (ctx.state == GameOverState::CONTINUE) {
|
||||||
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
|
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
|
} // namespace Systems::ContinueScreen
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace Systems::InitHud {
|
|||||||
auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2 {
|
auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2 {
|
||||||
const float EASED = Easing::easeOutQuad(progress);
|
const float EASED = Easing::easeOutQuad(progress);
|
||||||
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
|
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_INI = zone.y + zone.h + Defaults::Hud::InitAnim::SHIP_SPAWN_Y_OFFSET;
|
||||||
const float Y_ANIM = Y_INI + ((final_position.y - Y_INI) * EASED);
|
const float Y_ANIM = Y_INI + ((final_position.y - Y_INI) * EASED);
|
||||||
return Vec2{.x = final_position.x, .y = Y_ANIM};
|
return Vec2{.x = final_position.x, .y = Y_ANIM};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Cubre la animación INIT_HUD del comienzo de cada partida/stage:
|
// 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.
|
// 1. Crecimiento de los marcos del PLAYAREA con efecto pincel en 3 fases.
|
||||||
// 2. Marcador subiendo desde abajo.
|
// 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
|
// Todas las funciones son puras (sin estado interno propio). GameScene aporta
|
||||||
// el contexto que necesitan: posiciones finales, texto del scoreboard y el
|
// el contexto que necesitan: posiciones finales, texto del scoreboard y el
|
||||||
@@ -21,29 +21,29 @@
|
|||||||
|
|
||||||
namespace Systems::InitHud {
|
namespace Systems::InitHud {
|
||||||
|
|
||||||
// Convierte un progreso global 0..1 al sub-progreso de un elemento que solo
|
// Convierte un progreso global 0..1 al sub-progreso de un elemento que solo
|
||||||
// se anima en la ventana [ratio_init, ratio_end].
|
// se anima en la ventana [ratio_init, ratio_end].
|
||||||
// < ratio_init → 0.0 (no empezó)
|
// < ratio_init → 0.0 (no empezó)
|
||||||
// > ratio_end → 1.0 (terminó)
|
// > ratio_end → 1.0 (terminó)
|
||||||
// en rango → interpolación lineal 0..1
|
// en rango → interpolación lineal 0..1
|
||||||
[[nodiscard]] auto computeRangeProgress(float global_progress,
|
[[nodiscard]] auto computeRangeProgress(float global_progress,
|
||||||
float ratio_init,
|
float ratio_init,
|
||||||
float ratio_end) -> float;
|
float ratio_end) -> float;
|
||||||
|
|
||||||
// Calcula posición Y animada de una nave durante INIT_HUD. La nave sube
|
// 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.
|
// desde 50 px bajo el PLAYAREA hasta `final_position` con easing.
|
||||||
[[nodiscard]] auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2;
|
[[nodiscard]] auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2;
|
||||||
|
|
||||||
// Dibuja los 4 lados del PLAYAREA con efecto pincel en 3 fases:
|
// Dibuja los 4 lados del PLAYAREA con efecto pincel en 3 fases:
|
||||||
// 0..33% → línea superior crece desde el centro hacia los lados.
|
// 0..33% → línea superior crece desde el centro hacia los lados.
|
||||||
// 33..66% → líneas verticales bajan por los laterales.
|
// 33..66% → líneas verticales bajan por los laterales.
|
||||||
// 66..100% → línea inferior crece desde los lados hacia el centro.
|
// 66..100% → línea inferior crece desde los lados hacia el centro.
|
||||||
void drawBordersAnimated(Rendering::Renderer* renderer, float progress);
|
void drawBordersAnimated(Rendering::Renderer* renderer, float progress);
|
||||||
|
|
||||||
// Dibuja el scoreboard centrado, subiendo desde fuera de la pantalla
|
// Dibuja el scoreboard centrado, subiendo desde fuera de la pantalla
|
||||||
// hasta su posición final con easing.
|
// hasta su posición final con easing.
|
||||||
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
||||||
const std::string& scoreboard_text,
|
const std::string& scoreboard_text,
|
||||||
float progress);
|
float progress);
|
||||||
|
|
||||||
} // namespace Systems::InitHud
|
} // namespace Systems::InitHud
|
||||||
|
|||||||
Reference in New Issue
Block a user