feat(hud): marcador de la franja superior amb 3 blocs i animació d'entrada
This commit is contained in:
1 parent
67ecb704d2
commit
82b229222f
6 files changed
+474
-1
No files matched your search
@@ -0,0 +1,164 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "core/rendering/surface.hpp" // Palette
|
||||
#include "game/entities/event_bus.hpp"
|
||||
|
||||
class Text;
|
||||
|
||||
// Marcador (HUD) de la GameScene. Ocupa la franja superior de 24 px del
|
||||
// canvas i es divideix en tres "blocs" simètrics (P1 · COMÚ · P2) que
|
||||
// funcionen com a panells independents — estil panell de pinball: cada un
|
||||
// té el seu propi mode visible, animació d'entrada, shake i flash.
|
||||
//
|
||||
// Vida: instanciat per `GameScene` via `unique_ptr` (mateix patró que
|
||||
// `PaintLayer`). Cada partida nova crea un HUD net; el destructor
|
||||
// desubscriu del bus automàticament. **No és singleton** — és estat de
|
||||
// partida, no configuració.
|
||||
//
|
||||
// Comunicació: subscrit al `EventBus` de l'`EntityManager`. Reacciona a
|
||||
// events del joc (`player_hit`, `level_complete`, ...) sense que les
|
||||
// entitats hagin de saber que existeix.
|
||||
class Hud {
|
||||
public:
|
||||
// Identificador semàntic de bloc (també índex dins `blocks_`).
|
||||
enum class BlockId : std::uint8_t { LEFT = 0,
|
||||
CENTER = 1,
|
||||
RIGHT = 2 };
|
||||
|
||||
// Mode actual d'un bloc. Tret d'`IDLE`, els altres modes són
|
||||
// *placeholders* per a la primera versió (el render fa res); la API
|
||||
// ja està fixada perquè afegir-los més endavant no toqui callers.
|
||||
enum class Mode : std::uint8_t { IDLE,
|
||||
DYING,
|
||||
COUNTDOWN,
|
||||
WIN,
|
||||
EMPTY };
|
||||
|
||||
Hud(EventBus& bus, std::shared_ptr<Text> font, const Palette& ui_palette);
|
||||
~Hud();
|
||||
|
||||
Hud(const Hud&) = delete;
|
||||
Hud(Hud&&) = delete;
|
||||
auto operator=(const Hud&) -> Hud& = delete;
|
||||
auto operator=(Hud&&) -> Hud& = delete;
|
||||
|
||||
// Cada frame: avança les animacions actives (entrada, shake, flash).
|
||||
void update(int delta_ms);
|
||||
|
||||
// Pinta els tres blocs sobre el canvas en la seva posició actual.
|
||||
// El caller decideix l'ordre respecte a la resta de capes (en general
|
||||
// cal cridar-lo a sobre del tilemap perquè quedi visible).
|
||||
void render() const;
|
||||
|
||||
// Substitueix el mode d'un bloc (commutar el que es veu sense canviar
|
||||
// la geometria — útil per a Dying/Countdown/Win quan estiguin fets).
|
||||
void setMode(BlockId block, Mode mode);
|
||||
|
||||
// Actualitza l'identificador de piràmide i sala (apareix al bloc
|
||||
// central). El caller en farà ús quan canvi de pantalla amb scroll o
|
||||
// entre sales.
|
||||
void setPyramidRoom(int pyramid, int room);
|
||||
|
||||
// Activa el bloc d'un jugador (1-based: 1 → P1, 2 → P2). Mentre està
|
||||
// inactiu, el bloc mostra zeros i no rep flash/shake; quan s'activa,
|
||||
// arrenca amb les vides per defecte.
|
||||
void activatePlayer(int player_one_based);
|
||||
|
||||
// Geometria pública perquè la GameScene puga calcular si el HUD
|
||||
// s'està movent encara (p. ex. retardar l'spawn fins que estiga al
|
||||
// seu lloc) — opcional, no obligatori.
|
||||
static constexpr int HEIGHT_PX = 24;
|
||||
|
||||
private:
|
||||
// Amplades dels tres blocs. Sumen exactament `GameCanvas::WIDTH` (320).
|
||||
// Centrats: el bloc del mig agafa els píxels sobrants (108) i els
|
||||
// laterals queden idèntics (106 cadascun) per garantir simetria.
|
||||
static constexpr int LEFT_W = 106;
|
||||
static constexpr int CENTER_W = 108;
|
||||
static constexpr int RIGHT_W = 106;
|
||||
|
||||
// Files de text dins del bloc (y relatiu al bloc), per a una font de
|
||||
// 8 px d'alçada amb un petit padding visual a dalt i a baix.
|
||||
static constexpr int LINE1_Y = 4;
|
||||
static constexpr int LINE2_Y = 13;
|
||||
|
||||
// Durada de l'animació d'entrada — els tres blocs alhora, amb easing
|
||||
// outCubic. 400 ms és prou per a percebre el moviment sense fer
|
||||
// esperar massa al jugador.
|
||||
static constexpr int ENTRY_MS = 400;
|
||||
|
||||
// Durades per defecte d'efectes transitoris.
|
||||
static constexpr int FLASH_MS = 250;
|
||||
static constexpr int SHAKE_MS = 300;
|
||||
|
||||
// Vides inicials per jugador (placeholder fins que GameScene ho
|
||||
// configure des del Config del nivell).
|
||||
static constexpr int DEFAULT_LIVES = 3;
|
||||
|
||||
struct Block {
|
||||
Mode mode{Mode::IDLE};
|
||||
// Coordenades de pantalla on viu el bloc (constants durant la
|
||||
// partida; només varien els offsets transitoris d'entrada/shake).
|
||||
int x{0};
|
||||
int y{0};
|
||||
int w{0};
|
||||
int h{0};
|
||||
|
||||
// Animació d'entrada: ms restants fins a quedar-se a la posició
|
||||
// final. Quan val 0 el bloc està quiet.
|
||||
int entry_ms_left{ENTRY_MS};
|
||||
// Vector inicial des d'on entra: {-LEFT_W,0} per al bloc esquerre,
|
||||
// {0,-HEIGHT_PX} per al central i {+RIGHT_W,0} per al dret.
|
||||
int entry_offset_x{0};
|
||||
int entry_offset_y{0};
|
||||
|
||||
// Shake actiu: amplitud i ms restants. Decau linealment.
|
||||
int shake_ms_left{0};
|
||||
float shake_intensity{0.0F};
|
||||
|
||||
// Flash actiu: mentre val >0, el text del valor es pinta amb el
|
||||
// color de flash en compte del de valor.
|
||||
int flash_ms_left{0};
|
||||
};
|
||||
|
||||
struct PlayerStats {
|
||||
int lives{DEFAULT_LIVES};
|
||||
int score{0};
|
||||
int treasures{0};
|
||||
// `false` → el bloc mostra `000000`/`0`/`00` (jugador inactiu).
|
||||
// Sempre es renderitza per mantenir el layout fix.
|
||||
bool active{false};
|
||||
};
|
||||
|
||||
// Pinta el bloc indicat amb el seu fons + el contingut del seu mode
|
||||
// actual. Aplica l'offset (entrada + shake) abans de dibuixar.
|
||||
void renderBlock(BlockId id) const;
|
||||
|
||||
// Subrutines del mode `Idle` per als tres blocs. Reben el rectangle
|
||||
// ja desplaçat (x, y) on han d'escriure.
|
||||
void renderIdleSide(int x, int y, int w, const Block& blk, int player_index, const char* label) const;
|
||||
void renderIdleCenter(int x, int y, int w) const;
|
||||
|
||||
// Calcula l'offset transitori (entrada + shake) en píxels per a un
|
||||
// bloc. Static perquè depèn només dels camps del propi `Block` i de
|
||||
// les constants de classe (no toca cap altre membre).
|
||||
[[nodiscard]] static auto computeOffset(const Block& b) -> SDL_FPoint;
|
||||
|
||||
EventBus& bus_;
|
||||
std::vector<EventBus::SubId> subs_;
|
||||
|
||||
std::shared_ptr<Text> font_;
|
||||
Palette ui_palette_{};
|
||||
|
||||
std::array<Block, 3> blocks_;
|
||||
std::array<PlayerStats, 2> players_;
|
||||
int pyramid_{1};
|
||||
int room_{1};
|
||||
};
|
||||
Reference in new issue
Block a user