Files
aee/source/scenes/intro_scene.cpp

219 lines
8.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "scenes/intro_scene.hpp"
#include "core/jail/jdraw8.hpp"
#include "core/jail/jinput.hpp"
#include "game/info.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("music/menu.ogg");
gfx_ = SurfaceHandle("gfx/logo.gif");
pal_ = JD8_LoadPalette("gfx/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::Sprites:
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 revelat/paleta salta TOTA la intro
// (inclou saltar la fase de sprites). Durant Sprites deixem que
// la sub-escena gestione el seu propi skip internament, que a més
// respecta la fase "final" no skippable de la variant 0.
if (phase_ != Phase::Sprites && 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::Sprites;
}
break;
case Phase::Sprites:
// Sub-escena construïda al vol al primer tick d'aquesta fase.
// Transferim el gfx_ per move — la sub-escena se n'ocupa
// fins que es destruix. Una vegada feta, els ticks delegats
// avancen l'animació dels sprites.
if (!sprites_scene_) {
sprites_scene_ = std::make_unique<IntroSpritesScene>(std::move(gfx_));
sprites_scene_->onEnter();
}
sprites_scene_->tick(delta_ms);
if (sprites_scene_->done()) {
// Equivalent al vell `Go()` post-switch: passem al menú.
// Sense açò el while del fiber tornaria a crear IntroScene
// infinitament amb num_piramide encara a 255.
info::ctx.num_piramide = 0;
phase_ = Phase::Done;
}
break;
case Phase::Done:
break;
}
}
} // namespace scenes