From 6063b1c6068e6e996996e4a9c7fe3d4b90ac148a Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 16 Apr 2026 00:13:02 +0200 Subject: [PATCH] =?UTF-8?q?step=207:=20secreta=5Fscene=20amb=20swap=20de?= =?UTF-8?q?=20tomba1=E2=86=92tomba2=20i=20red=20pulse=20animat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + source/core/system/director.cpp | 4 +- source/game/defaults.hpp | 1 + source/game/modulesequence.cpp | 91 +------------- source/game/modulesequence.hpp | 2 +- source/game/options.cpp | 3 + source/game/options.hpp | 1 + source/scenes/secreta_scene.cpp | 205 ++++++++++++++++++++++++++++++++ source/scenes/secreta_scene.hpp | 66 ++++++++++ 9 files changed, 283 insertions(+), 91 deletions(-) create mode 100644 source/scenes/secreta_scene.cpp create mode 100644 source/scenes/secreta_scene.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ebf5f95..9eb2972 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set(APP_SOURCES source/scenes/intro_new_logo_scene.cpp source/scenes/slides_scene.cpp source/scenes/credits_scene.cpp + source/scenes/secreta_scene.cpp # Game source/game/options.cpp diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 7b094ae..9148239 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -27,6 +27,7 @@ #include "scenes/mort_scene.hpp" #include "scenes/scene.hpp" #include "scenes/scene_registry.hpp" +#include "scenes/secreta_scene.hpp" #include "scenes/slides_scene.hpp" // Cheats del joc original — declarats a jinput.cpp @@ -43,7 +44,7 @@ namespace { void gameFiberEntry() { info::ctx.num_habitacio = Options::game.habitacio_inicial; info::ctx.num_piramide = Options::game.piramide_inicial; - info::ctx.diners = 0; + info::ctx.diners = Options::game.diners_inicial; info::ctx.diamants = Options::game.diamants_inicial; info::ctx.vida = Options::game.vides; info::ctx.momies = 0; @@ -130,6 +131,7 @@ void Director::init() { // l'usuari no té prou diners per a la Secreta) registry.registerScene(1, [] { return std::make_unique(); }); registry.registerScene(7, [] { return std::make_unique(); }); + registry.registerScene(6, [] { return std::make_unique(); }); registry.registerScene(8, [] { return std::make_unique(); }); // IntroNewLogoScene només es registra quan `use_new_logo` està actiu; // si no, la factory retorna nullptr i el gameFiberEntry cau al vell diff --git a/source/game/defaults.hpp b/source/game/defaults.hpp index ef9e588..4edbbc5 100644 --- a/source/game/defaults.hpp +++ b/source/game/defaults.hpp @@ -58,6 +58,7 @@ namespace Defaults::Game { constexpr int PIRAMIDE_INICIAL = 255; constexpr int VIDES = 5; constexpr int DIAMANTS_INICIAL = 0; + constexpr int DINERS_INICIAL = 0; constexpr bool USE_NEW_LOGO = true; constexpr bool SHOW_TITLE_CREDITS = true; } // namespace Defaults::Game diff --git a/source/game/modulesequence.cpp b/source/game/modulesequence.cpp index 252576b..33355a4 100644 --- a/source/game/modulesequence.cpp +++ b/source/game/modulesequence.cpp @@ -41,9 +41,7 @@ int ModuleSequence::Go() { // case 0 (Menú) → migrat a scenes::MenuScene. // case 1 i 7 (Slides) → migrat a scenes::SlidesScene. // case 2..5 (Pre-piràmide) → migrat a scenes::BannerScene. - case 6: // Pre-Secreta - doSecreta(); - break; + // case 6 (Pre-Secreta) → migrat a scenes::SecretaScene. // case 8 (Credits) → migrat a scenes::CreditsScene. // case 100 (Mort) → migrat a scenes::MortScene, dispatch via SceneRegistry. } @@ -822,92 +820,7 @@ void ModuleSequence::doIntroSprites(JD8_Surface gfx) { // doBanner() — migrat a scenes::BannerScene (source/scenes/banner_scene.cpp) -void ModuleSequence::doSecreta() { - play_music("00000002.ogg"); - JG_SetUpdateTicks(20); - JD8_FadeOut(); - JD8_Surface gfx = JD8_LoadSurface("tomba1.gif"); - JD8_Palette pal_aux = JD8_LoadPalette("tomba1.gif"); - JD8_Palette pal = (JD8_Palette)malloc(768); - memcpy(pal, pal_aux, 768); - JD8_ClearScreen(255); - JD8_SetScreenPalette(pal); - - bool exit = false; - int step = 0; - contador = 1; - while (!exit && !JG_Quitting()) { - if (JG_ShouldUpdate()) { - JI_Update(); - - if (JI_AnyKey()) { - exit = true; - } - - switch (step) { - case 0: - JD8_Blit(70, 60, gfx, 0, contador, 178, 70); // Put_Sprite(from, where, 0 + (320 * i), 178, 70, 70, 60); - JD8_BlitCK(70, 60, gfx, 178, contador >> 1, 142, 70, 255); // Put_Sprite(from, where, 178 + (320 * (i div 2)), 142, 70, 70, 60); - JD8_Flip(); - contador++; - if (contador == 128) step++; - break; - case 1: - case 4: - case 7: - case 9: - contador--; - if (contador == 0) step++; - break; - case 2: - JD8_ClearScreen(255); - JD8_Flip(); - gfx = JD8_LoadSurface("tomba2.gif"); - pal_aux = JD8_LoadPalette("tomba2.gif"); - memcpy(pal, pal_aux, 768); - JD8_SetScreenPalette(pal); - step++; - break; - case 3: - JD8_Blit(55, 53, gfx, 0, 158 - contador, 211, contador); // Put_Sprite(from, where, 0 + ((158 - i) * 320), 211, i, 55, 53); - JD8_Flip(); - contador++; - if (contador == 94) step++; - break; - case 5: - JD8_ClearScreen(0); - JD8_Flip(); - JD8_SetPaletteColor(254, 12, 11, 11); - JD8_SetPaletteColor(253, 12, 11, 11); - step++; - break; - case 6: - JD8_Blit(80, 68, gfx, 160 - (contador * 2), 0, contador * 2, 64); // Put_Sprite(from, where, 160 - (i * 2), (i * 2), 64, 80, 68); - JD8_Flip(); - contador++; - if (contador == 80) step++; - break; - case 8: - JD8_SetPaletteColor(254, contador + 12, 11, 11); - JD8_SetPaletteColor(253, (contador + 12) >> 1, 11, 11); - JD8_Flip(); - contador++; - if (contador == 51) step++; - break; - case 10: - JD8_FadeOut(); - memcpy(pal, pal_aux, 768); - JD8_ClearScreen(255); - JA_FadeOutMusic(250); - exit = true; - break; - } - } - } - - JD8_FreeSurface(gfx); - free(pal_aux); -} +// doSecreta() — migrat a scenes::SecretaScene (source/scenes/secreta_scene.cpp) // doCredits() — migrat a scenes::CreditsScene (source/scenes/credits_scene.cpp) diff --git a/source/game/modulesequence.hpp b/source/game/modulesequence.hpp index a55d2d3..2b059b6 100644 --- a/source/game/modulesequence.hpp +++ b/source/game/modulesequence.hpp @@ -23,7 +23,7 @@ class ModuleSequence { // doMenu() → migrat a scenes::MenuScene // doSlides() → migrat a scenes::SlidesScene // doBanner() → migrat a scenes::BannerScene - void doSecreta(); + // doSecreta() → migrat a scenes::SecretaScene // doCredits() → migrat a scenes::CreditsScene // doMort() → migrat a scenes::MortScene diff --git a/source/game/options.cpp b/source/game/options.cpp index 6c38165..e86e849 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -146,6 +146,8 @@ namespace Options { game.vides = node["vides"].get_value(); if (node.contains("diamants_inicial")) game.diamants_inicial = node["diamants_inicial"].get_value(); + if (node.contains("diners_inicial")) + game.diners_inicial = node["diners_inicial"].get_value(); if (node.contains("use_new_logo")) game.use_new_logo = node["use_new_logo"].get_value(); if (node.contains("show_title_credits")) @@ -281,6 +283,7 @@ namespace Options { file << " piramide_inicial: " << game.piramide_inicial << "\n"; file << " vides: " << game.vides << "\n"; file << " diamants_inicial: " << game.diamants_inicial << "\n"; + file << " diners_inicial: " << game.diners_inicial << "\n"; file << " use_new_logo: " << (game.use_new_logo ? "true" : "false") << "\n"; file << " show_title_credits: " << (game.show_title_credits ? "true" : "false") << "\n"; file << "\n"; diff --git a/source/game/options.hpp b/source/game/options.hpp index e5cf5a7..82517bf 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -84,6 +84,7 @@ namespace Options { int piramide_inicial{Defaults::Game::PIRAMIDE_INICIAL}; int vides{Defaults::Game::VIDES}; int diamants_inicial{Defaults::Game::DIAMANTS_INICIAL}; + int diners_inicial{Defaults::Game::DINERS_INICIAL}; bool use_new_logo{Defaults::Game::USE_NEW_LOGO}; bool show_title_credits{Defaults::Game::SHOW_TITLE_CREDITS}; }; diff --git a/source/scenes/secreta_scene.cpp b/source/scenes/secreta_scene.cpp new file mode 100644 index 0000000..dda784e --- /dev/null +++ b/source/scenes/secreta_scene.cpp @@ -0,0 +1,205 @@ +#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 diff --git a/source/scenes/secreta_scene.hpp b/source/scenes/secreta_scene.hpp new file mode 100644 index 0000000..2ba8ff1 --- /dev/null +++ b/source/scenes/secreta_scene.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include "core/jail/jdraw8.hpp" +#include "scenes/palette_fade.hpp" +#include "scenes/scene.hpp" +#include "scenes/surface_handle.hpp" + +namespace scenes { + +// Pre-Secreta. Reemplaça `ModuleSequence::doSecreta()`. +// +// Flux: +// 1. Arranca música "00000002.ogg" i fa fade-out de la paleta anterior. +// 2. Carrega tomba1.gif + paleta i pinta un scroll vertical doble +// (dos blits solapats, un a velocitat meitat que l'altre) durant +// ~2.5 s + ~2.5 s de pausa. +// 3. Swap a tomba2.gif + reset de paleta, scroll vertical del segon +// asset (~1.9 s + ~1.9 s de pausa). +// 4. ClearScreen a 0, set colors 253/254 a vermell fosc (12,11,11) +// i pinta un revelat horitzontal (~1.6 s + ~1.6 s de pausa). +// 5. "Red pulse": anima els colors 253/254 incrementant el canal R +// de 12 a 62 durant ~1 s (+ ~1 s de pausa). +// 6. FadeOut + JA_FadeOutMusic(250). +// 7. Retorna nextState=0 per entrar al ModuleGame amb num_piramide=6. +// +// Registrada al SceneRegistry amb state_key = 6. +class SecretaScene : public Scene { + public: + SecretaScene() = default; + ~SecretaScene() override; + + void onEnter() override; + void tick(int delta_ms) override; + bool done() const override { return phase_ == Phase::Done; } + int nextState() const override { return 0; } + + private: + enum class Phase { + InitialFadeOut, + Tomba1ScrollIn, + Tomba1Hold, + Tomba2ScrollIn, + Tomba2Hold, + Tomba2Reveal, + Tomba2RevealHold, + RedPulse, + RedPulseHold, + FinalFadeOut, + Done, + }; + + void swapToTomba2(); + void beginRedPulseSetup(); + void beginFinalFade(); + + SurfaceHandle gfx_; + JD8_Palette pal_aux_{nullptr}; + JD8_Palette pal_active_{nullptr}; // propietat transferida a main_palette + PaletteFade fade_; + + Phase phase_{Phase::InitialFadeOut}; + int phase_acc_ms_{0}; + bool skip_triggered_{false}; +}; + +} // namespace scenes