feat(hazard): nou hazard Hole — caiguda per forat amb clip rect

This commit is contained in:
JailDesigner committed 2026-04-30 13:32:17 +02:00
1 parent d4593d0686
commit 4da50571eb
17 files changed
+376 -15

No files matched your search

@@ -10,6 +10,10 @@ FloorTrap::FloorTrap(const fkyaml::node& yaml) {
name_ = yaml.contains("name") ? yaml["name"].get_value<std::string>() : std::string("floor_trap");
collides_ = false;
interactable_ = false;
// Trigger d'àrea: la trampa no bloqueja el pas però vol rebre
// `onPlayerContact` quan el jugador la solapa. Tanca el gap latent
// que la deixava muda (`checkPlayerCollisions` filtrava pel `collides`).
triggers_on_overlap_ = true;
if (yaml.contains("cycle")) {
const auto& c = yaml["cycle"];
+51
View File
@@ -0,0 +1,51 @@
#include "game/entities/concrete/hole.hpp"
#include <SDL3/SDL.h>
#include <string>
#include "game/entities/entity_factory.hpp"
#include "game/entities/player.hpp"
Hole::Hole(const fkyaml::node& yaml) {
name_ = yaml.contains("name") ? yaml["name"].get_value<std::string>() : std::string("hole");
// Sense xoc físic ni interacció — només trigger d'àrea. El bucle de
// contacte ens admet via `triggers_on_overlap_=true`. Damage queda a
// 0 (no publiquem `player_hit`); la mort la dispara `integrateJump`
// del jugador quan supera `HOLE_FALL_DEPTH`.
collides_ = false;
interactable_ = false;
triggers_on_overlap_ = true;
// Decal de sòl: `Entity::getFeetY()` torna sentinel mínim si aquest
// flag està actiu — així el forat es pinta abans que qualsevol actor.
is_floor_decal_ = true;
setDamage(0);
}
void Hole::onPlayerContact(Player* player) {
if (player == nullptr) { return; }
// Només mossega quan el jugador trepitja el forat de peu (GROUNDED).
// En l'aire, el filtre d'altura del bucle ja l'ha rebutjat (height_=0
// i jump_z_>0 prou per superar-ho); però mantenim el gat per defensa
// davant de futurs canvis al filtre.
if (player->getState() != Player::State::GROUNDED) { return; }
// Re-verificació estricta amb la hit-box interna del jugador (8×8):
// així el contacte només es dispara quan el centre del jugador ha
// entrat al tile del forat, no quan només el frega per la vora.
const SDL_FRect HIT = player->getHitBox();
const SDL_FRect BOX = getCollisionRect();
const bool OVERLAP = HIT.x + HIT.w > BOX.x && BOX.x + BOX.w > HIT.x && HIT.y + HIT.h > BOX.y && BOX.y + BOX.h > HIT.y;
if (!OVERLAP) { return; }
// Snap al centre XY del forat + Y de la base del tile (per al clip
// rect del sprite mentre cau). `enterHoleFall` rep coordenades
// absolutes en el canvas — el player s'encarrega de convertir a la
// seua convenció (cantó superior-esquerra del sprite).
const float CENTER_X = BOX.x + (BOX.w * 0.5F);
const float CENTER_Y = BOX.y + (BOX.h * 0.5F);
const float FLOOR_Y = BOX.y + BOX.h;
player->enterHoleFall(CENTER_X, CENTER_Y, FLOOR_Y);
}
REGISTER_ENTITY("hole", Hole)
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "external/fkyaml_node.hpp"
#include "game/entities/hazard.hpp"
// Forat al sòl: hazard estàtic de 16×16 que NO bloqueja el pas però
// detecta quan el jugador hi cau a sobre. Característiques:
// - `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).
// - 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_`.
// - Verificació amb hit-box interna: `onPlayerContact` re-verifica
// l'overlap amb `Player::getHitBox()` (8×8 centrada al sprite),
// així només es dispara quan el centre del jugador entra al tile.
// - Z-order: `is_floor_decal_=true` (heretat) — `Entity::getFeetY()`
// retorna sentinel mínim, així el forat es pinta abans que qualsevol
// actor i les momies passen per davant. L'efecte "el sòl es traga el
// jugador" no es fa amb Y-sort: el `Player::render` aplica un clip
// rect mentre `falling_through_hole_=true` que retalla el sprite per
// sota de la base del tile del forat.
class Hole : public StaticHazard {
public:
explicit Hole(const fkyaml::node& yaml);
// No publica `player_hit`. En el seu lloc, dispara
// `Player::enterHoleFall(center_x, center_y, floor_y)` perquè el
// jugador armi el clip rect a la Y de la base del forat.
void onPlayerContact(Player* player) override;
};
@@ -10,6 +10,9 @@ WallTrap::WallTrap(const fkyaml::node& yaml) {
name_ = yaml.contains("name") ? yaml["name"].get_value<std::string>() : std::string("wall_trap");
collides_ = false;
interactable_ = false;
// Trigger d'àrea: igual que FloorTrap. Tanca el gap latent del filtre
// de `checkPlayerCollisions` que ho deixava mut.
triggers_on_overlap_ = true;
if (yaml.contains("cycle")) {
const auto& c = yaml["cycle"];