fix(player): snapToLane respecta col·lisió PvP i empeny amb push_factor
This commit is contained in:
1 parent
1af43ef052
commit
4f1a3260a5
3 files changed
+108
-5
No files matched your search
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user