treballant en INIT_HUD
This commit is contained in:
@@ -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)
|
||||
|
||||
20
source/core/math/easing.hpp
Normal file
20
source/core/math/easing.hpp
Normal 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
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user