96 lines
3.9 KiB
C++
96 lines
3.9 KiB
C++
#pragma once
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
|
|
#include "scenes/scene.hpp"
|
|
|
|
// El Director és l'únic thread del runtime. Cada iterate() fa input →
|
|
// tick de l'escena actual → JD8_Flip → overlay → present → sleep al frame
|
|
// target. Totes les escenes (`scenes::Scene` i `ModuleGame`) són
|
|
// tick-based i no bloquegen — no hi ha fibers, mutex ni condition_variable.
|
|
// Compatible amb SDL_AppIterate i amb el futur port a emscripten.
|
|
class Director {
|
|
public:
|
|
static void init();
|
|
static void destroy();
|
|
static auto get() -> Director*;
|
|
|
|
// Bucle principal clàssic (build natiu sense SDL_MAIN_USE_CALLBACKS).
|
|
// Internament crida setup() + bucle d'iterate() + teardown(). Crida des de main().
|
|
void run();
|
|
|
|
// Punts d'entrada compatibles amb SDL_AppInit / SDL_AppIterate /
|
|
// SDL_AppEvent / SDL_AppQuit. Permeten que el Director siga driven
|
|
// per l'event loop de SDL3 en lloc d'un bucle propi — imprescindible
|
|
// per al port a emscripten, on el runtime posseïx el main loop.
|
|
void setup();
|
|
bool iterate(); // torna false quan el joc vol eixir
|
|
void teardown();
|
|
void handleEvent(const SDL_Event& event);
|
|
|
|
// Demana l'eixida (ex: segona pulsació d'ESC o SDL_QUIT)
|
|
void requestQuit();
|
|
auto isQuitRequested() const -> bool { return quit_requested_; }
|
|
|
|
// Demana un reinici "suau": para música i sons, reseteja info::ctx i
|
|
// torna a l'intro (state 255). Es processa al començament del pròxim
|
|
// iterate() per evitar manipular l'escena des d'una lambda del menú.
|
|
void requestRestart();
|
|
|
|
// Consumeix el flag de "tecla polsada" (com l'antic JI_AnyKey)
|
|
auto consumeKeyPressed() -> bool;
|
|
|
|
// Indica si ESC està bloquejada (el joc no l'ha de veure)
|
|
auto isEscBlocked() const -> bool { return esc_blocked_ || esc_swallow_until_release_; }
|
|
|
|
// Pausa: mentre està activa, iterate() no avança l'escena — es
|
|
// continua presentant el darrer frame amb overlay fresc.
|
|
void togglePause();
|
|
auto isPaused() const -> bool { return paused_; }
|
|
|
|
private:
|
|
Director() = default;
|
|
~Director();
|
|
|
|
static Director* instance_;
|
|
|
|
void pollAllEvents(); // drenatge amb SDL_PollEvent, només per al bucle natiu
|
|
|
|
// Inicialitza info::ctx a partir de Options::game.* i comprova trick.ini.
|
|
// Es crida una sola vegada des d'iterate() a la primera invocació.
|
|
void initGameContext();
|
|
// Construeix l'escena apropiada segons game_state_ i info::ctx.
|
|
// Retorna nullptr si l'state actual no té escena registrada (bug).
|
|
std::unique_ptr<scenes::Scene> createNextScene();
|
|
|
|
// Buffers persistents entre iteracions. Abans eren locals a run(),
|
|
// ara són membres perquè iterate() els pot reutilitzar sense tornar-los
|
|
// a reservar en cada crida del callback.
|
|
Uint32 game_frame_[320 * 200]{};
|
|
Uint32 presentation_buffer_[320 * 200]{};
|
|
bool has_frame_{false};
|
|
|
|
// Estat de l'escena actual. Abans vivia al stack del GameFiber; des
|
|
// de la Phase B.2 de la migració viu directament al Director.
|
|
std::unique_ptr<scenes::Scene> current_scene_;
|
|
int game_state_{1}; // 0 = gameplay (ModuleGame), 1 = via SceneRegistry, -1 = quit
|
|
Uint32 last_tick_ms_{0};
|
|
bool context_initialized_{false};
|
|
|
|
std::atomic<bool> quit_requested_{false};
|
|
std::atomic<bool> restart_requested_{false};
|
|
std::atomic<bool> key_pressed_{false};
|
|
std::atomic<bool> esc_blocked_{false};
|
|
std::atomic<bool> paused_{false};
|
|
// Quan el menú tanca amb ESC, empassem-nos l'ESC fins que l'usuari la deixe anar,
|
|
// per no fer eixir el joc al proper poll de JI_KeyPressed.
|
|
std::atomic<bool> esc_swallow_until_release_{false};
|
|
// Tecles consumides pel menú (KEY_DOWN): el KEY_UP associat cal empassar-lo
|
|
// per evitar que el joc (JI_AnyKey / JI_moveCheats) les veja quan el menú tanca.
|
|
bool menu_keys_held_[SDL_SCANCODE_COUNT]{};
|
|
};
|