fix(player): snapToLane respecta col·lisió PvP i empeny amb push_factor

This commit is contained in:
JailDesigner committed 2026-04-24 22:38:36 +02:00
1 parent 1af43ef052
commit 4f1a3260a5
3 files changed
+108 -5

No files matched your search

+80 -5
View File
@@ -531,12 +531,87 @@ void Player::snapToLane(
const float LANE = base + (static_cast<float>(IDX) * pitch);
const float DELTA = LANE - (this->*get_pos)();
if (std::abs(DELTA) > 0.0F && std::abs(DELTA) <= THRESHOLD) {
const float STEP = std::min(std::abs(DELTA), SPEED_PX_PER_S * delta_time);
const float SAVED = (this->*get_pos)();
(this->*set_pos)(SAVED + std::copysign(STEP, DELTA));
if (map_.collides(baseBox())) { (this->*set_pos)(SAVED); }
if (std::abs(DELTA) <= 0.0F || std::abs(DELTA) > THRESHOLD) { return; }
const float FULL_STEP = std::min(std::abs(DELTA), SPEED_PX_PER_S * delta_time);
const float SAVED = (this->*get_pos)();
// Fase 1: snap a velocitat plena. Si no xoca amb res, commit — és el
// cas habitual (carril lliure) i no ha de patir la friccó de l'empenta.
(this->*set_pos)(SAVED + std::copysign(FULL_STEP, DELTA));
if (!collidesWithWorld(baseBox())) { return; }
// Mur al carril objectiu: no hi ha ningú a qui empentar, revert directe.
if (map_.collides(baseBox())) {
(this->*set_pos)(SAVED);
return;
}
// Fase 2: sols un altre jugador al carril. Reintentem amb step escalat
// per `push_factor_` (mateixa friccó que el push horitzontal del
// resolver) i mirem d'arrossegar els blockers. Si la pushada no cap
// (contra mur o tercer jugador), el snap es revertix sencer.
const float PUSH_STEP = std::min(std::abs(DELTA), SPEED_PX_PER_S * push_factor_ * delta_time);
const float SIGNED_PUSH = std::copysign(PUSH_STEP, DELTA);
(this->*set_pos)(SAVED + SIGNED_PUSH);
if (collidesWithWorld(baseBox())) {
if (map_.collides(baseBox()) || !tryPushSnapBlockers(SIGNED_PUSH, get_pos, set_pos)) {
(this->*set_pos)(SAVED);
}
}
}
auto Player::tryPushSnapBlockers(
float signed_step,
float (Player::*get_pos)() const,
void (Player::*set_pos)(float)) -> bool {
if (all_players_ == nullptr) { return false; }
// Blockers = jugadors que ara mateix solapen la nostra hit-box (ja
// amb el snap aplicat). Mateix criteri actor-actor que `collidesWithWorld`.
std::vector<Player*> blockers;
const SDL_FRect MY_HIT = getHitBox();
for (Player* p : *all_players_) {
if (p == nullptr || p == this) { continue; }
const SDL_FRect OTHER_HIT = p->getHitBox();
if (SDL_HasRectIntersectionFloat(&MY_HIT, &OTHER_HIT)) {
blockers.push_back(p);
}
}
if (blockers.empty()) { return false; }
// Desem la posició original de cada blocker i el desplacem el mateix
// step que s'ha aplicat a `this`: com que els dos es mouen igual en el
// mateix eix, la distància relativa es conserva i, per tant, si abans
// del snap no es solapaven, després de l'empenta tampoc.
std::vector<float> saved;
saved.reserve(blockers.size());
for (Player* p : blockers) {
const float S = (p->*get_pos)();
saved.push_back(S);
(p->*set_pos)(S + signed_step);
}
// Validació: `this` ha de quedar net (ni mur ni cap jugador), i cada
// blocker també (cobreix el cas de 3+ jugadors on un blocker podria
// acabar dins d'un tercer o contra un mur).
const bool OK = !collidesWithWorld(baseBox()) && std::ranges::none_of(blockers, [](const Player* p) {
return p->collidesWithWorld(p->getBaseBox());
});
if (!OK) {
for (size_t i = 0; i < blockers.size(); ++i) {
(blockers[i]->*set_pos)(saved[i]);
}
return false;
}
// Els blockers s'han desplaçat sense input propi: mateixa convenció
// d'animació que el push horitzontal del resolver (stand_*, no walk).
for (Player* p : blockers) {
p->setBeingPushed(true);
}
return true;
}
void Player::snapToHorizontalLane(float delta_time) {