feat(menu): primera versió del menú de servei

This commit is contained in:
JailDesigner committed 2026-04-22 18:15:00 +02:00
1 parent e4a8b4cd3e
commit 350625dec0
21 files changed
+1477 -16

No files matched your search

+55
View File
@@ -29,6 +29,7 @@
#include "game/scenes/intro_scene.hpp"
#include "game/scenes/logo_scene.hpp"
#include "game/scenes/title_scene.hpp"
#include "game/ui/service_menu.hpp"
#include "game/ui/ui_palette.hpp"
#include "utils/defaults.hpp"
@@ -106,6 +107,7 @@ Director::Director() {
// d'ells hagi de conèixer els noms dels recursos concrets.
auto hud_font_surface = Resource::Cache::get().getSurface(HUD_FONT_GIF_KEY);
auto hud_text = std::make_shared<Text>(hud_font_surface, Defaults::UI::Font::HUD_FNT);
hud_text->setDefaultKerning(Defaults::UI::Font::HUD_KERNING);
const auto& ui_palette = Resource::Cache::get().getPalette("ui");
RenderInfo::get().init(Options::buildRenderInfoFrame(UiColor::RENDER_INFO_TEXT, UiColor::RENDER_INFO_SHADOW), hud_text);
@@ -116,6 +118,35 @@ Director::Director() {
// finals; mentrestant tenen estètica pròpia.
auto placeholder_font_surface = Resource::Cache::get().getSurface(PLACEHOLDER_FONT_GIF_KEY);
auto placeholder_text = std::make_shared<Text>(placeholder_font_surface, Defaults::UI::Font::PLACEHOLDER_FNT);
placeholder_text->setDefaultKerning(Defaults::UI::Font::PLACEHOLDER_KERNING);
// 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
// files). Els dos noms i kernings viuen a `Defaults::UI::Font`.
auto load_menu_font = [&](const char* name, const char* gif, const char* fnt, int kerning) {
const std::string KEY = std::string(name) + ".gif";
Resource::Cache::get().loadSurface(KEY, gif);
Resource::Cache::get().getSurface(KEY)->setPalette(Resource::Cache::get().getPalette("ui"));
auto text = std::make_shared<Text>(Resource::Cache::get().getSurface(KEY), fnt);
text->setDefaultKerning(kerning);
return text;
};
auto menu_title_text = load_menu_font(
Defaults::UI::Font::MENU_TITLE_NAME,
Defaults::UI::Font::MENU_TITLE_GIF,
Defaults::UI::Font::MENU_TITLE_FNT,
Defaults::UI::Font::MENU_TITLE_KERNING);
auto menu_items_text = load_menu_font(
Defaults::UI::Font::MENU_ITEMS_NAME,
Defaults::UI::Font::MENU_ITEMS_GIF,
Defaults::UI::Font::MENU_ITEMS_FNT,
Defaults::UI::Font::MENU_ITEMS_KERNING);
ServiceMenu::get().init(menu_title_text, menu_items_text, ui_palette);
// Overlay hook: Screen::present() cridarà aquesta lambda al principi,
// ABANS de pujar els píxels al backend GPU. Així el menú queda per
// damunt de qualsevol escena sense que Screen hagi de conèixer game/ui.
Screen::get().setOverlayRenderer([]() { ServiceMenu::get().render(); });
// Flux d'escenes LOGO → TITLE → INTRO → GAME. Cada placeholder rep una
// factory que construeix la següent, construïda bottom-up des de GAME.
@@ -204,6 +235,24 @@ void Director::processEvents() {
if (QUIT_REQUESTED) { current_scene_.reset(); }
}
void Director::processMenuInput() {
// Intercepta input abans que l'escena vegi els events. Si el menú està
// obert, consumeix TOTS els KEY_DOWN perquè ni GlobalInputs (F1–F8,
// F11, F12) ni el jugador els veuen. F9 obre el menú quan està tancat.
// Erase invalida iteradors, per això actualitzem `it` a cada branca.
for (auto it = EventBuffer::events.begin(); it != EventBuffer::events.end();) {
if (ServiceMenu::get().isOpen() && ServiceMenu::get().handleEvent(*it)) {
it = EventBuffer::events.erase(it);
} else if (it->type == SDL_EVENT_KEY_DOWN &&
it->key.scancode == KeyConfig::get().scancode("menu_toggle")) {
ServiceMenu::get().toggle();
it = EventBuffer::events.erase(it);
} else {
++it;
}
}
}
void Director::switchSceneIfRequested() {
if (!current_scene_) { return; }
if (auto next = current_scene_->nextScene()) {
@@ -214,6 +263,11 @@ void Director::switchSceneIfRequested() {
}
auto Director::iterate() -> SDL_AppResult {
// Ordre important: primer el menú (pot consumir tecles i INJECTAR un
// SDL_EVENT_QUIT des de l'acció "Eixir del joc"), després processEvents
// perquè vegi el QUIT al mateix frame i tanqui l'escena — altrament el
// QUIT injectat es perdria quan es netegi EventBuffer al final d'iterate.
processMenuInput();
processEvents();
if (!current_scene_) {
@@ -227,6 +281,7 @@ auto Director::iterate() -> SDL_AppResult {
last_ticks_ms_ = NOW_MS;
Audio::update();
ServiceMenu::get().update(delta_ms);
current_scene_->update(delta_ms);
current_scene_->render();