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.
This commit is contained in:
1 parent
b147590a39
commit
738cde12bb
5 files changed
+81
-1
No files matched your search
@@ -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<Scene> {
|
||||
auto cfg = GameScene::defaultConfig();
|
||||
cfg.starting_players = STARTING_PLAYERS;
|
||||
|
||||
@@ -103,6 +103,34 @@ namespace DebugConfig {
|
||||
return parseStartingPlayers(CONTENT);
|
||||
}
|
||||
|
||||
auto parseStartingLevel(const std::string& yaml_content) -> std::optional<StartingLevel> {
|
||||
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<int>(),
|
||||
.room = sl["room"].get_value<int>(),
|
||||
};
|
||||
} 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<StartingLevel> {
|
||||
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";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// 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<StartingLevel>;
|
||||
[[nodiscard]] auto resolveStartingLevel(const std::string& file_path) -> std::optional<StartingLevel>;
|
||||
|
||||
// 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*;
|
||||
|
||||
@@ -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<int>(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
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user