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