206 lines
6.7 KiB
C++
206 lines
6.7 KiB
C++
#include "scenes/secreta_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"
|
||
|
||
namespace {
|
||
|
||
constexpr int TICK_MS = 20; // JG_SetUpdateTicks(20) del vell doSecreta
|
||
|
||
// Durades per fase, derivades dels contador-thresholds del vell:
|
||
// tomba1 scroll: 127 passos (contador 1→128) × 20ms
|
||
// tomba1 hold: 128 passos (contador 128→0) × 20ms
|
||
// tomba2 scroll: 94 passos × 20ms
|
||
// tomba2 hold: 94 passos × 20ms
|
||
// reveal horit: 80 passos × 20ms
|
||
// reveal hold: 80 passos × 20ms
|
||
// red pulse: 51 passos × 20ms
|
||
// red pulse hold: 51 passos × 20ms
|
||
constexpr int TOMBA1_SCROLL_MS = 127 * TICK_MS;
|
||
constexpr int TOMBA1_HOLD_MS = 128 * TICK_MS;
|
||
constexpr int TOMBA2_SCROLL_MS = 94 * TICK_MS;
|
||
constexpr int TOMBA2_HOLD_MS = 94 * TICK_MS;
|
||
constexpr int TOMBA2_REVEAL_MS = 80 * TICK_MS;
|
||
constexpr int TOMBA2_REVEAL_HOLD_MS = 80 * TICK_MS;
|
||
constexpr int RED_PULSE_MS = 51 * TICK_MS;
|
||
constexpr int RED_PULSE_HOLD_MS = 51 * TICK_MS;
|
||
|
||
} // namespace
|
||
|
||
namespace scenes {
|
||
|
||
SecretaScene::~SecretaScene() {
|
||
if (pal_aux_) std::free(pal_aux_);
|
||
// pal_active_ NO s'allibera: propietat de main_palette via SetScreenPalette.
|
||
}
|
||
|
||
void SecretaScene::onEnter() {
|
||
playMusic("music/00000002.ogg");
|
||
|
||
// Fade-out de la paleta anterior. Els assets es carreguen ja
|
||
// però no fem SetScreenPalette fins que acabe el fade — així
|
||
// el fade opera sobre la paleta del mòdul anterior.
|
||
fade_.startFadeOut();
|
||
|
||
gfx_ = SurfaceHandle("gfx/tomba1.gif");
|
||
pal_aux_ = JD8_LoadPalette("gfx/tomba1.gif");
|
||
pal_active_ = static_cast<JD8_Palette>(std::malloc(768));
|
||
std::memcpy(pal_active_, pal_aux_, 768);
|
||
|
||
phase_ = Phase::InitialFadeOut;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
|
||
void SecretaScene::swapToTomba2() {
|
||
JD8_ClearScreen(255);
|
||
gfx_.reset("gfx/tomba2.gif");
|
||
|
||
std::free(pal_aux_);
|
||
pal_aux_ = JD8_LoadPalette("gfx/tomba2.gif");
|
||
// pal_active_ continua sent el mateix buffer: només actualitzem
|
||
// el seu contingut. main_palette ja apunta ací.
|
||
std::memcpy(pal_active_, pal_aux_, 768);
|
||
}
|
||
|
||
void SecretaScene::beginRedPulseSetup() {
|
||
JD8_ClearScreen(0);
|
||
JD8_SetPaletteColor(254, 12, 11, 11);
|
||
JD8_SetPaletteColor(253, 12, 11, 11);
|
||
}
|
||
|
||
void SecretaScene::beginFinalFade() {
|
||
JA_FadeOutMusic(250);
|
||
fade_.startFadeOut();
|
||
phase_ = Phase::FinalFadeOut;
|
||
}
|
||
|
||
void SecretaScene::tick(int delta_ms) {
|
||
// Skip per tecla (després del fade inicial, no mentre). Salta
|
||
// directament al FinalFadeOut. Mateix patró que el vell, on
|
||
// qualsevol tecla sortia del loop.
|
||
if (!skip_triggered_ && phase_ != Phase::InitialFadeOut && JI_AnyKey()) {
|
||
skip_triggered_ = true;
|
||
beginFinalFade();
|
||
}
|
||
|
||
switch (phase_) {
|
||
case Phase::InitialFadeOut:
|
||
fade_.tick(delta_ms);
|
||
if (fade_.done()) {
|
||
// Ara main_palette (la vella) té tots els canals a 0.
|
||
// SetScreenPalette allibera la vella i adopta pal_active_
|
||
// — des d'ara main_palette == pal_active_, així que les
|
||
// futures escriptures a pal_active_ afecten la pantalla.
|
||
JD8_SetScreenPalette(pal_active_);
|
||
JD8_ClearScreen(255);
|
||
phase_ = Phase::Tomba1ScrollIn;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
|
||
case Phase::Tomba1ScrollIn: {
|
||
phase_acc_ms_ += delta_ms;
|
||
const int contador = std::min(128, phase_acc_ms_ / TICK_MS + 1);
|
||
// Dos blits solapats: el primer avança a velocitat completa,
|
||
// el segon (contingut de la dreta del src) a meitat (contador>>1).
|
||
JD8_Blit(70, 60, gfx_, 0, contador, 178, 70);
|
||
JD8_BlitCK(70, 60, gfx_, 178, contador >> 1, 142, 70, 255);
|
||
if (phase_acc_ms_ >= TOMBA1_SCROLL_MS) {
|
||
phase_ = Phase::Tomba1Hold;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case Phase::Tomba1Hold:
|
||
phase_acc_ms_ += delta_ms;
|
||
if (phase_acc_ms_ >= TOMBA1_HOLD_MS) {
|
||
swapToTomba2();
|
||
phase_ = Phase::Tomba2ScrollIn;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
|
||
case Phase::Tomba2ScrollIn: {
|
||
phase_acc_ms_ += delta_ms;
|
||
const int contador = std::min(94, phase_acc_ms_ / TICK_MS + 1);
|
||
JD8_Blit(55, 53, gfx_, 0, 158 - contador, 211, contador);
|
||
if (phase_acc_ms_ >= TOMBA2_SCROLL_MS) {
|
||
phase_ = Phase::Tomba2Hold;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case Phase::Tomba2Hold:
|
||
phase_acc_ms_ += delta_ms;
|
||
if (phase_acc_ms_ >= TOMBA2_HOLD_MS) {
|
||
beginRedPulseSetup();
|
||
phase_ = Phase::Tomba2Reveal;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
|
||
case Phase::Tomba2Reveal: {
|
||
phase_acc_ms_ += delta_ms;
|
||
const int contador = std::min(80, phase_acc_ms_ / TICK_MS + 1);
|
||
// Revelat horitzontal simètric: l'amplada creix 2px per tick
|
||
// i el src_x es desplaça a l'esquerra el mateix.
|
||
JD8_Blit(80, 68, gfx_, 160 - (contador * 2), 0, contador * 2, 64);
|
||
if (phase_acc_ms_ >= TOMBA2_REVEAL_MS) {
|
||
phase_ = Phase::Tomba2RevealHold;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case Phase::Tomba2RevealHold:
|
||
phase_acc_ms_ += delta_ms;
|
||
if (phase_acc_ms_ >= TOMBA2_REVEAL_HOLD_MS) {
|
||
phase_ = Phase::RedPulse;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
|
||
case Phase::RedPulse: {
|
||
phase_acc_ms_ += delta_ms;
|
||
const int contador = std::min(51, phase_acc_ms_ / TICK_MS);
|
||
// Anima el canal R dels índexs 254 i 253 (aquest a la meitat
|
||
// de brillantor). Va de (12,11,11) fins a (63,11,11) / (31,11,11).
|
||
JD8_SetPaletteColor(254, contador + 12, 11, 11);
|
||
JD8_SetPaletteColor(253, (contador + 12) >> 1, 11, 11);
|
||
if (phase_acc_ms_ >= RED_PULSE_MS) {
|
||
phase_ = Phase::RedPulseHold;
|
||
phase_acc_ms_ = 0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case Phase::RedPulseHold:
|
||
phase_acc_ms_ += delta_ms;
|
||
if (phase_acc_ms_ >= RED_PULSE_HOLD_MS) {
|
||
beginFinalFade();
|
||
}
|
||
break;
|
||
|
||
case Phase::FinalFadeOut:
|
||
fade_.tick(delta_ms);
|
||
if (fade_.done()) {
|
||
phase_ = Phase::Done;
|
||
}
|
||
break;
|
||
|
||
case Phase::Done:
|
||
break;
|
||
}
|
||
}
|
||
|
||
} // namespace scenes
|