feat(game-scene): FSM mínima ROOM_INTRO/PLAYING/ROOM_OUTRO sense efectes
This commit is contained in:
1 parent
decf9f5d90
commit
651db50a93
2 files changed
+100
-4
No files matched your search
@@ -263,11 +263,13 @@ GameScene::GameScene(Config config)
|
|||||||
// Les surfaces de les entitats es carreguen al Resource::Cache aquí.
|
// Les surfaces de les entitats es carreguen al Resource::Cache aquí.
|
||||||
entity_manager_.loadFromLevel(config_.map_yaml);
|
entity_manager_.loadFromLevel(config_.map_yaml);
|
||||||
|
|
||||||
// Subscripcions bàsiques al bus del nivell: de moment només logs, sense
|
// Subscripcions bàsiques al bus del nivell. `level_complete` també
|
||||||
// tocar ni la vida del jugador ni l'estat de la escena. Serà la base
|
// dispara la transició a ROOM_OUTRO si encara estem a PLAYING; les
|
||||||
// sobre la qual cablejar HUD i fi de nivell més endavant.
|
// re-emissions es filtren per estat per no re-entrar mai des d'una
|
||||||
entity_manager_.getBus().subscribe("level_complete", [](const Event& ev) {
|
// transició ja en marxa.
|
||||||
|
entity_manager_.getBus().subscribe("level_complete", [this](const Event& ev) {
|
||||||
std::cout << "[LEVEL COMPLETE] player_id=" << ev.player_id << '\n';
|
std::cout << "[LEVEL COMPLETE] player_id=" << ev.player_id << '\n';
|
||||||
|
if (state_ == State::PLAYING) { setState(State::ROOM_OUTRO); }
|
||||||
});
|
});
|
||||||
entity_manager_.getBus().subscribe("player_hit", [](const Event& ev) {
|
entity_manager_.getBus().subscribe("player_hit", [](const Event& ev) {
|
||||||
const auto* dmg = std::get_if<int>(&ev.payload);
|
const auto* dmg = std::get_if<int>(&ev.payload);
|
||||||
@@ -395,10 +397,64 @@ GameScene::GameScene(Config config)
|
|||||||
auto pause_surface = Resource::Cache::get().getSurface(PAUSE_FONT_KEY);
|
auto pause_surface = Resource::Cache::get().getSurface(PAUSE_FONT_KEY);
|
||||||
pause_font_ = std::make_shared<Text>(pause_surface, UiConfig::get().placeholderFont().fnt);
|
pause_font_ = std::make_shared<Text>(pause_surface, UiConfig::get().placeholderFont().fnt);
|
||||||
pause_font_->setDefaultKerning(UiConfig::get().placeholderFont().kerning);
|
pause_font_->setDefaultKerning(UiConfig::get().placeholderFont().kerning);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
std::cout << "[ROOM] state=ROOM_INTRO (initial)" << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
GameScene::~GameScene() = default;
|
GameScene::~GameScene() = default;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Nom textual d'un State per als logs de transició. L'enum és privat
|
||||||
|
// a GameScene, així que la funció ha de viure aquí com a lambda
|
||||||
|
// local del setState — la deixem com a constexpr que rep l'enum
|
||||||
|
// intern via auto-deducció a partir del seu valor entrant.
|
||||||
|
constexpr auto stateName(GameScene::State s) -> const char* {
|
||||||
|
using S = GameScene::State;
|
||||||
|
switch (s) {
|
||||||
|
case S::ROOM_INTRO:
|
||||||
|
return "ROOM_INTRO";
|
||||||
|
case S::PLAYING:
|
||||||
|
return "PLAYING";
|
||||||
|
case S::ROOM_OUTRO:
|
||||||
|
return "ROOM_OUTRO";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void GameScene::setState(State new_state) {
|
||||||
|
if (new_state == state_) { return; }
|
||||||
|
std::cout << "[ROOM] " << stateName(state_) << " -> " << stateName(new_state) << '\n';
|
||||||
|
state_ = new_state;
|
||||||
|
// Els acumuladors de la transició de sala (sub-fase, elapsed) viuran
|
||||||
|
// aquí als commits següents. De moment no en tenim cap.
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::updateRoomTransition(int /*delta_ms*/) {
|
||||||
|
// Implementació mínima: no hi ha sub-fases ni fades encara, així que
|
||||||
|
// ROOM_INTRO i ROOM_OUTRO són efectivament instantanis. El bucle de
|
||||||
|
// sala queda demostrat (INTRO → PLAYING; al level_complete OUTRO →
|
||||||
|
// INTRO → PLAYING) sense efectes visuals. Els commits 3 i 4
|
||||||
|
// afegiran timings i sub-fases reals.
|
||||||
|
if (state_ == State::ROOM_INTRO) {
|
||||||
|
setState(State::PLAYING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state_ == State::ROOM_OUTRO) {
|
||||||
|
// Placeholder fins a l'itinerari de sales: tornem a la mateixa
|
||||||
|
// sala. No es reinicia res — porta + sarcòfags continuen a
|
||||||
|
// l'estat OPEN, com era l'acord.
|
||||||
|
// TODO(itinerari): seleccionar la pròxima sala (incrementar
|
||||||
|
// `room_index_`, intercanviar `config_.map_yaml`, recrear
|
||||||
|
// tilemap/entitats) abans de tornar a ROOM_INTRO.
|
||||||
|
setState(State::ROOM_INTRO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GameScene::spawnDust(float feet_x_center, float feet_y, bool is_landing) {
|
void GameScene::spawnDust(float feet_x_center, float feet_y, bool is_landing) {
|
||||||
// Mida del sprite de Dust llegida del seu YAML. Hardcoded per
|
// Mida del sprite de Dust llegida del seu YAML. Hardcoded per
|
||||||
// simplificar — si canvia, només cal tocar dust.yaml + aquesta
|
// simplificar — si canvia, només cal tocar dust.yaml + aquesta
|
||||||
@@ -516,6 +572,11 @@ void GameScene::update(int delta_ms) {
|
|||||||
handlePauseInput();
|
handlePauseInput();
|
||||||
updatePauseTransition(delta_ms);
|
updatePauseTransition(delta_ms);
|
||||||
|
|
||||||
|
// Transició de sala: viu en temps real (no escalada per la pausa)
|
||||||
|
// perquè és UI. Avança avans del bloc de món perquè quan PLAYING
|
||||||
|
// arrenque ja sigui efectiu en el mateix frame.
|
||||||
|
updateRoomTransition(delta_ms);
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Toggle de l'editor: només si no hi ha cap transició de pausa ni
|
// Toggle de l'editor: només si no hi ha cap transició de pausa ni
|
||||||
// pausa consolidada. L'editor mateix no consumeix la tecla — la
|
// pausa consolidada. L'editor mateix no consumeix la tecla — la
|
||||||
|
|||||||
@@ -38,6 +38,18 @@ class GameScene : public Scene {
|
|||||||
|
|
||||||
[[nodiscard]] static auto defaultConfig() -> Config;
|
[[nodiscard]] static auto defaultConfig() -> Config;
|
||||||
|
|
||||||
|
// Estat de l'escena dins del cicle d'una sala. La màquina és interna a
|
||||||
|
// GameScene: SceneManager segueix tractant GameScene com una sola
|
||||||
|
// escena. ROOM_INTRO i ROOM_OUTRO bloquegen input i render dels
|
||||||
|
// jugadors; l'EntityManager segueix avançant i el HUD/Notifier també.
|
||||||
|
// L'enum es declara públic perquè els helpers de log (stateName) i
|
||||||
|
// tests viuen fora de la classe; el camp `state_` segueix privat.
|
||||||
|
enum class State : std::uint8_t {
|
||||||
|
ROOM_INTRO, // Entrada de sala: fade in + text d'entrada.
|
||||||
|
PLAYING, // Joc normal.
|
||||||
|
ROOM_OUTRO, // Sortida de sala: text de sortida + fade out.
|
||||||
|
};
|
||||||
|
|
||||||
explicit GameScene(Config config = defaultConfig());
|
explicit GameScene(Config config = defaultConfig());
|
||||||
~GameScene() override;
|
~GameScene() override;
|
||||||
|
|
||||||
@@ -45,6 +57,18 @@ class GameScene : public Scene {
|
|||||||
void render() override;
|
void render() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Punt centralitzat per canviar d'estat. Imprimeix la transició i
|
||||||
|
// reinicialitza els acumuladors de la transició de sala. Cap estat
|
||||||
|
// s'ha de canviar fora d'aquí.
|
||||||
|
void setState(State new_state);
|
||||||
|
|
||||||
|
// Avança la transició activa (sub-fases, timings). En aquesta fita
|
||||||
|
// les transicions són instantànies (sense efectes); les sub-fases
|
||||||
|
// amb fades i text arribaran als commits següents. S'executa cada
|
||||||
|
// frame amb delta real (no escalat per la pausa) perquè la
|
||||||
|
// transició viu en temps de UI.
|
||||||
|
void updateRoomTransition(int delta_ms);
|
||||||
|
|
||||||
// Consumeix events d'aquest frame (F11 del KeyConfig "pause" o botó
|
// Consumeix events d'aquest frame (F11 del KeyConfig "pause" o botó
|
||||||
// START del gamepad primari) per disparar les transicions de pausa.
|
// START del gamepad primari) per disparar les transicions de pausa.
|
||||||
// No alterna un flag binari: inicia (o inverteix mid-flight) un
|
// No alterna un flag binari: inicia (o inverteix mid-flight) un
|
||||||
@@ -131,6 +155,17 @@ class GameScene : public Scene {
|
|||||||
UiConfig::CurveFn curve{nullptr}; // Corba activa.
|
UiConfig::CurveFn curve{nullptr}; // Corba activa.
|
||||||
} pause_;
|
} pause_;
|
||||||
|
|
||||||
|
// 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`.
|
||||||
|
State state_{State::ROOM_INTRO};
|
||||||
|
|
||||||
|
// Índex de sala que es passa com a paràmetre {n} al text d'intro.
|
||||||
|
// Avui és sempre 1 (no hi ha itinerari de sales encara). Quan
|
||||||
|
// arribi l'itinerari, és el camp que es modificarà al transicionar
|
||||||
|
// de ROOM_OUTRO → ROOM_INTRO per saltar a la pròxima.
|
||||||
|
int room_index_{1};
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Editor modal del tilemap. Mútuament excloent amb la pausa: si la
|
// Editor modal del tilemap. Mútuament excloent amb la pausa: si la
|
||||||
// pausa no està IDLE la tecla de toggle de l'editor s'ignora, i
|
// pausa no està IDLE la tecla de toggle de l'editor s'ignora, i
|
||||||
|
|||||||
Reference in new issue
Block a user