step B.1: fades de ModuleGame tick-based amb scenes::PaletteFade (fases FadingIn/FadingOut sense redibuixar, per no perdre el frame final)

This commit is contained in:
2026-04-16 10:27:04 +02:00
parent f9346add79
commit 4e18f83ec5
3 changed files with 284 additions and 218 deletions

View File

@@ -37,11 +37,11 @@ Director* Director::instance_ = nullptr;
namespace { namespace {
// Entry del fiber del joc. Alterna entre la capa `scenes::` (cinemàtiques // Entry del fiber del joc. Dispatcha a una escena segons l'estat actual:
// i menús, triats per `info::ctx.num_piramide` via el SceneRegistry) i // `gameState == 0` → ModuleGame (gameplay), `gameState == 1` → una
// ModuleGame (gameplay) fins que el joc demana eixir. Quan el codi de // `scenes::Scene` del registry triada per `info::ctx.num_piramide`. Cada
// gameplay o una escena crida JD8_Flip(), el control torna automàticament // escena és tick-based; el JD8_Flip() entre ticks cedeix al Director
// al Director via `GameFiber::yield()`. // via `GameFiber::yield()`.
void gameFiberEntry() { void gameFiberEntry() {
info::ctx.num_habitacio = Options::game.habitacio_inicial; info::ctx.num_habitacio = Options::game.habitacio_inicial;
info::ctx.num_piramide = Options::game.piramide_inicial; info::ctx.num_piramide = Options::game.piramide_inicial;
@@ -60,31 +60,29 @@ void gameFiberEntry() {
int gameState = 1; int gameState = 1;
while (gameState != -1 && !JG_Quitting()) { while (gameState != -1 && !JG_Quitting()) {
std::unique_ptr<scenes::Scene> scene;
if (gameState == 0) { if (gameState == 0) {
// Gameplay pur (ModuleGame encara és cooperatiu-clàssic: conté // Gameplay. ModuleGame és una scenes::Scene des de Phase A de
// el seu while intern i crida JD8_Flip manualment). Fora // la migració — mateix mini-loop tick+flip que la resta.
// d'abast de la migració scenes:: — es tractarà en una fase scene = std::make_unique<ModuleGame>();
// posterior quan el fiber es puga eliminar. } else {
auto* mg = new ModuleGame(); // gameState == 1: dispatch al registry per num_piramide. El
gameState = mg->Go(); // vell ModuleSequence::Go() feia aquest redirect al principi:
delete mg; // si el jugador arriba a la Secreta (6) sense prou diners,
continue; // 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);
} }
// gameState == 1: dispatch a la capa scenes::.
// 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 al registry.
if (info::ctx.num_piramide == 6 && info::ctx.diners < 200) {
info::ctx.num_piramide = 7;
}
auto scene = scenes::SceneRegistry::instance().tryCreate(info::ctx.num_piramide);
if (!scene) { if (!scene) {
// State no registrat — indica un bug del dispatcher o del // State no registrat — indica un bug del dispatcher o del
// registre d'escenes. Eixim ordenadament en lloc de cremar CPU. // registre d'escenes. Eixim ordenadament en lloc de cremar CPU.
break; break;
} }
scene->onEnter(); scene->onEnter();
Uint32 last = SDL_GetTicks(); Uint32 last = SDL_GetTicks();
while (!scene->done() && !JG_Quitting()) { while (!scene->done() && !JG_Quitting()) {

View File

@@ -1,165 +1,204 @@
#include "game/modulegame.hpp" #include "game/modulegame.hpp"
#include "core/jail/jail_audio.hpp" #include "core/jail/jail_audio.hpp"
#include "core/jail/jdraw8.hpp" #include "core/jail/jdraw8.hpp"
#include "core/jail/jfile.hpp" #include "core/jail/jfile.hpp"
#include "core/jail/jgame.hpp" #include "core/jail/jgame.hpp"
#include "core/jail/jinput.hpp" #include "core/jail/jinput.hpp"
ModuleGame::ModuleGame() { ModuleGame::ModuleGame() {
this->gfx = JD8_LoadSurface(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif"); this->gfx = JD8_LoadSurface(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif");
JG_SetUpdateTicks(10); JG_SetUpdateTicks(10);
this->sam = new Prota(this->gfx); this->sam = new Prota(this->gfx);
this->mapa = new Mapa(this->gfx, this->sam); this->mapa = new Mapa(this->gfx, this->sam);
this->marcador = new Marcador(this->gfx, this->sam); this->marcador = new Marcador(this->gfx, this->sam);
if (info::ctx.num_piramide == 2) { if (info::ctx.num_piramide == 2) {
this->bola = new Bola(this->gfx, this->sam); this->bola = new Bola(this->gfx, this->sam);
} else { } else {
this->bola = NULL; this->bola = nullptr;
} }
this->momies = NULL; this->momies = nullptr;
this->final = 0; this->iniciarMomies();
this->iniciarMomies(); }
}
ModuleGame::~ModuleGame() {
ModuleGame::~ModuleGame(void) { if (this->bola != nullptr) delete this->bola;
JD8_FadeOut(); if (this->momies != nullptr) {
this->momies->clear();
if (this->bola != NULL) delete this->bola; delete this->momies;
if (this->momies != NULL) { }
this->momies->clear(); delete this->marcador;
delete this->momies; delete this->mapa;
} delete this->sam;
delete this->marcador;
delete this->mapa; JD8_FreeSurface(this->gfx);
delete this->sam; }
JD8_FreeSurface(this->gfx); void ModuleGame::onEnter() {
} // Primera Draw per omplir `screen` amb el contingut del gameplay
// abans que el fade-in arranque. Si no, les primeres iteracions del
int ModuleGame::Go() { // fade interpolarien cap a una paleta amb pantalla buida.
this->Draw(); this->Draw();
const char* music = info::ctx.num_piramide == 3 ? "00000008.ogg" : (info::ctx.num_piramide == 2 ? "00000007.ogg" : (info::ctx.num_piramide == 6 ? "00000002.ogg" : "00000006.ogg")); const char* music = info::ctx.num_piramide == 3 ? "00000008.ogg"
const char* current_music = JA_GetMusicFilename(); : info::ctx.num_piramide == 2 ? "00000007.ogg"
if ((JA_GetMusicState() != JA_MUSIC_PLAYING) || !(strcmp(music, current_music) == 0)) { : info::ctx.num_piramide == 6 ? "00000002.ogg"
auto buffer = file_readfile(music); : "00000006.ogg";
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer.data()), const char* current_music = JA_GetMusicFilename();
static_cast<Uint32>(buffer.size()), music)); if ((JA_GetMusicState() != JA_MUSIC_PLAYING) || !current_music ||
} strcmp(music, current_music) != 0) {
auto buffer = file_readfile(music);
JD8_FadeToPal(JD8_LoadPalette(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif")); JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer.data()),
static_cast<Uint32>(buffer.size()), music));
while (this->final == 0 && !JG_Quitting()) { }
this->Draw();
this->Update(); // Arranca el fade-in tick-based. El `PaletteFade` avança un pas (de
} // 32) per cada tick; durant aquesta fase el gameplay no corre,
// només Draw+fade. Substituïx la crida bloquejant `JD8_FadeToPal`.
// JS_FadeOutMusic(); fade_.startFadeTo(JD8_LoadPalette(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif"));
phase_ = Phase::FadingIn;
if (this->final == 1) { }
info::ctx.num_habitacio++;
if (info::ctx.num_habitacio == 6) { void ModuleGame::tick(int delta_ms) {
info::ctx.num_habitacio = 1; switch (phase_) {
info::ctx.num_piramide++; case Phase::FadingIn:
} // No redibuixem durant el fade: el `screen` ja va ser omplit
if (info::ctx.num_piramide == 6 && info::ctx.num_habitacio == 2) info::ctx.num_piramide++; // per la Draw() d'onEnter. Només el JD8_Flip del caller muta
} else if (this->final == 2) { // pixel_data segons la paleta que avança pas a pas.
info::ctx.num_piramide = 100; fade_.tick(delta_ms);
} if (fade_.done()) phase_ = Phase::Playing;
break;
if (JG_Quitting()) {
return -1; case Phase::Playing:
} else { this->Draw();
if (info::ctx.num_habitacio == 1 || info::ctx.num_piramide == 100 || info::ctx.num_piramide == 7) { this->Update();
return 1; if (this->final_ != 0) {
} else { this->applyFinalTransitions();
return 0; fade_.startFadeOut();
} phase_ = Phase::FadingOut;
} }
} break;
void ModuleGame::Draw() { case Phase::FadingOut:
this->mapa->draw(); // No redibuixem: el `screen` té l'últim frame pintat per la
this->marcador->draw(); // fase Playing (just abans que Update() setegés `final_`).
this->sam->draw(); // El vell `JD8_FadeOut` feia exactament això — flips amb
if (this->momies != NULL) this->momies->draw(); // paleta fading però sense tocar el buffer. Redibuixar ací
if (this->bola != NULL) this->bola->draw(); // mostraria l'estat post-Update del sprite (p.ex. el prota
// "tornant" davant la porta després d'haver eixit).
JD8_Flip(); fade_.tick(delta_ms);
} if (fade_.done()) phase_ = Phase::Done;
break;
void ModuleGame::Update() {
if (JG_ShouldUpdate()) { case Phase::Done:
JI_Update(); break;
}
this->final = this->sam->update(); }
if (this->momies != NULL && this->momies->update()) {
Momia* seguent = this->momies->next; int ModuleGame::nextState() const {
delete this->momies; if (JG_Quitting()) return -1;
this->momies = seguent; if (info::ctx.num_habitacio == 1 ||
info::ctx.momies--; info::ctx.num_piramide == 100 ||
} info::ctx.num_piramide == 7) {
if (this->bola != NULL) this->bola->update(); return 1;
this->mapa->update(); }
if (this->mapa->novaMomia()) { return 0;
if (this->momies != NULL) { }
this->momies->insertar(new Momia(this->gfx, true, 0, 0, this->sam));
info::ctx.momies++; void ModuleGame::applyFinalTransitions() {
} else { if (this->final_ == 1) {
this->momies = new Momia(this->gfx, true, 0, 0, this->sam); info::ctx.num_habitacio++;
info::ctx.momies++; if (info::ctx.num_habitacio == 6) {
} info::ctx.num_habitacio = 1;
} info::ctx.num_piramide++;
}
if (JI_CheatActivated("reviu")) info::ctx.vida = 5; if (info::ctx.num_piramide == 6 && info::ctx.num_habitacio == 2) info::ctx.num_piramide++;
if (JI_CheatActivated("alone")) { } else if (this->final_ == 2) {
if (this->momies != NULL) { info::ctx.num_piramide = 100;
this->momies->clear(); }
delete this->momies; }
this->momies = NULL;
info::ctx.momies = 0; void ModuleGame::Draw() {
} // No crida JD8_Flip — el caller (mini-loop del fiber, o Director a
} // Phase B.2) ho fa després de cada tick.
if (JI_CheatActivated("obert")) { this->mapa->draw();
for (int i = 0; i < 16; i++) { this->marcador->draw();
this->mapa->tombes[i].costat[0] = true; this->sam->draw();
this->mapa->tombes[i].costat[1] = true; if (this->momies != nullptr) this->momies->draw();
this->mapa->tombes[i].costat[2] = true; if (this->bola != nullptr) this->bola->draw();
this->mapa->tombes[i].costat[3] = true; }
this->mapa->comprovaCaixa(i);
} void ModuleGame::Update() {
} if (JG_ShouldUpdate()) {
JI_Update();
if (JI_KeyPressed(SDL_SCANCODE_ESCAPE)) {
JG_QuitSignal(); this->final_ = this->sam->update();
} if (this->momies != nullptr && this->momies->update()) {
} Momia* seguent = this->momies->next;
} delete this->momies;
this->momies = seguent;
void ModuleGame::iniciarMomies() { info::ctx.momies--;
if (info::ctx.num_habitacio == 1) { }
info::ctx.momies = 1; if (this->bola != nullptr) this->bola->update();
} else { this->mapa->update();
info::ctx.momies++; if (this->mapa->novaMomia()) {
} if (this->momies != nullptr) {
if (info::ctx.num_piramide == 6) info::ctx.momies = 8; this->momies->insertar(new Momia(this->gfx, true, 0, 0, this->sam));
info::ctx.momies++;
int x = 20; } else {
int y = 170; this->momies = new Momia(this->gfx, true, 0, 0, this->sam);
bool dimonis = info::ctx.num_piramide == 6; info::ctx.momies++;
for (int i = 0; i < info::ctx.momies; i++) { }
if (this->momies == NULL) { }
this->momies = new Momia(this->gfx, dimonis, x, y, this->sam);
} else { if (JI_CheatActivated("reviu")) info::ctx.vida = 5;
this->momies->insertar(new Momia(this->gfx, dimonis, x, y, this->sam)); if (JI_CheatActivated("alone")) {
} if (this->momies != nullptr) {
x += 65; this->momies->clear();
if (x == 345) { delete this->momies;
x = 20; this->momies = nullptr;
y -= 35; info::ctx.momies = 0;
} }
} }
} if (JI_CheatActivated("obert")) {
for (int i = 0; i < 16; i++) {
this->mapa->tombes[i].costat[0] = true;
this->mapa->tombes[i].costat[1] = true;
this->mapa->tombes[i].costat[2] = true;
this->mapa->tombes[i].costat[3] = true;
this->mapa->comprovaCaixa(i);
}
}
if (JI_KeyPressed(SDL_SCANCODE_ESCAPE)) {
JG_QuitSignal();
}
}
}
void ModuleGame::iniciarMomies() {
if (info::ctx.num_habitacio == 1) {
info::ctx.momies = 1;
} else {
info::ctx.momies++;
}
if (info::ctx.num_piramide == 6) info::ctx.momies = 8;
int x = 20;
int y = 170;
bool dimonis = info::ctx.num_piramide == 6;
for (int i = 0; i < info::ctx.momies; i++) {
if (this->momies == nullptr) {
this->momies = new Momia(this->gfx, dimonis, x, y, this->sam);
} else {
this->momies->insertar(new Momia(this->gfx, dimonis, x, y, this->sam));
}
x += 65;
if (x == 345) {
x = 20;
y -= 35;
}
}
}

View File

@@ -1,31 +1,60 @@
#pragma once #pragma once
#include "game/bola.hpp" #include "game/bola.hpp"
#include "game/info.hpp" #include "game/info.hpp"
#include "game/mapa.hpp" #include "game/mapa.hpp"
#include "game/marcador.hpp" #include "game/marcador.hpp"
#include "game/momia.hpp" #include "game/momia.hpp"
#include "game/prota.hpp" #include "game/prota.hpp"
#include "scenes/palette_fade.hpp"
class ModuleGame { #include "scenes/scene.hpp"
public:
ModuleGame(); // Escena de gameplay pur. Reemplaça el vell `Go()` bloquejant amb
~ModuleGame(void); // l'interfície `scenes::Scene` tick-based: `onEnter()` arranca la
// música i un fade-in, el `tick()` avança un frame (Draw + Update
int Go(); // gated per JG_ShouldUpdate), i quan la partida acaba fa un fade-out
// abans de retornar el next state.
private: //
void Draw(); // Tres fases internes:
void Update(); // 1. FadingIn — fade-in 32 passos mentre el render segueix viu.
// 2. Playing — gameplay normal; `final_` es setja quan el prota mor
void iniciarMomies(); // o canvia de sala. `Update()` només avança cada 10 ms
// via `JG_ShouldUpdate` (ticker fix del jail).
Uint8 final; // 3. FadingOut — fade-out 32 passos mantenint l'últim frame visible
JD8_Surface gfx; // (substituïx el `JD8_FadeOut` bloquejant que feia el
// destructor legacy).
Mapa* mapa; class ModuleGame : public scenes::Scene {
Prota* sam; public:
Marcador* marcador; ModuleGame();
Momia* momies; ~ModuleGame() override;
Bola* bola;
}; void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override;
private:
enum class Phase {
FadingIn,
Playing,
FadingOut,
Done,
};
void Draw(); // render a `screen`; no crida JD8_Flip (ho fa el caller)
void Update(); // gated per JG_ShouldUpdate
void iniciarMomies();
void applyFinalTransitions(); // muta info::ctx quan final_ passa a !=0
Phase phase_{Phase::FadingIn};
scenes::PaletteFade fade_;
Uint8 final_{0};
JD8_Surface gfx{nullptr};
Mapa* mapa{nullptr};
Prota* sam{nullptr};
Marcador* marcador{nullptr};
Momia* momies{nullptr};
Bola* bola{nullptr};
};