Files
aee/source/game/scenes/intro_sprites_scene.cpp
T

371 lines
15 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 "game/scenes/intro_sprites_scene.hpp"
#include <algorithm>
#include <cstdlib>
#include "core/jail/jdraw8.hpp"
#include "core/jail/jinput.hpp"
#include "game/options.hpp"
namespace {
// Duració d'un pas. El vell doIntroSprites feia JG_SetUpdateTicks(20);
// cada iteració del seu for (i) consumia un tick de 20 ms.
constexpr int TICK_MS = 20;
// Taules de frames. Ubicacions de cada sprite dins el gfx de la intro
// (gfx/logo.gif o gfx/logo_new.gif — el layout de sprites és el mateix).
// Cada sprite ocupa 15×15 px, disposats horitzontalment per fila.
// Els valors són els offsets x (la y la posa l'invocador al src_y).
// Derivats dels `fr_ani_N[i] = ...` del vell doIntroSprites.
constexpr Uint16 FR1[] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180}; // camina dreta (y=0)
constexpr Uint16 FR2[] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180}; // camina esquerra (y=15)
constexpr Uint16 FR3[] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150}; // trau mapa dreta (y=30)
constexpr Uint16 FR4[] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150}; // trau mapa esquerra (y=45)
constexpr Uint16 FR5[] = {165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 300, 285, 270, 255, 240, 225, 210, 195, 180, 165}; // bot de susto (y=45, mirror)
constexpr Uint16 FR6[] = {0, 15, 30, 45, 60, 75, 90, 105}; // momia (y=60)
constexpr Uint16 FR7[] = {75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, // paper (y=75, idx 0..13)
0,
15,
30,
45,
60,
75,
90,
105,
120,
135,
150,
165,
180,
195,
210}; // sombra (y=105, idx 14..28)
constexpr Uint16 FR8[] = {15, 30, 45, 60}; // pedra (y=75)
constexpr Uint16 FR9[] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225}; // prota ball (y=120)
constexpr Uint16 FR10[] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225}; // momia ball (y=135)
constexpr Uint16 FR11[] = {15, 30, 45, 60, 75, 60}; // altaveu (y=90, [5]=[3] pel loop de 4)
constexpr Uint16 CREU = 75; // src_y de la creu (overlay)
constexpr Uint16 INTERROGANT = 90; // src_y del signe d'interrogant
// Equivalent de la funció `drawIntroWordmark` de modulesequence.cpp.
// Branqueja segons use_new_logo perquè la mateixa sub-escena es
// reutilitza des de IntroScene (logo vell) i IntroNewLogoScene (logo
// nou) amb arxius diferents però mateix layout de sprites.
void drawWordmark(const Uint8* gfx) {
if (Options::game.use_new_logo) {
// Centrat: (320 188) / 2 = 66 (IntroNewLogoScene usa la mateixa x).
Jd8::blit(66, 78, gfx, 60, 158, 188, 28);
} else {
Jd8::blit(43, 78, gfx, 43, 155, 231, 45);
}
}
using RenderFn = void (*)(const Uint8*, int);
// Una fase — rang [start_i..end_i] inclusive (direcció implícita per
// signe), funció de render, i flag d'skippable. Totes les fases actuals
// són skippables; el flag es conserva per si alguna futura ha de ser
// no interrompuda (p.ex. un logo fatídic que cal veure sencer).
struct SpritePhase {
int start_i;
int end_i;
RenderFn render;
bool skippable;
};
// =========================================================================
// Variant 0 — Interrogant / Momia
// =========================================================================
void v0WalkRight(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(i, 150, gfx, FR1[(i / 5) % 13], 0, 15, 15, 0);
}
void v0PullMapRight(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 150, gfx, FR3[std::min(i / 5, 10)], 30, 15, 15, 0);
}
void v0WalkLeftTo80(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(i, 150, gfx, FR2[(i / 5) % 13], 15, 15, 15, 0);
}
void v0PullMapLeft(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(80, 150, gfx, FR4[std::min(i / 5, 10)], 45, 15, 15, 0);
}
void v0MomiaLeft(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(i, 150, gfx, FR6[(i / 5) % 8], 60, 15, 15, 0);
Jd8::blitCK(80, 150, gfx, FR4[10], 45, 15, 15, 0);
}
void v0Turn(const Uint8* gfx, int /*i*/) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(80, 150, gfx, FR1[1], 0, 15, 15, 0);
Jd8::blitCK(95, 150, gfx, FR6[4], 60, 15, 15, 0);
Jd8::blitCK(80, 133, gfx, 0, INTERROGANT, 15, 15, 0);
}
void v0Jump1(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(80, 150 - ((i % 50) / 5), gfx, FR5[std::min(i / 5, 19)], 45, 15, 15, 0);
Jd8::blitCK(95, 150, gfx, FR6[4], 60, 15, 15, 0);
}
void v0Jump2(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(80, 140 + ((i % 50) / 5), gfx, FR5[std::min(i / 5, 19)], 45, 15, 15, 0);
Jd8::blitCK(95, 150, gfx, FR6[4], 60, 15, 15, 0);
}
void v0WalkFinal(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(i, 150, gfx, FR2[(i / 5) % 13], 15, 15, 15, 0);
Jd8::blitCK(95, 150, gfx, FR6[4], 60, 15, 15, 0);
}
void v0Final(const Uint8* gfx, int /*i*/) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(95, 150, gfx, FR6[4], 60, 15, 15, 0);
Jd8::blitCK(95, 133, gfx, 0, INTERROGANT, 15, 15, 0);
}
constexpr SpritePhase VARIANT_0[] = {
{.start_i = 0, .end_i = 200, .render = v0WalkRight, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v0PullMapRight, .skippable = true},
{.start_i = 200, .end_i = 0, .render = v0PullMapRight, .skippable = true}, // guarda el mapa (reprodueix inversament)
{.start_i = 200, .end_i = 80, .render = v0WalkLeftTo80, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v0PullMapLeft, .skippable = true},
{.start_i = 300, .end_i = 95, .render = v0MomiaLeft, .skippable = true},
{.start_i = 0, .end_i = 50, .render = v0Turn, .skippable = true},
{.start_i = 0, .end_i = 49, .render = v0Jump1, .skippable = true},
{.start_i = 50, .end_i = 99, .render = v0Jump2, .skippable = true},
{.start_i = 80, .end_i = 0, .render = v0WalkFinal, .skippable = true},
{.start_i = 0, .end_i = 150, .render = v0Final, .skippable = true},
};
// =========================================================================
// Variant 1 — Creu / Pedra
// =========================================================================
void v1WalkRight(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
Jd8::blitCK(i, 150, gfx, FR1[(i / 5) % 13], 0, 15, 15, 255);
}
void v1PullMap(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
Jd8::blitCK(200, 150, gfx, FR3[std::min(i / 5, 10)], 30, 15, 15, 255);
}
void v1Interrogant(const Uint8* gfx, int /*i*/) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
Jd8::blitCK(200, 134, gfx, 0, INTERROGANT, 15, 15, 255);
Jd8::blitCK(200, 150, gfx, FR3[10], 30, 15, 15, 255);
}
void v1DropMap(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
const int IDX = std::min(i / 5, 28);
// FR7 té 29 frames dividits en dos grups: paper (idx 0..13, src_y=75)
// i sombra (idx 14..28, src_y=105). El vell feia una branca al bucle.
if (IDX <= 13) {
Jd8::blitCK(200, 150, gfx, FR7[IDX], 75, 15, 15, 255);
} else {
Jd8::blitCK(200, 150, gfx, FR7[IDX], 105, 15, 15, 255);
}
}
void v1StoneFall(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
Jd8::blitCK(200, 150, gfx, FR7[28], 105, 15, 15, 255);
Jd8::blitCK(200, i * 2, gfx, FR8[0], 75, 15, 15, 255);
}
void v1StoneBreak(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
Jd8::blitCK(200, 150, gfx, FR8[i / 10], 75, 15, 15, 255);
}
void v1Final(const Uint8* gfx, int /*i*/) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(200, 155, gfx, 0, CREU, 15, 15, 255);
Jd8::blitCK(200, 150, gfx, FR8[1], 75, 15, 15, 255);
Jd8::blitCK(185, 150, gfx, FR8[2], 75, 15, 15, 255);
Jd8::blitCK(215, 150, gfx, FR8[3], 75, 15, 15, 255);
}
constexpr SpritePhase VARIANT_1[] = {
{.start_i = 0, .end_i = 200, .render = v1WalkRight, .skippable = true},
{.start_i = 0, .end_i = 300, .render = v1PullMap, .skippable = true},
{.start_i = 0, .end_i = 100, .render = v1Interrogant, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v1DropMap, .skippable = true},
{.start_i = 0, .end_i = 75, .render = v1StoneFall, .skippable = true},
{.start_i = 0, .end_i = 19, .render = v1StoneBreak, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v1Final, .skippable = true},
};
// =========================================================================
// Variant 2 — Ball de carnaval
// =========================================================================
void v2Approach(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(i, 150, gfx, FR1[(i / 5) % 13], 0, 15, 15, 255);
Jd8::blitCK(304 - i, 150, gfx, FR6[(i / 10) % 8], 60, 15, 15, 255);
}
void v2Still(const Uint8* gfx, int /*i*/) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(145, 150, gfx, FR1[1], 0, 15, 15, 255);
Jd8::blitCK(160, 150, gfx, FR6[1], 60, 15, 15, 255);
}
void v2Horn(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(125, 150, gfx, FR11[(i / 10) % 2], 90, 15, 15, 255);
Jd8::blitCK(145, 150, gfx, FR1[1], 0, 15, 15, 255);
Jd8::blitCK(160, 150, gfx, FR6[1], 60, 15, 15, 255);
}
void v2Ball(const Uint8* gfx, int i) {
Jd8::clearScreen(0);
drawWordmark(gfx);
Jd8::blitCK(145, 150, gfx, FR9[(i / 10) % 16], 120, 15, 15, 255);
Jd8::blitCK(160, 150, gfx, FR10[(i / 10) % 16], 135, 15, 15, 255);
Jd8::blitCK(125, 150, gfx, FR11[((i / 5) % 4) + 2], 90, 15, 15, 255);
}
constexpr SpritePhase VARIANT_2[] = {
{.start_i = 0, .end_i = 145, .render = v2Approach, .skippable = true},
{.start_i = 0, .end_i = 100, .render = v2Still, .skippable = true},
{.start_i = 0, .end_i = 50, .render = v2Horn, .skippable = true},
{.start_i = 0, .end_i = 800, .render = v2Ball, .skippable = true},
};
// =========================================================================
// Dispatch per variant
// =========================================================================
auto variantTable(int variant) -> const SpritePhase* {
switch (variant) {
case 0:
return VARIANT_0;
case 1:
return VARIANT_1;
case 2:
return VARIANT_2;
default:
return VARIANT_0;
}
}
auto variantLength(int variant) -> int {
switch (variant) {
case 0:
return sizeof(VARIANT_0) / sizeof(VARIANT_0[0]);
case 1:
return sizeof(VARIANT_1) / sizeof(VARIANT_1[0]);
case 2:
return sizeof(VARIANT_2) / sizeof(VARIANT_2[0]);
default:
return 0;
}
}
auto phaseStepCount(const SpritePhase& p) -> int {
return std::abs(p.end_i - p.start_i) + 1;
}
auto phaseCurrentI(const SpritePhase& p, int step) -> int {
return p.end_i >= p.start_i ? p.start_i + step : p.start_i - step;
}
} // namespace
namespace scenes {
IntroSpritesScene::IntroSpritesScene(SurfaceHandle&& gfx)
: gfx_(std::move(gfx)) {}
void IntroSpritesScene::onEnter() {
// El vell doIntroSprites feia `rand() % 3` al principi. El seed ve
// establert per `srand(time(0))` al boot del joc (info.cpp / main),
// així que la variant canvia entre execucions.
variant_ = std::rand() % 3;
phase_ = 0;
phase_step_ = 0;
step_acc_ms_ = 0;
done_ = false;
// Renderitzem ja el primer frame (step 0 de la primera fase) perquè
// el Jd8::flip del mini-loop del fiber el pinte al primer cicle.
const SpritePhase* phases = variantTable(variant_);
phases[0].render(gfx_.get(), phaseCurrentI(phases[0], 0));
}
void IntroSpritesScene::tick(int delta_ms) {
if (done_) {
return;
}
const SpritePhase* phases = variantTable(variant_);
const int NUM_PHASES = variantLength(variant_);
// Skip per tecla. Durant la fase marcada com a no skippable (només
// v0Final al vell codi) s'ignora — preserva la semàntica del vell
// bucle final de la variant 0 que no cridava wait_frame_or_skip.
if (phases[phase_].skippable && JI_AnyKey()) {
done_ = true;
return;
}
step_acc_ms_ += delta_ms;
while (step_acc_ms_ >= TICK_MS && !done_) {
step_acc_ms_ -= TICK_MS;
++phase_step_;
if (phase_step_ >= phaseStepCount(phases[phase_])) {
++phase_;
phase_step_ = 0;
if (phase_ >= NUM_PHASES) {
done_ = true;
return;
}
}
}
phases[phase_].render(gfx_.get(), phaseCurrentI(phases[phase_], phase_step_));
}
} // namespace scenes