Files
aee-arcade/source/game/levels/level_manager.hpp
T
JailDesigner 738cde12bb feat(debug): starting_level a debug.yaml
Nova clau opcional al debug.yaml:
    starting_level: { pyramid: 2, room: 3 }
DebugConfig::resolveStartingLevel retorna optional<StartingLevel>; el
Director, abans de crear el GameScene, demana al LevelManager
setStartingPoint(pyramid, room) — que cerca l'entrada al levels.yaml
i hi posiciona l'índex. Si la clau no està (cas habitual) o
(pyramid, room) no existeix, la progressió arrenca a la primera sala
amb un avís a stderr. createDefaultIfMissing documenta la nova clau
sense escriure-la (la deixem opcional).

Drive-by: NOLINT puntual al capture de STARTING_PLAYERS al Director.
És pre-existent i s'ha activat ara perquè el commit toca director.cpp;
la captura és imprescindible en Debug (valor runtime), clang la veu
redundant en Release on és constexpr literal.
2026-04-26 15:07:34 +02:00

82 lines
3.3 KiB
C++

#pragma once
#include <string>
#include <vector>
namespace Levels {
// Font de veritat de la progressió de sales del joc. Carrega
// `data/maps/levels.yaml` a la primera crida a `get()` i exposa l'estat
// "on sóc ara" + les operacions per moure'm. La GameScene, l'Editor i
// els salts de debug consulten aquest singleton; ningú no manté el seu
// propi índex de progressió.
//
// Meyers singleton: la destrucció estàtica només toca std::string i
// std::vector, així que és segura sense ordenació explícita.
class LevelManager {
public:
static auto get() -> LevelManager&;
LevelManager(const LevelManager&) = delete;
LevelManager(LevelManager&&) = delete;
auto operator=(const LevelManager&) -> LevelManager& = delete;
auto operator=(LevelManager&&) -> LevelManager& = delete;
// Una entrada del `levels.yaml`. `file` és la ruta relativa al data
// root (mateixa convenció que la resta de YAML del joc); pyramid i
// room són purament informatius (logs i text d'intro).
struct Entry {
int pyramid{0};
int room{0};
std::string file;
};
// Ruta del nivell actualment apuntat. Sempre vàlida un cop carregat
// l'índex (entries_ no pot estar buit). Pensada per a `Map::loadFrom`,
// `EntityManager::reload` i l'Editor.
[[nodiscard]] auto currentPath() const -> const std::string&;
[[nodiscard]] auto currentPyramid() const -> int;
[[nodiscard]] auto currentRoom() const -> int;
// "P1-R3" — útil per a logs i el text d'intro de sala.
[[nodiscard]] auto currentLabel() const -> std::string;
// Posició dins de la seqüència. `count()` és el total de sales
// carregades del YAML (no necessàriament 30: si afegeixes/treus
// entrades de `levels.yaml`, el manager s'adapta).
[[nodiscard]] auto index() const -> int;
[[nodiscard]] auto count() const -> int;
// Avança al següent nivell. Retorna true si avança, false si ja
// estava a l'últim. NO fa loop — la GameScene decideix la política
// (loop a la primera, fi de joc, etc.) segons el context.
auto advance() -> bool;
// Torna al primer nivell.
void restart();
// Salta a un índex concret. Clamp als bordes [0, count()-1].
// Pensat per als hotkeys debug.
void setIndex(int new_index);
// Posiciona l'índex a la primera entrada que coincideix amb
// (pyramid, room). Retorna true si trobada; si no, l'índex no
// es modifica i el caller decideix què fer (típicament ignorar i
// arrencar des de la primera sala). Pensat per al
// `debug.yaml.starting_level` i per a futures lookups per coords.
auto setStartingPoint(int pyramid, int room) -> bool;
private:
LevelManager();
~LevelManager() = default;
// Carrega `data/maps/levels.yaml` via `Resource::Helper::loadFile`.
// Si manca, és invàlid o té la seqüència buida, llança — no té sentit
// continuar sense progressió de sales.
void loadIndex();
std::vector<Entry> entries_;
int index_{0};
};
} // namespace Levels