Files
orni_attack/source/game/escenes/escena_logo.cpp

229 lines
6.9 KiB
C++

// escena_logo.cpp - Implementació de l'escena logo
// © 2025 Port a C++20
#include "escena_logo.hpp"
#include "../../core/system/gestor_escenes.hpp"
#include "../../core/system/global_events.hpp"
#include "../../core/graphics/shape_loader.hpp"
#include "../../core/rendering/shape_renderer.hpp"
#include <iostream>
#include <cfloat>
#include <algorithm>
// Helper: calcular el progrés individual d'una lletra
// en funció del progrés global (efecte seqüencial)
static float calcular_progress_letra(size_t letra_index,
size_t num_letras,
float global_progress) {
if (num_letras == 0)
return 1.0f;
constexpr float THRESHOLD = 0.5f; // Següent comença al 50%
// Calcular temps per lletra
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;
// Interpolar progrés
if (global_progress < start) {
return 0.0f; // Encara no ha començat
} else if (global_progress >= end) {
return 1.0f; // Completament apareguda
} else {
return (global_progress - start) / (end - start);
}
}
EscenaLogo::EscenaLogo(SDLManager& sdl)
: sdl_(sdl), temps_acumulat_(0.0f) {
std::cout << "Escena Logo: Inicialitzant...\n";
inicialitzar_lletres();
}
void EscenaLogo::executar() {
SDL_Event event;
Uint64 last_time = SDL_GetTicks();
while (GestorEscenes::actual == GestorEscenes::Escena::LOGO) {
// Calcular delta_time real
Uint64 current_time = SDL_GetTicks();
float delta_time = (current_time - last_time) / 1000.0f;
last_time = current_time;
// Limitar delta_time per evitar grans salts
if (delta_time > 0.05f) {
delta_time = 0.05f;
}
// Processar events SDL
while (SDL_PollEvent(&event)) {
// Manejo de finestra
if (sdl_.handleWindowEvent(event)) {
continue;
}
// Events globals (F1/F2/F3/ESC/QUIT)
if (GlobalEvents::handle(event, sdl_)) {
continue;
}
// Processar events de l'escena (qualsevol tecla/clic salta al joc)
processar_events(event);
}
// Actualitzar lògica
actualitzar(delta_time);
// Actualitzar colors oscil·lats (efecte verd global)
sdl_.updateColors(delta_time);
// Dibuixar
dibuixar();
}
std::cout << "Escena Logo: Finalitzant...\n";
}
void EscenaLogo::inicialitzar_lletres() {
using namespace Graphics;
// Llista de fitxers .shp (A repetida per a les dues A's)
std::vector<std::string> fitxers = {
"letra_j.shp", "letra_a.shp", "letra_i.shp", "letra_l.shp",
"letra_g.shp", "letra_a.shp", "letra_m.shp", "letra_e.shp", "letra_s.shp"
};
// Pas 1: Carregar totes les formes i calcular amplades
float ancho_total = 0.0f;
for (const auto& fitxer : fitxers) {
auto forma = ShapeLoader::load(fitxer);
if (!forma || !forma->es_valida()) {
std::cerr << "[EscenaLogo] Error carregant " << fitxer << std::endl;
continue;
}
// Calcular bounding box de la forma (trobar ancho)
float min_x = FLT_MAX;
float max_x = -FLT_MAX;
for (const auto& prim : forma->get_primitives()) {
for (const auto& punt : prim.points) {
min_x = std::min(min_x, punt.x);
max_x = std::max(max_x, punt.x);
}
}
float ancho_sin_escalar = max_x - min_x;
// IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL
// per que les posicions finals coincideixin amb la mida real de les lletres
float ancho = ancho_sin_escalar * ESCALA_FINAL;
float offset_centre = (forma->get_centre().x - min_x) * ESCALA_FINAL;
lletres_.push_back({
forma,
{0.0f, 0.0f}, // Posició es calcularà després
ancho,
offset_centre
});
ancho_total += ancho;
}
// Pas 2: Afegir espaiat entre lletres
ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1);
// Pas 3: Calcular posició inicial (centrat horitzontal)
constexpr float PANTALLA_ANCHO = 640.0f;
constexpr float PANTALLA_ALTO = 480.0f;
float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0f;
float y_centre = PANTALLA_ALTO / 2.0f;
// Pas 4: Assignar posicions a cada lletra
float x_actual = x_inicial;
for (auto& lletra : lletres_) {
// Posicionar el centre de la forma (shape_centre) en pantalla
// Usar offset_centre en lloc de ancho/2 perquè shape_centre
// pot no estar exactament al mig del bounding box
lletra.posicio.x = x_actual + lletra.offset_centre;
lletra.posicio.y = y_centre;
// Avançar per a següent lletra
x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
}
std::cout << "[EscenaLogo] " << lletres_.size()
<< " lletres carregades, ancho total: " << ancho_total << " px\n";
}
void EscenaLogo::actualitzar(float delta_time) {
temps_acumulat_ += delta_time;
// Després de DURACIO_TOTAL segons, saltar al joc
if (temps_acumulat_ >= DURACIO_TOTAL) {
GestorEscenes::actual = GestorEscenes::Escena::JOC;
}
}
void EscenaLogo::dibuixar() {
// Fons negre
sdl_.neteja(0, 0, 0);
// Progrés global normalitzat (0.0 → 1.0)
float global_progress = std::min(temps_acumulat_ / DURACIO_ZOOM, 1.0f);
// Centre de la pantalla (punt inicial de totes les lletres)
constexpr Punt CENTRE_PANTALLA = {640.0f / 2.0f, 480.0f / 2.0f};
// Dibuixar cada lletra amb animació seqüencial
for (size_t i = 0; i < lletres_.size(); i++) {
const auto& lletra = lletres_[i];
// Calcular progrés individual d'aquesta lletra (0.0 → 1.0)
float letra_progress = calcular_progress_letra(i, lletres_.size(), global_progress);
// Si la lletra encara no ha començat, saltar-la
if (letra_progress <= 0.0f) {
continue;
}
// Interpolar posició: des del centre cap a posició final
Punt pos_actual;
pos_actual.x = CENTRE_PANTALLA.x + (lletra.posicio.x - CENTRE_PANTALLA.x) * letra_progress;
pos_actual.y = CENTRE_PANTALLA.y + (lletra.posicio.y - CENTRE_PANTALLA.y) * letra_progress;
// Aplicar ease-out quadràtic per suavitat
float t = letra_progress;
float ease_factor = 1.0f - (1.0f - t) * (1.0f - t);
// Interpolar escala amb ease-out: des de ESCALA_INICIAL cap a ESCALA_FINAL
float escala_actual = ESCALA_INICIAL + (ESCALA_FINAL - ESCALA_INICIAL) * ease_factor;
// Renderitzar la lletra
Rendering::render_shape(
sdl_.obte_renderer(),
lletra.forma,
pos_actual, // Posició interpolada
0.0f, // Sense rotació
escala_actual, // Escala interpolada amb ease-out
true, // Dibuixar
1.0f // Progress = 1.0 (lletra completa, sense animació de primitives)
);
}
sdl_.presenta();
}
void EscenaLogo::processar_events(const SDL_Event& event) {
// Qualsevol tecla o clic de ratolí salta al joc
if (event.type == SDL_EVENT_KEY_DOWN ||
event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
GestorEscenes::actual = GestorEscenes::Escena::JOC;
}
}