From 0db219540427fde89cc5d1bc2d29e4817154599d Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 5 May 2026 13:05:29 +0200 Subject: [PATCH] feat(immunity): timer al tick, gates a enterHurt/enterDying i trigger al respawn --- source/game/entities/player.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index f93090c..65097f0 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -497,6 +497,16 @@ void Player::tick(float delta_time) { // que GameScene el reactiva (respawn / spawnFromAir). 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 // l'input (ni moviment, ni salt, ni atac). El moviment queda // 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) { + // 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 // 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; } @@ -1168,6 +1183,13 @@ void Player::applyMovement(float delta_time) { } 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à // en DYING no reinicia l'animació ni el comptador. Tampoc disparem // 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"); setPosX(x); 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() {