feat(debug): selector d'escena inicial via debug.yaml
Director encadena LOGO → TITLE → INTRO → GAME amb factories injectades al ctor de cada placeholder (sense que cap escena conegui el tipus de la següent). L'escena d'arrencada surt de `debug.yaml` en debug build i és sempre LOGO en release. Comportament: - Release: `debug.yaml` s'ignora; arranca sempre a LOGO. - Debug: es llig `debug.yaml`. Si no existeix, es crea automàticament amb `start_scene: GAME` per no arrossegar 15 s de placeholders al dia a dia de gameplay. Valor invàlid o YAML corrupte → warning + GAME, però el fitxer no es reescriu (respectem l'edició manual). `debug.yaml` viu a `$XDG_CONFIG_HOME/jailgames/aee_2026/`, al costat de `config.yaml`, i queda fora del repo (ajust per màquina de dev).
This commit is contained in:
1 parent
d31483124e
commit
87a9f098b5
4 files changed
+180
-1
No files matched your search
@@ -76,6 +76,7 @@ set(APP_SOURCES
|
|||||||
source/core/resources/palette_loader.cpp
|
source/core/resources/palette_loader.cpp
|
||||||
source/utils/easing.cpp
|
source/utils/easing.cpp
|
||||||
source/utils/utils.cpp
|
source/utils/utils.cpp
|
||||||
|
source/game/debug_config.cpp
|
||||||
source/game/options.cpp
|
source/game/options.cpp
|
||||||
source/game/scenes/placeholder_input.cpp
|
source/game/scenes/placeholder_input.cpp
|
||||||
source/game/scenes/logo_scene.cpp
|
source/game/scenes/logo_scene.cpp
|
||||||
|
|||||||
@@ -21,8 +21,12 @@
|
|||||||
#include "core/resources/resource_cache.hpp"
|
#include "core/resources/resource_cache.hpp"
|
||||||
#include "core/scene/scene.hpp"
|
#include "core/scene/scene.hpp"
|
||||||
#include "core/system/event_buffer.hpp"
|
#include "core/system/event_buffer.hpp"
|
||||||
|
#include "game/debug_config.hpp"
|
||||||
#include "game/options.hpp"
|
#include "game/options.hpp"
|
||||||
#include "game/scenes/game_scene.hpp"
|
#include "game/scenes/game_scene.hpp"
|
||||||
|
#include "game/scenes/intro_scene.hpp"
|
||||||
|
#include "game/scenes/logo_scene.hpp"
|
||||||
|
#include "game/scenes/title_scene.hpp"
|
||||||
#include "game/ui/ui_palette.hpp"
|
#include "game/ui/ui_palette.hpp"
|
||||||
#include "utils/defaults.hpp"
|
#include "utils/defaults.hpp"
|
||||||
|
|
||||||
@@ -91,7 +95,45 @@ Director::Director() {
|
|||||||
RenderInfo::get().init(Options::buildRenderInfoFrame(UiColor::RENDER_INFO_TEXT, UiColor::RENDER_INFO_SHADOW), hud_text);
|
RenderInfo::get().init(Options::buildRenderInfoFrame(UiColor::RENDER_INFO_TEXT, UiColor::RENDER_INFO_SHADOW), hud_text);
|
||||||
Notifier::get().init(hud_text, ui_palette);
|
Notifier::get().init(hud_text, ui_palette);
|
||||||
|
|
||||||
current_scene_ = std::make_unique<GameScene>();
|
// Flux d'escenes LOGO → TITLE → INTRO → GAME. Cada placeholder rep una
|
||||||
|
// factory que construeix la següent, construïda bottom-up des de GAME.
|
||||||
|
//
|
||||||
|
// Release: sempre s'arranca a LOGO (el flux complet, el que veu el
|
||||||
|
// jugador final). Debug: s'obri `debug.yaml`; si no existeix, el creem
|
||||||
|
// amb `start_scene: GAME` perquè el dia a dia de gameplay no es mengi
|
||||||
|
// els 15 s de placeholders. L'usuari pot editar-lo a mà per escollir
|
||||||
|
// un punt d'entrada intermedi.
|
||||||
|
#ifdef _DEBUG
|
||||||
|
const auto DEBUG_YAML = system_folder_ + "debug.yaml";
|
||||||
|
DebugConfig::createDefaultIfMissing(DEBUG_YAML);
|
||||||
|
const auto START = DebugConfig::resolveStartScene(DEBUG_YAML);
|
||||||
|
#else
|
||||||
|
const auto START = DebugConfig::StartScene::LOGO;
|
||||||
|
#endif
|
||||||
|
std::cout << "Director: start_scene=" << DebugConfig::toString(START) << '\n';
|
||||||
|
|
||||||
|
auto make_game = []() -> std::unique_ptr<Scene> { return std::make_unique<GameScene>(); };
|
||||||
|
auto make_intro = [hud_text, make_game]() -> std::unique_ptr<Scene> {
|
||||||
|
return std::make_unique<IntroScene>(hud_text, make_game);
|
||||||
|
};
|
||||||
|
auto make_title = [hud_text, make_intro]() -> std::unique_ptr<Scene> {
|
||||||
|
return std::make_unique<TitleScene>(hud_text, make_intro);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (START) {
|
||||||
|
case DebugConfig::StartScene::LOGO:
|
||||||
|
current_scene_ = std::make_unique<LogoScene>(hud_text, make_title);
|
||||||
|
break;
|
||||||
|
case DebugConfig::StartScene::TITLE:
|
||||||
|
current_scene_ = make_title();
|
||||||
|
break;
|
||||||
|
case DebugConfig::StartScene::INTRO:
|
||||||
|
current_scene_ = make_intro();
|
||||||
|
break;
|
||||||
|
case DebugConfig::StartScene::GAME:
|
||||||
|
current_scene_ = make_game();
|
||||||
|
break;
|
||||||
|
}
|
||||||
last_ticks_ms_ = SDL_GetTicks();
|
last_ticks_ms_ = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +185,8 @@ void Director::processEvents() {
|
|||||||
void Director::switchSceneIfRequested() {
|
void Director::switchSceneIfRequested() {
|
||||||
if (!current_scene_) { return; }
|
if (!current_scene_) { return; }
|
||||||
if (auto next = current_scene_->nextScene()) {
|
if (auto next = current_scene_->nextScene()) {
|
||||||
|
// TODO: migrar a logger unificat (INC-03 a docs/audit-core-rendering.md).
|
||||||
|
std::cout << "Director: scene transition\n";
|
||||||
current_scene_ = std::move(next);
|
current_scene_ = std::move(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "game/debug_config.hpp"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "external/fkyaml_node.hpp"
|
||||||
|
|
||||||
|
namespace DebugConfig {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
auto readFile(const std::string& path) -> std::string {
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (!file) { return {}; }
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << file.rdbuf();
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fromName(const std::string& name) -> StartScene {
|
||||||
|
if (name == "LOGO") { return StartScene::LOGO; }
|
||||||
|
if (name == "TITLE") { return StartScene::TITLE; }
|
||||||
|
if (name == "INTRO") { return StartScene::INTRO; }
|
||||||
|
if (name == "GAME") { return StartScene::GAME; }
|
||||||
|
return StartScene::GAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto isKnown(const std::string& name) -> bool {
|
||||||
|
return name == "LOGO" || name == "TITLE" || name == "INTRO" || name == "GAME";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto parseStartScene(const std::string& yaml_content) -> StartScene {
|
||||||
|
if (yaml_content.empty()) { return StartScene::GAME; }
|
||||||
|
try {
|
||||||
|
auto yaml = fkyaml::node::deserialize(yaml_content);
|
||||||
|
if (!yaml.contains("start_scene")) { return StartScene::GAME; }
|
||||||
|
const auto RAW = yaml["start_scene"].get_value<std::string>();
|
||||||
|
if (!isKnown(RAW)) {
|
||||||
|
std::cerr << "debug.yaml: start_scene \"" << RAW << "\" invàlid, arrancant a GAME\n";
|
||||||
|
return StartScene::GAME;
|
||||||
|
}
|
||||||
|
return fromName(RAW);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "debug.yaml: error parsejant: " << e.what() << " — arrancant a GAME\n";
|
||||||
|
return StartScene::GAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolveStartScene(const std::string& file_path) -> StartScene {
|
||||||
|
std::error_code error;
|
||||||
|
if (!std::filesystem::exists(file_path, error)) {
|
||||||
|
// Silenciós: la no-existència és el cas per defecte en una màquina
|
||||||
|
// neta, no un error. L'usuari crea el fitxer quan el necessita.
|
||||||
|
return StartScene::GAME;
|
||||||
|
}
|
||||||
|
const auto CONTENT = readFile(file_path);
|
||||||
|
if (CONTENT.empty()) {
|
||||||
|
std::cerr << "debug.yaml: fitxer buit a " << file_path << ", arrancant a GAME\n";
|
||||||
|
return StartScene::GAME;
|
||||||
|
}
|
||||||
|
return parseStartScene(CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto toString(StartScene scene) -> const char* {
|
||||||
|
switch (scene) {
|
||||||
|
case StartScene::LOGO:
|
||||||
|
return "LOGO";
|
||||||
|
case StartScene::TITLE:
|
||||||
|
return "TITLE";
|
||||||
|
case StartScene::INTRO:
|
||||||
|
return "INTRO";
|
||||||
|
case StartScene::GAME:
|
||||||
|
return "GAME";
|
||||||
|
}
|
||||||
|
return "GAME";
|
||||||
|
}
|
||||||
|
|
||||||
|
void createDefaultIfMissing(const std::string& file_path) {
|
||||||
|
std::error_code error;
|
||||||
|
if (std::filesystem::exists(file_path, error)) { return; }
|
||||||
|
std::ofstream file(file_path);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "debug.yaml: no s'ha pogut crear " << file_path << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file << "# debug.yaml — ajust personal de dev (no versionat).\n";
|
||||||
|
file << "# Valors vàlids: LOGO, TITLE, INTRO, GAME.\n";
|
||||||
|
file << "start_scene: GAME\n";
|
||||||
|
std::cout << "debug.yaml: creat a " << file_path << " amb start_scene=GAME\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace DebugConfig
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Configuració personal de desenvolupament, carregada de `debug.yaml` dins la
|
||||||
|
// carpeta de sistema (`$XDG_CONFIG_HOME/jailgames/aee_2026/debug.yaml`). No es
|
||||||
|
// versiona: cada dev el crea a la seua màquina. Si no existeix, s'aplica el
|
||||||
|
// valor per defecte (GAME) sense warning de nivell error.
|
||||||
|
namespace DebugConfig {
|
||||||
|
|
||||||
|
enum class StartScene : std::uint8_t { LOGO,
|
||||||
|
TITLE,
|
||||||
|
INTRO,
|
||||||
|
GAME };
|
||||||
|
|
||||||
|
// Parseja un contingut YAML en cru. Si no conté `start_scene`, el valor és
|
||||||
|
// desconegut o el YAML és invàlid, retorna GAME. Pura: no fa I/O.
|
||||||
|
[[nodiscard]] auto parseStartScene(const std::string& yaml_content) -> StartScene;
|
||||||
|
|
||||||
|
// Llig el fitxer i delega a parseStartScene. Si el fitxer no existeix
|
||||||
|
// retorna GAME sense warning (és el cas per defecte en una màquina neta);
|
||||||
|
// si existeix però el valor és invàlid, imprimeix un warning i retorna
|
||||||
|
// GAME.
|
||||||
|
[[nodiscard]] auto resolveStartScene(const std::string& file_path) -> StartScene;
|
||||||
|
|
||||||
|
// Nom canònic de l'enum (útil per a logs): "LOGO" / "TITLE" / "INTRO" / "GAME".
|
||||||
|
[[nodiscard]] auto toString(StartScene scene) -> const char*;
|
||||||
|
|
||||||
|
// Si el fitxer no existeix, el crea amb `start_scene: GAME` (el valor
|
||||||
|
// per defecte pensat per al dia a dia de dev). Si ja existeix, no el
|
||||||
|
// toca — preservem qualsevol edició manual de l'usuari. Usat només al
|
||||||
|
// path de debug del Director (el binari release ignora `debug.yaml`).
|
||||||
|
void createDefaultIfMissing(const std::string& file_path);
|
||||||
|
|
||||||
|
} // namespace DebugConfig
|
||||||
Reference in new issue
Block a user