219 lines
7.6 KiB
C++
219 lines
7.6 KiB
C++
#include "scenes/intro_scene.hpp"
|
||
|
||
#include "core/jail/jdraw8.hpp"
|
||
#include "core/jail/jinput.hpp"
|
||
#include "game/info.hpp"
|
||
#include "game/modulesequence.hpp"
|
||
#include "scenes/scene_utils.hpp"
|
||
|
||
namespace {
|
||
|
||
// Timings idèntics als del vell `doIntro()`: el JG_SetUpdateTicks(1000)
|
||
// inicial, (100) per a les 3 primeres lletres (J, A, I), (200) per a
|
||
// "JAIL" i el seu clear, (100) per a les 4 lletres centrals
|
||
// (G, A, M, E) i (200) per a la resta fins al cicle de paleta.
|
||
constexpr int INITIAL_MS = 1000;
|
||
constexpr int PALETTE_CYCLE_STEP_MS = 20;
|
||
constexpr int PALETTE_CYCLE_STEPS = 256;
|
||
constexpr int FINAL_WAIT_MS = 200;
|
||
|
||
// Un pas del revelat. Dos blits configurables (cos del wordmark + avió)
|
||
// més una variant per al wordmark sencer i un flag de ClearScreen previ.
|
||
struct RevealStep {
|
||
int duration_ms;
|
||
int body_w; // amplada del blit body (43,78) ← (43,155, body_w, 45); 0 si s'usa wordmark
|
||
int plane_x; // x del blit de l'avió (274,155, 27×45); -1 = no avió
|
||
bool clear; // fa ClearScreen(0) abans dels blits
|
||
bool wordmark; // usa drawWordmark() en lloc del blit body (wordmark complet)
|
||
};
|
||
|
||
constexpr RevealStep REVEAL_STEPS[] = {
|
||
{100, 27, 68, false, false}, // J
|
||
{100, 53, 96, false, false}, // JA
|
||
{100, 66, 109, false, false}, // JAI
|
||
{200, 92, 136, false, false}, // JAIL
|
||
{200, 92, -1, true, false}, // JAIL (clear, sense avió — parpelleig)
|
||
{100, 118, 160, false, false}, // JAILG
|
||
{100, 145, 188, false, false}, // JAILGA
|
||
{100, 178, 221, false, false}, // JAILGAM
|
||
{100, 205, 248, false, false}, // JAILGAME
|
||
{200, 0, 274, false, true}, // JAILGAMES (wordmark complet) + avió
|
||
{200, 0, -1, true, true}, // JAILGAMES (clear, sense avió)
|
||
{200, 0, 274, false, true}, // JAILGAMES + avió (sense clear)
|
||
{200, 0, -1, true, true}, // JAILGAMES (clear, sense avió)
|
||
{200, 0, 274, false, true}, // JAILGAMES + avió (sense clear)
|
||
{200, 0, -1, true, true}, // JAILGAMES (clear, sense avió)
|
||
};
|
||
constexpr int REVEAL_COUNT = sizeof(REVEAL_STEPS) / sizeof(REVEAL_STEPS[0]);
|
||
|
||
// Branca `!use_new_logo` del drawIntroWordmark de modulesequence.cpp:
|
||
// blit únic del wordmark "JAILGAMES" complet (231×45 al destí 43,78).
|
||
// IntroScene només s'activa quan use_new_logo == false, així que la
|
||
// branca use_new_logo d'aquell helper aquí no es necessita.
|
||
void drawWordmark(JD8_Surface gfx) {
|
||
JD8_Blit(43, 78, gfx, 43, 155, 231, 45);
|
||
}
|
||
|
||
} // namespace
|
||
|
||
namespace scenes {
|
||
|
||
IntroScene::IntroScene() = default;
|
||
|
||
IntroScene::~IntroScene() {
|
||
// No alliberem `pal_`: JD8_SetScreenPalette n'ha pres ownership i el
|
||
// proper SetScreenPalette / FadeToPal la lliurarà. Alliberar-la ací
|
||
// provocaria double free.
|
||
}
|
||
|
||
void IntroScene::onEnter() {
|
||
playMusic("00000003.ogg");
|
||
|
||
gfx_ = SurfaceHandle("logo.gif");
|
||
pal_ = JD8_LoadPalette("logo.gif");
|
||
JD8_SetScreenPalette(pal_);
|
||
|
||
JD8_ClearScreen(0);
|
||
|
||
phase_ = Phase::InitialWait;
|
||
phase_acc_ms_ = 0;
|
||
reveal_index_ = 0;
|
||
palette_step_ = 0;
|
||
}
|
||
|
||
void IntroScene::render() {
|
||
switch (phase_) {
|
||
case Phase::InitialWait:
|
||
JD8_ClearScreen(0);
|
||
break;
|
||
|
||
case Phase::Reveal: {
|
||
const RevealStep& s = REVEAL_STEPS[reveal_index_];
|
||
if (s.clear) JD8_ClearScreen(0);
|
||
if (s.wordmark) {
|
||
drawWordmark(gfx_);
|
||
} else if (s.body_w > 0) {
|
||
JD8_Blit(43, 78, gfx_, 43, 155, s.body_w, 45);
|
||
}
|
||
if (s.plane_x >= 0) {
|
||
JD8_Blit(s.plane_x, 78, gfx_, 274, 155, 27, 45);
|
||
}
|
||
break;
|
||
}
|
||
|
||
case Phase::PaletteCycle:
|
||
case Phase::FinalWait:
|
||
// Wordmark complet fix mentre cicla la paleta — l'últim
|
||
// pas del revelat (PAS 15) deixa la pantalla en aquest mateix
|
||
// estat, i el vell doIntro no redibuixava durant el cicle.
|
||
JD8_ClearScreen(0);
|
||
drawWordmark(gfx_);
|
||
break;
|
||
|
||
case Phase::Delegate:
|
||
case Phase::Done:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void IntroScene::advancePaletteCycle() {
|
||
// Replica exacta del cicle del doIntro vell sobre pal[16..31] — el
|
||
// grup del verd brillant del logo. Index 17 s'acosta a blanc mentre
|
||
// els altres convergeixen cap al mateix gris mitjà.
|
||
for (int i = 16; i < 32; i++) {
|
||
if (i == 17) {
|
||
if (pal_[i].r < 255) pal_[i].r++;
|
||
if (pal_[i].g < 255) pal_[i].g++;
|
||
if (pal_[i].b < 255) pal_[i].b++;
|
||
}
|
||
if (pal_[i].b < pal_[i].g) pal_[i].b++;
|
||
if (pal_[i].b > pal_[i].g) pal_[i].b--;
|
||
if (pal_[i].r < pal_[i].g) pal_[i].r++;
|
||
if (pal_[i].r > pal_[i].g) pal_[i].r--;
|
||
}
|
||
}
|
||
|
||
void IntroScene::tick(int delta_ms) {
|
||
// Qualsevol tecla durant el revelat/paleta salta TOTA la intro
|
||
// (inclou saltar doIntroSprites). El vell `doIntro` tornava abans
|
||
// de cridar doIntroSprites quan `wait_frame_or_skip` detectava una
|
||
// tecla. Durant `Delegate` deixem que doIntroSprites gestione el
|
||
// seu propi skip internament.
|
||
if (phase_ != Phase::Delegate && phase_ != Phase::Done && JI_AnyKey()) {
|
||
info::ctx.num_piramide = 0;
|
||
phase_ = Phase::Done;
|
||
return;
|
||
}
|
||
|
||
switch (phase_) {
|
||
case Phase::InitialWait:
|
||
phase_acc_ms_ += delta_ms;
|
||
render();
|
||
if (phase_acc_ms_ >= INITIAL_MS) {
|
||
phase_ = Phase::Reveal;
|
||
phase_acc_ms_ = 0;
|
||
reveal_index_ = 0;
|
||
}
|
||
break;
|
||
|
||
case Phase::Reveal:
|
||
phase_acc_ms_ += delta_ms;
|
||
render();
|
||
if (phase_acc_ms_ >= REVEAL_STEPS[reveal_index_].duration_ms) {
|
||
phase_acc_ms_ = 0;
|
||
++reveal_index_;
|
||
if (reveal_index_ >= REVEAL_COUNT) {
|
||
phase_ = Phase::PaletteCycle;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case Phase::PaletteCycle:
|
||
phase_acc_ms_ += delta_ms;
|
||
// Avancem tants passos com permet el delta, per evitar
|
||
// saltar-ne si el frame ha vingut lent.
|
||
while (phase_acc_ms_ >= PALETTE_CYCLE_STEP_MS &&
|
||
palette_step_ < PALETTE_CYCLE_STEPS) {
|
||
phase_acc_ms_ -= PALETTE_CYCLE_STEP_MS;
|
||
advancePaletteCycle();
|
||
++palette_step_;
|
||
}
|
||
render();
|
||
if (palette_step_ >= PALETTE_CYCLE_STEPS) {
|
||
phase_ = Phase::FinalWait;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
|
||
case Phase::FinalWait:
|
||
phase_acc_ms_ += delta_ms;
|
||
render();
|
||
if (phase_acc_ms_ >= FINAL_WAIT_MS) {
|
||
phase_ = Phase::Delegate;
|
||
}
|
||
break;
|
||
|
||
case Phase::Delegate: {
|
||
// Delegació temporal al codi legacy. La funció legacy sempre
|
||
// allibera `gfx` ella mateixa (al final i als paths de skip
|
||
// amb JI_AnyKey), així que transferim ownership via release()
|
||
// per evitar double free al destructor de SurfaceHandle.
|
||
// Step 9 reescriurà doIntroSprites com a IntroSpritesScene i
|
||
// aquesta delegació desapareixerà.
|
||
ModuleSequence legacy;
|
||
legacy.doIntroSprites(gfx_.release());
|
||
// El vell `Go()` post-switch feia `num_piramide = 0` per
|
||
// passar al menú. Replicat ací: si no, el while del fiber
|
||
// tornaria a crear IntroScene infinitament.
|
||
info::ctx.num_piramide = 0;
|
||
phase_ = Phase::Done;
|
||
break;
|
||
}
|
||
|
||
case Phase::Done:
|
||
break;
|
||
}
|
||
}
|
||
|
||
} // namespace scenes
|