From cba46642c6467fb2524b250b58ee81c21ce5a95a Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 11 May 2026 19:47:28 +0200 Subject: [PATCH] =?UTF-8?q?feat(mummy):=20SPAWN=20dirigit=20per=20l'animac?= =?UTF-8?q?i=C3=B3=20raise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/entities/mummy/mummy.gif | Bin 1207 -> 1953 bytes data/entities/mummy/mummy.yaml | 13 ++++---- source/game/entities/concrete/mummy.cpp | 38 +++++++++++++++++------- source/game/entities/concrete/mummy.hpp | 6 ++++ source/game/entities/hazard.hpp | 5 ++++ tests/test_entities_smoke.cpp | 13 ++++---- tests/test_whip_attack.cpp | 13 ++++---- 7 files changed, 60 insertions(+), 28 deletions(-) diff --git a/data/entities/mummy/mummy.gif b/data/entities/mummy/mummy.gif index 9352a9688e68beb38eaee257ad0197571f526203..7b56a4af635aec71301afcd35825d480b6ebdfa1 100644 GIT binary patch delta 786 zcmV+t1MU2`384=OM@dFFH(@{k;E@Sd0pPJ<0|Wv8lVk)=f3N-a+;{K&_uz*w{`lmV zZ~pn{r?39{?6>d!`|!s?7s$W{ z7J+}@vfuw^Lj#YMfjqm(j1vTd!F3VqKSB`9A%<{24@fX%LQsHPCZPwl(2F?$n*agQ zutB$=LKPxxe;9TqCV?4Ns$I`H;%`h~uB9;t31cHTsGNd$fV8(xG z^B=RY2o)~>QqO^445JvwNX9an(Ode9pc8LX!6S4qHyR|}8qi~d4^nJ|IXvRJ))+?| zsL&8BJlO?a=teqPV1_S|qZ@8mjCk$qJEQU;O-2<&e^jS}S64BQSL+Lg4_1Kb)BoqR_`_I!Y0lSacD_q_HaVsEO&e_>8I* z=^^D9f83$68Ja~=?^?uh(wNlqCd}Eb8~(f$9umh9x&_EifC^J0G-4`TdB=>}tJNs~hBz!at< z^yDFFXwvpV<`v8u~7zcLJ3pQdQi!Vehda5kY2 zF^-*86;N1zaSNYC<3${b2R>kxJg&Th8mru8TTi8=6%EN*#A#+P!J&>@f^}&RsuDcA zq$7i9GoYaoCNaT=7_>==O8wN4iY!XYXIg|&Z7Ulmppnousi8IWIXyr)d4ibWy<{=>YQz= zu2n@lFm7f0Qaal*mo7Y*UR*SDO;1USXr4)4hIo}bV{}G&p!vn_o<}X`!3~=3Em?cP Qqs9GA@QWLDK?DE*JNT|?() : 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).