migrades portes i plataformes a solidActor
This commit is contained in:
197
source/game/gameplay/solid_actor_manager.cpp
Normal file
197
source/game/gameplay/solid_actor_manager.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "solid_actor_manager.hpp"
|
||||
|
||||
#include <algorithm> // Para std::ranges::find, std::max, std::min
|
||||
|
||||
#include "game/entities/solid_actor.hpp" // Para SolidActor
|
||||
#include "utils/defines.hpp" // Para Collision::NONE, PlayArea::WIDTH/HEIGHT
|
||||
|
||||
// Registro de actores
|
||||
void SolidActorManager::registerActor(SolidActor* actor) {
|
||||
if (actor == nullptr) { return; }
|
||||
actors_.push_back(actor);
|
||||
}
|
||||
|
||||
// Desregistro (no borra el objeto — la entidad concreta vive en su propio manager)
|
||||
void SolidActorManager::unregisterActor(SolidActor* actor) {
|
||||
auto it = std::ranges::find(actors_, actor);
|
||||
if (it != actors_.end()) {
|
||||
actors_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void SolidActorManager::clear() {
|
||||
actors_.clear();
|
||||
}
|
||||
|
||||
void SolidActorManager::setAdjacent(const AdjacentActors& adjacent) {
|
||||
adjacent_ = adjacent;
|
||||
}
|
||||
|
||||
void SolidActorManager::clearAdjacent() {
|
||||
adjacent_ = {};
|
||||
}
|
||||
|
||||
// Itera sobre todos los actores (local + 4 vecinos cardinales), aplicando
|
||||
// el offset pertinente al AABB vecino para traerlo al sistema de coordenadas
|
||||
// de la room actual. Las rooms vecinas tienen sus actores en coordenadas
|
||||
// locales propias; para verlos desde aquí, se traslada:
|
||||
// - left: +WIDTH en X (porque en la room actual están a la izquierda del
|
||||
// borde 0 de coordenadas; trasladándolos por -WIDTH los colocamos "a la
|
||||
// izquierda" del Player, con x negativas).
|
||||
// - right: -WIDTH en X (análogo).
|
||||
// - upper: +HEIGHT en Y.
|
||||
// - lower: -HEIGHT en Y.
|
||||
//
|
||||
// NOTA: el Player siempre está en [0, WIDTH) × [0, HEIGHT) de la room actual.
|
||||
// Los actores vecinos se muestran con coordenadas fuera de ese rango, y los
|
||||
// sweeps solo los ven cuando el Player está lo bastante cerca del borde como
|
||||
// para solapar con ellos.
|
||||
template <typename Fn>
|
||||
void SolidActorManager::forEachActor(Fn&& fn) const {
|
||||
// Locales
|
||||
for (auto* a : actors_) {
|
||||
fn(a, a->getAABB());
|
||||
}
|
||||
|
||||
// Adyacente izquierda: sus actores están en [0, WIDTH) de SU room;
|
||||
// desde la room actual, esos actores están en [-WIDTH, 0). Offset: -WIDTH en X.
|
||||
if (adjacent_.left != nullptr) {
|
||||
for (auto* a : adjacent_.left->actors_) {
|
||||
SDL_FRect r = a->getAABB();
|
||||
r.x -= PlayArea::WIDTH;
|
||||
fn(a, r);
|
||||
}
|
||||
}
|
||||
// Adyacente derecha: offset +WIDTH en X.
|
||||
if (adjacent_.right != nullptr) {
|
||||
for (auto* a : adjacent_.right->actors_) {
|
||||
SDL_FRect r = a->getAABB();
|
||||
r.x += PlayArea::WIDTH;
|
||||
fn(a, r);
|
||||
}
|
||||
}
|
||||
// Adyacente superior: offset -HEIGHT en Y (arriba tiene y negativa).
|
||||
if (adjacent_.upper != nullptr) {
|
||||
for (auto* a : adjacent_.upper->actors_) {
|
||||
SDL_FRect r = a->getAABB();
|
||||
r.y -= PlayArea::HEIGHT;
|
||||
fn(a, r);
|
||||
}
|
||||
}
|
||||
// Adyacente inferior: offset +HEIGHT en Y.
|
||||
if (adjacent_.lower != nullptr) {
|
||||
for (auto* a : adjacent_.lower->actors_) {
|
||||
SDL_FRect r = a->getAABB();
|
||||
r.y += PlayArea::HEIGHT;
|
||||
fn(a, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Devuelve el borde derecho del actor bloqueante "a la izquierda" del Player.
|
||||
// Criterio: el AABB del actor solapa el rect del Player y el borde izquierdo
|
||||
// del actor está a la izquierda del borde izquierdo del Player (el Player se
|
||||
// mete en él por la izquierda).
|
||||
auto SolidActorManager::checkWallLeft(float px, float py, float pw, float ph) const -> float {
|
||||
float result = Collision::NONE;
|
||||
forEachActor([&](const SolidActor* a, const SDL_FRect& r) {
|
||||
if (!a->isBlocking()) { return; }
|
||||
if (a->isOneWayTop()) { return; } // Los jump-through no son pared lateral
|
||||
// Y-overlap
|
||||
if (py + ph <= r.y) { return; }
|
||||
if (py >= r.y + r.h) { return; }
|
||||
// X-overlap y "a la izquierda": el Player (px) está dentro del actor.
|
||||
const float RIGHT_EDGE = r.x + r.w;
|
||||
if (r.x < px && RIGHT_EDGE > px) {
|
||||
if (result == Collision::NONE || RIGHT_EDGE > result) {
|
||||
result = RIGHT_EDGE;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Devuelve el borde izquierdo del actor bloqueante "a la derecha" del Player.
|
||||
auto SolidActorManager::checkWallRight(float px, float py, float pw, float ph) const -> float {
|
||||
float result = Collision::NONE;
|
||||
forEachActor([&](const SolidActor* a, const SDL_FRect& r) {
|
||||
if (!a->isBlocking()) { return; }
|
||||
if (a->isOneWayTop()) { return; }
|
||||
// Y-overlap
|
||||
if (py + ph <= r.y) { return; }
|
||||
if (py >= r.y + r.h) { return; }
|
||||
// X-overlap y "a la derecha": el borde derecho del Player entra en el actor.
|
||||
const float PLAYER_RIGHT = px + pw;
|
||||
if (r.x < PLAYER_RIGHT && r.x + r.w > PLAYER_RIGHT) {
|
||||
if (result == Collision::NONE || r.x < result) {
|
||||
result = r.x;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Techo: devuelve el borde inferior del actor bloqueante que está por encima
|
||||
// del Player y solapa su parte superior.
|
||||
auto SolidActorManager::checkCeiling(float px, float py, float pw) const -> float {
|
||||
float result = Collision::NONE;
|
||||
forEachActor([&](const SolidActor* a, const SDL_FRect& r) {
|
||||
if (!a->isBlocking()) { return; }
|
||||
if (a->isOneWayTop()) { return; } // Los jump-through no tienen techo
|
||||
// X-overlap
|
||||
if (px + pw <= r.x) { return; }
|
||||
if (px >= r.x + r.w) { return; }
|
||||
// Y: el actor debe estar por encima del Player (su bottom edge entre py y py+ph)
|
||||
const float BOTTOM_EDGE = r.y + r.h;
|
||||
if (r.y < py && BOTTOM_EDGE > py) {
|
||||
if (result == Collision::NONE || BOTTOM_EDGE > result) {
|
||||
result = BOTTOM_EDGE;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Suelo: el actor más cercano (por arriba) cuyo top edge esté entre
|
||||
// foot_y_cur y foot_y_new. Para ONEWAY_TOP, solo cuentan si foot_y_cur <= r.y.
|
||||
auto SolidActorManager::checkFloor(float px, float foot_y_cur, float pw, float foot_y_new) const -> FloorHit {
|
||||
FloorHit hit;
|
||||
forEachActor([&](SolidActor* a, const SDL_FRect& r) {
|
||||
// Un actor participa en el suelo si es bloqueante O si tiene CARRY_ON_TOP
|
||||
// (las plataformas jump-through tienen CARRY_ON_TOP sin BLOCKS_PLAYER).
|
||||
const bool PARTICIPATES = a->isBlocking() || a->carriesOnTop();
|
||||
if (!PARTICIPATES) { return; }
|
||||
// X-overlap
|
||||
if (px + pw <= r.x) { return; }
|
||||
if (px >= r.x + r.w) { return; }
|
||||
// Y: el top del actor debe estar en [foot_y_cur, foot_y_new]
|
||||
if (r.y < foot_y_cur) { return; } // actor por encima: no es suelo, es techo
|
||||
if (r.y > foot_y_new) { return; } // actor fuera del rango de caída
|
||||
// ONEWAY_TOP: ya garantizado por foot_y_cur <= r.y (chequeo implícito arriba)
|
||||
// Actualizar hit si es más alto (menor y) que el actual
|
||||
if (hit.y == Collision::NONE || r.y < hit.y) {
|
||||
hit.y = r.y;
|
||||
hit.carrier = a->carriesOnTop() ? a : nullptr;
|
||||
}
|
||||
});
|
||||
return hit;
|
||||
}
|
||||
|
||||
// Hay suelo "inmediato" debajo (tolerancia 1 px). Usado por checkFalling para
|
||||
// no desengancharse de plataformas móviles que bajan/suben pixel a pixel.
|
||||
auto SolidActorManager::hasGroundBelow(float px, float foot_y, float pw) const -> bool {
|
||||
bool found = false;
|
||||
forEachActor([&](const SolidActor* a, const SDL_FRect& r) {
|
||||
if (found) { return; }
|
||||
const bool PARTICIPATES = a->isBlocking() || a->carriesOnTop();
|
||||
if (!PARTICIPATES) { return; }
|
||||
// X-overlap
|
||||
if (px + pw <= r.x) { return; }
|
||||
if (px >= r.x + r.w) { return; }
|
||||
// Top del actor dentro de [foot_y, foot_y + 1]
|
||||
if (r.y >= foot_y && r.y <= foot_y + 1.0F) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
Reference in New Issue
Block a user