Files
aee/source/scenes/secreta_scene.cpp
2026-04-18 11:41:34 +02:00

206 lines
7.4 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/secreta_scene.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include "core/audio/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/secreta.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() {
Audio::get()->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