test(game-scene): extracció RoomFsm i smoke test de la FSM de sala
This commit is contained in:
1 parent
9a2c677153
commit
63bb41425a
6 files changed
+493
-211
No files matched your search
@@ -0,0 +1,131 @@
|
||||
#include "game/scenes/room_fsm.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
|
||||
#include "core/locale/locale.hpp"
|
||||
#include "core/resources/resource_helper.hpp"
|
||||
#include "external/fkyaml_node.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// Llegeix un sub-camp sencer d'un node YAML. Si manca, retorna el
|
||||
// fallback. Dissenyat per al consum puntual de `loadConfig`.
|
||||
auto readChildInt(const fkyaml::node& parent, const char* key, int fallback) -> int {
|
||||
if (!parent.contains(key)) { return fallback; }
|
||||
return parent[key].get_value<int>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace RoomFsm {
|
||||
|
||||
auto loadConfig(const std::string& yaml_path) -> Config {
|
||||
Config 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 << "RoomFsm: " << yaml_path << " absent — uso fallbacks compilats\n";
|
||||
return cfg;
|
||||
}
|
||||
const auto* begin = reinterpret_cast<const char*>(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<std::string>());
|
||||
}
|
||||
if (n.contains("exit_curve")) {
|
||||
cfg.text_exit_curve = UiConfig::curveByName(n["exit_curve"].get_value<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "RoomFsm: 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;
|
||||
}
|
||||
|
||||
auto advance(State current, Transition& t, const Config& cfg, int delta_ms) -> State {
|
||||
if (current == State::PLAYING) { return current; }
|
||||
|
||||
t.elapsed_ms += delta_ms;
|
||||
|
||||
if (current == State::ROOM_INTRO) {
|
||||
const std::array<int, 5> DURS = {
|
||||
cfg.intro_fade_in_ms,
|
||||
cfg.intro_text_delay_ms,
|
||||
cfg.intro_text_enter_ms,
|
||||
cfg.intro_text_hold_ms,
|
||||
cfg.intro_text_exit_ms,
|
||||
};
|
||||
while (t.phase < static_cast<int>(DURS.size()) && t.elapsed_ms >= DURS[t.phase]) {
|
||||
t.elapsed_ms -= DURS[t.phase];
|
||||
++t.phase;
|
||||
}
|
||||
if (t.phase >= static_cast<int>(DURS.size())) { return State::PLAYING; }
|
||||
return current;
|
||||
}
|
||||
|
||||
// ROOM_OUTRO
|
||||
const std::array<int, 4> DURS = {
|
||||
cfg.outro_text_enter_ms,
|
||||
cfg.outro_text_hold_ms,
|
||||
cfg.outro_text_exit_ms,
|
||||
cfg.outro_fade_out_ms,
|
||||
};
|
||||
while (t.phase < static_cast<int>(DURS.size()) && t.elapsed_ms >= DURS[t.phase]) {
|
||||
t.elapsed_ms -= DURS[t.phase];
|
||||
++t.phase;
|
||||
}
|
||||
// Placeholder fins a l'itinerari de sales: l'OUTRO recau a INTRO.
|
||||
// TODO(itinerari): aquest mateix retorn sortirà cap a una nova
|
||||
// sala (incrementant `room_index_` i recreant tilemap/entitats al
|
||||
// GameScene) abans de tornar a ROOM_INTRO.
|
||||
if (t.phase >= static_cast<int>(DURS.size())) { return State::ROOM_INTRO; }
|
||||
return current;
|
||||
}
|
||||
|
||||
auto formatRoomTitle(int n) -> std::string {
|
||||
auto tpl = Locale::get().text("game.room_intro");
|
||||
constexpr std::string_view PLACEHOLDER = "{n}";
|
||||
auto pos = tpl.find(PLACEHOLDER);
|
||||
if (pos != std::string::npos) {
|
||||
tpl.replace(pos, PLACEHOLDER.size(), std::to_string(n));
|
||||
}
|
||||
return tpl;
|
||||
}
|
||||
|
||||
auto stateName(State s) -> const char* {
|
||||
switch (s) {
|
||||
case State::ROOM_INTRO:
|
||||
return "ROOM_INTRO";
|
||||
case State::PLAYING:
|
||||
return "PLAYING";
|
||||
case State::ROOM_OUTRO:
|
||||
return "ROOM_OUTRO";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
} // namespace RoomFsm
|
||||
Reference in new issue
Block a user