feat(player): nou estat HURT — caure damunt d'un altre el llança projectat

This commit is contained in:
JailDesigner committed 2026-04-25 14:56:15 +02:00
1 parent 188f035333
commit e8407ce35e
5 files changed
+310 -119

No files matched your search

+54 -14
View File
@@ -28,15 +28,22 @@ class Player : public AnimatedSprite {
// Estat d'alt nivell del jugador. Ortogonal a `Facing`/`Cardinal`
// (direcció) i a `being_pushed_` (flag d'animació). Determina què pot
// fer el jugador aquest frame: GROUNDED accepta tot l'input habitual
// i el botó de salt; AIRBORNE ignora canvis de direcció (la velocitat
// i el facing queden fixats al moment del despegue, sols la física
// del salt mou jump_z_/jump_vz_); LANDING_STUN bloca tot l'input fins
// que el timer s'esgota.
// fer el jugador aquest frame:
// GROUNDED → input normal, pot saltar.
// AIRBORNE → física del salt; ignora input direccional, sense
// col·lisió amb caminants però sí amb altres
// saltadors a la mateixa franja d'alçada (rebot).
// LANDING_STUN → 200ms post-aterratge, sense input ni moviment.
// HURT → projectat sense control (cau d'algú al damunt o,
// en el futur, fuetada): velocitat inicial alta
// que decau per inèrcia fins a parar (~2 tiles).
// No col·lisiona amb cap altre jugador. Quan
// s'atura, torna a GROUNDED.
enum class State : std::uint8_t {
GROUNDED,
AIRBORNE,
LANDING_STUN,
HURT,
};
// Geometria carregada des del YAML del player (p.ex. `data/player/sam.yaml`).
@@ -169,6 +176,12 @@ class Player : public AnimatedSprite {
// GameScene (per saber si renderitzar la sombra).
[[nodiscard]] auto getState() const -> State { return state_; }
[[nodiscard]] auto isAirborne() const -> bool { return state_ == State::AIRBORNE; }
[[nodiscard]] auto isHurt() const -> bool { return state_ == State::HURT; }
// Tangible per a contactes jugador↔jugador: només els estats a terra
// amb cos sòlid. AIRBORNE i HURT són intangibles (es travessen).
[[nodiscard]] auto isPvpTangible() const -> bool {
return state_ == State::GROUNDED || state_ == State::LANDING_STUN;
}
// Alçada actual del salt (píxels visuals per damunt del sòl).
// 0 quan està a terra. L'usa el render i el sistema de col·lisions
@@ -385,6 +398,27 @@ class Player : public AnimatedSprite {
// manca, deixa la mecànica del salt desactivada.
static void readJumpBlock(const fkyaml::node& yaml);
// Configuració de l'estat HURT (estat post-impacte). Si
// `initial_speed_px_per_s == 0` el jugador entra i surt
// instantàniament (cap moviment).
struct HurtConfig {
float initial_speed_px_per_s{0.0F}; // Velocitat amb què surt projectat (px/s).
int brake_time_ms{0}; // Temps en que la velocitat decau a 0.
};
static HurtConfig hurt_config;
// Llig el bloc `hurt:` del YAML i n'omple `hurt_config`.
static void readHurtBlock(const fkyaml::node& yaml);
// Activa l'estat HURT: el jugador queda projectat amb velocitat
// inicial `hurt_config.initial_speed_px_per_s` cap a la direcció que
// doni més recorregut lliure (provem els 4 cardinals i triem el de
// major distància fins a topar amb un mur). Decelera linealment fins
// a parar i, en eixe instant, torna a GROUNDED. Sense col·lisió amb
// altres jugadors mentre dura — ja que es travessa l'eventual
// solapament inicial.
void enterHurt();
// Estat de la màquina d'estats. Mutat per `tick`/`applyMovement`. Per
// defecte GROUNDED al spawn.
State state_{State::GROUNDED};
@@ -418,17 +452,23 @@ class Player : public AnimatedSprite {
// `applyMovement` puga disparar la resolució del solapament PvP).
auto integrateJump(float delta_time) -> bool;
// En aterrar, si la hit-box del jugador solapa amb la d'un altre,
// separa els dos sense ficar-los dins d'un mur. Algoritme:
// 1. Calcula la direcció centre→centre i la magnitud de
// solapament (MTV axis-aligned).
// 2. Empenta l'altre cap a fora; si no cap (mur al darrere),
// reverteix i empenta el saltador cap al sentit oposat.
// 3. Si tampoc cap, es deixa l'estat com està (overlap residual);
// a la pràctica aquest cas és molt rar i el usuari ja ha
// acceptat que no és crític.
// En aterrar, si la base-box del jugador solapa amb la d'un altre,
// l'altre entra en `HURT` i surt projectat per `enterHurt`. El
// saltador (this) es queda al lloc — el moviment per separar-los
// el fa el HURT en cours, no una empenta forçada.
void resolveLandingOverlap();
// Branques de `applyMovement` extretes per acotar la complexitat
// cognitiva. Cada estat especial gestiona el seu propi cicle
// (animació, tryMove o no, publicació de tile).
void applyHurtMovement(float delta_time);
void applyLandingStunMovement(float delta_time);
// Publica `on_tile_entered` al bus si el tile ha canviat respecte a
// l'últim publicat. Comú a totes les branques de `applyMovement` que
// pinten el sòl (GROUNDED, HURT, LANDING_STUN; en AIRBORNE no).
void publishTileEnteredIfChanged();
// Estat del gir en curs. Mentre `turning_remaining_ms_ > 0`, el facing/
// flip es manté forçat a `intermediate_cardinal_` i l'animació es força
// a `stand` encara que el moviment continuï. En esgotar-se el timer,