feat(immunity): timer al tick, gates a enterHurt/enterDying i trigger al respawn

This commit is contained in:
JailDesigner committed 2026-05-05 13:05:29 +02:00
1 parent 2e3816783f
commit 0db2195404
1 file changed
+28
+28
View File
@@ -497,6 +497,16 @@ void Player::tick(float delta_time) {
// que GameScene el reactiva (respawn / spawnFromAir). // que GameScene el reactiva (respawn / spawnFromAir).
if (isOutOfPlay()) { return; } if (isOutOfPlay()) { return; }
// Decrement del timer d'immunitat. Independent de l'estat: si el
// jugador mor durant la immunitat (només via forat, que no es gateja),
// el `respawn` rearma una nova finestra. Si la finestra acaba mentre
// és AIRBORNE/HURT, no passa res — el gate dels hazards passa a
// quedar obert al primer frame post-zero.
if (immunity_remaining_ms_ > 0.0F) {
immunity_remaining_ms_ -= delta_time * 1000.0F;
immunity_remaining_ms_ = std::max(immunity_remaining_ms_, 0.0F);
}
// DYING: animació `death_spinning` + pausa de respawn. Ignorem tot // DYING: animació `death_spinning` + pausa de respawn. Ignorem tot
// l'input (ni moviment, ni salt, ni atac). El moviment queda // l'input (ni moviment, ni salt, ni atac). El moviment queda
// congelat al lloc de la mort; `applyMovement` avança l'animació i // congelat al lloc de la mort; `applyMovement` avança l'animació i
@@ -914,6 +924,11 @@ auto Player::chooseHurtDirection(Cardinal preferred) const -> Cardinal {
} }
void Player::enterHurt(Cardinal preferred) { void Player::enterHurt(Cardinal preferred) {
// Gate d'immunitat: tots els callers de dany (whip friendly fire,
// caiguda damunt, futurs hazards) passen per ací. Una sola línia
// cobreix la finestra de gràcia post-spawn.
if (isImmune()) { return; }
// Si la mecànica no està configurada (speed = 0), no fem res — el // Si la mecànica no està configurada (speed = 0), no fem res — el
// comportament queda equivalent al d'abans, sense empenta. // comportament queda equivalent al d'abans, sense empenta.
if (hurt_config.initial_speed_px_per_s <= 0.0F || hurt_config.brake_time_ms <= 0) { return; } if (hurt_config.initial_speed_px_per_s <= 0.0F || hurt_config.brake_time_ms <= 0) { return; }
@@ -1168,6 +1183,13 @@ void Player::applyMovement(float delta_time) {
} }
void Player::enterDying(DeathCause cause) { void Player::enterDying(DeathCause cause) {
// Gate d'immunitat amb bypass per a HOLE: la caiguda per agujero NO
// és dany convencional sinó "sortida lògica del món", així que els
// forats han de matar fins i tot el jugador immune. Qualsevol altra
// causa (mòmia letal via `player_killed`, etc.) queda neutralitzada
// mentre dura la finestra de gràcia.
if (cause != DeathCause::HOLE && isImmune()) { return; }
// Idempotent. Un segon contacte de momia mentre el jugador ja està // Idempotent. Un segon contacte de momia mentre el jugador ja està
// en DYING no reinicia l'animació ni el comptador. Tampoc disparem // en DYING no reinicia l'animació ni el comptador. Tampoc disparem
// el cicle si el jugador està fora de joc (EXITED al passar la // el cicle si el jugador està fora de joc (EXITED al passar la
@@ -1514,6 +1536,12 @@ void Player::respawn(float x, float y) {
setCurrentAnimation("stand_south"); setCurrentAnimation("stand_south");
setPosX(x); setPosX(x);
setPosY(y); setPosY(y);
// Immunitat post-spawn: armem la finestra de gràcia amb la durada del
// YAML. Cobreix l'aparició inicial (`spawnFromAir` → `respawn`) i el
// respawn post-mort (`respawnFromDying` → `respawn`). Si el bloc
// `immunity:` manca al YAML, `duration_ms` és 0 i `setImmunity` és
// no-op — comportament idèntic al d'abans.
setImmunity(immunity_config.duration_ms);
} }
void Player::disable() { void Player::disable() {