step 5: slides_scene amb wipe suau per easing (substituix doSlides)
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "core/jail/jdraw8.hpp"
|
||||
#include "core/jail/jinput.hpp"
|
||||
#include "game/info.hpp"
|
||||
#include "game/modulesequence.hpp"
|
||||
#include "scenes/scene_utils.hpp"
|
||||
|
||||
@@ -120,11 +121,14 @@ void IntroNewLogoScene::advancePaletteCycle() {
|
||||
}
|
||||
|
||||
void IntroNewLogoScene::tick(int delta_ms) {
|
||||
// Qualsevol tecla salta tota la intro del logo i va directament
|
||||
// a la delegació final — el mini-while del fiber no ha pintat
|
||||
// res encara, però doIntroSprites fa la seua pròpia intro.
|
||||
if (JI_AnyKey()) {
|
||||
phase_ = Phase::Delegate;
|
||||
// Qualsevol tecla durant el revelat o el ciclo de paleta salta
|
||||
// TOTA la intro (inclou saltar doIntroSprites). Aquest era el
|
||||
// comportament del vell `doIntroNewLogo`: a cada `waitTick()`
|
||||
// retornava abans de cridar `doIntroSprites`.
|
||||
if (phase_ != Phase::Delegate && phase_ != Phase::Done && JI_AnyKey()) {
|
||||
info::ctx.num_piramide = 0;
|
||||
phase_ = Phase::Done;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phase_) {
|
||||
@@ -192,13 +196,18 @@ void IntroNewLogoScene::tick(int delta_ms) {
|
||||
case Phase::Delegate: {
|
||||
// Delegació temporal al codi legacy: crea un ModuleSequence
|
||||
// instància i li crida `doIntroSprites(gfx)`. La funció
|
||||
// legacy fa els seus propis `JD8_Flip()` (que cedeixen al
|
||||
// Director via GameFiber::yield) i torna quan la cinemàtica
|
||||
// de sprites ha acabat. Step 9 d'aquesta migració la
|
||||
// reescriurà com a scenes::IntroSpritesScene i aquesta
|
||||
// delegació desapareixerà.
|
||||
// legacy *sempre* allibera `gfx` ella mateixa (al final o en
|
||||
// els paths de skip amb JI_AnyKey), així que necessitem
|
||||
// transferir-li ownership via `release()` per evitar un
|
||||
// double free al destructor de SurfaceHandle. Step 9
|
||||
// d'aquesta migració la reescriurà com a IntroSpritesScene
|
||||
// i la delegació desapareixerà.
|
||||
ModuleSequence legacy;
|
||||
legacy.doIntroSprites(gfx_.get());
|
||||
legacy.doIntroSprites(gfx_.release());
|
||||
// El vell `Go()` post-switch feia `num_piramide = 0` per a
|
||||
// passar al menú. Ho reproduïm ací — si no, el while extern
|
||||
// del fiber tornaria a crear IntroNewLogoScene infinitament.
|
||||
info::ctx.num_piramide = 0;
|
||||
phase_ = Phase::Done;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace scenes {
|
||||
|
||||
void playMusic(const char* filename) {
|
||||
void playMusic(const char* filename, int loop) {
|
||||
if (!filename) return;
|
||||
int size = 0;
|
||||
char* buffer = file_getfilebuffer(filename, size);
|
||||
@@ -15,7 +15,7 @@ void playMusic(const char* filename) {
|
||||
// JA_LoadMusic fa una còpia del OGG comprimit (SDL_malloc), així que
|
||||
// el `buffer` original es queda huérfano. Leak conegut heredat del
|
||||
// codi original — es tractarà quan jfile tinga una API std::vector.
|
||||
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer), size, filename));
|
||||
JA_PlayMusic(JA_LoadMusic(reinterpret_cast<Uint8*>(buffer), size, filename), loop);
|
||||
}
|
||||
|
||||
} // namespace scenes
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace scenes {
|
||||
|
||||
// Carrega un OGG de `data/` i arranca'l com a música de fons. Substituïx
|
||||
// el `play_music()` repetit en tots els doX() del vell modulesequence.
|
||||
void playMusic(const char* filename);
|
||||
// `loop`: -1 = infinit (per defecte), 0 = una sola vegada, N = N+1 passades.
|
||||
void playMusic(const char* filename, int loop = -1);
|
||||
|
||||
} // namespace scenes
|
||||
|
||||
186
source/scenes/slides_scene.cpp
Normal file
186
source/scenes/slides_scene.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "scenes/slides_scene.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "core/jail/jail_audio.hpp"
|
||||
#include "core/jail/jdraw8.hpp"
|
||||
#include "core/jail/jinput.hpp"
|
||||
#include "game/info.hpp"
|
||||
#include "scenes/scene_utils.hpp"
|
||||
#include "utils/easing.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int SCROLL_MS = 1600; // 80 iters × 20 ms del vell doSlides
|
||||
constexpr int HOLD_MS = 4600; // 230 iters × 20 ms (80 + 150) del vell
|
||||
constexpr int SLIDE_Y = 65;
|
||||
constexpr int SLIDE_H = 65;
|
||||
constexpr int BG_COLOR_INDEX = 255;
|
||||
|
||||
// Desplaçament inicial del slide segons direcció del wipe.
|
||||
// Slide 1 i 3: "scroll in from right" (pos_x va de 320 → 0).
|
||||
// Slide 2: "wipe reverse" (pos_x va de -320 → 0), el mateix efecte
|
||||
// estrany del doSlides vell on la imatge es desplaça des de l'esquerra
|
||||
// però revela primer el lateral dret del src.
|
||||
constexpr int SLIDE_START_X[3] = {320, -320, 320};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace scenes {
|
||||
|
||||
SlidesScene::~SlidesScene() {
|
||||
if (pal_aux_) std::free(pal_aux_);
|
||||
// pal_active_ NO s'allibera: propietat de main_palette via SetScreenPalette.
|
||||
}
|
||||
|
||||
void SlidesScene::onEnter() {
|
||||
num_piramide_at_start_ = info::ctx.num_piramide;
|
||||
|
||||
const char* arxiu = nullptr;
|
||||
if (num_piramide_at_start_ == 7) {
|
||||
// loop=1 per replicar el vell `play_music("00000005.ogg", 1)`.
|
||||
playMusic("00000005.ogg", 1);
|
||||
arxiu = (info::ctx.diners < 200) ? "intro2.gif" : "intro3.gif";
|
||||
} else {
|
||||
arxiu = "intro.gif";
|
||||
}
|
||||
|
||||
gfx_ = SurfaceHandle(arxiu);
|
||||
pal_aux_ = JD8_LoadPalette(arxiu);
|
||||
|
||||
// Còpia editable de la paleta. `pal_active_` comparteix memòria amb
|
||||
// main_palette després del SetScreenPalette — modificar-la modifica
|
||||
// main_palette directament. `pal_aux_` es manté intacte per a poder
|
||||
// restaurar després de cada fade-out intermedi.
|
||||
pal_active_ = static_cast<JD8_Palette>(std::malloc(768));
|
||||
std::memcpy(pal_active_, pal_aux_, 768);
|
||||
JD8_SetScreenPalette(pal_active_);
|
||||
|
||||
JD8_ClearScreen(BG_COLOR_INDEX);
|
||||
|
||||
phase_ = Phase::Slide1Enter;
|
||||
phase_acc_ms_ = 0;
|
||||
next_state_ = 0;
|
||||
}
|
||||
|
||||
void SlidesScene::drawSlide(int slide_idx, int pos_x) {
|
||||
const int src_y = slide_idx * SLIDE_H;
|
||||
|
||||
// Clipping manual: translada un rect de 320×65 des de (pos_x, SLIDE_Y)
|
||||
// a l'àrea visible (0..319, SLIDE_Y..SLIDE_Y+64).
|
||||
int dst_x = pos_x;
|
||||
int src_x = 0;
|
||||
int w = 320;
|
||||
|
||||
if (dst_x < 0) {
|
||||
src_x = -dst_x;
|
||||
w = 320 + dst_x;
|
||||
dst_x = 0;
|
||||
} else if (dst_x > 0) {
|
||||
w = 320 - dst_x;
|
||||
}
|
||||
|
||||
if (w > 0) {
|
||||
JD8_Blit(dst_x, SLIDE_Y, gfx_, src_x, src_y, w, SLIDE_H);
|
||||
}
|
||||
}
|
||||
|
||||
void SlidesScene::restorePalette() {
|
||||
std::memcpy(pal_active_, pal_aux_, 768);
|
||||
}
|
||||
|
||||
void SlidesScene::beginFinalFade() {
|
||||
if (num_piramide_at_start_ != 7) {
|
||||
JA_FadeOutMusic(250);
|
||||
}
|
||||
fade_.startFadeOut();
|
||||
phase_ = Phase::FadeFinal;
|
||||
}
|
||||
|
||||
void SlidesScene::tick(int delta_ms) {
|
||||
// Skip: qualsevol tecla salta directament al fade final. Per fidelitat
|
||||
// al vell doSlides, el skip NO atura la música explícitament — només
|
||||
// el final natural crida JA_FadeOutMusic (beginFinalFade() distingeix).
|
||||
if (!skip_triggered_ && JI_AnyKey()) {
|
||||
skip_triggered_ = true;
|
||||
if (num_piramide_at_start_ != 7) JA_FadeOutMusic(250);
|
||||
fade_.startFadeOut();
|
||||
phase_ = Phase::FadeFinal;
|
||||
}
|
||||
|
||||
switch (phase_) {
|
||||
case Phase::Slide1Enter:
|
||||
case Phase::Slide2Enter:
|
||||
case Phase::Slide3Enter: {
|
||||
phase_acc_ms_ += delta_ms;
|
||||
const int slide_idx = (phase_ == Phase::Slide1Enter ? 0
|
||||
: phase_ == Phase::Slide2Enter ? 1
|
||||
: 2);
|
||||
const float t = std::min(1.0f, static_cast<float>(phase_acc_ms_) /
|
||||
static_cast<float>(SCROLL_MS));
|
||||
const float eased = Easing::outCubic(t);
|
||||
const int pos_x = Easing::lerpInt(SLIDE_START_X[slide_idx], 0, eased);
|
||||
drawSlide(slide_idx, pos_x);
|
||||
|
||||
if (phase_acc_ms_ >= SCROLL_MS) {
|
||||
// Garanteix posició final exacta (pos_x=0).
|
||||
drawSlide(slide_idx, 0);
|
||||
if (phase_ == Phase::Slide1Enter) phase_ = Phase::Slide1Hold;
|
||||
else if (phase_ == Phase::Slide2Enter) phase_ = Phase::Slide2Hold;
|
||||
else phase_ = Phase::Slide3Hold;
|
||||
phase_acc_ms_ = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Phase::Slide1Hold:
|
||||
case Phase::Slide2Hold:
|
||||
phase_acc_ms_ += delta_ms;
|
||||
if (phase_acc_ms_ >= HOLD_MS) {
|
||||
fade_.startFadeOut();
|
||||
if (phase_ == Phase::Slide1Hold) phase_ = Phase::FadeOut1;
|
||||
else phase_ = Phase::FadeOut2;
|
||||
phase_acc_ms_ = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Slide3Hold:
|
||||
phase_acc_ms_ += delta_ms;
|
||||
if (phase_acc_ms_ >= HOLD_MS) {
|
||||
beginFinalFade();
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::FadeOut1:
|
||||
case Phase::FadeOut2:
|
||||
fade_.tick(delta_ms);
|
||||
if (fade_.done()) {
|
||||
restorePalette();
|
||||
JD8_ClearScreen(BG_COLOR_INDEX);
|
||||
if (phase_ == Phase::FadeOut1) phase_ = Phase::Slide2Enter;
|
||||
else phase_ = Phase::Slide3Enter;
|
||||
phase_acc_ms_ = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::FadeFinal:
|
||||
fade_.tick(delta_ms);
|
||||
if (fade_.done()) {
|
||||
if (num_piramide_at_start_ == 7) {
|
||||
info::ctx.num_piramide = 8;
|
||||
next_state_ = 1;
|
||||
} else {
|
||||
next_state_ = 0;
|
||||
}
|
||||
phase_ = Phase::Done;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Done:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace scenes
|
||||
79
source/scenes/slides_scene.hpp
Normal file
79
source/scenes/slides_scene.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/jail/jdraw8.hpp"
|
||||
#include "scenes/palette_fade.hpp"
|
||||
#include "scenes/scene.hpp"
|
||||
#include "scenes/surface_handle.hpp"
|
||||
|
||||
namespace scenes {
|
||||
|
||||
// 3 slides narratius amb scroll d'entrada + espera + transició amb
|
||||
// fade-out. Reemplaça `ModuleSequence::doSlides()`.
|
||||
//
|
||||
// Tria d'asset segons context:
|
||||
// - num_piramide == 7 i diners < 200: intro2.gif + música "00000005.ogg"
|
||||
// - num_piramide == 7 i diners >= 200: intro3.gif + música "00000005.ogg"
|
||||
// - altre cas (num_piramide == 1): intro.gif, sense música nova
|
||||
//
|
||||
// Flux:
|
||||
// Slide1Enter (1600 ms scroll dreta→centre, easing outCubic)
|
||||
// → Slide1Hold (4600 ms)
|
||||
// → FadeOut1 + clear + reset paleta
|
||||
// → Slide2Enter (1600 ms scroll esquerra→centre)
|
||||
// → Slide2Hold (4600 ms)
|
||||
// → FadeOut2 + clear + reset paleta
|
||||
// → Slide3Enter (1600 ms scroll dreta→centre)
|
||||
// → Slide3Hold (4600 ms)
|
||||
// → FadeFinal (JA_FadeOutMusic si num_piramide != 7 + fade paleta)
|
||||
// → Done
|
||||
//
|
||||
// Qualsevol tecla salta directament a FadeFinal (sense cortar la música
|
||||
// si hem entrat per num_piramide==7, per fidelitat al vell).
|
||||
//
|
||||
// NextState:
|
||||
// - num_piramide==7 al entrar → num_piramide=8 + return 1 (a Credits)
|
||||
// - altre cas → return 0 (entra al ModuleGame)
|
||||
class SlidesScene : public Scene {
|
||||
public:
|
||||
SlidesScene() = default;
|
||||
~SlidesScene() override;
|
||||
|
||||
void onEnter() override;
|
||||
void tick(int delta_ms) override;
|
||||
bool done() const override { return phase_ == Phase::Done; }
|
||||
int nextState() const override { return next_state_; }
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
Slide1Enter,
|
||||
Slide1Hold,
|
||||
FadeOut1,
|
||||
Slide2Enter,
|
||||
Slide2Hold,
|
||||
FadeOut2,
|
||||
Slide3Enter,
|
||||
Slide3Hold,
|
||||
FadeFinal,
|
||||
Done,
|
||||
};
|
||||
|
||||
// Pinta un slide amb desplaçament horitzontal. `slide_idx` = 0..2
|
||||
// (correspon a la franja 65x65 a y = 0, 65, 130 dins de gfx_).
|
||||
// `pos_x` = desplaçament, amb clipping manual quan surt de pantalla.
|
||||
void drawSlide(int slide_idx, int pos_x);
|
||||
void restorePalette();
|
||||
void beginFinalFade();
|
||||
|
||||
SurfaceHandle gfx_;
|
||||
JD8_Palette pal_aux_{nullptr}; // còpia "neta" que preservem
|
||||
JD8_Palette pal_active_{nullptr}; // propietat transferida a main_palette
|
||||
PaletteFade fade_;
|
||||
|
||||
Phase phase_{Phase::Slide1Enter};
|
||||
int phase_acc_ms_{0};
|
||||
int num_piramide_at_start_{1};
|
||||
int next_state_{0};
|
||||
bool skip_triggered_{false};
|
||||
};
|
||||
|
||||
} // namespace scenes
|
||||
@@ -33,4 +33,10 @@ void SurfaceHandle::adopt(JD8_Surface raw) {
|
||||
surface_ = raw;
|
||||
}
|
||||
|
||||
JD8_Surface SurfaceHandle::release() {
|
||||
JD8_Surface r = surface_;
|
||||
surface_ = nullptr;
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace scenes
|
||||
|
||||
@@ -29,6 +29,13 @@ class SurfaceHandle {
|
||||
// — la surface adoptada s'allibera al destructor o al següent reset/adopt.
|
||||
void adopt(JD8_Surface raw);
|
||||
|
||||
// Allibera ownership sense destruir la surface. Retorna el pointer cru;
|
||||
// el caller passa a ser responsable d'alliberar-lo (o de passar-lo a un
|
||||
// altre propietari). Usat quan una escena delega a codi legacy que
|
||||
// també allibera la mateixa surface — cal "soltar" el ownership per
|
||||
// evitar double free.
|
||||
[[nodiscard]] JD8_Surface release();
|
||||
|
||||
// Conversió implícita per al confort d'ús: JD8_Blit(handle)
|
||||
// en lloc de JD8_Blit(handle.get()).
|
||||
operator JD8_Surface() const { return surface_; }
|
||||
|
||||
Reference in New Issue
Block a user