diff --git a/data/scenes/game.yaml b/data/scenes/game.yaml new file mode 100644 index 0000000..93e04ee --- /dev/null +++ b/data/scenes/game.yaml @@ -0,0 +1,24 @@ +# Configuració de la GameScene: timings i corbes de la FSM de sala +# (ROOM_INTRO i ROOM_OUTRO). Tots els valors són ms i tota la geometria +# i font del text es reutilitzen del tema UI (data/ui.yaml > pause + +# notification). Si manca el fitxer o alguna clau, GameScene cau als +# fallbacks compilats; veure `readSceneConfig` a game_scene.cpp. + +room_intro: + fade_in_ms: 500 # negre opac → món visible + text_delay_ms: 500 # espera amb el món net abans de sortir el text + text_enter_ms: 500 # text esquerra → centre + text_hold_ms: 1000 # text quiet al centre + text_exit_ms: 500 # text centre → fora per l'esquerra + +room_outro: + text_enter_ms: 500 + text_hold_ms: 1000 + text_exit_ms: 500 + fade_out_ms: 500 # món visible → negre opac + +text: + # Noms d'easing tal com els resol UiConfig::curveByName (vegeu + # `data/ui.yaml`). El text entra suau i surt accelerant. + enter_curve: outCubic + exit_curve: inCubic diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 29b5250..be6254e 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -74,6 +74,63 @@ namespace { return yaml[key].get_value(); } + // Llegeix un sub-camp sencer del bloc `parent` (o el fallback) sense + // imprimir res. Pensat per a la lectura puntual del data/scenes/game.yaml. + auto readChildInt(const fkyaml::node& parent, const char* key, int fallback) -> int { + if (!parent.contains(key)) { return fallback; } + return parent[key].get_value(); + } + + // Carrega `data/scenes/game.yaml` i emplena un SceneTransitionConfig. + // Si el fitxer falta o algun camp no hi és, els valors de fallback + // que ja porta l'estructura es conserven. Les dues corbes es resolen + // per nom amb `UiConfig::curveByName` (mateix mecanisme que la pausa); + // si el nom és desconegut, cau a `Easing::linear` amb warning al log. + auto readSceneConfig(const std::string& yaml_path) -> GameScene::SceneTransitionConfig { + GameScene::SceneTransitionConfig cfg{}; + cfg.text_enter_curve = UiConfig::curveByName("outCubic"); + cfg.text_exit_curve = UiConfig::curveByName("inCubic"); + + auto bytes = Resource::Helper::loadFile(yaml_path); + if (bytes.empty()) { + std::cout << "GameScene: " << yaml_path << " absent — uso fallbacks compilats\n"; + return cfg; + } + const auto* begin = reinterpret_cast(bytes.data()); + const auto* end = begin + bytes.size(); + auto yaml = fkyaml::node::deserialize(begin, end); + + if (yaml.contains("room_intro")) { + const auto& n = yaml["room_intro"]; + cfg.intro_fade_in_ms = readChildInt(n, "fade_in_ms", cfg.intro_fade_in_ms); + cfg.intro_text_delay_ms = readChildInt(n, "text_delay_ms", cfg.intro_text_delay_ms); + cfg.intro_text_enter_ms = readChildInt(n, "text_enter_ms", cfg.intro_text_enter_ms); + cfg.intro_text_hold_ms = readChildInt(n, "text_hold_ms", cfg.intro_text_hold_ms); + cfg.intro_text_exit_ms = readChildInt(n, "text_exit_ms", cfg.intro_text_exit_ms); + } + if (yaml.contains("room_outro")) { + const auto& n = yaml["room_outro"]; + cfg.outro_text_enter_ms = readChildInt(n, "text_enter_ms", cfg.outro_text_enter_ms); + cfg.outro_text_hold_ms = readChildInt(n, "text_hold_ms", cfg.outro_text_hold_ms); + cfg.outro_text_exit_ms = readChildInt(n, "text_exit_ms", cfg.outro_text_exit_ms); + cfg.outro_fade_out_ms = readChildInt(n, "fade_out_ms", cfg.outro_fade_out_ms); + } + if (yaml.contains("text")) { + const auto& n = yaml["text"]; + if (n.contains("enter_curve")) { + cfg.text_enter_curve = UiConfig::curveByName(n["enter_curve"].get_value()); + } + if (n.contains("exit_curve")) { + cfg.text_exit_curve = UiConfig::curveByName(n["exit_curve"].get_value()); + } + } + + std::cout << "GameScene: timings carregats des de " << yaml_path + << " (intro fade " << cfg.intro_fade_in_ms + << "ms, outro fade " << cfg.outro_fade_out_ms << "ms)\n"; + return cfg; + } + // Comprova si `a` i `b` se solapen amb una tolerància positiva al // contacte perfecte (cantos que es toquen = no-solapament). SDL3 ja // ofereix `SDL_HasRectIntersectionFloat`, però aquí volem la mateixa @@ -398,6 +455,10 @@ GameScene::GameScene(Config config) pause_font_ = std::make_shared(pause_surface, UiConfig::get().placeholderFont().fnt); pause_font_->setDefaultKerning(UiConfig::get().placeholderFont().kerning); + // Timings i corbes de la FSM de sala. Llegits del YAML una sola + // vegada; canvis requereixen reiniciar el joc. + transition_config_ = readSceneConfig(Defaults::GameScene::CONFIG_YAML); + // Log d'estat inicial: la FSM arrenca a ROOM_INTRO. En aquesta fita // les transicions són instantànies (sense fades ni text) i el primer // tick d'`updateRoomTransition` ja saltarà a PLAYING. diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index 113bee7..3dd06e6 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -50,6 +50,27 @@ class GameScene : public Scene { ROOM_OUTRO, // Sortida de sala: text de sortida + fade out. }; + // Configuració de la FSM de sala llegida de `data/scenes/game.yaml`. + // Si el YAML manca, els camps queden amb els fallbacks compilats. Es + // carrega un sol cop al ctor; canvis al fitxer requereixen reiniciar. + // Pública pel mateix motiu que State: el reader viu en un anonymous + // namespace fora de la classe per simetria amb els altres readers. + struct SceneTransitionConfig { + int intro_fade_in_ms{500}; + int intro_text_delay_ms{500}; + int intro_text_enter_ms{500}; + int intro_text_hold_ms{1000}; + int intro_text_exit_ms{500}; + + int outro_text_enter_ms{500}; + int outro_text_hold_ms{1000}; + int outro_text_exit_ms{500}; + int outro_fade_out_ms{500}; + + UiConfig::CurveFn text_enter_curve{nullptr}; + UiConfig::CurveFn text_exit_curve{nullptr}; + }; + explicit GameScene(Config config = defaultConfig()); ~GameScene() override; @@ -155,6 +176,10 @@ class GameScene : public Scene { UiConfig::CurveFn curve{nullptr}; // Corba activa. } pause_; + // Configuració de la FSM de sala (intro/outro). Carregada al ctor des + // de `data/scenes/game.yaml`; valors per defecte si manca el YAML. + SceneTransitionConfig transition_config_{}; + // Estat actual de la FSM de sala. S'inicialitza a ROOM_INTRO al ctor // perquè cada vegada que entres al joc passes per la presentació de // la sala. La transició a PLAYING l'orquestra `updateRoomTransition`. diff --git a/source/utils/defaults.hpp b/source/utils/defaults.hpp index bedd74f..0b4149e 100644 --- a/source/utils/defaults.hpp +++ b/source/utils/defaults.hpp @@ -220,6 +220,13 @@ namespace Defaults { } // namespace Editor #endif + // Configuració de l'escena de joc (timings i corbes de la FSM de sala). + // Independent de la piràmide concreta — quan un dia hi haja diferents + // ritmes per piràmide, aquest YAML pot adoptar variants per nivell. + namespace GameScene { + constexpr const char* CONFIG_YAML = "data/scenes/game.yaml"; + } // namespace GameScene + // Rutes d'assets per piràmide. Centralitzats ací perquè `GameScene` no // en contingui cap literal; afegir una piràmide nova serà un altre // conjunt de constants + un Config distint passat al ctor.