88 lines
3.8 KiB
C++
88 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <optional>
|
|
|
|
#include "core/rendering/effects/heat_haze.hpp"
|
|
#include "core/rendering/transitions/fade_to_black.hpp"
|
|
#include "core/rendering/transitions/mosaic.hpp"
|
|
#include "core/scene/scene.hpp"
|
|
#include "core/ui/ui_config.hpp"
|
|
#include "game/debug_config.hpp"
|
|
|
|
class Surface;
|
|
class Text;
|
|
|
|
// Escena TITLE provisional. Pinta dos blocs de text sobre un fons gairebé
|
|
// negre i un prompt "press start" intermitent fins que apareix l'artwork
|
|
// real. Tota la cosmètica (colors, durada, ritme del parpadeig) viu a
|
|
// data/config/ui.yaml secció `title:` per poder afinar-la sense recompilar.
|
|
class TitleScene : public Scene {
|
|
public:
|
|
// Fases de l'escena.
|
|
// - IDLE: animació atràctil. Només P1 START o P2 START hi fan
|
|
// transició; un timeout `title.duration_ms` porta a ATTRACT_OUT.
|
|
// - STARTING: comptador fix entre la primera pulsació de START i
|
|
// la transició cap a INTRO. Si l'altre jugador prem START dins
|
|
// d'aquesta finestra, el comptador es reseteja amb
|
|
// `pending_players_=BOTH`. Visual: mosaic dissolve + fade-out
|
|
// de música.
|
|
// - ATTRACT_OUT: fase de sortida silenciosa cap a LOGO quan
|
|
// expira el timeout sense pulsacions. Visual: fade-to-black net.
|
|
// NO toca la música ni canvia la freqüència del parpadeig.
|
|
enum class Phase : std::uint8_t { IDLE,
|
|
STARTING,
|
|
ATTRACT_OUT };
|
|
|
|
TitleScene(std::shared_ptr<Text> condensed_font, std::shared_ptr<Text> copyright_font, std::shared_ptr<Surface> background, std::shared_ptr<Surface> game_logo, std::shared_ptr<Surface> mini_logo);
|
|
~TitleScene() override;
|
|
|
|
void update(int delta_ms) override;
|
|
void render() override;
|
|
|
|
private:
|
|
void tickIdle(int delta_ms, bool p1_start, bool p2_start);
|
|
void tickStarting(int delta_ms, bool p1_start, bool p2_start);
|
|
void tickAttract(int delta_ms);
|
|
void enterStarting(DebugConfig::StartingPlayers players);
|
|
void enterAttract();
|
|
void advanceToIntro(DebugConfig::StartingPlayers players);
|
|
void advanceToLogo();
|
|
|
|
// Helpers de `render()` extrets per evitar que la funció gran acumuli
|
|
// massa branques. Cadascun pinta un bloc independent del frame.
|
|
void renderBackground();
|
|
void renderGameLogo(const UiConfig::TitleIntro& intro);
|
|
void renderPressStart(const UiConfig::TitleIntro& intro);
|
|
void renderFooter(const UiConfig::TitleIntro& intro);
|
|
void renderTransitions();
|
|
|
|
std::shared_ptr<Text> condensed_font_;
|
|
std::shared_ptr<Text> copyright_font_;
|
|
std::shared_ptr<Surface> background_;
|
|
std::shared_ptr<Surface> game_logo_;
|
|
std::shared_ptr<Surface> mini_logo_;
|
|
int timer_ms_;
|
|
int blink_elapsed_ms_{0};
|
|
// Rellotge de la seqüència d'intro (vegeu `title.intro` a ui.yaml).
|
|
// Avança sempre amb `delta_ms`. La visibilitat i el slide de cada
|
|
// element es deriven d'ell a `render()` — no cal cap altre estat.
|
|
int intro_elapsed_ms_{0};
|
|
// Detector de l'instant en què `intro_elapsed_ms_` creua per primera
|
|
// vegada `press_start_at_ms`: en aquell frame es reseteja
|
|
// `blink_elapsed_ms_` perquè el text aparegui en estat visible.
|
|
bool press_start_armed_{false};
|
|
std::optional<Effects::HeatHaze> heat_haze_;
|
|
Phase phase_{Phase::IDLE};
|
|
int starting_timer_ms_{0};
|
|
DebugConfig::StartingPlayers pending_players_{DebugConfig::StartingPlayers::BOTH};
|
|
std::optional<Transitions::Mosaic> fade_out_;
|
|
std::optional<Transitions::FadeToBlack> attract_fade_;
|
|
// Fade-in inicial: capa negra que s'esvaeix cap a la imatge de fons
|
|
// durant els primers `title.intro.fade_in_ms` ms. Sempre activa al
|
|
// ctor; un cop finished, `render()` pinta alpha=0 (no-op) la resta
|
|
// del cicle de l'escena.
|
|
Transitions::FadeToBlack intro_fade_;
|
|
};
|