// escena_joc.cpp - Implementació de la lògica del joc // © 1999 Visente i Sergi (versió Pascal) // © 2025 Port a C++20 amb SDL3 #include "escena_joc.hpp" #include #include #include #include #include #include "../../core/rendering/line_renderer.hpp" #include "../../core/system/gestor_escenes.hpp" #include "../../core/system/global_events.hpp" EscenaJoc::EscenaJoc(SDLManager& sdl) : sdl_(sdl), debris_manager_(sdl.obte_renderer()), nau_(sdl.obte_renderer()), itocado_(0), text_(sdl.obte_renderer()) { // Inicialitzar bales amb renderer for (auto& bala : bales_) { bala = Bala(sdl.obte_renderer()); } // Inicialitzar enemics amb renderer for (auto& enemy : orni_) { enemy = Enemic(sdl.obte_renderer()); } } void EscenaJoc::executar() { std::cout << "Escena Joc: Inicialitzant...\n"; // Inicialitzar estat del joc inicialitzar(); SDL_Event event; Uint64 last_time = SDL_GetTicks(); while (GestorEscenes::actual == GestorEscenes::Escena::JOC) { // 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; } // Actualitzar comptador de FPS sdl_.updateFPS(delta_time); // 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; } // Processament específic del joc (SPACE per disparar) processar_input(event); } // Actualitzar física del joc amb delta_time real actualitzar(delta_time); // Actualitzar colors oscil·lats sdl_.updateColors(delta_time); // Netejar pantalla (usa color oscil·lat) sdl_.neteja(0, 0, 0); // Dibuixar joc dibuixar(); // Presentar renderer (swap buffers) sdl_.presenta(); } std::cout << "Escena Joc: Finalitzant...\n"; } void EscenaJoc::inicialitzar() { // Inicialitzar generador de números aleatoris // Basat en el codi Pascal original: line 376 std::srand(static_cast(std::time(nullptr))); // Inicialitzar estat de col·lisió itocado_ = 0; // Inicialitzar nau nau_.inicialitzar(); // Inicialitzar enemics (ORNIs) for (auto& enemy : orni_) { enemy.inicialitzar(); } // Inicialitzar bales for (auto& bala : bales_) { bala.inicialitzar(); } } void EscenaJoc::actualitzar(float delta_time) { // Actualitzar nau (input + física) nau_.processar_input(delta_time); nau_.actualitzar(delta_time); // Actualitzar moviment i rotació dels enemics (ORNIs) for (auto& enemy : orni_) { enemy.actualitzar(delta_time); } // Actualitzar moviment de bales (Fase 9) for (auto& bala : bales_) { bala.actualitzar(delta_time); } // Actualitzar fragments d'explosions debris_manager_.actualitzar(delta_time); } void EscenaJoc::dibuixar() { // Dibuixar marges de la zona de joc dibuixar_marges(); // Dibuixar nau nau_.dibuixar(); // Dibuixar ORNIs (enemics) for (const auto& enemy : orni_) { enemy.dibuixar(); } // Dibuixar bales (Fase 9) for (const auto& bala : bales_) { bala.dibuixar(); } // Dibuixar fragments d'explosions (després d'altres objectes) debris_manager_.dibuixar(); // Dibuixar marcador dibuixar_marcador(); } void EscenaJoc::processar_input(const SDL_Event& event) { // Processament d'input per events puntuals (no continus) // L'input continu (fletxes) es processa en actualitzar() amb // SDL_GetKeyboardState() if (event.type == SDL_EVENT_KEY_DOWN) { switch (event.key.key) { case SDLK_SPACE: { // No disparar si la nau està morta if (!nau_.esta_viva()) { break; } // Disparar bala des del front de la nau // El ship.shp té el front a (0, -12) en coordenades locals // 1. Calcular posició del front de la nau constexpr float LOCAL_TIP_X = 0.0f; constexpr float LOCAL_TIP_Y = -12.0f; const Punt& ship_centre = nau_.get_centre(); float ship_angle = nau_.get_angle(); // Aplicar transformació: rotació + trasllació float cos_a = std::cos(ship_angle); float sin_a = std::sin(ship_angle); float tip_x = LOCAL_TIP_X * cos_a - LOCAL_TIP_Y * sin_a + ship_centre.x; float tip_y = LOCAL_TIP_X * sin_a + LOCAL_TIP_Y * cos_a + ship_centre.y; Punt posicio_dispar = {tip_x, tip_y}; // 2. Buscar primera bala inactiva i disparar for (auto& bala : bales_) { if (!bala.esta_activa()) { bala.disparar(posicio_dispar, ship_angle); break; // Només una bala per polsació } } break; } default: break; } } } void EscenaJoc::tocado() { // TODO: Implementar seqüència de mort } void EscenaJoc::dibuixar_marges() const { // Dibuixar rectangle de la zona de joc const SDL_FRect& zona = Defaults::Zones::PLAYAREA; // Coordenades dels cantons int x1 = static_cast(zona.x); int y1 = static_cast(zona.y); int x2 = static_cast(zona.x + zona.w); int y2 = static_cast(zona.y + zona.h); // 4 línies per formar el rectangle Rendering::linea(sdl_.obte_renderer(), x1, y1, x2, y1, true); // Top Rendering::linea(sdl_.obte_renderer(), x1, y2, x2, y2, true); // Bottom Rendering::linea(sdl_.obte_renderer(), x1, y1, x1, y2, true); // Left Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2, true); // Right } void EscenaJoc::dibuixar_marcador() { // Text estàtic (hardcoded) const std::string text = "SCORE: 01000 LIFE: 3 LEVEL: 01"; // Paràmetres de renderització const float escala = 0.85f; const float spacing = 0.0f; // Calcular dimensions del text float text_width = text_.get_text_width(text, escala, spacing); float text_height = text_.get_text_height(escala); // Centrat horitzontal dins de la zona del marcador float x = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f; // Centrat vertical dins de la zona del marcador float y = Defaults::Zones::SCOREBOARD.y + (Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f; // Renderitzar text_.render(text, {x, y}, escala, spacing); }