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.
82 lines
3.3 KiB
C++
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
|