diff --git a/data/player/morcus.yaml b/data/player/morcus.yaml index 53fd579..07823bb 100644 --- a/data/player/morcus.yaml +++ b/data/player/morcus.yaml @@ -73,11 +73,8 @@ animations: frame_ms: [60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280] loop: false - # Mort per caiguda a un agujero (DeathCause::HOLE). Frame i durada - # **placeholders** — pendents de Sergio: idealment l'índex de la - # casella buida del spritesheet i una durada curta. `loop: false` - # com `death_spinning` perquè `animationIsCompleted()` dispari el - # respawn timer determinísticament. + # Mort per caiguda a un agujero (DeathCause::HOLE). `loop: false` perquè + # `animationIsCompleted()` dispari el respawn timer determinísticament. death_hole: frames: [15] frame_ms: 60 diff --git a/data/player/sam.yaml b/data/player/sam.yaml index 5fad03a..7c33023 100644 --- a/data/player/sam.yaml +++ b/data/player/sam.yaml @@ -73,11 +73,8 @@ animations: frame_ms: [60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280] loop: false - # Mort per caiguda a un agujero (DeathCause::HOLE). Frame i durada - # **placeholders** — pendents de Sergio: idealment l'índex de la - # casella buida del spritesheet i una durada curta. `loop: false` - # com `death_spinning` perquè `animationIsCompleted()` dispari el - # respawn timer determinísticament. + # Mort per caiguda a un agujero (DeathCause::HOLE). `loop: false` perquè + # `animationIsCompleted()` dispari el respawn timer determinísticament. death_hole: frames: [15] frame_ms: 60 diff --git a/source/game/entities/concrete/hole.hpp b/source/game/entities/concrete/hole.hpp index 3f05bb3..1dbda64 100644 --- a/source/game/entities/concrete/hole.hpp +++ b/source/game/entities/concrete/hole.hpp @@ -8,9 +8,12 @@ // - `collides_=false` + `triggers_on_overlap_=true`: rep `onPlayerContact` // pel bucle de `EntityManager::checkPlayerCollisions` sense bloquejar // el moviment del jugador. -// - `height_=0`: el filtre de col·lisió per altura existent deixa que -// un jugador AIRBORNE amb `jump_z_>0` el creui sense disparar res -// (saltar per damunt d'un forat). +// - `height_=0`: el filtre per altura del bucle (`jump_z > height`) NO +// s'activa amb height a zero, així que el bucle de contacte SÍ crida +// `onPlayerContact` també per a jugadors AIRBORNE. Qui rebutja els +// estats distints de GROUNDED (saltant, caient, HURT, DYING...) és el +// guard explícit dins de `Hole::onPlayerContact`. El filtre d'altura +// és una optimització ortogonal del bucle, no la defensa principal. // - Damage 0: el contacte no publica `player_hit`. En lloc d'això, // `onPlayerContact` invoca `Player::enterHoleFall`, que transiciona // el jugador a AIRBORNE amb subestat `falling_through_hole_`. @@ -32,3 +35,7 @@ class Hole : public StaticHazard { // jugador armi el clip rect a la Y de la base del forat. void onPlayerContact(Player* player) override; }; + +// TODO(hole-feature): afegir reset de `falling_through_hole_` i `hole_clip_bottom_y_` a `Player::disable()` per simetria. +// TODO(hole-feature): si apareix un quart callsite del càlcul "pixel→tile" o "tileRect", factoritzar a `Map::` (avui: Mummy::reactToCurrentTile, Mummy::isDecalAheadInDir, Player::integrateJump). +// TODO(hole-feature): afegir tests dedicats del comportament de la mòmia amb forats (no requereixen Player real). diff --git a/source/game/entities/entity_manager_loadlevel.cpp b/source/game/entities/entity_manager_loadlevel.cpp index 7d25c40..f776f17 100644 --- a/source/game/entities/entity_manager_loadlevel.cpp +++ b/source/game/entities/entity_manager_loadlevel.cpp @@ -77,6 +77,15 @@ void EntityManager::checkPlayerCollisions(const std::vector& players) { // Mentre el jugador cau per un forat, ignorem qualsevol altre // hazard: la transició a `enterDying()` ja la dispara // `integrateJump` quan supera `HOLE_FALL_DEPTH`. + // + // NOTA: aquest skip és la defensa que evita que un jugador + // caient per un forat reba contacte d'altres hazards + // "subterranis" durant la caiguda. La cadena de mort per + // forat (`enterDying(HOLE)` → respawn) depén de no ser + // interrompuda per un event `player_hit`/`player_killed` + // mentre `falling_through_hole_=true`. Si el dia que es + // toque aquest skip s'elimina, cal revisar la cadena + // mort/respawn per forat. if (p->isFallingThroughHole()) { continue; } // Filtre per altura: si el jugador està en l'aire i està per // damunt de l'alçada del cos de l'entitat, no hi ha contacte. diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index 4cc909f..ec7c2d2 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -1369,10 +1369,9 @@ auto Player::statePrefix(State state, bool moving, bool in_attack_swing) -> cons case State::ATTACKING: return in_attack_swing ? "attack" : "recover"; case State::DYING: - // L'animació concreta (`death_spinning`) la fixa `enterDying`; - // `applyDyingMovement` no torna a cridar `setCurrentAnimation`, - // així que aquest valor no s'usa mai. El retornem només per - // exhaustivitat del switch. + // L'animació concreta (`death_spinning` o `death_hole`) la + // selecciona `enterDying` segons `DeathCause`; aquest retorn + // només cobreix exhaustivitat del switch i no s'usa al runtime. return "death"; case State::GROUNDED: case State::EXITED: diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index e269240..c1a8617 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -250,6 +250,14 @@ class Player : public AnimatedSprite { // visual a partir de `floor_y` (Y de la base del tile del forat, // canvas píxels). Desactiva input direccional reusant la branca // AIRBORNE existent. No-op si el jugador no està a GROUNDED. + // + // CONTRACTE: `floor_y` ha de correspondre a la vora inferior de + // l'AABB del decal trigger (`hole.y + hole.h`). Els dos callers + // actuals — `Hole::onPlayerContact` i `Player::integrateJump` — + // passen exactament aquest valor. Qualsevol caller futur ha de + // respectar la convenció: el clip rect del render es calcula com + // `floor_y - SPRITE_FLOOR_OFFSET`, i un valor incorrecte desplaçaria + // visualment el "sòl" sobre el qual el sprite del jugador desapareix. void enterHoleFall(float center_x, float center_y, float floor_y); // Despawn lògic: el jugador acaba de creuar la porta de sortida.