refactor(scenes): flux centralitzat amb SceneId+ExitRequest; TitleScene attract → LOGO amb fade-to-black
This commit is contained in:
1 parent
25ce524470
commit
b6d6ee471c
17 files changed
+368
-121
No files matched your search
@@ -3,6 +3,7 @@
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <any>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "core/audio/audio.hpp"
|
||||
#include "core/audio/sound_effects_config.hpp"
|
||||
@@ -196,6 +198,7 @@ Director::Director() {
|
||||
auto placeholder_font_surface = Resource::Cache::get().getSurface(PLACEHOLDER_FONT_GIF_KEY);
|
||||
auto placeholder_text = std::make_shared<Text>(placeholder_font_surface, UiConfig::get().placeholderFont().fnt);
|
||||
placeholder_text->setDefaultKerning(UiConfig::get().placeholderFont().kerning);
|
||||
placeholder_text_ = placeholder_text;
|
||||
|
||||
// Font del menú de servei (F9). El títol va amb la variant `display`
|
||||
// i els items amb `condensed-display` (més compacte perquè caben més
|
||||
@@ -210,6 +213,7 @@ Director::Director() {
|
||||
};
|
||||
auto menu_title_text = load_menu_font(UiConfig::get().menuTitleFont());
|
||||
auto menu_items_text = load_menu_font(UiConfig::get().menuItemsFont());
|
||||
menu_title_text_ = menu_title_text;
|
||||
|
||||
// Mini-logo del peu de la TITLE. Es preloda ací per consistència
|
||||
// amb la resta d'assets d'UI; manté la seua paleta original (no la
|
||||
@@ -218,12 +222,14 @@ Director::Director() {
|
||||
constexpr const char* MINI_LOGO_KEY = "logo_jailgames_mini.gif";
|
||||
Resource::Cache::get().loadSurface(MINI_LOGO_KEY, "data/logo/logo_jailgames_mini.gif");
|
||||
auto mini_logo_surface = Resource::Cache::get().getSurface(MINI_LOGO_KEY);
|
||||
mini_logo_surface_ = mini_logo_surface;
|
||||
|
||||
// Fons sencer de la TITLE (320×240). Igual que el minilogo, conserva
|
||||
// la seua paleta nadiua perquè el GIF ja porta els colors finals.
|
||||
constexpr const char* TITLE_BG_KEY = "title_bg.gif";
|
||||
Resource::Cache::get().loadSurface(TITLE_BG_KEY, "data/title/title_bg.gif");
|
||||
auto title_bg_surface = Resource::Cache::get().getSurface(TITLE_BG_KEY);
|
||||
title_bg_surface_ = title_bg_surface;
|
||||
ServiceMenu::get().init(menu_title_text, menu_items_text, ui_palette);
|
||||
|
||||
// Overlay hook: Screen::present() cridarà aquesta lambda al principi,
|
||||
@@ -258,35 +264,18 @@ Director::Director() {
|
||||
std::cout << "Director: start_scene=" << DebugConfig::toString(START)
|
||||
<< " starting_players=" << DebugConfig::toString(STARTING_PLAYERS) << '\n';
|
||||
|
||||
// `make_game` i `make_intro` reben `StartingPlayers` com a paràmetre
|
||||
// perquè la TitleScene tria al moment quins jugadors entren (P1, P2,
|
||||
// BOTH) segons la pulsació. La IntroScene ho reenvia a la GameScene.
|
||||
// El valor de `STARTING_PLAYERS` només s'usa com a punt d'entrada en
|
||||
// mode Debug quan saltem directament a INTRO o GAME (saltant TITLE).
|
||||
auto make_game = [](DebugConfig::StartingPlayers players) -> std::unique_ptr<Scene> {
|
||||
auto cfg = GameScene::defaultConfig();
|
||||
cfg.starting_players = players;
|
||||
return std::make_unique<GameScene>(std::move(cfg));
|
||||
};
|
||||
auto make_intro = [placeholder_text, make_game](DebugConfig::StartingPlayers players) -> std::unique_ptr<Scene> {
|
||||
return std::make_unique<IntroScene>(placeholder_text, players, make_game);
|
||||
};
|
||||
auto make_title = [menu_title_text, placeholder_text, title_bg_surface, mini_logo_surface, make_intro]() -> std::unique_ptr<Scene> {
|
||||
return std::make_unique<TitleScene>(menu_title_text, placeholder_text, title_bg_surface, mini_logo_surface, make_intro);
|
||||
};
|
||||
|
||||
switch (START) {
|
||||
case DebugConfig::StartScene::LOGO:
|
||||
current_scene_ = std::make_unique<LogoScene>(placeholder_text, make_title);
|
||||
current_scene_ = buildScene(SceneId::LOGO, LogoParams{});
|
||||
break;
|
||||
case DebugConfig::StartScene::TITLE:
|
||||
current_scene_ = make_title();
|
||||
current_scene_ = buildScene(SceneId::TITLE, TitleParams{});
|
||||
break;
|
||||
case DebugConfig::StartScene::INTRO:
|
||||
current_scene_ = make_intro(STARTING_PLAYERS);
|
||||
current_scene_ = buildScene(SceneId::INTRO, IntroParams{.starting_players = STARTING_PLAYERS});
|
||||
break;
|
||||
case DebugConfig::StartScene::GAME:
|
||||
current_scene_ = make_game(STARTING_PLAYERS);
|
||||
current_scene_ = buildScene(SceneId::GAME, GameParams{.starting_players = STARTING_PLAYERS});
|
||||
break;
|
||||
}
|
||||
last_ticks_ms_ = SDL_GetTicks();
|
||||
@@ -404,13 +393,46 @@ void Director::processMenuInput() {
|
||||
|
||||
void Director::switchSceneIfRequested() {
|
||||
if (!current_scene_) { return; }
|
||||
if (auto next = current_scene_->nextScene()) {
|
||||
// TODO: migrar a logger unificat (INC-03 a docs/audit-core-rendering.md).
|
||||
std::cout << "Director: scene transition\n";
|
||||
current_scene_ = std::move(next);
|
||||
// L'escena ha publicat un `ExitRequest` dins de `exit_request_`.
|
||||
// Director el desempaqueta i construeix la nova escena via
|
||||
// `buildScene`. Si la `std::any_cast` falla (tipus inesperat), el
|
||||
// log avisa i no canviem d'escena.
|
||||
auto request = current_scene_->takeExitRequest();
|
||||
if (!request.has_value()) { return; }
|
||||
try {
|
||||
const auto& req = std::any_cast<const ExitRequest&>(request);
|
||||
std::cout << "Director: scene transition → " << toString(req.next) << '\n';
|
||||
current_scene_ = buildScene(req.next, req.params);
|
||||
} catch (const std::bad_any_cast&) {
|
||||
std::cerr << "Director: exit_request_ amb tipus inesperat — ignorant\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto Director::buildScene(SceneId id, const SceneParams& params) -> std::unique_ptr<Scene> {
|
||||
// Durant la migració, les escenes que encara fan servir factories
|
||||
// legacy reben lambdes primes que tornen a delegar a `buildScene`.
|
||||
// Així el motor d'escena ja és Director-driven encara que els
|
||||
// ctors antics no l'utilitzen directament. Quan totes estiguen
|
||||
// migrades a `ExitRequest`, aquestes lambdes desapareixeran.
|
||||
switch (id) {
|
||||
case SceneId::LOGO:
|
||||
return std::make_unique<LogoScene>(placeholder_text_);
|
||||
case SceneId::TITLE:
|
||||
return std::make_unique<TitleScene>(menu_title_text_, placeholder_text_, title_bg_surface_, mini_logo_surface_);
|
||||
case SceneId::INTRO: {
|
||||
const auto& p = std::get<IntroParams>(params);
|
||||
return std::make_unique<IntroScene>(placeholder_text_, p.starting_players);
|
||||
}
|
||||
case SceneId::GAME: {
|
||||
const auto& p = std::get<GameParams>(params);
|
||||
auto cfg = GameScene::defaultConfig();
|
||||
cfg.starting_players = p.starting_players;
|
||||
return std::make_unique<GameScene>(std::move(cfg));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Director::iterate() -> SDL_AppResult {
|
||||
// Ocultació automàtica del cursor: observem els events abans que els
|
||||
// altres steps els consumeixin; Mouse no els modifica, només reacciona
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "game/scenes/scene_flow.hpp"
|
||||
|
||||
class Scene;
|
||||
class Surface;
|
||||
class Text;
|
||||
|
||||
class Director {
|
||||
public:
|
||||
@@ -20,9 +24,25 @@ class Director {
|
||||
std::unique_ptr<Scene> current_scene_;
|
||||
std::uint64_t last_ticks_ms_{0};
|
||||
|
||||
// Recursos compartits per la fabricació d'escenes. S'instancien una
|
||||
// vegada al ctor (parsejant fnt's, retenint shared_ptrs del
|
||||
// Resource::Cache) i `buildScene` els passa pel valor a cada nova
|
||||
// escena. No són `static`/Meyers perquè depenen de l'inici d'SDL i
|
||||
// de Resource::Cache.
|
||||
std::shared_ptr<Text> placeholder_text_;
|
||||
std::shared_ptr<Text> menu_title_text_;
|
||||
std::shared_ptr<Surface> title_bg_surface_;
|
||||
std::shared_ptr<Surface> mini_logo_surface_;
|
||||
|
||||
void createSystemFolder();
|
||||
void processEvents();
|
||||
static void processGamepadEvents();
|
||||
static void processMenuInput();
|
||||
void switchSceneIfRequested();
|
||||
|
||||
// Fabrica una escena nova a partir d'un identificador i els seus
|
||||
// paràmetres. És l'únic punt del codi que coneix com construir cada
|
||||
// tipus concret d'escena, així les escenes només declaren `ExitRequest`
|
||||
// i no s'enllacen entre si.
|
||||
[[nodiscard]] auto buildScene(SceneId id, const SceneParams& params) -> std::unique_ptr<Scene>;
|
||||
};
|
||||
Reference in new issue
Block a user