feat(logo,menu): reiniciar des de servei → LOGO amb reset (atura música) i logo_jailgames.gif centrat
This commit is contained in:
1 parent
8eaa91f309
commit
f7ab6a641a
7 files changed
+92
-35
No files matched your search
@@ -224,6 +224,13 @@ Director::Director() {
|
||||
auto mini_logo_surface = Resource::Cache::get().getSurface(MINI_LOGO_KEY);
|
||||
mini_logo_surface_ = mini_logo_surface;
|
||||
|
||||
// Logo sencer per a l'escena LOGO (centrat damunt fons negre). També
|
||||
// conserva la seua paleta nadiua: els colors finals del logo viuen
|
||||
// dins del propi GIF i no han de passar per la paleta UI.
|
||||
constexpr const char* LOGO_FULL_KEY = "logo_jailgames.gif";
|
||||
Resource::Cache::get().loadSurface(LOGO_FULL_KEY, "data/logo/logo_jailgames.gif");
|
||||
logo_full_surface_ = Resource::Cache::get().getSurface(LOGO_FULL_KEY);
|
||||
|
||||
// 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";
|
||||
@@ -393,6 +400,16 @@ void Director::processMenuInput() {
|
||||
|
||||
void Director::switchSceneIfRequested() {
|
||||
if (!current_scene_) { return; }
|
||||
// Prioritat al menú: si un item del ServiceMenu ha encuat una
|
||||
// transició (típicament "Reiniciar" → LOGO amb reset), s'aplica
|
||||
// primer. Substitueix l'`ExitRequest` que pugui haver publicat
|
||||
// l'escena en aquest mateix frame — l'usuari ha cancel·lat l'estat
|
||||
// de joc explícitament i no ens interessa mantenir-ne la sortida.
|
||||
if (auto menu_req = ServiceMenu::get().takePendingSceneRequest(); menu_req.has_value()) {
|
||||
std::cout << "Director: scene transition (menu) → " << toString(menu_req->next) << '\n';
|
||||
current_scene_ = buildScene(menu_req->next, menu_req->params);
|
||||
return;
|
||||
}
|
||||
// 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
|
||||
@@ -415,8 +432,10 @@ auto Director::buildScene(SceneId id, const SceneParams& params) -> std::unique_
|
||||
// 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::LOGO: {
|
||||
const auto& p = std::get<LogoParams>(params);
|
||||
return std::make_unique<LogoScene>(logo_full_surface_, p.reset);
|
||||
}
|
||||
case SceneId::TITLE:
|
||||
return std::make_unique<TitleScene>(menu_title_text_, placeholder_text_, title_bg_surface_, mini_logo_surface_);
|
||||
case SceneId::INTRO: {
|
||||
|
||||
@@ -33,6 +33,7 @@ class Director {
|
||||
std::shared_ptr<Text> menu_title_text_;
|
||||
std::shared_ptr<Surface> title_bg_surface_;
|
||||
std::shared_ptr<Surface> mini_logo_surface_;
|
||||
std::shared_ptr<Surface> logo_full_surface_;
|
||||
|
||||
void createSystemFolder();
|
||||
void processEvents();
|
||||
|
||||
@@ -5,26 +5,23 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "core/audio/audio.hpp"
|
||||
#include "core/input/global_inputs.hpp"
|
||||
#include "core/rendering/notifier.hpp"
|
||||
#include "core/rendering/render_info.hpp"
|
||||
#include "core/rendering/screen.hpp"
|
||||
#include "core/rendering/text.hpp"
|
||||
#include "core/rendering/surface.hpp"
|
||||
#include "core/system/event_buffer.hpp"
|
||||
#include "core/ui/ui_config.hpp"
|
||||
#include "game/options.hpp"
|
||||
#include "game/scenes/placeholder_input.hpp"
|
||||
#include "game/scenes/scene_flow.hpp"
|
||||
#include "utils/defaults.hpp"
|
||||
#include "utils/defines.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int NAME_Y_OFFSET = -8;
|
||||
constexpr int TIMER_Y_OFFSET = 8;
|
||||
// TODO: migrar a un logger unificat quan es decideixi (veure INC-03 a
|
||||
// docs/audit-core-rendering.md). De moment std::cout per rastrejar el
|
||||
// flux d'escenes als smoke runs.
|
||||
@@ -32,9 +29,19 @@ namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
LogoScene::LogoScene(std::shared_ptr<Text> font)
|
||||
: font_(std::move(font)) {
|
||||
std::cout << "[scene] " << SCENE_NAME << " start\n";
|
||||
LogoScene::LogoScene(std::shared_ptr<Surface> logo, bool reset)
|
||||
: logo_(std::move(logo)) {
|
||||
std::cout << "[scene] " << SCENE_NAME << (reset ? " start (reset)\n" : " start\n");
|
||||
// RESET = "Reiniciar" del menú: tornem al punt zero del joc, així que
|
||||
// qualsevol música o so en marxa (la del joc, els sons d'UI, etc.) ha
|
||||
// d'apagar-se abans que el LOGO comenci. En el flux normal no toquem
|
||||
// res — la música de TITLE pot seguir sonant durant l'attract-out.
|
||||
if (reset) {
|
||||
if (auto* audio = Audio::get(); audio != nullptr) {
|
||||
audio->stopMusic();
|
||||
audio->stopAllSounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogoScene::~LogoScene() = default;
|
||||
@@ -64,20 +71,10 @@ void LogoScene::render() {
|
||||
constexpr std::uint32_t BLACK_ARGB = 0xFF000000U;
|
||||
Screen::get().fillRect(0, 0, GameCanvas::WIDTH, GameCanvas::HEIGHT, BLACK_ARGB);
|
||||
|
||||
if (font_) {
|
||||
const int SECS_LEFT = std::max(1, (timer_ms_ + 999) / 1000);
|
||||
font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
||||
GameCanvas::CENTER_X,
|
||||
GameCanvas::CENTER_Y + NAME_Y_OFFSET,
|
||||
SCENE_NAME,
|
||||
1,
|
||||
UiConfig::get().notificationText());
|
||||
font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
||||
GameCanvas::CENTER_X,
|
||||
GameCanvas::CENTER_Y + TIMER_Y_OFFSET,
|
||||
std::to_string(SECS_LEFT),
|
||||
1,
|
||||
UiConfig::get().notificationText());
|
||||
if (logo_) {
|
||||
const int X = (GameCanvas::WIDTH - logo_->getWidth()) / 2;
|
||||
const int Y = (GameCanvas::HEIGHT - logo_->getHeight()) / 2;
|
||||
logo_->render(X, Y);
|
||||
}
|
||||
|
||||
Notifier::get().render();
|
||||
|
||||
@@ -4,23 +4,29 @@
|
||||
|
||||
#include "core/scene/scene.hpp"
|
||||
|
||||
class Text;
|
||||
class Surface;
|
||||
|
||||
// Escena placeholder del logotip. Durada fixa (5 s) o skip amb qualsevol tecla
|
||||
// no reservada. Quan expira, publica un `ExitRequest` cap a TITLE; Director
|
||||
// és qui construeix la nova escena (l'escena no coneix els altres tipus i
|
||||
// es pot exercir en tests sense arrossegar dependències).
|
||||
// Escena del logotip de l'editora. Durada fixa (5 s) o skip amb qualsevol
|
||||
// tecla no reservada. Quan expira, publica un `ExitRequest` cap a TITLE;
|
||||
// Director és qui construeix la nova escena (l'escena no coneix els altres
|
||||
// tipus i es pot exercir en tests sense arrossegar dependències).
|
||||
//
|
||||
// Pinta el GIF `data/logo/logo_jailgames.gif` centrat dins del canvas
|
||||
// lògic, amb la seua paleta nadiua (no s'aplica la paleta UI). Si arriba
|
||||
// amb `reset=true` (acció "Reiniciar" del menú de servei), atura la
|
||||
// música i tots els sons al constructor — així el següent LOGO arrenca
|
||||
// silenciós encara que veníssem del joc en marxa.
|
||||
class LogoScene : public Scene {
|
||||
public:
|
||||
static constexpr int DURATION_MS = 5000;
|
||||
|
||||
explicit LogoScene(std::shared_ptr<Text> font);
|
||||
LogoScene(std::shared_ptr<Surface> logo, bool reset);
|
||||
~LogoScene() override;
|
||||
|
||||
void update(int delta_ms) override;
|
||||
void render() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Text> font_;
|
||||
std::shared_ptr<Surface> logo_;
|
||||
int timer_ms_{DURATION_MS};
|
||||
};
|
||||
@@ -30,7 +30,15 @@ enum class SceneId : std::uint8_t {
|
||||
// rep cap dada de l'anterior; en cas contrari, només els camps que
|
||||
// fluixen pel canal de transició (els recursos, defaults i config
|
||||
// interna els resol Director quan construeix).
|
||||
struct LogoParams {};
|
||||
struct LogoParams {
|
||||
// Quan val `true`, indica que aquesta entrada al LOGO és una reinicialització
|
||||
// completa (acció "Reiniciar" del menú de servei): l'escena haurà d'aturar
|
||||
// música i sons abans de mostrar-se. En el flux normal LOGO→TITLE→… es
|
||||
// queda a `false` perquè qualsevol música prèvia (p. ex. la pista de
|
||||
// TITLE durant l'attract-out) continui sonant fins que la nova escena la
|
||||
// substituïsca.
|
||||
bool reset{false};
|
||||
};
|
||||
struct TitleParams {};
|
||||
struct IntroParams {
|
||||
DebugConfig::StartingPlayers starting_players{DebugConfig::StartingPlayers::BOTH};
|
||||
|
||||
@@ -1168,12 +1168,18 @@ auto ServiceMenu::buildSystem() -> Page {
|
||||
page.items.push_back(makeLabel(
|
||||
std::string("ver.") + Texts::VERSION + " (" + Version::GIT_HASH + ")"));
|
||||
|
||||
// Reiniciar el joc: placeholder sense efecte fins que Director exposi
|
||||
// un pas de restart en calent (fora d'abast d'aquesta iteració).
|
||||
// Reiniciar el joc: tornem al LOGO amb `reset=true` perquè LogoScene
|
||||
// ature música i sons abans de mostrar-se. Tanquem el menú a la
|
||||
// mateixa acció — Director recollirà la transició al final del frame
|
||||
// via `takePendingSceneRequest()` i reconstruirà l'escena.
|
||||
page.items.push_back(makeAction(
|
||||
"menu.items.restart",
|
||||
[]() {
|
||||
// TODO: connectar amb un hipotètic Director::requestRestart().
|
||||
ServiceMenu::get().requestSceneTransition(ExitRequest{
|
||||
.next = SceneId::LOGO,
|
||||
.params = LogoParams{.reset = true},
|
||||
});
|
||||
ServiceMenu::get().close();
|
||||
}));
|
||||
|
||||
page.items.push_back(makeAction(
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/input/define_buttons.hpp"
|
||||
#include "core/rendering/surface.hpp" // Palette
|
||||
#include "game/scenes/scene_flow.hpp"
|
||||
|
||||
class Text;
|
||||
|
||||
@@ -73,6 +75,19 @@ class ServiceMenu {
|
||||
// `define_buttons_->enable(...)` de coffee_crisis_arcade_edition.
|
||||
void startDefineButtons(SDL_Gamepad* pad, const std::string& name, const std::string& path);
|
||||
|
||||
// Bústia de transició d'escena des del menú: alguns items (p.ex.
|
||||
// "Reiniciar" a Sistema) no es poden expressar com a `ExitRequest` de
|
||||
// l'escena actual perquè el menú és global i no té handle a l'escena.
|
||||
// Director consulta `takePendingSceneRequest()` cada frame i, si hi ha
|
||||
// valor, força la transició — el mateix camí que per a `ExitRequest`
|
||||
// d'una escena, però el dispara el menú directament.
|
||||
void requestSceneTransition(ExitRequest req) { pending_request_ = req; }
|
||||
[[nodiscard]] auto takePendingSceneRequest() -> std::optional<ExitRequest> {
|
||||
auto out = pending_request_;
|
||||
pending_request_.reset();
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
// --- Struct d'un item d'una pàgina ---
|
||||
//
|
||||
@@ -289,4 +304,9 @@ class ServiceMenu {
|
||||
// persisteix la nova configuració via `GamepadBindings`. Replica el
|
||||
// patró `define_buttons_` de coffee_crisis_arcade_edition.
|
||||
DefineButtons define_buttons_;
|
||||
|
||||
// Sol·licitud de canvi d'escena pendent (vore `requestSceneTransition`).
|
||||
// La consumeix Director al final del frame; si l'item no força la
|
||||
// transició, queda buida.
|
||||
std::optional<ExitRequest> pending_request_;
|
||||
};
|
||||
Reference in new issue
Block a user