diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cb5a6f..bd22e43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ set(APP_SOURCES source/scenes/scene_utils.cpp source/scenes/mort_scene.cpp source/scenes/banner_scene.cpp + source/scenes/menu_scene.cpp # Game source/game/options.cpp diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 737cbbc..971c2ea 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -21,6 +21,7 @@ #include "game/modulesequence.hpp" #include "game/options.hpp" #include "scenes/banner_scene.hpp" +#include "scenes/menu_scene.hpp" #include "scenes/mort_scene.hpp" #include "scenes/scene.hpp" #include "scenes/scene_registry.hpp" @@ -63,6 +64,7 @@ void gameFiberEntry() { scene->onEnter(); Uint32 last = SDL_GetTicks(); while (!scene->done() && !JG_Quitting()) { + JI_Update(); // refresca key_pressed/any_key per a les escenes const Uint32 now = SDL_GetTicks(); scene->tick(static_cast(now - last)); last = now; @@ -102,6 +104,7 @@ void Director::init() { // caient, el fallback al switch legacy de gameFiberEntry deixa de // rebre aquests states. auto& registry = scenes::SceneRegistry::instance(); + registry.registerScene(0, [] { return std::make_unique(); }); registry.registerScene(100, [] { return std::make_unique(); }); // BannerScene cobreix les piràmides 2..5 (el vell doBanner decideix // pel switch intern llegint info::ctx.num_piramide). diff --git a/source/game/modulesequence.cpp b/source/game/modulesequence.cpp index e59547c..d00ac9c 100644 --- a/source/game/modulesequence.cpp +++ b/source/game/modulesequence.cpp @@ -38,9 +38,7 @@ int ModuleSequence::Go() { case 255: // Intro doIntro(); break; - case 0: // Men� - doMenu(); - break; + // case 0 (Menú) → migrat a scenes::MenuScene. case 1: // Slides case 7: doSlides(); @@ -945,67 +943,7 @@ void ModuleSequence::doIntroNewLogo() { doIntroSprites(gfx); } -void ModuleSequence::doMenu() { - JG_SetUpdateTicks(20); - JD8_Surface fondo = JD8_LoadSurface("menu.gif"); - JD8_Surface gfx = JD8_LoadSurface("menu2.gif"); - JD8_Palette pal = JD8_LoadPalette("menu2.gif"); - - JD8_Blit(fondo); - JD8_BlitCK(100, 25, gfx, 0, 74, 124, 68, 255); // logo - JD8_BlitCK(130, 100, gfx, 0, 0, 80, 74, 255); - JD8_BlitCK(0, 150, gfx, 0, 150, 320, 50, 255); - JD8_FadeToPal(pal); - - contador = 0; - int palmeres = 0; - int horitzo = 0; - int camello = 0; - - JI_Update(); - while (!JI_AnyKey() && !JG_Quitting() && !JI_KeyPressed(SDL_SCANCODE_P)) { - JD8_Blit(0, 0, fondo, 0, 0, 320, 100); // fondo sol estatic - - JD8_BlitCK(horitzo, 100, fondo, 0, 100, 320 - horitzo, 100, 255); // fondo moviment - JD8_BlitCK(0, 100, fondo, 320 - horitzo, 100, horitzo, 100, 255); - - JD8_BlitCK(100, 25, gfx, 0, 74, 124, 68, 255); // logo - JD8_BlitCK(130, 100, gfx, camello * 80, 0, 80, 74, 255); // camello - - JD8_BlitCK(palmeres, 150, gfx, 0, 150, 320 - palmeres, 50, 255); // palemeres moviment - JD8_BlitCK(0, 150, gfx, 320 - palmeres, 150, palmeres, 50, 255); - - JD8_BlitCK(87, 167, gfx, 127, 124, 150, 24, 255); // jdes - JD8_BlitCK(303, 193, gfx, 305, 143, 15, 5, 255); // versio - - if (contador % 100 > 30) JD8_BlitCK(98, 130, gfx, 161, 92, 127, 9, 255); // pulsa tecla... - if ((contador % 100 > 30) && info::ctx.nou_personatge) JD8_BlitCK(68, 141, gfx, 128, 105, 189, 9, 255); // 'p' per a personatge nou... - - JD8_Flip(); - - if (JG_ShouldUpdate()) { - if (contador % 4 == 0) { - palmeres--; - if (palmeres < 0) palmeres = 319; - } - if (contador % 8 == 0) { - camello++; - if (camello == 4) camello = 0; - } - if (contador % 16 == 0) { - horitzo--; - if (horitzo < 0) horitzo = 319; - } - contador++; - JI_Update(); - } - } - info::ctx.pepe_activat = JI_KeyPressed(SDL_SCANCODE_P); - JI_DisableKeyboard(60); - JD8_FreeSurface(fondo); - JD8_FreeSurface(gfx); - free(pal); -} +// doMenu() — migrat a scenes::MenuScene (source/scenes/menu_scene.cpp) void ModuleSequence::doSlides() { JG_SetUpdateTicks(20); diff --git a/source/game/modulesequence.hpp b/source/game/modulesequence.hpp index 1d4cfce..e7dc29e 100644 --- a/source/game/modulesequence.hpp +++ b/source/game/modulesequence.hpp @@ -15,7 +15,7 @@ class ModuleSequence { void doIntro(); void doIntroNewLogo(); void doIntroSprites(Uint8* gfx); - void doMenu(); + // doMenu() → migrat a scenes::MenuScene void doSlides(); // doBanner() → migrat a scenes::BannerScene void doSecreta(); diff --git a/source/scenes/menu_scene.cpp b/source/scenes/menu_scene.cpp new file mode 100644 index 0000000..8a65cdd --- /dev/null +++ b/source/scenes/menu_scene.cpp @@ -0,0 +1,113 @@ +#include "scenes/menu_scene.hpp" + +#include + +#include "core/jail/jdraw8.hpp" +#include "core/jail/jinput.hpp" +#include "game/info.hpp" + +namespace scenes { + +void MenuScene::onEnter() { + fondo_ = SurfaceHandle("menu.gif"); + gfx_ = SurfaceHandle("menu2.gif"); + + // Pintat inicial (congelat durant el fade-in de paleta). El loop + // d'animació repintarà tot des de zero en el primer tick de Showing. + JD8_Blit(fondo_); + JD8_BlitCK(100, 25, gfx_, 0, 74, 124, 68, 255); // logo + JD8_BlitCK(130, 100, gfx_, 0, 0, 80, 74, 255); // camell (frame 0) + JD8_BlitCK(0, 150, gfx_, 0, 150, 320, 50, 255); // base "jdes" + + JD8_Palette pal = JD8_LoadPalette("menu2.gif"); + fade_.startFadeTo(pal); + std::free(pal); + + phase_ = Phase::FadingIn; +} + +void MenuScene::render() { + // Cel estàtic (els primers 100 pixels verticals) + JD8_Blit(0, 0, fondo_, 0, 0, 320, 100); + + // Fondo mòvil (horitzó) amb wrap a 320 + JD8_BlitCK(horitzo_, 100, fondo_, 0, 100, 320 - horitzo_, 100, 255); + JD8_BlitCK(0, 100, fondo_, 320 - horitzo_, 100, horitzo_, 100, 255); + + // Logo i camell animat + JD8_BlitCK(100, 25, gfx_, 0, 74, 124, 68, 255); + JD8_BlitCK(130, 100, gfx_, camello_.frame() * 80, 0, 80, 74, 255); + + // Palmeres mòvils amb wrap a 320 + JD8_BlitCK(palmeres_, 150, gfx_, 0, 150, 320 - palmeres_, 50, 255); + JD8_BlitCK(0, 150, gfx_, 320 - palmeres_, 150, palmeres_, 50, 255); + + // "jdes" estàtic (davant dels scrollers) i versió a la cantonada + JD8_BlitCK(87, 167, gfx_, 127, 124, 150, 24, 255); + JD8_BlitCK(303, 193, gfx_, 305, 143, 15, 5, 255); + + // "Polsa tecla" parpallejant. Al vell `contador % 100 > 30` amb + // updateTicks=20 ms, el cicle són 2000 ms amb un llindar de 600 ms: + // amagat els primers 600 ms, visible els següents 1400 ms. + const bool blink_on = (blink_ms_ % 2000) > 600; + if (blink_on) { + JD8_BlitCK(98, 130, gfx_, 161, 92, 127, 9, 255); + if (info::ctx.nou_personatge) { + JD8_BlitCK(68, 141, gfx_, 128, 105, 189, 9, 255); + } + } +} + +void MenuScene::tick(int delta_ms) { + switch (phase_) { + case Phase::FadingIn: + fade_.tick(delta_ms); + if (fade_.done()) phase_ = Phase::Showing; + break; + + case Phase::Showing: { + // Palmeres: 1 pixel cada 80 ms (= cada 4 ticks × 20 ms originals) + palmeres_acc_ms_ += delta_ms; + while (palmeres_acc_ms_ >= 80) { + palmeres_acc_ms_ -= 80; + if (--palmeres_ < 0) palmeres_ = 319; + } + + // Horitzó: 1 pixel cada 320 ms (= cada 16 ticks × 20 ms) + horitzo_acc_ms_ += delta_ms; + while (horitzo_acc_ms_ >= 320) { + horitzo_acc_ms_ -= 320; + if (--horitzo_ < 0) horitzo_ = 319; + } + + camello_.tick(delta_ms); + + blink_ms_ += delta_ms; + if (blink_ms_ >= 2000) blink_ms_ %= 2000; + + render(); + + // Qualsevol tecla tanca el menú. Llegim 'P' explícitament abans + // de reiniciar el flag de input perquè `info::ctx.pepe_activat` + // reflecteixca si l'usuari estava polsant P al moment d'eixir. + if (JI_AnyKey() || JI_KeyPressed(SDL_SCANCODE_P)) { + info::ctx.pepe_activat = JI_KeyPressed(SDL_SCANCODE_P); + JI_DisableKeyboard(60); + info::ctx.num_piramide = 1; + fade_.startFadeOut(); + phase_ = Phase::FadingOut; + } + break; + } + + case Phase::FadingOut: + fade_.tick(delta_ms); + if (fade_.done()) phase_ = Phase::Done; + break; + + case Phase::Done: + break; + } +} + +} // namespace scenes diff --git a/source/scenes/menu_scene.hpp b/source/scenes/menu_scene.hpp new file mode 100644 index 0000000..1ff4748 --- /dev/null +++ b/source/scenes/menu_scene.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "scenes/frame_animator.hpp" +#include "scenes/palette_fade.hpp" +#include "scenes/scene.hpp" +#include "scenes/surface_handle.hpp" + +namespace scenes { + +// Menú del títol. Reemplaça `ModuleSequence::doMenu()`. +// +// Flux: +// 1. Carrega menu.gif (fondo) i menu2.gif (sprites) + paleta. +// 2. Pintat inicial estàtic (fondo, logo, camell frame 0, base "jdes"), +// fade-in de paleta. +// 3. Loop d'animació: escroll parallax de horitzó (cada 320 ms) i +// palmeres (cada 80 ms), cicle del camell (4 frames × 160 ms), +// i el text "polsa tecla" parpallejant cada 2 s (visible 1.4 s, +// amagat 0.6 s, igual que el `contador % 100 > 30` original). +// 4. Quan l'usuari polsa qualsevol tecla — o 'P' per a activar Pepe — +// llegim `info::ctx.pepe_activat`, disparem fade-out i marquem +// num_piramide=1 (vas a doSlides). +// +// Registrat al SceneRegistry amb state_key = 0. +class MenuScene : public Scene { + public: + void onEnter() override; + void tick(int delta_ms) override; + bool done() const override { return phase_ == Phase::Done; } + int nextState() const override { return 1; } + + private: + enum class Phase { FadingIn, + Showing, + FadingOut, + Done }; + + void render(); + + SurfaceHandle fondo_; + SurfaceHandle gfx_; + PaletteFade fade_; + FrameAnimator camello_{4, 160, true}; + + Phase phase_{Phase::FadingIn}; + + // Scrollers horizontals. Mouen 1 pixel per pas. + int palmeres_{0}; + int horitzo_{0}; + int palmeres_acc_ms_{0}; + int horitzo_acc_ms_{0}; + + // Acumulador per al parpalleig del text "polsa tecla". + int blink_ms_{0}; +}; + +} // namespace scenes