#include "scenes/secreta_scene.hpp" #include #include #include #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("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("tomba1.gif"); pal_aux_ = JD8_LoadPalette("tomba1.gif"); pal_active_ = static_cast(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("tomba2.gif"); std::free(pal_aux_); pal_aux_ = JD8_LoadPalette("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