diff --git a/data/entities/mummy/mummy.gif b/data/entities/mummy/mummy.gif index 9352a96..7b56a4a 100644 Binary files a/data/entities/mummy/mummy.gif and b/data/entities/mummy/mummy.gif differ diff --git a/data/entities/mummy/mummy.yaml b/data/entities/mummy/mummy.yaml index 3c42890..39071e0 100644 --- a/data/entities/mummy/mummy.yaml +++ b/data/entities/mummy/mummy.yaml @@ -36,18 +36,17 @@ animations: walk_north: { frames: [4, 5, 6, 7], fps: 8, loop: true } walk_east: { frames: [8, 9, 10, 11], fps: 8, loop: true } walk_west: { frames: [8, 9, 10, 11], fps: 8, loop: true, flip_h: true } + raise: { frames: [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27], fps: 8 , loop: false } ai: type: corridor_wanderer direction_choice: random initial_direction: east -# Estat SPAWN: la mòmia s'instancia visible al pixel_rect_ inicial just -# quan GameScene crida `spawnPendingActors()` (a la subfase text-exit -# de ROOM_INTRO, fase 4). El `triggerSpawnExit` es dispara llavors -# mateix; passats `duration_s` segons transita a ROAMING i empieza a -# deambular. Sense aquest bloc, default 0 ⇒ ACTIVE immediat. -spawn: - duration_s: 1.0 +# Estat SPAWN: la mòmia s'instancia al pixel_rect_ inicial quan +# GameScene crida `spawnPendingActors()` (subfase text-exit de +# ROOM_INTRO, fase 4) amb l'animació `raise` activa. La duració del +# SPAWN la marca la pròpia animació (no-loop): al `completed()`, +# transita a ROAMING i comença a deambular. stats: speed: 30 damage: 1 diff --git a/source/game/entities/concrete/mummy.cpp b/source/game/entities/concrete/mummy.cpp index b293655..a281b3d 100644 --- a/source/game/entities/concrete/mummy.cpp +++ b/source/game/entities/concrete/mummy.cpp @@ -106,7 +106,8 @@ Mummy::Mummy(const fkyaml::node& yaml) : anim_walk_south_(buildAnimation(yaml, "walk_south")), anim_walk_north_(buildAnimation(yaml, "walk_north")), anim_walk_east_(buildAnimation(yaml, "walk_east")), - anim_walk_west_(buildAnimation(yaml, "walk_west")) { + anim_walk_west_(buildAnimation(yaml, "walk_west")), + anim_raise_(buildAnimation(yaml, "raise")) { name_ = yaml.contains("name") ? yaml["name"].get_value() : std::string("mummy"); collides_ = true; interactable_ = false; @@ -141,16 +142,18 @@ Mummy::Mummy(const fkyaml::node& yaml) changeDirection(0.0F, 1.0F); } - // Estat SPAWN inicial: la mòmia naix visible i quieta, esperant que el - // GameScene li armi el comptador via `triggerSpawnExit()` (cridat al - // final de la subfase text-exit de ROOM_INTRO). Si el YAML no declara - // `spawn.duration_s`, default 0 ⇒ al primer update post-trigger passa - // directament a ACTIVE. - float spawn_duration_s = 0.0F; - if (yaml.contains("spawn") && yaml["spawn"].contains("duration_s")) { - spawn_duration_s = yaml["spawn"]["duration_s"].get_value(); + // 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(); } - enterSpawn(spawn_duration_s); // Configuració de l'estat HURT: opcional. Si el bloc no apareix, la // mòmia no entrarà mai a HURT efectiu (defensiu); el WhipAttack la @@ -692,6 +695,21 @@ void Mummy::hurtStep(float delta_time) { } void Mummy::update(float delta_time) { + if (isSpawning()) { + // SPAWN dirigit per animació: la `raise` és no-loop, així que + // `completed()` ens marca el final. Mentre dura, NO cridem + // `DynamicHazard::update`: la base hi té el countdown i ja + // hauria saltat a ACTIVE (passem `enterSpawn(0)`), i el `step()` + // d'IA mouria la mòmia abans de "aixecar-se". En completar, + // tornem a la `walk_*` que toca segons la direcció ja triada + // al ctor (initial_direction de l'IA). + if (anim_.has_value()) { anim_->tick(delta_time); } + if (anim_.has_value() && anim_->completed()) { + endSpawn(); + changeDirection(getDirX(), getDirY()); + } + return; + } if (state_ == State::HURT) { // Avancem l'animació inline (no podem cridar `Entity::update` ni // `DynamicHazard::update`: el primer salta una capa virtual i el diff --git a/source/game/entities/concrete/mummy.hpp b/source/game/entities/concrete/mummy.hpp index 3a766a9..e94eb92 100644 --- a/source/game/entities/concrete/mummy.hpp +++ b/source/game/entities/concrete/mummy.hpp @@ -177,6 +177,12 @@ class Mummy : public DynamicHazard { AnimationComponent anim_walk_east_; AnimationComponent anim_walk_west_; + // Animació d'aparició (no-loop). Mentre `isSpawning()`, `update()` + // la tickeja sense moure la mòmia; quan `completed()`, la mòmia + // transita a ACTIVE via `endSpawn()` i conmuta a la walk_* que + // toca segons direcció. + AnimationComponent anim_raise_; + // Estat HURT i la seua configuració. `hurt_speed_` és l'efectiva // (resultat d'aplicar el `speed` del bloc `hurt:` o, si val 0, la // velocitat normal de la mòmia). diff --git a/source/game/entities/hazard.hpp b/source/game/entities/hazard.hpp index b200b57..31e6fe3 100644 --- a/source/game/entities/hazard.hpp +++ b/source/game/entities/hazard.hpp @@ -96,6 +96,11 @@ class DynamicHazard : public Hazard { // Arma el comptador del SPAWN. A partir d'aquí, cada `update(dt)` // descompta `dt` fins arribar a 0 i transitar a ACTIVE. void triggerSpawnExit() { spawn_exit_armed_ = true; } + // Transició directa a ACTIVE, sense passar pel countdown. Pensat + // per a subclasses amb SPAWN dirigit per animació: gestionen el + // seu propi `update()` mentre `isSpawning()` i criden ací quan la + // seua animació d'aparició ha completat. + void endSpawn() { spawn_state_ = SpawnState::ACTIVE; } [[nodiscard]] auto isSpawning() const -> bool { return spawn_state_ == SpawnState::SPAWNING; } [[nodiscard]] auto getSpawnDurationS() const -> float { return spawn_duration_s_; } diff --git a/tests/test_entities_smoke.cpp b/tests/test_entities_smoke.cpp index d94e13b..697f329 100644 --- a/tests/test_entities_smoke.cpp +++ b/tests/test_entities_smoke.cpp @@ -69,13 +69,14 @@ namespace { #define REQUIRE(cond) check((cond), #cond, __FILE__, __LINE__) - // Treu la mòmia de l'estat SPAWN inicial (Mummy::Mummy ara hi entra). - // Equival al que fa GameScene al final del text-exit de ROOM_INTRO: arma - // el comptador i consumeix la durada amb un update. Tests que operin - // directament sobre HURT/ROAMING l'han de cridar abans. + // Treu la mòmia de l'estat SPAWN inicial (Mummy::Mummy ara hi entra + // amb l'animació `raise` activa). Tickem amb dt gran fins sortir de + // SPAWNING — mateix path que `Mummy::update` executa al final de + // l'animació en partida real. El for limita el bucle defensivament. void exitMummySpawn(Mummy& mummy) { - mummy.triggerSpawnExit(); - mummy.update(mummy.getSpawnDurationS() + 0.001F); + for (int i = 0; i < 32 && mummy.isSpawning(); ++i) { + mummy.update(0.5F); + } } void testFactory() { diff --git a/tests/test_whip_attack.cpp b/tests/test_whip_attack.cpp index cf43c07..8f03f81 100644 --- a/tests/test_whip_attack.cpp +++ b/tests/test_whip_attack.cpp @@ -32,12 +32,15 @@ namespace { #define REQUIRE(cond) check((cond), #cond, __FILE__, __LINE__) - // Treu la mòmia de l'estat SPAWN inicial (Mummy::Mummy ara hi entra). Els - // tests del whip necessiten que la mòmia estigui ACTIVE per reaccionar - // al fueteig — sense això, `enterHurt` torna early per `isSpawning()`. + // Treu la mòmia de l'estat SPAWN inicial (Mummy::Mummy ara hi entra + // amb l'animació `raise` activa). Els tests del whip necessiten que la + // mòmia estigui ACTIVE per reaccionar al fueteig — sense això, + // `enterHurt` torna early per `isSpawning()`. Tickem amb dt gran fins + // sortir de SPAWNING (mateix path real, només accelerat). void exitMummySpawn(Mummy& mummy) { - mummy.triggerSpawnExit(); - mummy.update(mummy.getSpawnDurationS() + 0.001F); + for (int i = 0; i < 32 && mummy.isSpawning(); ++i) { + mummy.update(0.5F); + } } // Caja de jugador "estàndard" AEE: 16×32 amb cantonada (x, y).