From da361a14527ddcf1b0667724dbd037aa5bf41622 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 12 May 2026 13:58:45 +0200 Subject: [PATCH] feat(editor): mostra mommies i altres dynamic hazards al mode editor --- data/maps/p1/r1.yaml | 22 +++++++++++--- data/maps/p1/r2.yaml | 3 -- source/game/entities/concrete/mummy.cpp | 30 +++++++++++-------- source/game/entities/concrete/mummy.hpp | 6 ++++ source/game/entities/entity.hpp | 5 +++- source/game/entities/entity_manager.hpp | 9 ++++++ .../entities/entity_manager_loadlevel.cpp | 8 +++++ source/game/scenes/game_scene.cpp | 8 +++++ 8 files changed, 71 insertions(+), 20 deletions(-) diff --git a/data/maps/p1/r1.yaml b/data/maps/p1/r1.yaml index cf0a269..752f7c2 100644 --- a/data/maps/p1/r1.yaml +++ b/data/maps/p1/r1.yaml @@ -114,8 +114,6 @@ entities: sarc_position: [0, 3] - type: sarcophagus sarc_position: [1, 3] - - type: sarcophagus - sarc_position: [2, 3] - type: sarcophagus sarc_position: [3, 3] - type: sarcophagus @@ -129,8 +127,6 @@ entities: - type: torch position: [4, 2] - type: door - - type: mummy - position: [10, 11] - type: column position: [3, 4] frame: 2 @@ -140,3 +136,21 @@ entities: - type: stone position: [4, 4] frame: 19 + - type: column + position: [13, 10] + frame: 4 + - type: stone + position: [12, 10] + frame: 13 + - type: stone + position: [11, 10] + frame: 23 + - type: hole + position: [5, 9] + - type: hole + position: [9, 11] + - type: stone + position: [10, 10] + frame: 18 + - type: mummy + position: [10, 13] diff --git a/data/maps/p1/r2.yaml b/data/maps/p1/r2.yaml index 9199ace..8d08951 100644 --- a/data/maps/p1/r2.yaml +++ b/data/maps/p1/r2.yaml @@ -131,9 +131,6 @@ entities: - type: sarcophagus sarc_position: [3, 4] - type: door - # Sense `position`: la porta sempre viu al mur trasser. La columna - # transitable la dicta `geometry.door_col`; el render i la caixa - # de col·lisió es deriven d'aquesta dada al loadFromLevel. - type: mummy position: [10, 7] - type: mummy diff --git a/source/game/entities/concrete/mummy.cpp b/source/game/entities/concrete/mummy.cpp index a281b3d..91e9e67 100644 --- a/source/game/entities/concrete/mummy.cpp +++ b/source/game/entities/concrete/mummy.cpp @@ -142,18 +142,10 @@ Mummy::Mummy(const fkyaml::node& yaml) changeDirection(0.0F, 1.0F); } - // Estat SPAWN inicial: la mòmia naix en SPAWNING amb l'animació - // `raise` activa (16 frames @ 8fps, no-loop). La duració del SPAWN - // la marca la pròpia animació — `Mummy::update` la tickeja, i quan - // `completed()`, crida `endSpawn()` i restaura la `walk_*` per la - // direcció actual. Passem 0 al countdown del DynamicHazard perquè - // no s'utilitza en aquest cas (el nostre `update` retorna abans). - enterSpawn(0.0F); - anim_ = anim_raise_; - if (anim_.has_value()) { - anim_->reset(); - flip_ = anim_->flipMode(); - } + // L'estat SPAWNING amb l'animació `raise` s'arma a `onJustSpawned`, + // no al ctor. Així el manager pot instanciar la mòmia sense disparar + // la cinemàtica (cas del mode editor) i veure-la ja en ROAMING amb + // la `walk_*` triada per `changeDirection` més amunt. // Configuració de l'estat HURT: opcional. Si el bloc no apareix, la // mòmia no entrarà mai a HURT efectiu (defensiu); el WhipAttack la @@ -180,6 +172,20 @@ Mummy::Mummy(const fkyaml::node& yaml) } } +void Mummy::onJustSpawned() { + // Cinemàtica d'aparició: la mòmia "s'aixeca" reproduint `raise` + // (16 frames no-loop). El countdown del DynamicHazard NO s'utilitza + // (passem 0): `Mummy::update` controla la transició a ACTIVE per + // `anim_->completed()`. Cap crida a la base perquè el `triggerSpawnExit` + // del DynamicHazard arma aquest mateix countdown que aquí no toca. + enterSpawn(0.0F); + anim_ = anim_raise_; + if (anim_.has_value()) { + anim_->reset(); + flip_ = anim_->flipMode(); + } +} + void Mummy::changeDirection(float dx, float dy) { setDirection(dx, dy); // Una animació per cada cardinal — el flip viu al YAML diff --git a/source/game/entities/concrete/mummy.hpp b/source/game/entities/concrete/mummy.hpp index e94eb92..1d9ff62 100644 --- a/source/game/entities/concrete/mummy.hpp +++ b/source/game/entities/concrete/mummy.hpp @@ -61,6 +61,12 @@ class Mummy : public DynamicHazard { // En estat ROAMING delega a `step()` com sempre via la base. void update(float delta_time) override; + // Inicia la fase SPAWNING amb l'animació `raise`. Es crida al + // spawnPendingActors() del joc, no al ctor: així l'editor (que + // materialitza les entitats sense aquest hook) veu la mòmia ja en + // ROAMING visible, sense quedar-se "soterrada". + void onJustSpawned() override; + // Contacte amb el jugador: publica `player_hit` amb el dany. void onPlayerContact(Player* player) override; diff --git a/source/game/entities/entity.hpp b/source/game/entities/entity.hpp index 23f11ed..068669a 100644 --- a/source/game/entities/entity.hpp +++ b/source/game/entities/entity.hpp @@ -102,7 +102,10 @@ class Entity { // entitat. La base no fa res; `DynamicHazard` override per disparar // el seu `triggerSpawnExit` (anima la fase EMERGING). Substitueix el // `dynamic_cast(observer)->triggerSpawnExit()` que el - // manager feia abans — ara el dispatch és polimòrfic. + // manager feia abans — ara el dispatch és polimòrfic. Entitats que + // requereixen una fase d'aparició (mòmia amb animació `raise`) + // l'inicien aquí, NO al ctor: així `materializePendingActorsStatic` + // (editor) les salta i queden visibles directament. virtual void onJustSpawned(); // Hook just abans que el manager esborri l'entitat (via diff --git a/source/game/entities/entity_manager.hpp b/source/game/entities/entity_manager.hpp index 9aa2b09..101ccd8 100644 --- a/source/game/entities/entity_manager.hpp +++ b/source/game/entities/entity_manager.hpp @@ -134,6 +134,15 @@ class EntityManager { // `respawnPlayersInstant` (salt debug). void spawnPendingActors(); + // Materialitza els pending actors al món sense disparar la cinemàtica + // de SPAWN (`onJustSpawned`). Pensat per a l'editor: vol veure les + // mòmies a la seva posició inicial per poder editar-les, però no + // disparar l'animació EMERGING (l'editor congela el món, l'animació + // no avançaria i quedaria invisible). En sortir de l'editor, GameScene + // fa un `reload` que torna els actors a `pending_actors_` i el flux + // normal els spawneja amb la cinemàtica al final del ROOM_INTRO. + void materializePendingActorsStatic(); + // Crea un actor on-demand i el deixa en EMERGING (com a // `spawnPendingActors` però per a un sol). Pensat per a futurs // generadors (StaticHazards que produeixen DynamicHazards en runtime, diff --git a/source/game/entities/entity_manager_loadlevel.cpp b/source/game/entities/entity_manager_loadlevel.cpp index 8ce57ed..443e6a3 100644 --- a/source/game/entities/entity_manager_loadlevel.cpp +++ b/source/game/entities/entity_manager_loadlevel.cpp @@ -527,6 +527,14 @@ void EntityManager::spawnPendingActors() { pending_actors_.clear(); } +void EntityManager::materializePendingActorsStatic() { + // Igual que `spawnPendingActors` però sense `onJustSpawned`: l'actor + // queda en el seu estat inicial (mòmia: ROAMING, walk_east) en lloc + // d'EMERGING. Vegeu el comentari del header per la motivació. + for (const auto& pa : pending_actors_) { addSpawn(pa.spawn_node); } + pending_actors_.clear(); +} + auto EntityManager::spawnActor(const std::string& type, int col, int row) -> DynamicHazard* { // Construïm un node de spawn sintètic amb el mínim que `placeGeneric` // necessita: `type` i `position: [col, row]`. Si el caller vol diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index f30d941..d63ea26 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -1484,6 +1484,14 @@ void GameScene::update(int delta_ms) { // totes maneres) i es reactivaran amb el spawn instantani // a la sortida. loadRoom(Levels::LevelManager::get().currentPath()); + // Materialitzem els dynamic hazards (mòmies, roques) en estat + // estàtic perquè es vegin a la posició inicial: així l'editor + // les pot seleccionar i moure com qualsevol altra entitat. El + // flux normal del joc les enruta a `pending_actors_` per a + // disparar la cinemàtica EMERGING al final del ROOM_INTRO; a + // l'editor el món està congelat i aquesta animació quedaria + // travada. + entity_manager_.materializePendingActorsStatic(); } else if (WAS_ACTIVE && !NOW_ACTIVE) { // Acabem de sortir: el writer ja ha persistit el YAML. // Carreguem fresh i fem spawn instantani sense cinemàtica.