Files
orni-attack/source/core/system/service_menu.hpp
T

113 lines
3.9 KiB
C++

// service_menu.hpp - Menu de servei (singleton)
// © 2026 JailDesigner
//
// Overlay de configuracio global accessible amb F12 des de qualsevol escena
// (LOGO, TITLE, GAME). Captura tots els KEY_DOWN excepte F1-F12 i ESC, que
// continuen arribant a GlobalEvents. Mentre esta obert, GameScene::update()
// fa early return per pausar el joc; LOGO i TITLE continuen renderitzant-se
// sota el menu.
//
// Arquitectura inspirada en aee_arcade service_menu.{hpp,cpp}: pila de
// pagines amb cursor, animacio open/close amb easing easeOutQuad i clipping
// del contingut mentre la caixa creix/decreix.
//
// API singleton equivalent a Notifier: init() al startup amb un renderer,
// get() retorna el punter, destroy() al teardown.
#pragma once
#include <SDL3/SDL.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "core/graphics/vector_text.hpp"
#include "core/rendering/render_context.hpp"
namespace System {
class ServiceMenu {
public:
// Tipus d'item de menu. En aquesta iteracio nomes s'usen SUBMENU i
// LABEL; la resta queden reservats per a iteracions futures (toggles
// de vsync/zoom, picker d'idioma, restart, exit...).
enum class Kind : std::uint8_t {
LABEL, // No interactiu, nomes es dibuixa
TOGGLE, // bool flip — reservat
CYCLE, // index amb modul — reservat
INT_RANGE, // step ± — reservat
SUBMENU, // pushPage en activar — usat
ACTION // call al lambda en activar — reservat
};
struct Item {
Kind kind = Kind::LABEL;
std::string label_key; // Clau de locale
bool selectable = true;
std::function<void()> on_activate;
};
struct Page {
std::string title_key;
std::vector<Item> items;
std::size_t cursor = 0;
};
// Inicialitza el singleton amb el renderer global (propietat del
// Director via SDLManager). Posterior get() retorna instancia valida.
static void init(Rendering::Renderer* renderer);
static void destroy();
[[nodiscard]] static auto get() -> ServiceMenu*;
// F12: alterna obrir/tancar amb animacio.
void toggle();
[[nodiscard]] auto isOpen() const -> bool;
void update(float delta_time);
void draw() const;
// Processa el KEY_DOWN. Retorna true si l'ha consumit (UP/DOWN/ENTER/
// RIGHT/BACKSPACE/LEFT mentre esta obert). false en qualsevol altre cas.
auto handleEvent(const SDL_Event& event) -> bool;
private:
explicit ServiceMenu(Rendering::Renderer* renderer);
void buildRootPage();
void pushSubmenuPlaceholder(const std::string& title_key);
void pushPage(Page page);
void popPage();
void moveCursor(int direction);
void activateCurrent();
// Alçada objectiu de la caixa per a la pagina superior (sense animacio).
[[nodiscard]] auto computeTargetHeight() const -> float;
// Y (top) de l'item index dins una caixa col·locada a box_y.
[[nodiscard]] static auto computeItemTopY(float box_y, std::size_t index) -> float;
Rendering::Renderer* renderer_;
Graphics::VectorText text_;
std::vector<Page> stack_;
bool open_ = false;
bool closing_ = false;
float open_anim_ = 0.0F; // 0..1 raw (sense easing)
float animated_h_ = 0.0F; // Alçada animada amb smoothing exponencial
// Estat del highlight (rectangle del cursor). Es lerpa cap a l'item
// actiu amb ease-out exponencial; quan el cursor "salta" (open o
// push/pop de pagina), s'enganxa directament al nou objectiu.
float highlight_y_ = 0.0F;
float highlight_h_ = 0.0F;
bool highlight_snap_ = true;
static std::unique_ptr<ServiceMenu> instance;
};
} // namespace System