step B.2: elimina fiber — Director posseeix l'escena, JD8_Flip sense yield, fiber.hpp/cpp esborrats

This commit is contained in:
2026-04-16 11:14:48 +02:00
parent 4e18f83ec5
commit 96a3cf9ebc
8 changed files with 105 additions and 296 deletions

View File

@@ -15,7 +15,6 @@
#include "core/rendering/menu.hpp"
#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
#include "core/system/fiber.hpp"
#include "game/info.hpp"
#include "game/modulegame.hpp"
#include "game/options.hpp"
@@ -35,14 +34,9 @@ extern void JI_moveCheats(Uint8 new_key);
Director* Director::instance_ = nullptr;
namespace {
Director::~Director() = default;
// Entry del fiber del joc. Dispatcha a una escena segons l'estat actual:
// `gameState == 0` → ModuleGame (gameplay), `gameState == 1` → una
// `scenes::Scene` del registry triada per `info::ctx.num_piramide`. Cada
// escena és tick-based; el JD8_Flip() entre ticks cedeix al Director
// via `GameFiber::yield()`.
void gameFiberEntry() {
void Director::initGameContext() {
info::ctx.num_habitacio = Options::game.habitacio_inicial;
info::ctx.num_piramide = Options::game.piramide_inicial;
info::ctx.diners = Options::game.diners_inicial;
@@ -57,55 +51,31 @@ void gameFiberEntry() {
info::ctx.nou_personatge = true;
fclose(ini);
}
int gameState = 1;
while (gameState != -1 && !JG_Quitting()) {
std::unique_ptr<scenes::Scene> scene;
if (gameState == 0) {
// Gameplay. ModuleGame és una scenes::Scene des de Phase A de
// la migració — mateix mini-loop tick+flip que la resta.
scene = std::make_unique<ModuleGame>();
} else {
// gameState == 1: dispatch al registry per num_piramide. El
// vell ModuleSequence::Go() feia aquest redirect al principi:
// si el jugador arriba a la Secreta (6) sense prou diners,
// salta als slides de fracàs (7) abans de buscar l'escena.
if (info::ctx.num_piramide == 6 && info::ctx.diners < 200) {
info::ctx.num_piramide = 7;
}
scene = scenes::SceneRegistry::instance().tryCreate(info::ctx.num_piramide);
}
if (!scene) {
// State no registrat — indica un bug del dispatcher o del
// registre d'escenes. Eixim ordenadament en lloc de cremar CPU.
break;
}
scene->onEnter();
Uint32 last = SDL_GetTicks();
while (!scene->done() && !JG_Quitting()) {
JI_Update(); // refresca key_pressed/any_key per a les escenes
const Uint32 now = SDL_GetTicks();
scene->tick(static_cast<int>(now - last));
last = now;
JD8_Flip(); // presenta i cedix al Director
}
gameState = scene->nextState();
}
}
} // namespace
std::unique_ptr<scenes::Scene> Director::createNextScene() {
if (game_state_ == 0) {
// Gameplay. ModuleGame és una scenes::Scene des de la Phase A.
return std::make_unique<ModuleGame>();
}
// game_state_ == 1: dispatch al registry per num_piramide. Replica
// del redirect que el vell ModuleSequence::Go() feia: si el jugador
// arriba a la Secreta (6) sense prou diners, salta als slides de
// fracàs (7) abans de buscar l'escena al registry.
if (info::ctx.num_piramide == 6 && info::ctx.diners < 200) {
info::ctx.num_piramide = 7;
}
return scenes::SceneRegistry::instance().tryCreate(info::ctx.num_piramide);
}
void Director::init() {
instance_ = new Director();
Gamepad::init();
// Registre d'escenes. Cada entrada = un state_key (`num_piramide`)
// amb una factory de `scenes::Scene`. El gameFiberEntry consulta
// aquest registry per a tots els states de seqüència; si una clau
// no apareix ací, el fiber eixirà del loop.
// amb una factory de `scenes::Scene`. iterate() consulta aquest
// registry per a tots els states de seqüència (game_state_ == 1); si
// una clau no apareix ací, Director surt ordenadament.
auto& registry = scenes::SceneRegistry::instance();
registry.registerScene(0, [] { return std::make_unique<scenes::MenuScene>(); });
registry.registerScene(100, [] { return std::make_unique<scenes::MortScene>(); });
@@ -132,12 +102,9 @@ void Director::init() {
}
return std::make_unique<scenes::IntroScene>();
});
GameFiber::init(gameFiberEntry);
}
void Director::destroy() {
GameFiber::destroy();
Gamepad::destroy();
delete instance_;
instance_ = nullptr;
@@ -164,17 +131,17 @@ void Director::setup() {
}
bool Director::iterate() {
if (GameFiber::is_done() || quit_requested_) {
// Si el joc encara no ha acabat (p.ex. eixida per ESC doble-press),
// li donem l'oportunitat de tornar net: marquem quit i reprenem el
// fiber fins que detecte JG_Quitting() i retorne de forma natural.
if (quit_requested_) {
JG_QuitSignal();
while (!GameFiber::is_done()) {
GameFiber::resume();
}
current_scene_.reset(); // destrueix l'escena actual ordenadament
return false;
}
if (!context_initialized_) {
initGameContext();
context_initialized_ = true;
}
constexpr Uint32 FRAME_MS_VSYNC = 16; // ~60 FPS amb VSync
constexpr Uint32 FRAME_MS_NO_VSYNC = 4; // ~250 FPS sense VSync (límit superior)
@@ -205,15 +172,41 @@ bool Director::iterate() {
esc_blocked_ = false;
}
// Cedeix el control al fiber del joc. Quan retorne (per un JD8_Flip()
// dins del joc) tindrem un nou frame a pixel_data. Si estem en pausa,
// no executem el fiber: es queda congelat al seu últim yield i
// continuem presentant l'últim frame conegut.
// Avança l'escena (si no estem pausats). En pausa, es manté l'escena
// congelada i re-presentem l'últim frame amb l'overlay fresc per
// damunt.
if (!paused_) {
GameFiber::resume();
if (GameFiber::is_done()) {
return false;
// Transicions: si l'escena actual ha acabat (o s'ha senyalat
// quit), llegim el seu next state i la destruïm per crear la
// següent a continuació.
if (current_scene_ && (current_scene_->done() || JG_Quitting())) {
game_state_ = current_scene_->nextState();
current_scene_.reset();
}
// Si no hi ha escena activa, construeix la pròxima segons
// game_state_ i info::ctx. Si és impossible (game_state_ == -1,
// quit, o state no registrat), eixim del loop.
if (!current_scene_) {
if (game_state_ == -1 || JG_Quitting()) return false;
current_scene_ = createNextScene();
if (!current_scene_) return false;
current_scene_->onEnter();
last_tick_ms_ = SDL_GetTicks();
}
// Tick de l'escena. JI_Update refresca key_pressed/any_key; el
// delta_ms és el temps real transcorregut des de l'últim tick.
JI_Update();
const Uint32 now = SDL_GetTicks();
const int delta_ms = static_cast<int>(now - last_tick_ms_);
last_tick_ms_ = now;
current_scene_->tick(delta_ms);
// Converteix `screen` indexat → `pixel_data` ARGB amb la paleta
// actual. JD8_Flip ja no fa yield (Phase B.2 eliminà els fibers);
// ara només omple el framebuffer perquè el Director l'aprofite.
JD8_Flip();
std::memcpy(game_frame_, JD8_GetFramebuffer(), sizeof(game_frame_));
has_frame_ = true;
}
@@ -238,12 +231,11 @@ bool Director::iterate() {
}
void Director::teardown() {
// Si el joc encara no ha acabat (p.ex. eixida per SDL_QUIT des del
// sistema), li donem l'oportunitat de tornar net.
// Senyal de quit i descàrrega ordenada de l'escena en curs. Els
// destructors de cada escena són no-bloquejants — ja no fan fades
// bloquejants. La resta de cleanup la gestiona `destroy()`.
JG_QuitSignal();
while (!GameFiber::is_done()) {
GameFiber::resume();
}
current_scene_.reset();
}
void Director::run() {