diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index dbca5d3..3515074 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -27,6 +27,7 @@ #include "core/system/event_buffer.hpp" #include "core/ui/ui_config.hpp" #include "game/debug_config.hpp" +#include "game/levels/level_manager.hpp" #include "game/options.hpp" #include "game/scenes/game_scene.hpp" #include "game/scenes/intro_scene.hpp" @@ -182,6 +183,13 @@ Director::Director() { DebugConfig::createDefaultIfMissing(DEBUG_YAML); const auto START = DebugConfig::resolveStartScene(DEBUG_YAML); const auto STARTING_PLAYERS = DebugConfig::resolveStartingPlayers(DEBUG_YAML); + // `starting_level` (opcional) sobreescriu l'índex inicial del + // LevelManager abans de crear cap GameScene. Si la clau no està o el + // (pyramid, room) no apareix al levels.yaml, el LevelManager + // continua a la primera entrada (P1-R1). + if (const auto SL = DebugConfig::resolveStartingLevel(DEBUG_YAML); SL.has_value()) { + Levels::LevelManager::get().setStartingPoint(SL->pyramid, SL->room); + } #else const auto START = DebugConfig::StartScene::LOGO; const auto STARTING_PLAYERS = DebugConfig::StartingPlayers::BOTH; @@ -189,6 +197,12 @@ Director::Director() { std::cout << "Director: start_scene=" << DebugConfig::toString(START) << " starting_players=" << DebugConfig::toString(STARTING_PLAYERS) << '\n'; + // El branch Release inicialitza STARTING_PLAYERS amb una constant + // literal i clang considera la captura redundant; en Debug el valor és + // runtime i la captura sí és imprescindible. La lambda viu dins de + // IntroScene, més enllà del scope d'aquest ctor — capturar per + // referència seria UB. + // NOLINTNEXTLINE(clang-diagnostic-unused-lambda-capture) auto make_game = [STARTING_PLAYERS]() -> std::unique_ptr { auto cfg = GameScene::defaultConfig(); cfg.starting_players = STARTING_PLAYERS; diff --git a/source/game/debug_config.cpp b/source/game/debug_config.cpp index 7ff9291..c14cf54 100644 --- a/source/game/debug_config.cpp +++ b/source/game/debug_config.cpp @@ -103,6 +103,34 @@ namespace DebugConfig { return parseStartingPlayers(CONTENT); } + auto parseStartingLevel(const std::string& yaml_content) -> std::optional { + if (yaml_content.empty()) { return std::nullopt; } + try { + auto yaml = fkyaml::node::deserialize(yaml_content); + if (!yaml.contains("starting_level")) { return std::nullopt; } + const auto& sl = yaml["starting_level"]; + if (!sl.contains("pyramid") || !sl.contains("room")) { + std::cerr << "debug.yaml: starting_level necessita els camps `pyramid` i `room` — ignorat\n"; + return std::nullopt; + } + return StartingLevel{ + .pyramid = sl["pyramid"].get_value(), + .room = sl["room"].get_value(), + }; + } catch (const std::exception& e) { + std::cerr << "debug.yaml: error parsejant starting_level: " << e.what() << " — ignorat\n"; + return std::nullopt; + } + } + + auto resolveStartingLevel(const std::string& file_path) -> std::optional { + std::error_code error; + if (!std::filesystem::exists(file_path, error)) { return std::nullopt; } + const auto CONTENT = readFile(file_path); + if (CONTENT.empty()) { return std::nullopt; } + return parseStartingLevel(CONTENT); + } + auto toString(StartScene scene) -> const char* { switch (scene) { case StartScene::LOGO: @@ -140,6 +168,9 @@ namespace DebugConfig { file << "# debug.yaml — ajust personal de dev (no versionat).\n"; file << "# start_scene: LOGO | TITLE | INTRO | GAME\n"; file << "# starting_players: P1 | P2 | BOTH\n"; + file << "# starting_level (opcional): { pyramid: N, room: M } — salta a la sala\n"; + file << "# corresponent del levels.yaml. Sense la\n"; + file << "# clau, comença a la primera (P1-R1).\n"; file << "start_scene: GAME\n"; file << "starting_players: BOTH\n"; std::cout << "debug.yaml: creat a " << file_path << " amb start_scene=GAME starting_players=BOTH\n"; diff --git a/source/game/debug_config.hpp b/source/game/debug_config.hpp index 182fd21..56bbcba 100644 --- a/source/game/debug_config.hpp +++ b/source/game/debug_config.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include // Configuració personal de desenvolupament, carregada de `debug.yaml` dins la @@ -37,6 +38,18 @@ namespace DebugConfig { [[nodiscard]] auto parseStartingPlayers(const std::string& yaml_content) -> StartingPlayers; [[nodiscard]] auto resolveStartingPlayers(const std::string& file_path) -> StartingPlayers; + // Punt d'arrencada dins de la progressió de sales (consumit pel + // `Levels::LevelManager`). YAML: + // starting_level: { pyramid: 2, room: 3 } + // L'absència de la clau és el cas habitual i deixa la progressió a + // la primera entrada del `levels.yaml`. + struct StartingLevel { + int pyramid{0}; + int room{0}; + }; + [[nodiscard]] auto parseStartingLevel(const std::string& yaml_content) -> std::optional; + [[nodiscard]] auto resolveStartingLevel(const std::string& file_path) -> std::optional; + // Nom canònic de l'enum (útil per a logs): "LOGO" / "TITLE" / "INTRO" / "GAME". [[nodiscard]] auto toString(StartScene scene) -> const char*; [[nodiscard]] auto toString(StartingPlayers players) -> const char*; diff --git a/source/game/levels/level_manager.cpp b/source/game/levels/level_manager.cpp index dc4eb92..1839433 100644 --- a/source/game/levels/level_manager.cpp +++ b/source/game/levels/level_manager.cpp @@ -86,4 +86,19 @@ namespace Levels { } } + auto LevelManager::setStartingPoint(int pyramid, int room) -> bool { + for (std::size_t i = 0; i < entries_.size(); ++i) { + if (entries_[i].pyramid == pyramid && entries_[i].room == room) { + index_ = static_cast(i); + std::cout << "LevelManager: starting_point P" << pyramid << "-R" << room + << " trobat a l'índex " << i << " (" << currentPath() << ")\n"; + return true; + } + } + std::cerr << "LevelManager: starting_point P" << pyramid << "-R" << room + << " no trobat a levels.yaml — es manté l'índex actual (" + << currentLabel() << ")\n"; + return false; + } + } // namespace Levels diff --git a/source/game/levels/level_manager.hpp b/source/game/levels/level_manager.hpp index c6532f4..9fe0763 100644 --- a/source/game/levels/level_manager.hpp +++ b/source/game/levels/level_manager.hpp @@ -55,9 +55,16 @@ namespace Levels { void restart(); // Salta a un índex concret. Clamp als bordes [0, count()-1]. - // Pensat per als hotkeys debug i per `debug.yaml.starting_level`. + // 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;