treballant en INIT_HUD

This commit is contained in:
2025-12-09 22:09:24 +01:00
parent 64ab08973c
commit 57d623d6bc
8 changed files with 271 additions and 12 deletions

View File

@@ -86,6 +86,15 @@ constexpr float LEVEL_START_TYPING_RATIO = 0.3f; // 30% escribiendo, 70%
// Transición LEVEL_COMPLETED (mensaje "GOOD JOB COMMANDER!")
constexpr float LEVEL_COMPLETED_DURATION = 3.0f; // Duración total
constexpr float LEVEL_COMPLETED_TYPING_RATIO = 0.0f; // 0.0 = sin typewriter (directo)
// Transición INIT_HUD (animación inicial del HUD)
constexpr float INIT_HUD_DURATION = 3.0f; // Duración total del estado
constexpr float INIT_HUD_RECT_DURATION = 2.0f; // Duración animación rectángulo
constexpr float INIT_HUD_SCORE_DURATION = 2.5f; // Duración animación marcador
constexpr float INIT_HUD_SHIP_DURATION = 2.5f; // Duración animación nave
// Posición inicial de la nave en INIT_HUD (75% de altura de zona de juego)
constexpr float INIT_HUD_SHIP_START_Y_RATIO = 0.75f; // 75% desde el top de PLAYAREA
} // namespace Game
// Física (valores actuales del juego, sincronizados con joc_asteroides.cpp)

View File

@@ -0,0 +1,20 @@
// easing.hpp - Funcions d'interpolació i easing
// © 2025 Orni Attack
#pragma once
namespace Easing {
// Ease-out quadratic: empieza rápido, desacelera suavemente
// t = progreso normalizado [0.0 - 1.0]
// retorna valor interpolado [0.0 - 1.0]
inline float ease_out_quad(float t) {
return 1.0f - (1.0f - t) * (1.0f - t);
}
// Interpolación lineal básica (para referencia)
inline float lerp(float start, float end, float t) {
return start + (end - start) * t;
}
} // namespace Easing

View File

@@ -26,6 +26,7 @@ class Nau {
const Punt& get_centre() const { return centre_; }
float get_angle() const { return angle_; }
bool esta_viva() const { return !esta_tocada_; }
bool esta_tocada() const { return esta_tocada_; }
const std::shared_ptr<Graphics::Shape>& get_forma() const { return forma_; }
float get_brightness() const { return brightness_; }
Punt get_velocitat_vector() const {
@@ -35,6 +36,9 @@ class Nau {
};
}
// Setters
void set_centre(const Punt& nou_centre) { centre_ = nou_centre; }
// Col·lisions (Fase 10)
void marcar_tocada() { esta_tocada_ = true; }

View File

@@ -12,6 +12,7 @@
#include "core/audio/audio.hpp"
#include "core/input/mouse.hpp"
#include "core/math/easing.hpp"
#include "core/rendering/line_renderer.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/global_events.hpp"
@@ -146,11 +147,13 @@ void EscenaJoc::inicialitzar() {
puntuacio_total_ = 0;
gestor_puntuacio_.reiniciar();
// Set spawn point to center of play area
Constants::obtenir_centre_zona(punt_spawn_.x, punt_spawn_.y);
// Set spawn point to center X, 75% Y (same as INIT_HUD final position)
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
punt_spawn_.x = zona.x + zona.w * 0.5f;
punt_spawn_.y = zona.y + zona.h * Defaults::Game::INIT_HUD_SHIP_START_Y_RATIO;
// Inicialitzar nau
nau_.inicialitzar();
// Inicialitzar nau amb posició especial
nau_.inicialitzar(&punt_spawn_);
// [MODIFIED] Initialize enemies as inactive (stage system will spawn them)
for (auto& enemy : orni_) {
@@ -164,8 +167,9 @@ void EscenaJoc::inicialitzar() {
bala.inicialitzar();
}
// Iniciar música de joc (sense stopMusic, ja s'ha parat en destructor de TITOL)
Audio::get()->playMusic("game.ogg");
// [ELIMINAT] Iniciar música de joc (ara es gestiona en stage_manager)
// La música s'inicia quan es transiciona de INIT_HUD a LEVEL_START
// Audio::get()->playMusic("game.ogg");
}
void EscenaJoc::actualitzar(float delta_time) {
@@ -240,6 +244,32 @@ void EscenaJoc::actualitzar(float delta_time) {
StageSystem::EstatStage estat = stage_manager_->get_estat();
switch (estat) {
case StageSystem::EstatStage::INIT_HUD: {
// Update stage manager timer
stage_manager_->actualitzar(delta_time);
// Calcular progrés de l'animació de la nau
float ship_progress = 1.0f - (stage_manager_->get_timer_transicio() /
Defaults::Game::INIT_HUD_DURATION);
ship_progress = std::min(1.0f, ship_progress);
// Calcular quant ha avançat l'animació de la nau
float ship_anim_progress = ship_progress /
(Defaults::Game::INIT_HUD_SHIP_DURATION / Defaults::Game::INIT_HUD_DURATION);
ship_anim_progress = std::min(1.0f, ship_anim_progress);
// Actualitzar posició de la nau segons animació
if (ship_anim_progress < 1.0f) {
Punt pos_animada = calcular_posicio_nau_init_hud(ship_anim_progress);
nau_.set_centre(pos_animada);
}
// Una vegada l'animació acaba, permetre control normal
// però mantenir la posició inicial especial fins LEVEL_START
break;
}
case StageSystem::EstatStage::LEVEL_START:
// Update countdown timer
stage_manager_->actualitzar(delta_time);
@@ -312,12 +342,10 @@ void EscenaJoc::actualitzar(float delta_time) {
}
void EscenaJoc::dibuixar() {
// Draw borders (always visible)
dibuixar_marges();
// Check game over state
if (game_over_) {
// Game over: draw enemies, bullets, debris, and "GAME OVER" text
dibuixar_marges();
for (const auto& enemy : orni_) {
enemy.dibuixar();
@@ -352,7 +380,46 @@ void EscenaJoc::dibuixar() {
StageSystem::EstatStage estat = stage_manager_->get_estat();
switch (estat) {
case StageSystem::EstatStage::INIT_HUD: {
// Calcular progrés de cada animació independent
float timer = stage_manager_->get_timer_transicio();
float total_time = Defaults::Game::INIT_HUD_DURATION;
float global_progress = 1.0f - (timer / total_time);
// Progrés del rectangle (empieza inmediatament)
float rect_progress = global_progress /
(Defaults::Game::INIT_HUD_RECT_DURATION / total_time);
rect_progress = std::min(1.0f, rect_progress);
// Progrés del marcador (empieza inmediatament)
float score_progress = global_progress /
(Defaults::Game::INIT_HUD_SCORE_DURATION / total_time);
score_progress = std::min(1.0f, score_progress);
// Progrés de la nau (empieza inmediatament)
float ship_progress = global_progress /
(Defaults::Game::INIT_HUD_SHIP_DURATION / total_time);
ship_progress = std::min(1.0f, ship_progress);
// Dibuixar elements animats
if (rect_progress > 0.0f) {
dibuixar_marges_animat(rect_progress);
}
if (score_progress > 0.0f) {
dibuixar_marcador_animat(score_progress);
}
// Dibuixar nau (usant el sistema normal, la posició ja està actualitzada)
if (ship_progress > 0.0f && !nau_.esta_tocada()) {
nau_.dibuixar();
}
break;
}
case StageSystem::EstatStage::LEVEL_START:
dibuixar_marges();
// [NEW] Draw ship if alive
if (itocado_ == 0.0f) {
nau_.dibuixar();
@@ -373,6 +440,8 @@ void EscenaJoc::dibuixar() {
break;
case StageSystem::EstatStage::PLAYING:
dibuixar_marges();
// [EXISTING] Normal rendering
if (itocado_ == 0.0f) {
nau_.dibuixar();
@@ -392,6 +461,7 @@ void EscenaJoc::dibuixar() {
break;
case StageSystem::EstatStage::LEVEL_COMPLETED:
dibuixar_marges();
// [NEW] Draw ship if alive
if (itocado_ == 0.0f) {
nau_.dibuixar();
@@ -558,6 +628,131 @@ void EscenaJoc::dibuixar_marcador() {
text_.render(text, {x, y}, escala, spacing);
}
void EscenaJoc::dibuixar_marges_animat(float progress) const {
// Animació seqüencial del rectangle amb efecte de "pinzell"
// Dos pinzells comencen al centre superior i baixen pels laterals
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
// Aplicar easing al progrés global
float eased_progress = Easing::ease_out_quad(progress);
// Coordenades del rectangle complet
int x1 = static_cast<int>(zona.x);
int y1 = static_cast<int>(zona.y);
int x2 = static_cast<int>(zona.x + zona.w);
int y2 = static_cast<int>(zona.y + zona.h);
int cx = (x1 + x2) / 2;
// Dividir en 3 fases de 33% cada una
constexpr float PHASE_1_END = 0.33f;
constexpr float PHASE_2_END = 0.66f;
// --- FASE 1: Línies horitzontals superiors (0-33%) ---
if (eased_progress > 0.0f) {
float phase1_progress = std::min(eased_progress / PHASE_1_END, 1.0f);
// Línia esquerra: creix des del centre cap a l'esquerra
int x1_phase1 = static_cast<int>(cx - (cx - x1) * phase1_progress);
Rendering::linea(sdl_.obte_renderer(), cx, y1, x1_phase1, y1, true);
// Línia dreta: creix des del centre cap a la dreta
int x2_phase1 = static_cast<int>(cx + (x2 - cx) * phase1_progress);
Rendering::linea(sdl_.obte_renderer(), cx, y1, x2_phase1, y1, true);
}
// --- FASE 2: Línies verticals laterals (33-66%) ---
if (eased_progress > PHASE_1_END) {
float phase2_progress = std::min((eased_progress - PHASE_1_END) / (PHASE_2_END - PHASE_1_END), 1.0f);
// Línia esquerra: creix des de dalt cap a baix
int y2_phase2 = static_cast<int>(y1 + (y2 - y1) * phase2_progress);
Rendering::linea(sdl_.obte_renderer(), x1, y1, x1, y2_phase2, true);
// Línia dreta: creix des de dalt cap a baix
Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2_phase2, true);
}
// --- FASE 3: Línies horitzontals inferiors (66-100%) ---
if (eased_progress > PHASE_2_END) {
float phase3_progress = (eased_progress - PHASE_2_END) / (1.0f - PHASE_2_END);
// Línia esquerra: creix des de l'esquerra cap al centre
int x_left_phase3 = static_cast<int>(x1 + (cx - x1) * phase3_progress);
Rendering::linea(sdl_.obte_renderer(), x1, y2, x_left_phase3, y2, true);
// Línia dreta: creix des de la dreta cap al centre
int x_right_phase3 = static_cast<int>(x2 - (x2 - cx) * phase3_progress);
Rendering::linea(sdl_.obte_renderer(), x2, y2, x_right_phase3, y2, true);
}
}
void EscenaJoc::dibuixar_marcador_animat(float progress) {
// Animació del marcador pujant des de baix amb easing
// Calcular progrés amb easing
float eased_progress = Easing::ease_out_quad(progress);
// Posició final del marcador (normal)
const float escala = 0.85f;
const float spacing = 0.0f;
// Formatar text igual que en dibuixar_marcador() normal
uint8_t stage_num = stage_manager_->get_stage_actual();
std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num)
: std::to_string(stage_num);
std::string score_str = std::to_string(puntuacio_total_);
score_str = std::string(5 - std::min(5, static_cast<int>(score_str.length())), '0') + score_str;
std::string text = "SCORE: " + score_str + " LIFES: " + std::to_string(num_vides_) +
" LEVEL: " + stage_str;
// Calcular dimensions
float text_width = text_.get_text_width(text, escala, spacing);
float text_height = text_.get_text_height(escala);
// Posició X final (centrada horitzontalment)
float x_final = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f;
// Posició Y final (centrada verticalment en la zona de scoreboard)
float y_final = Defaults::Zones::SCOREBOARD.y +
(Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f;
// Posició Y inicial (offscreen, sota de la pantalla)
float y_inicial = static_cast<float>(Defaults::Game::HEIGHT) + text_height;
// Interpolació amb easing
float y_animada = y_inicial + (y_final - y_inicial) * eased_progress;
// Renderitzar en posició animada
text_.render(text, {x_final, y_animada}, escala, spacing);
}
Punt EscenaJoc::calcular_posicio_nau_init_hud(float progress) const {
// Animació de la nau pujant des de baix amb easing
// Calcular progrés amb easing
float eased_progress = Easing::ease_out_quad(progress);
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
// Posició X final (centre de la zona de joc)
float x_final = zona.x + zona.w / 2.0f;
// Posició Y final (75% de l'altura de la zona de joc)
float y_final = zona.y + zona.h * Defaults::Game::INIT_HUD_SHIP_START_Y_RATIO;
// Posició Y inicial (offscreen, sota de la zona de joc)
float y_inicial = zona.y + zona.h + 50.0f; // 50px sota
// X no canvia (sempre centrada)
// Y interpola amb easing
float y_animada = y_inicial + (y_final - y_inicial) * eased_progress;
return {x_final, y_animada};
}
void EscenaJoc::detectar_col·lisions_bales_enemics() {
// Constants amplificades per hitbox més generós (115%)
constexpr float RADI_BALA = Defaults::Entities::BULLET_RADIUS;

View File

@@ -74,6 +74,11 @@ class EscenaJoc {
// [NEW] Stage system helpers
void dibuixar_missatge_stage(const std::string& missatge);
// [NEW] Funcions d'animació per INIT_HUD
void dibuixar_marges_animat(float progress) const; // Rectangle amb creixement uniforme
void dibuixar_marcador_animat(float progress); // Marcador que puja des de baix
Punt calcular_posicio_nau_init_hud(float progress) const; // Posició animada de la nau
};
#endif // ESCENA_JOC_HPP

View File

@@ -5,6 +5,7 @@
#include <iostream>
#include "core/audio/audio.hpp"
#include "core/defaults.hpp"
namespace StageSystem {
@@ -22,7 +23,7 @@ StageManager::StageManager(const ConfigSistemaStages* config)
void StageManager::inicialitzar() {
stage_actual_ = 1;
carregar_stage(stage_actual_);
canviar_estat(EstatStage::LEVEL_START);
canviar_estat(EstatStage::INIT_HUD);
std::cout << "[StageManager] Inicialitzat a stage " << static_cast<int>(stage_actual_)
<< std::endl;
@@ -30,6 +31,10 @@ void StageManager::inicialitzar() {
void StageManager::actualitzar(float delta_time, bool pausar_spawn) {
switch (estat_) {
case EstatStage::INIT_HUD:
processar_init_hud(delta_time);
break;
case EstatStage::LEVEL_START:
processar_level_start(delta_time);
break;
@@ -64,7 +69,9 @@ void StageManager::canviar_estat(EstatStage nou_estat) {
estat_ = nou_estat;
// Set timer based on state type
if (nou_estat == EstatStage::LEVEL_START) {
if (nou_estat == EstatStage::INIT_HUD) {
timer_transicio_ = Defaults::Game::INIT_HUD_DURATION;
} else if (nou_estat == EstatStage::LEVEL_START) {
timer_transicio_ = Defaults::Game::LEVEL_START_DURATION;
} else if (nou_estat == EstatStage::LEVEL_COMPLETED) {
timer_transicio_ = Defaults::Game::LEVEL_COMPLETED_DURATION;
@@ -74,10 +81,19 @@ void StageManager::canviar_estat(EstatStage nou_estat) {
if (nou_estat == EstatStage::LEVEL_START) {
size_t index = static_cast<size_t>(std::rand()) % Constants::MISSATGES_LEVEL_START.size();
missatge_level_start_actual_ = Constants::MISSATGES_LEVEL_START[index];
// [NOU] Iniciar música al entrar en LEVEL_START (després de INIT_HUD)
// Només si no està sonant ja (per evitar reiniciar en loops posteriors)
if (Audio::get()->getMusicState() != Audio::MusicState::PLAYING) {
Audio::get()->playMusic("game.ogg");
}
}
std::cout << "[StageManager] Canvi d'estat: ";
switch (nou_estat) {
case EstatStage::INIT_HUD:
std::cout << "INIT_HUD";
break;
case EstatStage::LEVEL_START:
std::cout << "LEVEL_START";
break;
@@ -91,6 +107,14 @@ void StageManager::canviar_estat(EstatStage nou_estat) {
std::cout << std::endl;
}
void StageManager::processar_init_hud(float delta_time) {
timer_transicio_ -= delta_time;
if (timer_transicio_ <= 0.0f) {
canviar_estat(EstatStage::LEVEL_START);
}
}
void StageManager::processar_level_start(float delta_time) {
timer_transicio_ -= delta_time;

View File

@@ -13,6 +13,7 @@ namespace StageSystem {
// Estats del stage system
enum class EstatStage {
INIT_HUD, // Animació inicial del HUD (3s)
LEVEL_START, // Pantalla "ENEMY INCOMING" (3s)
PLAYING, // Gameplay normal
LEVEL_COMPLETED // Pantalla "GOOD JOB COMMANDER!" (3s)
@@ -52,6 +53,7 @@ class StageManager {
// State transitions
void canviar_estat(EstatStage nou_estat);
void processar_init_hud(float delta_time);
void processar_level_start(float delta_time);
void processar_playing(float delta_time, bool pausar_spawn);
void processar_level_completed(float delta_time);