neteja NOLINT obsolets (audio 14 + player + ajusta justificacions)

This commit is contained in:
JailDesigner committed 2026-05-18 16:41:15 +02:00
1 parent 822bed0748
commit 7a859c1e44
7 files changed
+175 -160

No files matched your search

+1 -3
View File
@@ -345,9 +345,7 @@ namespace {
} // namespace
// cppcheck-suppress constParameterReference
// Tilemap& mutable per simetria amb `toggle()` i per futures
// invalidacions de caches d'edició, encara que `save()` sigui const.
// cppcheck-suppress constParameterReference -- Tilemap& mutable per simetria amb `toggle()` i per futures invalidacions de caches d'edició; cppcheck no pot anticipar que les futures mutacions estan previstes en el contracte.
void Editor::exit(Tilemap& tilemap, const EntityManager& entities) {
if (!active_) { return; }
// Constraint del nivell: cal exactament una porta i com a mínim un
+13 -12
View File
@@ -53,18 +53,19 @@ class EntityFactory {
// IMPORTANT: perquè el registrador es cridi, el `.cpp` que el conté ha
// d'estar enllaçat directament a l'executable (no dins una STATIC lib
// sense `--whole-archive`). Tots els `concrete/*.cpp` entren a APP_SOURCES.
#define REGISTER_ENTITY_AS(NAME, CLASS, TAG) \
namespace { \
struct TAG##_Registrar { \
TAG##_Registrar() { \
EntityFactory::instance().registerCreator( \
NAME, \
[](const fkyaml::node& yaml) -> std::unique_ptr<Entity> { \
return std::make_unique<CLASS>(yaml); \
}); \
} \
}; \
const TAG##_Registrar g_##TAG##_registrar; /* NOLINT(cert-err58-cpp) */ \
#define REGISTER_ENTITY_AS(NAME, CLASS, TAG) \
namespace { \
struct TAG##_Registrar { \
TAG##_Registrar() { \
EntityFactory::instance().registerCreator( \
NAME, \
[](const fkyaml::node& yaml) -> std::unique_ptr<Entity> { \
return std::make_unique<CLASS>(yaml); \
}); \
} \
}; \
/* NOLINTNEXTLINE(cert-err58-cpp) -- inicialització estàtica intencionada: el constructor registra el creator a l'EntityFactory abans de main(); el patró és l'únic mecanisme C++ que permet auto-registre per tipus sense modificar la factory. */ \
const TAG##_Registrar g_##TAG##_registrar; \
}
#define REGISTER_ENTITY(NAME, CLASS) REGISTER_ENTITY_AS(NAME, CLASS, CLASS)
+150 -129
View File
@@ -513,143 +513,115 @@ void Player::integrateAxis(float& phys, float input, float delta_time, float mot
}
}
// Pipeline d'estats i input separats; extracció a helpers empitjoraria la
// llegibilitat. La complexitat puja 1 per la guarda EXITED.
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void Player::tick(float delta_time) {
// EXITED/DISABLED: jugador fora del joc, no fa res. Cap input, cap
// física, cap bus event. Continua sent al vector de jugadors per
// simplicitat (no s'esborra) però queda completament inert fins
// que GameScene el reactiva (respawn / spawnFromAir).
if (isOutOfPlay()) { return; }
// 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
// detecta la fi per disparar el respawn. Mantenim els flancs de
// botó actualitzats perquè una tecla sostinguda no es comporte com
// a flanc nou en sortir del cicle.
void Player::tickDying() {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
input_vx_ = 0.0F;
input_vy_ = 0.0F;
intended_vx_ = 0.0F;
intended_vy_ = 0.0F;
motion_time_ms_ = 0.0F;
setVelX(0.0F);
setVelY(0.0F);
}
// 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);
// En LANDING_STUN, descomptem el timer i ignorem qualsevol input
// (moviment i salt). Quan s'esgota, tornem a GROUNDED al pròxim
// frame. La velocitat queda a zero — després del stun hi tornarà
// a haver intenció si el jugador prem alguna tecla.
void Player::tickLandingStun(float delta_time) {
landing_stun_remaining_ms_ -= delta_time * 1000.0F;
if (landing_stun_remaining_ms_ <= 0.0F) {
landing_stun_remaining_ms_ = 0.0F;
state_ = State::GROUNDED;
}
input_vx_ = 0.0F;
input_vy_ = 0.0F;
intended_vx_ = 0.0F;
intended_vy_ = 0.0F;
motion_time_ms_ = 0.0F;
setVelX(0.0F);
setVelY(0.0F);
// Mantenim els flancs del botó de salt actualitzats fins i tot
// durant el stun: així si l'usuari l'ha estat prement abans, no
// se'l consumeix com a salt nou en sortir del stun.
(void)consumeJumpEdge();
(void)consumeAttackEdge();
}
// 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
// detecta la fi per disparar el respawn. Mantenim els flancs de
// botó actualitzats perquè una tecla sostinguda no es comporte com
// a flanc nou en sortir del cicle.
if (state_ == State::DYING) {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
input_vx_ = 0.0F;
input_vy_ = 0.0F;
intended_vx_ = 0.0F;
intended_vy_ = 0.0F;
motion_time_ms_ = 0.0F;
setVelX(0.0F);
setVelY(0.0F);
return;
// En AIRBORNE el jugador no llig input direccional: la velocitat
// horitzontal queda fixada al moment del despegue. Tampoc canvia
// facing. Sí actualitzem els flancs del botó perquè la propera
// pulsació quan torne a terra es detecte com a flanc nou.
void Player::tickAirborne() {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
setVelX(intended_vx_);
setVelY(intended_vy_);
}
// En ATTACKING el jugador queda bloquejat tot el `swing_ms +
// cooldown_ms`: ni input direccional, ni salt, ni atac, ni canvi
// de facing. El timer descompta amb `delta_time * 1000` per
// mantenir la convenció de delta_ms de la resta d'estats temporals.
// Mantenim els flancs dels botons actualitzats perquè una
// tecla mantinguda no es comporte com a flanc nou en sortir.
void Player::tickAttacking(float delta_time) {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
attacking_remaining_ms_ -= delta_time * 1000.0F;
if (attacking_remaining_ms_ <= 0.0F) {
attacking_remaining_ms_ = 0.0F;
state_ = State::GROUNDED;
}
input_vx_ = 0.0F;
input_vy_ = 0.0F;
intended_vx_ = 0.0F;
intended_vy_ = 0.0F;
motion_time_ms_ = 0.0F;
setVelX(0.0F);
setVelY(0.0F);
}
// Reset per frame: el resolver del GameScene el tornarà a posar a
// true si aquest frame hi ha push actiu. Així quan el push acaba
// (un deixa d'empentar, l'altre comença a caminar), l'animació torna
// a governar-se per la velocitat real sense estat romanent.
being_pushed_ = false;
// En LANDING_STUN, descomptem el timer i ignorem qualsevol input
// (moviment i salt). Quan s'esgota, tornem a GROUNDED al pròxim
// frame. La velocitat queda a zero — després del stun hi tornarà
// a haver intenció si el jugador prem alguna tecla.
if (state_ == State::LANDING_STUN) {
landing_stun_remaining_ms_ -= delta_time * 1000.0F;
if (landing_stun_remaining_ms_ <= 0.0F) {
landing_stun_remaining_ms_ = 0.0F;
state_ = State::GROUNDED;
// En HURT el jugador està projectat sense control: descomptem
// velocitat per inèrcia (decaiment lineal fins a 0). Quan arriba a
// zero, tornem a GROUNDED i el frame següent ja torna a llegir input.
// El moviment real l'aplica `applyMovement` (igual que els altres
// estats), que també gestiona el xoc contra murs.
void Player::tickHurt(float delta_time) {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
const float DECEL = (cfg_.hurt.brake_time_ms > 0)
? cfg_.hurt.initial_speed_px_per_s / (static_cast<float>(cfg_.hurt.brake_time_ms) / 1000.0F)
: 0.0F;
const float STEP = DECEL * delta_time;
auto decay = [STEP](float& v) {
if (std::fabs(v) <= STEP) {
v = 0.0F;
} else {
v -= std::copysign(STEP, v);
}
input_vx_ = 0.0F;
input_vy_ = 0.0F;
intended_vx_ = 0.0F;
intended_vy_ = 0.0F;
motion_time_ms_ = 0.0F;
setVelX(0.0F);
setVelY(0.0F);
// Mantenim els flancs del botó de salt actualitzats fins i tot
// durant el stun: així si l'usuari l'ha estat prement abans, no
// se'l consumeix com a salt nou en sortir del stun.
(void)consumeJumpEdge();
(void)consumeAttackEdge();
return;
}
// En AIRBORNE el jugador no llig input direccional: la velocitat
// horitzontal queda fixada al moment del despegue. Tampoc canvia
// facing. Sí actualitzem els flancs del botó perquè la propera
// pulsació quan torne a terra es detecte com a flanc nou.
if (state_ == State::AIRBORNE) {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
setVelX(intended_vx_);
setVelY(intended_vy_);
return;
}
// En ATTACKING el jugador queda bloquejat tot el `swing_ms +
// cooldown_ms`: ni input direccional, ni salt, ni atac, ni canvi
// de facing. El timer descompta amb `delta_time * 1000` per
// mantenir la convenció de delta_ms de la resta d'estats temporals.
// Mantenim els flancs dels botons actualitzats perquè una
// tecla mantinguda no es comporte com a flanc nou en sortir.
if (state_ == State::ATTACKING) {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
attacking_remaining_ms_ -= delta_time * 1000.0F;
if (attacking_remaining_ms_ <= 0.0F) {
attacking_remaining_ms_ = 0.0F;
state_ = State::GROUNDED;
}
input_vx_ = 0.0F;
input_vy_ = 0.0F;
intended_vx_ = 0.0F;
intended_vy_ = 0.0F;
motion_time_ms_ = 0.0F;
setVelX(0.0F);
setVelY(0.0F);
return;
}
// En HURT el jugador està projectat sense control: descomptem
// velocitat per inèrcia (decaiment lineal fins a 0). Quan arriba a
// zero, tornem a GROUNDED i el frame següent ja torna a llegir input.
// El moviment real l'aplica `applyMovement` (igual que els altres
// estats), que també gestiona el xoc contra murs.
if (state_ == State::HURT) {
(void)consumeJumpEdge();
(void)consumeAttackEdge();
const float DECEL = (cfg_.hurt.brake_time_ms > 0)
? cfg_.hurt.initial_speed_px_per_s / (static_cast<float>(cfg_.hurt.brake_time_ms) / 1000.0F)
: 0.0F;
const float STEP = DECEL * delta_time;
auto decay = [STEP](float& v) {
if (std::fabs(v) <= STEP) {
v = 0.0F;
} else {
v -= std::copysign(STEP, v);
}
};
decay(intended_vx_);
decay(intended_vy_);
if (intended_vx_ == 0.0F && intended_vy_ == 0.0F) {
state_ = State::GROUNDED;
}
input_vx_ = 0.0F;
input_vy_ = 0.0F;
setVelX(intended_vx_);
setVelY(intended_vy_);
return;
};
decay(intended_vx_);
decay(intended_vy_);
if (intended_vx_ == 0.0F && intended_vy_ == 0.0F) {
state_ = State::GROUNDED;
}
input_vx_ = 0.0F;
input_vy_ = 0.0F;
setVelX(intended_vx_);
setVelY(intended_vy_);
}
// Estat normal de joc (GROUNDED): llegeix input, integra física, actualitza
// facing i processa els flancs d'atac i salt.
void Player::tickGroundedInput(float delta_time) {
// Refresca les vinculacions des d'Options en calent: així un remap fet
// al menú de servei té efecte immediatament, sense esperar al pròxim
// GameScene::onEnter. Replica el comportament d'AEE
@@ -725,6 +697,55 @@ void Player::tick(float delta_time) {
}
}
// Dispatcher per estat. La complexitat real viu als handlers individuals.
void Player::tick(float delta_time) {
// EXITED/DISABLED: jugador fora del joc, no fa res. Cap input, cap
// física, cap bus event. Continua sent al vector de jugadors per
// simplicitat (no s'esborra) però queda completament inert fins
// 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);
}
if (state_ == State::DYING) {
tickDying();
return;
}
// Reset per frame: el resolver del GameScene el tornarà a posar a
// true si aquest frame hi ha push actiu. Així quan el push acaba
// (un deixa d'empentar, l'altre comença a caminar), l'animació torna
// a governar-se per la velocitat real sense estat romanent.
being_pushed_ = false;
if (state_ == State::LANDING_STUN) {
tickLandingStun(delta_time);
return;
}
if (state_ == State::AIRBORNE) {
tickAirborne();
return;
}
if (state_ == State::ATTACKING) {
tickAttacking(delta_time);
return;
}
if (state_ == State::HURT) {
tickHurt(delta_time);
return;
}
tickGroundedInput(delta_time);
}
void Player::refreshControlsFromOptions() {
if (id_ != 0 && id_ != 1) { return; }
const auto& kb = (id_ == 0) ? Options::controls().keyboard_p1
+9
View File
@@ -604,6 +604,15 @@ class Player : public AnimatedSprite {
void renderShadow() const;
private:
// Sub-handlers de tick(), un per estat amb sortida prematura. Mantenir-los
// a part redueix la complexitat cognitiva del dispatcher principal.
void tickDying();
void tickLandingStun(float delta_time);
void tickAirborne();
void tickAttacking(float delta_time);
void tickHurt(float delta_time);
void tickGroundedInput(float delta_time);
// Alias privat històric. Mantingut perquè viu dins de l'implementació
// (snapToLane, clamp). Els consumidors externs usen `getBaseBox()`.
[[nodiscard]] auto baseBox() const -> SDL_FRect { return getBaseBox(); }